Android Activity的滑动销毁

       转载请注明出处: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中会出现错误。

       总之就这么多了


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值