喜欢听音乐的朋友可能都看过天天动听这款 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,布局和测量就显得非常简单。
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: