结合支付宝和微信首页巩固android事件分发机制 (附项目源码)
2016-06-06 16:19
531 查看
结合支付宝和微信首页巩固android事件分发机制
文章出处大黑的博客--事件分发
源码地址 https://github.com/halibobo/TouchListenerConflict 欢迎star
android的事件分发和处理方式
对android开发有一定了解的同学一定或多或少知道android的触摸事件分发,整个事件的分发消耗流程都可以通过看源码理解,下面通过讲解demo帮助加深事件分发的理解和在实战中的应用。首先直接上demo截图:
![](https://raw.githubusercontent.com/halibobo/BlogImage/master/blog/touch_listener/1.gif)
demo布局
整个首页布局是这样的,最外层是ViewPager,里面包含四个子功能,每个子功能的视图都是一个Fragment。“功能1”里的列表项是一个GridView,此gridview外层是带有LinearLayout的ScrollView。
布局如下
![](https://raw.githubusercontent.com/halibobo/BlogImage/master/blog/touch_listener/border.png)
难点分析和讲解
一、GridView高度问题
二、长按GridView某一项可以替换位置,最后一项“更多”不参与滑动与替换位置
三、GridView长按并滑动某一项时,滑动过程中与ScrollView和ViewPager冲突问题
四、滑动GridView中某一项时,当滑动到顶部时ScrollView要能向下滚动;手指滑动到底部时要能项上滚动
解决问题
解决问题一
为了解决这一系列问题,我需要重写DragGridView继承GridView,第一步需要让DragGridView的每一项item占满整个视图,而gridview默认是限定高度的,解决办法是重写onMeasure,修改测量高度方法,如下@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = View.MeasureSpec.makeMeasureSpec( Integer.MAX_VALUE >> 2, View.MeasureSpec.AT_MOST);//1.精确模式(MeasureSpec.EXACTLY) 2.最大模式(MeasureSpec.AT_MOST) 3.未指定模式(MeasureSpec.UNSPECIFIED) super.onMeasure(widthMeasureSpec, expandSpec); }
解决问题二
现在我们需要长按GridView某一项可以替换位置,思路是首先需要捕捉到长按事件,那么怎么算长按事件呢?手指快速的从item上划是不算的,手指按下然后很快离开这是点击事件也不能算长按。只有手指在item上按下并且停留在item上一定时间才能算长按。有了思路,下面看关键的几处代码:private Handler mHandler = new Handler(); //用来处理是否为长按的Runnable private Runnable mLongClickRunnable = new Runnable() { @Override public void run() { //todo } }; @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch(ev.getAction()){ case MotionEvent.ACTION_DOWN: //使用Handler延迟dragResponseMS执行mLongClickRunnable mHandler.postDelayed(mLongClickRunnable, dragResponseMS); break; case MotionEvent.ACTION_MOVE: //如果我们在按下的item上面移动,只要不超过item的边界我们就不移除mRunnable if(!isTouchInItem(mStartDragItemView, moveX, moveY)){ mHandler.removeCallbacks(mLongClickRunnable); mHandler.removeCallbacks(mScrollRunnable); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mHandler.removeCallbacks(mLongClickRunnable); break; } return super.dispatchTouchEvent(ev); }
解析: 定义一个Handler,重写ViewGroup(这里即DragGridView)分发事件方法dispatchTouchEvent(),在接受到ACTION DOWN事件时handler向消息队列会延迟发送一条消息,延迟时间就是长按时间的阀值,当接受到消息时说明长按事件已成立,如果手指在消息延迟期间滑走或移出则取消消息。
有了长按事件如何滑动某一条item呢?如何替换两个item位置呢?
这里的解决思路是,当其中的某项假设是item1接收到长按事件后,先隐藏item1,在item1的位置上新建一个和item1长相位置都一样view假设叫它litem,手指滑动时对litem跟随滑动,接着重点来了,当手指滑动到item2区域后发出一个替换事件。这样我们就成功将两个item替换了位置并且用户体验比较好,代码片段如下:
//用来处理是否为长按的Runnable private Runnable mLongClickRunnable = new Runnable() { @Override public void run() { if (onDragStartListener != null) { onDragStartListener.onDragStart(); } isDrag = true; //设置可以拖拽 mVibrator.vibrate(50); //震动一下 mStartDragItemView.setVisibility(View.INVISIBLE);//隐藏该item //根据我们按下的点显示item镜像 createDragImage(mDragBitmap, mDownX, mDownY); } }; /** * 设置替换回调接口 * @param onChangeListener */ public void setOnChangeListener(OnChangeListener onChangeListener){ this.onChangeListener = onChangeListener; } /** * 创建拖动的镜像 * @param bitmap * @param downX * 按下的点相对父控件的X坐标 * @param downY * 按下的点相对父控件的X坐标 */ private void createDragImage(Bitmap bitmap, int downX , int downY){ mWindowLayoutParams = new WindowManager.LayoutParams(); mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; //图片之外的其他地方透明 mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left; mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top - mStatusHeight; mWindowLayoutParams.alpha = 0.55f; //透明度 mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE ; mDragImageView = new ImageView(getContext()); mDragImageView.setImageBitmap(bitmap); mWindowManager.addView(mDragImageView, mWindowLayoutParams); } @Override public boolean onTouchEvent(MotionEvent ev) { if(isDrag && mDragImageView != null){ switch(ev.getAction()){ case MotionEvent.ACTION_MOVE: moveX = (int) ev.getX(); moveY = (int) ev.getY(); moveRawY = (int) ev.getRawY(); //拖动item onDragItem(moveX, moveY); break; case MotionEvent.ACTION_UP: onStopDrag(); isDrag = false; break; } return true; } return super.onTouchEvent(ev); }
解析:在接收到长按事件后会调用 createDragImage(mDragBitmap, mDownX, mDownY)方法,这是为了在item的位置上创建一个长相一样的view,当手指滑动时在onTouch()方法中监听move事件处理view的位置
解决问题三
为题三的解决比较简单,当gridview接收到长按事件后,处理ViewPager和ScrollView的方法一样requestDisallowInterceptTouchEvent(false);这个方法是让ViewGroup本身不拦截触摸事件。当gridView滑动结束在
requestDisallowInterceptTouchEvent(true);
解决问题四
直接上源码public class ControlScrollView extends ScrollView { private boolean isInControl = true; private int moveSpeed = 5; private final int msgWhat = 1; private final int time = 20; private ScrollState scrollState; public ControlScrollView(Context context) { super(context); init(); } public ControlScrollView(Context context, AttributeSet attrs) { super(context, attrs); init(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_MOVE:/**/ if (!isInControl) { if (ev.getY() < 0) { if (!myHandler.hasMessages(msgWhat)) { Message msg = new Message(); msg.arg1 = -1; msg.what = msgWhat; myHandler.sendMessageDelayed(msg, time); } return super.dispatchTouchEvent(ev); } else if (ev.getY() > getHeight()) { if (!myHandler.hasMessages(msgWhat)) { Message msg = new Message(); msg.arg1 = 1; msg.what = msgWhat; myHandler.sendMessageDelayed(msg, time); } return super.dispatchTouchEvent(ev); } else { myHandler.removeMessages(msgWhat); } } else { myHandler.removeMessages(msgWhat); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (scrollState != null) { scrollState.stopTouch(); } myHandler.removeMessages(msgWhat); requestDisallowInterceptTouchEvent(false); break; } return super.dispatchTouchEvent(ev); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); myHandler.removeMessages(msgWhat); } private void init() { moveSpeed = ScreenUtils.dip2px(getContext(), moveSpeed); } public boolean isInControl() { return isInControl; } public ScrollState getScrollState() { return scrollState; } public void setScrollState(ScrollState scrollState) { this.scrollState = scrollState; } public void setInControl(boolean inControl) { isInControl = inControl; } private Handler myHandler = new Handler() { @Override public void dispatchMessage(Message msg) { super.dispatchMessage(msg); smoothScrollBy(0, moveSpeed * (msg.arg1 > 0 ? 1 : -1)); Message msg1 = new Message(); msg1.what = msg.what; msg1.arg1 = msg.arg1; myHandler.sendMessageDelayed(msg1, time); } }; public interface ScrollState { void stopTouch(); }
}
解析:大致逻辑就是判断手指位置,超出边界发送消息对scrollview进行滚动
结束
源码地址 https://github.com/halibobo/TouchListenerConflict 欢迎star相关文章推荐
- 重磅 l 全国首例微信三级分销被认定为传销,三级分销“身世”揭秘(下)
- 全国首例微信三级分销被认定为传销,三级分销“身世”揭秘(上)
- 微信三级分销为什么合法(模式与传销区别)
- 微信-------通过开发者请求验证
- [置顶] Androi:ListView+GridView实现仿微信微博朋友圈无焦点冲突
- 微信客服接口
- h5做微信登录
- 微信分享缩略图不显示
- 微信开放平台开发——网页微信扫码登录(OAuth2.0)
- 小小站长成长记(3)--微信个人订阅号鸡肋之痛
- 二维码登陆
- 微信 js api[转]
- 微信公众号开发小计
- 翻译windows internals 第12章文件系统
- 调试微信支付的时候 配置
- 读者说 | 产品小白眼中微信不完美的细节
- 一个想太多,一个做太少,微博、微信面临哪些挑战?
- 一位失败的运营者说:微信公众号不是流量来源,而是成本黑洞
- 域名微信可以打开,但是分享文章朋友圈看不到
- 微信网页授权页,业务域名,js域名的区别