您的位置:首页 > 移动开发 > Android开发

Android Activity的滑动销毁

2016-04-05 20:16 441 查看
转载请注明出处:http://blog.csdn.net/mcy456/article/details/51067795

写着篇文章的目的很简单,在接盘项目的时候发现,前任使用的第三方滑动销毁出现了不少问题,于是心生了想要自己写一个的想法,毕竟出现问题时,能更快速的定位,更好的解决。

View的ScrollBy、ScrollTo

想要实现滑动销毁,首先要做的肯定是能够让对应的View跟着手指滑动,想要滑动到指定位置,自然也就少不了View的ScrollBy或是ScrollTo。

scrollTo 是直接位移到指定位置,而scrollBy则是基于当前的位置

scrollBy源码:

public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}

由此可见,scrollBy本身是基于ScrollTo的。

有意思的是从注释:

This is called in response to an internal scroll in this view (i.e., the view scrolled its own contents)

可知 View.scrollBy、View.scrollTo 滚动的范围并不是自身在其Parent中,而是,自身的内容在自身View中的滚动,如果滚动超出了View自身大小的范围,内容会消失,但View还会在原地。

至于Scroller,在本文中就不多讲了,有意了解的直接百度......

onTouch事件的分配

博客中不少讲解Android的事件分配机制的,在这里大概描述下View的子类中大致有两种View,一种是实现了ViewParent.ViewManager接口的ViewGroup,例如个中Layout,另一种则是对View本身进行扩展的子View类似TextView、ImageView等。View中与Touch相关的方法有dispatchTouchEvent、onTouchEvent,以及一个接口OnTouchListener。继承自View的ViewGroup则多了一个方法onInterceptTouchEvent。

先讲下分配流程:

事件自最顶层View开始向下进行分配,View分配事件的方法是dispatchTouchEvent,也就是说,一个事件到达View的第一个方法必将是dispatchTouchEvent,之后,若是ViewGroup的类型,会有onInterceptTouchEvent,返回为True,则代表,该ViewGroup需要消耗此次事件不再向下传递,本次Touch的所有事件(DOWN MOVE UP)都将由本VIewGroup进行处理。如果返回为false,则会一次向下分发,由其子View或者子ViewGroup进行处理,若其子View、ViewGroup中都没有进行消耗,则会返回至本ViewGroup的onTouchEvent中(注意与OnTouchListener的onTouch进行区分),若是本ViewGroup也不消耗,则事件会交给本ViewGroup的Parent进行处理。

Activity销毁思路

明白了以上几点,完全就可以开搞做滑动销毁了。

首先,如果滑动销毁,必将上一个页面是可见的,这就需要在Application主题中设置

<item name="android:windowIsTranslucent">true</item>

其次,也需要在实现的layout中进行窗口透明设置,通过Activty中的getWindow.setBackDrawable进行设置。

准备工作完成,下边就可以开始写要实现的Layout了

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.Scroller;

import com.example.angusfine.mywidget.R;

/**
* Created by AngusFine on 2016/3/22.
*/
public class SwipBackLayout extends FrameLayout implements ViewTreeObserver.OnGlobalLayoutListener{
/**l
* SildingFinishLayout布局的父布局
*/
private ViewGroup mParentView;
/**
* 滑动的最小距离
*/
private int mTouchSlop;
/**
* 按下点的X坐标
*/
private int downX;
/**
* 按下点的Y坐标
*/
private int downY;
/**
* 临时存储X坐标
*/
private int tempX;
/**
* 滑动类
*/
private Scroller mScroller;
/**
* SildingFinishLayout的宽度
*/
private int viewWidth;
/**
* 记录是否正在滑动
*/
private boolean isSilding;

private Context context;

private Object o;

/**
* 由setContentView获取的View
*/
private View mChildView;

/**
* 判断是否结束滑动
*/
private boolean isFinish;

/**
* 设置是否自动结束当前activity,默认自动结束
*/

private boolean autoFinish = true;

private float locationX = 0.1f;

private boolean canScroll = false;

private Rect rect;
/**
* 结束监听
*/
OnSwipFinshed finshed;

/**
* 滑动中监听
*/
OnSwiping swiping;

Drawable drawable;

ViewTreeObserver observer;

private int[]location = new int[2];

public SwipBackLayout(Object o){
super((Context)o);
this.o = o;
this.context = (Context) o;
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
this.setLayoutParams(params);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mScroller = new Scroller(context);
rect = new Rect();
((Activity)o).getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
ViewGroup decor = (ViewGroup) ((Activity)context).getWindow().getDecorView();
for(int i = 0;i<decor.getChildCount();i++){
if(decor.getChildAt(i)instanceof LinearLayout){
ViewGroup ll = (ViewGroup)decor.getChildAt(i);
for(int j = 0;j<ll.getChildCount();j++){
if (ll.getChildAt(j)instanceof FrameLayout){
this.mParentView = (ViewGroup) ll.getChildAt(j);
break;
}
}
}
}
observer = this.getViewTreeObserver();
observer.addOnGlobalLayoutListener(this);
}

public SwipBackLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);

}

public SwipBackLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mScroller = new Scroller(context);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
viewWidth = this.getWidth();
mParentView.setBackground(new ColorDrawable(context.getResources().getColor(R.color.transparent_bg)));
}
}

public View getmChildView() {
return mChildView;
}

/**
* 滚动出界面
*/
private void scrollRight() {
final int delta = (viewWidth + mParentView.getScrollX());
// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
mScroller.startScroll(mParentView.getScrollX(), 0, -delta + 1, 0,
Math.abs(delta));
postInvalidate();
}

/**
* 滚动到起始位置
*/
private void scrollOrigin() {
int delta = mParentView.getScrollX();
mScroller.startScroll(mParentView.getScrollX(), 0, -delta, 0, Math.abs(delta));
postInvalidate();
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = tempX = (int) event.getRawX();
downY = (int) event.getRawY();
if(event.getRawX()/(float)(rect.right)<locationX) {
canScroll = true;
}
mChildView.dispatchTouchEvent(event);
break;
case MotionEvent.ACTION_MOVE:
if(canScroll) {
int moveX = (int) event.getRawX();
int deltaX = tempX - moveX;
tempX = moveX;
if (Math.abs(moveX - downX) > mTouchSlop && Math.abs((int) event.getRawY() - downY) < mTouchSlop) {
isSilding = true;
MotionEvent cancelEvent = MotionEvent.obtain(event);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
mChildView.dispatchTouchEvent(cancelEvent);
if (swiping != null)
swiping.swiping(event, mParentView, mChildView);
} else {
if (!isSilding)
mChildView.dispatchTouchEvent(event);
}

if (moveX - downX >= 0 && isSilding) {
mParentView.scrollBy(deltaX, 0);
drawable = mParentView.getBackground();
mChildView.getLocationOnScreen(location);
drawable.setAlpha((int) ((1 - (location[0] / 1080.0f)) * 255));
return true;
}
}else{
mChildView.dispatchTouchEvent(event);
}
break;
case MotionEvent.ACTION_UP:
if(canScroll) {
if (!isSilding) {
mChildView.dispatchTouchEvent(event);
}
isSilding = false;
if (mParentView.getScrollX() <= -viewWidth / 2) {
isFinish = true;
drawable.setAlpha(0);
scrollRight();
} else {
scrollOrigin();
isFinish = false;
}
}else{
mChildView.dispatchTouchEvent(event);
}
canScroll = false;
break;
}
return true;
}

@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
mParentView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();

if (mScroller.isFinished()) {

if (isFinish) {
if (autoFinish) {
((Activity) o).finish();
}
else {
if(finshed!=null)
finshed.onfinish();
}
}
}
}
}

public void setContentView(int res){
setBackground(new ColorDrawable(Color.WHITE));
LayoutInflater inflater=(LayoutInflater)((Activity)o).getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view=inflater.inflate(res, null);
mChildView = view;
this.addView(view);
((Activity)o).setContentView(this);
}

public void setContentView(View view){
setBackground(new ColorDrawable(Color.WHITE));
mChildView = view;
this.addView(view);
((Activity)o).setContentView(view);
}

public void setAutoFinish(boolean finish){
this.autoFinish = finish;
}

@Override
public void onGlobalLayout() {
((Activity)o).getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);//获取显示信息
}

interface OnSwipFinshed{
void onfinish();
}

/**
* 设置滑动结束监听
* @param finshed
*/

public void setOnSwipFinshed(OnSwipFinshed finshed){
this.finshed = finshed;
}

interface OnSwiping{
void swiping(MotionEvent event,View parent,View child);
}

/**
* 设置滑动中监听
* @param swiping
*/

public void setOnSwiping(OnSwiping swiping){
this.swiping = swiping;
}

public void setTouchLocation(float locationX){
this.locationX = locationX;
}

}


其中Scroller滚动部分(scrollOrigin scrollRight 借鉴夏安明 http://blog.csdn.net/xiaanming/article/details/20934541 )

效果图:

效果图:


使用方法:

1 在apptheme中添加:<item name="android:windowIsTranslucent">true</item>

2 在对应的activity的onCreat()方法中:SwipBackLayout swipBackLayout = new SwipBackLayout(this); 并用swipBackLayout.setContentView(int res);替代activity的setContentView(int res)

3 提供的有一些监听

4 可以设置是否需要结束监听接口,默认是不需要

5 滑动设置的默认监听范围是边缘到屏幕的十分之一,当然也提供的有对应方法进行修改

之所以选择DecorView->LinearLayout->FrameLayout作为mParent, 是因为在AppCompatActivity中使用sentContView会被包裹在ContentFrameLayout和ActionBarOverLayout中,导致使用默认的actionbar并不能跟随手指滑动。为了滑动销毁的更彻底,也有第三方会将本Layout作为DecorView和LinearLayout的中间层,使之形成 DecorView->SwipLayout->LinearLayout,
然而这种行为在有些第三方rom中会出现错误。

总之就这么多了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: