【android开发】手势滑动关闭Activity(随手指消失)的辅助类的实现

来源:互联网 发布:定车票的软件 编辑:程序博客网 时间:2024/06/06 03:13

【CSDN抽风,把我写一个多小时的东西覆盖了。真的是哔了狗了,自己又没有备份。。。重写吧。。。】

这个类主要是实现向右滑动关闭Activity,效果如下:
手势滑动关闭Activity效果

老套路,先写思路:
1)将Activity的背景设置为透明模式。(从而可以看到下一层Activity)
2)创建一个FrameLayout,将contentView从DecorView中移除,并将contentView添加到我们的FrameLayout中,最后再将我们的FrameLayout添加回原来contentView的地方。
【熟悉android ViewTree的人应该能轻松理解contentView和DecorView。针对不熟悉的朋友,我做简单介绍:我们将Activity界面看做由ActionBar部分 + ContentView部分(onCreate中的setContentView方法设置的部分) 组成,如果我们设置为NoActionBar类型的主题时,ActionBar部分将不存在,整个Activity都由ContentView部分组成。而Activity会有个rootView来容纳他们,这个rootView就是DecorView。】示意图如下:
DecorView示意图
3)实现FrameLayout的拦截逻辑,当手势向右滑动时,拦截事件,将contentView进行水平方向移动。

分析完开动代码。


1、创建自定义ViewGroup类并申明必须要的参数,代码如下:

class MySwipeView extends FrameLayout {    private Activity activity;// 绑定的Activity    private ViewGroup decorView;    private View contentView;// activity的ContentView    private float intercept_X = 0;// onInterceptTouchEvent刚触摸时的X坐标    private float intercept_Y = 0;// onInterceptTouchEvent手指刚触摸时的y坐标    private int touchSlop = 0;// 产生滑动的最小值    public MySwipeView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        touchSlop = ViewConfiguration.get(getContext())                .getScaledTouchSlop();        setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,                LayoutParams.MATCH_PARENT));    }    public MySwipeView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public MySwipeView(Context context) {        this(context, null);    }}

2、实现Activity的绑定,并将contentView从decorView中移除,然后添加到我们的FrameLayout中,最后将我们的FrameLayout添加回DecorView。代码如下:

/** * 绑定Activity * @param activity */public void setActivity(Activity activity) {    this.activity = activity;    initCoverView();}/** * 将contentView从DecorView中移除,并添加到CoverView中,最后再将CoverView添加到DecorView中 */private void initCoverView() {    decorView = (ViewGroup) activity.getWindow().getDecorView();    // decorView.setBackgroundColor(Color.parseColor("#33000000"));    contentView = (ViewGroup) decorView            .findViewById(android.R.id.content);    ViewGroup contentParent = (ViewGroup) contentView.getParent();    contentParent.removeView(contentView);    addView(contentView);    contentView.setBackgroundColor(Color.WHITE);    contentParent.addView(this);}

3、实现事件拦截代码。说到事件拦截,熟悉android事件分发机制的朋友,应该知道。ViewGroup有个public boolean onInterceptTouchEvent(MotionEvent ev)方法,当此方法返回true时,ViewGroup将不会传递事件给他的childView,同时会调用该ViewGroup的onTouchEvent来处理该事件;反之,返回false时,事件将会传递给他的childView处理。我们通过实现onInterceptTouchEvent的逻辑来过滤我们需要的事件。代码如下,看一两遍理解没问题的:

@Overridepublic boolean onInterceptTouchEvent(android.view.MotionEvent ev) {        return shouldInterceptEvent(ev);};/** * 判断是否应该拦截事件。 * 如果水平方向的偏移量(不取绝对值) > 垂直方向的偏移量(取绝对值),并且水平方向的偏移量大于最小滑动距离,我们将拦截事件。 * 【实际过程中,我们发现touchSlope还是偏小,所以取了其3倍的数值作为最小滑动距离】 * @param event *            事件对象 * @return true表示拦截,false反之 */private boolean shouldInterceptEvent(MotionEvent event) {    boolean shouldInterceptEvent = false;    switch (event.getAction()) {    case MotionEvent.ACTION_DOWN:        intercept_X = event.getX();        intercept_Y = event.getY();        break;    case MotionEvent.ACTION_MOVE:        float offsetY = Math.abs(event.getY() - intercept_Y);        float offsetX = Math.abs(event.getX() - intercept_X);        if (offsetY >= touchSlop * 3 || offsetY > offsetX) {            shouldInterceptEvent = false;        } else if (event.getX() - intercept_X >= touchSlop * 3) {            shouldInterceptEvent = true;        } else {            shouldInterceptEvent = false;        }        break;    case MotionEvent.ACTION_UP:        shouldInterceptEvent = false;        break;    default:        break;    }    return shouldInterceptEvent;}

4、实现onTouchEvent代码:

@Overridepublic boolean onTouchEvent(android.view.MotionEvent event) {            processTouchEvent(event);            return true;};/** * 对onTouchEvent事件进行处理 * @param event *            事件对象 */private void processTouchEvent(MotionEvent event) {    switch (event.getAction()) {    case MotionEvent.ACTION_DOWN:        break;    case MotionEvent.ACTION_MOVE:    //如果是向右滑动,我们动态改变contentView的便宜值        float offsetX = event.getX() - intercept_X;        if (offsetX > 0) {            contentView.setTranslationX(offsetX);        }        break;    case MotionEvent.ACTION_UP:    //如果手释放时是在屏幕的1/3之内,我们视为用户不想关闭Activity,则弹回。反之,关闭        if (contentView.getTranslationX() >= contentView                .getMeasuredWidth() / 3) {            collapse();        } else {            open();        }        break;    default:        break;    }}/** * 展开Activity */private void open() {    contentView.clearAnimation();    ObjectAnimator anim = ObjectAnimator.ofFloat(contentView,            View.TRANSLATION_X, 0);    anim.start();}/** * 折叠Activity(finish掉) */private void collapse() {    contentView.clearAnimation();    ObjectAnimator anim = ObjectAnimator.ofFloat(contentView,            View.TRANSLATION_X, contentView.getMeasuredWidth());    anim.addListener(new AnimatorListenerAdapter() {        @Override        public void onAnimationEnd(Animator animation) {            activity.finish();        }    });    anim.start();}

5、到此,我们基本完成了FrameLayout的开发。我们最后可以将此FrameLayout放到一个类中包裹起来,做成一个工具类。(可选)。代码如下:

public class SwipeToFinishView {    private MySwipeView mySwipeView;    public SwipeToFinishView(Activity activity) {        mySwipeView = new MySwipeView(activity);        mySwipeView.setActivity(activity);    }//核心Viewprivate class MySwipeView extends FrameLayout {        .....    }}

6、将Activity设置为背景透明模式。
1)新建透明Activity的style。代码形如:

<style name="SwipTheme" parent="BaseTheme">    <item name="android:windowBackground">@android:color/transparent</item>     <item name="android:windowIsTranslucent">true</item>       <item name="android:windowAnimationStyle">        @android:style/Animation.Translucent    </item></style> 

2)应用此Style到Activity,在AndroidManifest文件中指定的Activity代码添加代码如下:

android:theme="@style/SwipTheme"

7、如何使用:
在Activity的setContentView之后,new此类即可,代码如下。

SwipeToFinishView swipeToFinishView= new SwipeToFinishView(this); 

【 使 用 前 提 】
1、该Activity的主题应该是NoActionBar(没有ActionBar)
2、该Activity使用的主题应该包含下面三个item(用于将Activity的背景透明)

<item name="android:windowBackground">@android:color/transparent </item> <item name="android:windowIsTranslucent">true </item> <item name="android:windowAnimationStyle">@android:style/Animation.Translucent </item>

3、并在style.xml中新增加一个Style应用到该Activity的主题上。新增的style代码如下:

<style name="SwipTheme" parent="BaseTheme">    <item name="android:windowBackground">@android:color/transparent</item>     <item name="android:windowIsTranslucent">true</item>       <item name="android:windowAnimationStyle" >@android:style/Animation.Translucent</item> </style> 

最后在Activity的申明中添加下面的代码:

android:theme="@style/SwipTheme"

【 总 结 】

这个类写的很简单,基本就是一个ViewGroup的拦截代码和处理代码,但是中间也需要了一些小问题。
刚开始,我是使用的ViewDragHelper来做的,但是,后来当Activity有可获取焦点类型的View时(例如使用了ListView),ViewDragHelper无法正常工作。无奈之下自己手动实现拦截代码。

老样子,源代码如下。

/** * 跟随手势向右滑动消失的View帮助类。<br/> * 使用方法:在Activity的setContentView之后,new此类即可,代码如下。<br/> * SwipeToFinishView swipeToFinishView= new SwipeToFinishView(this); * <hr/> * 使用前提:<br/> * <ol> * <li>该Activity的主题应该是NoActionBar(没有ActionBar)</li> * <li>该Activity使用的主题应该包含下面三个item(用于将Activity的背景透明) * <ul> * <li> *  * &lt;item name="android:windowBackground">@android:color/transparent * &lt;/item> *  * </li> * <li> *  * &lt;item name="android:windowIsTranslucent">true &lt;/item> *  * </li> * <li> *  * &lt;item * name="android:windowAnimationStyle">@android:style/Animation.Translucent * &lt;/item> *  * </li> *  * </ul> * </li> * </ol> * 例如:我想给SecondActivity添加滑动消失功能,我就会在onCreate中添加代码:<br/> * SwipeToFinishView swipeToFinishView= new SwipeToFinishView(this);<br/> * 并在style.xml中新增加一个Style应用到该Activity的主题上。新增的style代码如下:<br/> * <!-- SecondActivity theme. --> <br/> * &lt;style name="SwipTheme" parent="BaseTheme"><br/> * &nbsp;&nbsp;&nbsp;&nbsp;&lt;item * name="android:windowBackground">@android:color/transparent&lt;/item> <br/> * &nbsp;&nbsp;&nbsp;&nbsp;&lt;item * name="android:windowIsTranslucent">true&lt;/item>   <br/> * &nbsp;&nbsp;&nbsp;&nbsp;&lt;item name="android:windowAnimationStyle" * >@android:style/Animation.Translucent&lt;/item> &lt;/style> <br/> * 最后在Activity的申明中添加下面的代码:<br/> * android:theme="@style/SwipTheme" *  * @author 蓝亭书序 2016.11.29 *  */public class SwipeToFinishView {    private MySwipeView mySwipeView;    public SwipeToFinishView(Activity activity) {        mySwipeView = new MySwipeView(activity);        mySwipeView.setActivity(activity);    }    /**     * 核心View     *      * @author 蓝亭书序     *      */    private class MySwipeView extends FrameLayout {        private Activity activity;// 绑定的Activity        private ViewGroup decorView;        private View contentView;// activity的ContentView        private float intercept_X = 0;// onInterceptTouchEvent刚触摸时的X坐标        private float intercept_Y = 0;// onInterceptTouchEvent手指刚触摸时的y坐标        private int touchSlop = 0;// 产生滑动的最小值        public MySwipeView(Context context, AttributeSet attrs, int defStyleAttr) {            super(context, attrs, defStyleAttr);            touchSlop = ViewConfiguration.get(getContext())                    .getScaledTouchSlop();            setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,                    LayoutParams.MATCH_PARENT));        }        public MySwipeView(Context context, AttributeSet attrs) {            this(context, attrs, 0);        }        public MySwipeView(Context context) {            this(context, null);        }        @Override        public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {            return shouldInterceptEvent(ev);        };        @SuppressLint("ClickableViewAccessibility")        public boolean onTouchEvent(android.view.MotionEvent event) {            processTouchEvent(event);            return true;        };        /**         * 绑定Activity         *          * @param activity         */        public void setActivity(Activity activity) {            this.activity = activity;            initCoverView();        }        /**         * 将contentView从DecorView中移除,并添加到CoverView中,最后再将CoverView添加到DecorView中         */        private void initCoverView() {            decorView = (ViewGroup) activity.getWindow().getDecorView();            // decorView.setBackgroundColor(Color.parseColor("#33000000"));            contentView = (ViewGroup) decorView                    .findViewById(android.R.id.content);            ViewGroup contentParent = (ViewGroup) contentView.getParent();            contentParent.removeView(contentView);            addView(contentView);            contentView.setBackgroundColor(Color.WHITE);            contentParent.addView(this);        }        /**         * 判断是否应该拦截事件         *          * @param event         *            事件对象         * @return true表示拦截,false反之         */        private boolean shouldInterceptEvent(MotionEvent event) {            boolean shouldInterceptEvent = false;            switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                intercept_X = event.getX();                intercept_Y = event.getY();                break;            case MotionEvent.ACTION_MOVE:                float offsetY = Math.abs(event.getY() - intercept_Y);                float offsetX = Math.abs(event.getX() - intercept_X);                if (offsetY >= touchSlop * 3 || offsetY > offsetX) {                    shouldInterceptEvent = false;                } else if (event.getX() - intercept_X >= touchSlop * 3) {                    shouldInterceptEvent = true;                } else {                    shouldInterceptEvent = false;                }                break;            case MotionEvent.ACTION_UP:                shouldInterceptEvent = false;                break;            default:                break;            }            return shouldInterceptEvent;        }        /**         * 对onTouchEvent事件进行处理         *          * @param event         *            事件对象         */        private void processTouchEvent(MotionEvent event) {            switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                break;            case MotionEvent.ACTION_MOVE:                float offsetX = event.getX() - intercept_X;                if (offsetX > 0) {                    contentView.setTranslationX(offsetX);                }                break;            case MotionEvent.ACTION_UP:                if (contentView.getTranslationX() >= contentView                        .getMeasuredWidth() / 3) {                    collapse();                } else {                    open();                }                break;            default:                break;            }        }        /**         * 展开Activity         */        private void open() {            contentView.clearAnimation();            ObjectAnimator anim = ObjectAnimator.ofFloat(contentView,                    View.TRANSLATION_X, 0);            anim.start();        }        /**         * 折叠Activity(finish掉)         */        private void collapse() {            contentView.clearAnimation();            ObjectAnimator anim = ObjectAnimator.ofFloat(contentView,                    View.TRANSLATION_X, contentView.getMeasuredWidth());            anim.addListener(new AnimatorListenerAdapter() {                @Override                public void onAnimationEnd(Animator animation) {                    activity.finish();                }            });            anim.start();        }    }}
1 0
原创粉丝点击