• 手势滑动结束 Activity(一)基本功能的实现


    喜欢听音乐的朋友可能都看过天天动听这款 app, 这款 app 有一个亮点就是在切换页面(Fragment)的时候能够通过手势滑动来结束当前页面。这里先说一下,我为什么会这么关心这个功能呢,由于前两天 PM说我们即将開始做的这款app 也要实现页面能通过手势滑动来结束的功能,所以我就拿着这款 app 滑了一上午;可是我要实现的跟天天动听这款 app又有点不同,细心观察的朋友可能会发现。天天动听是 Fragment 之间的切换,而我这里要实现的是 Activity 之间的切换,只是,无论是哪种,终于效果都是一样,就是页面能随着手势的滑动而滑动。终于达到某个特定条件,结束此页面。


    要实现这个功能事实上也不是特别难。这里我把这个功能的实现分为了下面两个步骤:
    1、识别手势滑动自己定义ViewGroup 的实现
    2、实现自己定义 ViewGroup 和 Activity 绑定

    依据以上两个步骤,我们发现,这当中涉及到的知识点有:Android 事件处理机制、自己定义 View(ViewGroup)的实现,Activity Window的知识,在开发的过程中还涉及到Activity 主题的配置。

    Android 事件处理和自己定义 View 都在我前面的 blog 中有讲到,假设不了解的朋友能够去看看。下面開始按步骤来实现功能

    一、自己定义 ViewGroup
    这个 ViewGroup 的功能仅仅要是对事件的拦截,能够实现手势滑动效果;显示 Activity 的内容包含 ActionBar 和内容区。
    1、实现測量和布局

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            /*获取默认的宽度*/
            int width = getDefaultSize(0, widthMeasureSpec);
            /*获取默认的高度*/
            int height = getDefaultSize(0, heightMeasureSpec);
            /*设置ViewGroup 的宽高*/
            setMeasuredDimension(width, height);
            /*获取子 View 的宽度*/
            final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width);
            /*获取子View 的高度*/
            final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);
            /*设置子View 的大小*/
            mContent.measure(contentWidth, contentHeight);
        }
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            final int width = r - l;
            final int height = b - t;
            mContent.layout(0, 0, width, height);
        }

    由于每一个 Activity 都仅仅有一个 Layout,所以这里仅仅有一个子 View,布局和測量就显得非常easy。

    2、事件拦截

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            if (!isEnable) {
                return false;
            }
            final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
    
            if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP
                    || action != MotionEvent.ACTION_DOWN && mIsUnableToDrag) {
                /*结束手势的滑动,不拦截*/
                endToDrag();
                return false;
            }
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    /*计算 x。y 的距离*/
                    int index = MotionEventCompat.getActionIndex(ev);
                    mActivePointerId = MotionEventCompat.getPointerId(ev, index);
                    if (mActivePointerId == INVALID_POINTER)
                        break;
                    mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);
                    mLastMotionY = MotionEventCompat.getY(ev, index);
                    /*这里判读。假设这个触摸区域是同意滑动拦截的,则拦截事件*/
                    if (thisTouchAllowed(ev)) {
                        mIsBeingDragged = false;
                        mIsUnableToDrag = false;
                    } else {
                        mIsUnableToDrag = true;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    /*继续推断是否须要拦截*/
                    determineDrag(ev);
                    break;
                case MotionEvent.ACTION_UP:
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    /*这里做了对多点触摸的处理,当有多个手指触摸的时候依旧能正确的滑动*/
                    onSecondaryPointerUp(ev);
                    break;
    
            }
            if (!mIsBeingDragged) {
                if (mVelocityTracker == null) {
                    mVelocityTracker = VelocityTracker.obtain();
                }
                mVelocityTracker.addMovement(ev);
            }
            return mIsBeingDragged;
        }

    事件拦截,是拦截而是其不会向子 View 分发。直接运行本级 View的 onTouchEvent方法;

    3、事件处理

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (!isEnable) {
                return false;
            }
            if (!mIsBeingDragged && !thisTouchAllowed(event))
                return false;
            final int action = event.getAction();
    
            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(event);
    
            switch (action & MotionEventCompat.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    /*按下则结束滚动*/
                    completeScroll();
                    int index = MotionEventCompat.getActionIndex(event);
                    mActivePointerId = MotionEventCompat.getPointerId(event, index);
                    mLastMotionX = mInitialMotionX = event.getX();
                    break;
                case MotionEventCompat.ACTION_POINTER_DOWN: {
                    /*有多个点按下的时候,取最后一个按下的点为有效点*/
                    final int indexx = MotionEventCompat.getActionIndex(event);
                    mLastMotionX = MotionEventCompat.getX(event, indexx);
                    mActivePointerId = MotionEventCompat.getPointerId(event, indexx);
                    break;
    
                }
                case MotionEvent.ACTION_MOVE:
                    if (!mIsBeingDragged) {
                        determineDrag(event);
                        if (mIsUnableToDrag)
                            return false;
                    }
                    /*假设已经是滑动状态,则依据手势滑动。而改变View 的位置*/
                    if (mIsBeingDragged) {
                        // 下面代码用来推断和运行View 的滑动
                        final int activePointerIndex = getPointerIndex(event, mActivePointerId);
                        if (mActivePointerId == INVALID_POINTER)
                            break;
                        final float x = MotionEventCompat.getX(event, activePointerIndex);
                        final float deltaX = mLastMotionX - x;
                        mLastMotionX = x;
                        float oldScrollX = getScrollX();
                        float scrollX = oldScrollX + deltaX;
                        final float leftBound = getLeftBound();
                        final float rightBound = getRightBound();
                        if (scrollX < leftBound) {
                            scrollX = leftBound;
                        } else if (scrollX > rightBound) {
                            scrollX = rightBound;
                        }
    
                        mLastMotionX += scrollX - (int) scrollX;
                        scrollTo((int) scrollX, getScrollY());
    
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    /*假设已经是滑动状态,抬起手指,须要推断滚动的位置*/
                    if (mIsBeingDragged) {
                        final VelocityTracker velocityTracker = mVelocityTracker;
                        velocityTracker.computeCurrentVelocity(1000, mMaxMunVelocity);
                        int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
                                velocityTracker, mActivePointerId);
                        final int scrollX = getScrollX();
                        final float pageOffset = (float) (-scrollX) / getContentWidth();
                        final int activePointerIndex = getPointerIndex(event, mActivePointerId);
                        if (mActivePointerId != INVALID_POINTER) {
                            final float x = MotionEventCompat.getX(event, activePointerIndex);
                            final int totalDelta = (int) (x - mInitialMotionX);
                            /*这里推断是否滚动到下一页。还是滚回原位置*/
                            int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);
                            setCurrentItemInternal(nextPage, true, initialVelocity);
                        } else {
                            setCurrentItemInternal(mCurItem, true, initialVelocity);
                        }
                        mActivePointerId = INVALID_POINTER;
                        endToDrag();
                    } else {
    //                    setCurrentItemInternal(0, true, 0);
                        endToDrag();
                    }
                    break;
                case MotionEventCompat.ACTION_POINTER_UP:
                    /*这里有事多点处理*/
                    onSecondaryPointerUp(event);
                    int pointerIndex = getPointerIndex(event, mActivePointerId);
                    if (mActivePointerId == INVALID_POINTER)
                        break;
                    mLastMotionX = MotionEventCompat.getX(event, pointerIndex);
                    break;
            }
    
            return true;
        }

    由于这里增加了多点控制,所以代码看起来有点复杂。事实上原理非常easy。就是不断的推断是否符合滑动的条件。其它就不细讲了。来看看这个自己定义 ViewGroup 的效果
    这里写图片描写叙述

    能够看到。这里我们已经实现了手势识别的 ViewGroup,事实上这个ViewGroup假设发挥想象,它能实现非常多效果。不单单是我今天要讲的效果,还能够用作側拉菜单。或者是做 QQ5.0版本号側滑效果都能够实现的。

    二、側滑 View绑定 Activity
    这里为了代码的简洁。还是通过一个 ViewGroup 来封装了一层。

    /**
     * Created by moon.zhong on 2015/3/13.
     */
    public class SlidingLayout extends FrameLayout {
        /*側滑View*/
        private SlidingView mSlidingView ;
        /*须要側滑结束的Activity*/
        private Activity mActivity ;
    
        public SlidingLayout(Context context) {
            this(context, null);
        }
    
        public SlidingLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public SlidingLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mSlidingView = new SlidingView(context) ;
            addView(mSlidingView);
            mSlidingView.setOnPageChangeListener(new SlidingView.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                    if (position == 1){                    Log.v("zgy","========position=========") ;
                        mActivity.finish();
                    }
                }
                @Override
                public void onPageSelected(int position) {
                }
            });
            mActivity = (Activity) context;
            bindActivity(mActivity) ;
        }
    
        /**
         * 側滑View 和Activity 绑定
         * @param activity
         */
        private void bindActivity(Activity activity){
            /*获取Activity 的最顶级ViewGroup*/
            ViewGroup root = (ViewGroup) activity.getWindow().getDecorView();
            /*获取Activity 显示内容区域的ViewGroup。包行ActionBar*/
            ViewGroup child = (ViewGroup) root.getChildAt(0);
            root.removeView(child);
            mSlidingView.setContent(child);
            root.addView(this);
        }
    }

    測试 Activity 这事就变的非常easy了

    public class SecondActivity extends ActionBarActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            /*绑定Activity*/
            new SlidingLayout(this) ;
        }
    
    }

    来看看效果怎么样:
    这里写图片描写叙述
    咦!能滑动结束页面,但为什么边滑走的同一时候看不到第一个 Acitivity,而是要等结束了才干看到呢?我们推測,应该是滑动的时候,这个 Activity 还有哪里把第一个 Activity 覆盖了,每一个 Activity 都是附在一个 Window 上面,所以这里就涉及到一个 Activity 的 window背景颜色问题, OK,把第二个 Activity 的 window 背景设为透明

        <style name="TranslucentTheme" parent="AppTheme">
            <item name="android:windowIsTranslucent">true</item>
            <item name="android:windowBackground">@android:color/transparent</item>
            <item name="android:windowContentOverlay">@null</item>
        </style>
            <activity android:name=".SecondActivity"
                      android:label="SecondActivity"
                      android:screenOrientation="portrait"
                      android:theme="@style/TranslucentTheme"
                />

    再来看看效果,效果图:
    这里写图片描写叙述

    完美实现!


    好了,今天就到这里,下期文章就是对这个功能的进一步优化和改善,假设感兴趣,能够继续关注我。

  • 相关阅读:
    编写第一个MapReduce程序—— 统计气温
    Oracle常用操作——创建表空间、临时表空间、创建表分区、创建索引、锁表处理
    Linux环境安装Eclipse及配置hadoop插件
    Hadoop的核心组件和生态圈
    安装hadoop集群服务器(hadoop1.2.1)
    python入门到精通[三]:基础学习(2)
    python入门到精通[二]:基础学习(1)
    com.mysql.jdbc.Driver 与 org.gjt.mm.mysql.Driver的区别
    Intellij IDEA中修改Maven项目的项目名称
    对CountDownLatch的初步学习
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5190293.html
Copyright © 2020-2023  润新知