之前做过《Android 滑动侧边栏(Sliding Menu)实现分析》,今天尝试选择一种解决方案来实现效果。
这一周工作都比较忙,今天先实现了布局+随手指移动,但是发现选择的方案有问题,先来看个效果图后面再介绍解决方案和不足之处。
一、效果图
二、选择的解决方案
方案 | 具体使用 | 可参考之前Demo |
布局 | ViewGroup(左测绿色视图和右侧蓝色视图分别是其子View) | 《自定义ViewGrup》 |
随手指移动 | View.mScrollX,scrollTo() ,scrollBy() | 《scrollBy使用》 |
1. 布局使用ViewGroup没有问题,从上图中就可以看到绿和蓝两个视图可以叠加在一起,也可以错开。
2. 但是使用mScrollX不行,虽然上图中看到了达到滚动的效果,但是效果实现其实很雷人,并不符合Android框架的设计。
选择使用mScrollX的目的是因为可以提高效率,因为其仅重新调用onDraw不会调用onMeasure,onLayout方法。《具体分析》
既然有些限制,不用着急换其他解决方案,毕竟写这些效果也是学习的目的,多尝试下总没有坏处,基于这种目的之后来看看如何绕过这个问题达到想要的滚动效果。
三、为什么Sliding Menu不能使用mScrollX来处理滚动?
1. mScrollX,mScrollY是用来控制父视图中的子视图滚动,这样随来也没什么问题啊?反正是控制滚动。但是这种滚动却不是想要的效果,如果想让视图滚动并需在其父视图调用scrollTo或scrollBy方法 ,但是这样会导致绿色子视图和蓝色子视图一起滚动。具体解释在《Android View.scrollTo, View.scrollBy控制视图滚动原理》
public SlidingMenu(Context context) { super(context); initSlidingMenu(context); } private void initSlidingMenu(Context context) { // 为了使用mScrollX滚动,并且仅滚动右侧 // 只能把右侧视图单独放入一个独立的ViewGruop中 mRightViewGroup = new LinearLayout(context); mRightViewGroup.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); addView(mRightViewGroup); } /** * 提供右侧显示视图 * * @param rightView */ public void addRightView(View rightView) { // 此ViewGroup是包裹在右侧蓝色视图外的ViewGroup mRightViewGroup.addView(rightView); // 设置Touch事件 mRightView.setOnTouchListener(new RightViewTouchEvent()); } /** * 右侧视图可以随手指移动 */ private class RightViewTouchEvent implements OnTouchListener { private int mMotionX; @Override public boolean onTouch(View view, MotionEvent event) { final int x = (int) event.getX(); int deltaX; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mMotionX = x; Log.e("Test", "ACTION_DOWN"); return true; case MotionEvent.ACTION_MOVE: deltaX = x - mMotionX; mRightViewGroup.scrollBy(-deltaX, 0); Log.e("Test", "ACTION_MOVE " + " , mMotionX = " + mMotionX + " , x = " + x + " , deltaX = " + deltaX + " , mX = " + mRightViewGroup.getScrollX()); return true; case MotionEvent.ACTION_UP: mMotionX = 0; Log.e("Test", "ACTION_UP"); break; } return false; } }
从上面的代码可以看出,既然scrollTo有上面提到的要求,但是可以通过把需要滚动的蓝色视图单独放入一个ViewGroup,这样可以使蓝色视图达到滚动效果。
在上面的代码中可以看到onTouch事件中添加了LOG,也是通过LOG才发现一个问题。
四、touch事件问题
这个问题也是由mScrollX引起的,因为解决上面三的问题,所以把蓝色视图放入一个独立的ViewgGroup,如果想让其滚动,需要滚动需要覆写此独立ViewGroup的onTouchEvent,这样做本身是符合逻辑的,但是从打印的LOG中发现出现问题。
ACTION_DOWN ACTION_MOVE , mMotionX = 66 , x = 66 , deltaX = 0 , mX = 1 ACTION_MOVE , mMotionX = 66 , x = 66 , deltaX = 0 , mX = 1 ACTION_MOVE , mMotionX = 66 , x = 73 , deltaX = 7 , mX = -6 ACTION_MOVE , mMotionX = 66 , x = 69 , deltaX = 3 , mX = -9 ACTION_MOVE , mMotionX = 66 , x = 68 , deltaX = 2 , mX = -11 ACTION_MOVE , mMotionX = 66 , x = 70 , deltaX = 4 , mX = -15 ACTION_MOVE , mMotionX = 66 , x = 69 , deltaX = 3 , mX = -18 ACTION_MOVE , mMotionX = 66 , x = 68 , deltaX = 2 , mX = -20 ACTION_MOVE , mMotionX = 66 , x = 69 , deltaX = 3 , mX = -23 ACTION_MOVE , mMotionX = 66 , x = 68 , deltaX = 2 , mX = -25 ACTION_MOVE , mMotionX = 66 , x = 67 , deltaX = 1 , mX = -26 ACTION_UP
图上效果正常是因为错误的使用,计算的deltaY是当前点与Action_Down按下时点的差值,而scrollBy的参数是相对上次移动的值。
为什么即使手指在蓝色区域内向右滑动,其获取的event.getX()的值还是基本没什么变化呢? 原因是点击在蓝色区域内,getX()获取的是当前手指与蓝色区域左侧的距离,因为蓝色区域所手指移动,所以获取到的值也没什么变化。《Android Touch事件rawX,rawY与x,y的区别》
当然这个问题也可以通过获取getRawX来解决,但是感觉没必要再接着处理因使用mScrollX引出的问题,因为其在View当中已经说明,是针对父视图中内容滚动支持提供的参数。
五、源码
源码下载
为什么这个解决方案不是想要的效果还要写出来,并且提供源码下载? 感觉这是一个尝试的过程,尝试有可能直接成功也可能失败,但是我也没感觉这是失败,毕竟是学习的从这个解决方案中加深了对mScrollX进行滚动的了解。
原文地址: http://blog.csdn.net/love_world_/article/details/8654885