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

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

2017-03-07 13:32 399 查看
这篇博客记录一下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的效果类似于下图:



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:

<?xml version="1.0" encoding="utf-8"?>
<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<Integer, Fragment> 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();
}
});
}
}


按照上述代码,就可以更简洁的实现滑动效果。

如下图所示,仍然可以有效滑动:



此外,还可以跟随手指取消滑动切换。



demo下载地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: