Android界面滑动切换:MotionEvent、GestureListener及ViewPager

这篇博客记录一下Android界面滑动切换的几种方式。


1、监听MotionEvent

通过监听MotionEvent来进行滑动切换的原理,实际上是比较MotionEvent初始的坐标及移动后的坐标,
来判断用户是否进行了滑动的操作。

我们看看对应实现中最核心的代码:

public class FirstActivity extends AppCompatActivity {
    ..............
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        ...........
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                //每次手指落下时,均记录初始横坐标
                mOrigin = event.getX();
                break;

            case MotionEvent.ACTION_MOVE:
                //手指移动时,记录移动后的横坐标
                float move = event.getX();
                .................
                //当移动距离大于门限后,就可以跳转了
                //这里实现左滑切换,因此要求orgin的横坐标 > move的横坐标
                if ((mOrigin - move > MIN_MOVE_INSTANCE)
                        //此外,还可以判断移动速度是否大于门限
                        //由于手指移动时,会多次触发ACTION_MOVE,因此引入一个标志位,避免多次启动
                        && (speed > SPEED_MIN) && !mAlreadyJump){
                    Log.d("ZJTest", "go to second activity");
                    startActivity(new Intent(this, SecondActivity.class));

                    .....................
                    mAlreadyJump = true;
                }
                break;

            case MotionEvent.ACTION_UP:
                ..................
                //手指离开屏幕后,重置mAlreadyJump
                mAlreadyJump = false;
                break;

            default:
                return super.onTouchEvent(event);
        }

        return true;
    }
}

如果需要计算滑动速度,可以使用VelocityTracker。
其使用方法类似于:

...................
@Override
public boolean onTouchEvent(MotionEvent event) {
    //让VelocityTracker监控event
    addEventToVelocityTracker(event);

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        ..........
        case MotionEvent.ACTION_MOVE:
            ......
            /获取event的速度
            float speed = getScrollVelocity();
            .....
            break;
        case MotionEvent.ACTION_UP:
            //回收Velocity
            recycleVelocityTracker();
            .....
            break;
        .........
    }
    return true;
}

private VelocityTracker mVelocityTracker;

private void addEventToVelocityTracker(MotionEvent event) {
    if (mVelocityTracker == null) {
        mVelocityTracker = VelocityTracker.obtain();
    }
    //利用addMovement监控event
    mVelocityTracker.addMovement(event);
}

private void recycleVelocityTracker() {
    //利用recycle接口回收VelocityTracker
    mVelocityTracker.recycle();
    mVelocityTracker = null;
}

private float getScrollVelocity() {
    //利用computeCurrentVelocity计算速度,参数为单位时间
    mVelocityTracker.computeCurrentVelocity(1000);

    //getXVelocity用于获取水平方向的速度
    return Math.abs(mVelocityTracker.getXVelocity());
}
.............

2、使用GestureDetector

除了直接监听MotionEvent外,还可以利用GestureDetector完成类似的功能。
示例代码如下:

public class SecondActivity extends AppCompatActivity {
    //这里使用的是兼容库中的GestureDetector
    GestureDetectorCompat mGestureDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        //与实现OnGestureListener的本地类配合使用
        mGestureDetector = new GestureDetectorCompat(
                this, new LocalGestureListener());
    }

    /**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     *
     * @param ev The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //如上面源代码的注释,此处拦截MotionEvent给GestureDetector处理
        mGestureDetector.onTouchEvent(ev);

        return super.dispatchTouchEvent(ev);
    }
    .............
    private class LocalGestureListener implements GestureDetector.OnGestureListener {
        @Override
        public boolean onDown(MotionEvent e) {
            return false;
        }

        @Override
        public void onShowPress(MotionEvent e) {
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            return false;
        }

        @Override
        public void onLongPress(MotionEvent e) {
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            //同样利用横坐标和移动速度,判断是否需要切换Activity、
            //这里实现的是,右滑切换,因此要求后一个点的横坐标大于前一个点
            float move = e2.getX() - e1.getX();

            if (move > MIN_MOVE_INSTANCE && velocityX > SPEED_MIN) {
                Log.d("ZJTest", "go to the first activity");

                //由于FirstActivity启动的SecondActivity,因此SecondActivity finish后就回到了前一个Activity
                finish();
                ................
            }

            return true;
        }
    }
}

为了整个切换工程更加平滑,可以在startActivity和finish后,利用overridePendingTransition接口实现一些动画效果。
上述demo的效果类似于下图:
Android界面滑动切换:MotionEvent、GestureListener及ViewPager_第1张图片

demo代码下载地址

3、使用ViewPager

上述监听MotionEvent或者使用GestureListener均有些重复造轮子的感觉,
而且滑动效果不够强大。

例如,一旦滑动后,就会直接切换到另一个Activity;
不能做到:在切换的过程中展示下一个Activity,同时在切换过程中取消本次切换。

此时,就轮到ViewPager上场了。当利用ViewPager时,最好与Fragment组合使用。

我们将上面的FirstActivity和SecondActivity均转变为Fragment,如下:

public class FirstFragment extends Fragment {
    public static FirstFragment newInstance() {
        return new FirstFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater,
            ViewGroup container, Bundle savedInstanceState ) {
        return inflater.inflate(R.layout.fragment_first, container, false);
    }
}
public class SecondFragment extends Fragment {
    public static SecondFragment newInstance() {
        return new SecondFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState ) {
        return inflater.inflate(R.layout.fragment_second, container, false);
    }
}

然后,引入support-v4包后,利用Activity管理这两个Fragment:

Activity的布局中直接使用ViewPager:


<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/view_pager"
    android:layout_height="match_parent"
    android:layout_width="match_parent"/>

其代码如下:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //定义数据
        final Map data = new TreeMap<>();
        data.put(0, FirstFragment.newInstance());
        data.put(1, SecondFragment.newInstance());

        //找到ViewPager
        ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);

        //为ViewPager配置Adapter
        viewPager.setAdapter(new FragmentStatePagerAdapter(
                getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return data.get(position);
            }

            @Override
            public int getCount() {
                return data.size();
            }
        });
    }
}

按照上述代码,就可以更简洁的实现滑动效果。
如下图所示,仍然可以有效滑动:
Android界面滑动切换:MotionEvent、GestureListener及ViewPager_第2张图片

此外,还可以跟随手指取消滑动切换。
Android界面滑动切换:MotionEvent、GestureListener及ViewPager_第3张图片

demo下载地址

你可能感兴趣的:(Android开发)