转载请标明出处:http://blog.csdn.net/android_ls/article/details/8758943

上一篇在Android仿人人客户端(v5.7.1)——应用主界面之滑动效果(一)中,滑动式菜单的初步效果已实现,这篇继续完善和优化。打个比方:就拿盖房子来说,上一篇完成的只是毛胚房,这篇要做的就是对毛胚房进行精美装修,之后才可以使用。好了,进入正题,假设ViewGroup中有两个子View A和B,B处于A上面,两个子View是叠在一起的。默认显示的是B,并占据着整个手机屏幕,A是看不见的。为了能看见A,并且可以操作,我们需要把B视图(子View)移动一定单位,当A、B均可见并都可以接受事件。此时B视图可见部分就是手柄的宽度。

存在的问题:

1、当长按B视图的可见部分(手柄),会发现会触发A视图的事件,这显然是不行的。用户明明在B视图上操作,响应的却是A视图中的子View。

2、当在B视图的可见部分(手柄)上,用手指向右滑动时,发现B是还可以向右滑动。

3、当B视图占据着整个手机屏幕时,在B视图的任何区域水平滑动都可以让B向右滑动,也就说触控区域太大,会引起误操作。

4、手指水平滑动,响应很迟钝,灵敏度问题。

解决办法:

添加触摸事件分发处理回调方法dispatchTouchEvent(MotionEvent ev),其返回值决定事件的分发给谁处理。当用户手指在屏幕上按下后,阅读下面代码片段:

       case MotionEvent.ACTION_DOWN:Log.i(TAG, "dispatchTouchEvent():  ACTION_DOWN");mFinished = mScroller.isFinished();if(mFinished){int x = (int) ev.getX();int width = getWidth();if(mPanelInvisible)// 左侧面板可见{if(x > (width - mHandlebarWidth)){ // 当前手指按下的坐标x值 ,是在手柄宽度范围内isClick = true;mAllowScroll = true;return true;} else {isClick = false;mAllowScroll = false;}} else { // 左侧面板不可见if(x < mHandlebarWidth ){ // 当前手指按下的坐标x值 < 手柄宽度 (也就是说在手柄宽度范围内,是可以相应用户的向右滑动手势)mAllowScroll = true;}else{mAllowScroll = false;}}} else {// 当前正在滚动子View,其它的事不响应return false;}break;

当前手指按下的坐标x值 ,是在手柄宽度范围内,改变标识值,返回true。表示onInterceptTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent event)中的MotionEvent.ACTION_DOWN事件不再处理。

上一个Action down事件处理完后,接下来响应dispatchTouchEvent(MotionEvent ev)的MotionEvent.ACTION_MOVE,代码如下:

  case MotionEvent.ACTION_MOVE:Log.i(TAG, "dispatchTouchEvent():  ACTION_MOVE");int margin = getWidth() - (int) ev.getX();if (margin < mHandlebarWidth && mAllowScroll) {Log.e(TAG, "dispatchTouchEvent ACTION_MOVE margin = " + margin + "\t mHandlebarWidth = " + mHandlebarWidth);return true;}break;

在MotionEvent.ACTION_MOVE代码块中,发现条件是满足的,返回true。表示和MotionEvent.ACTION_DOWN的一样,在后续的回调方法内Action move将不再处理。最后看当手指抬起,处理的代码如下:

  case MotionEvent.ACTION_UP:Log.i(TAG, "dispatchTouchEvent():  ACTION_UP");if (isClick && mPanelInvisible && mAllowScroll) {isClick = false;mPanelInvisible = false;int scrollX = getChildAt(1).getScrollX();mScroller.startScroll(scrollX, 0, -scrollX, 0, ANIMATION_DURATION_TIME);invalidate();return true;}break;

上面的代码片段含义:当用户手指在屏幕上按下,当前ViewGroup中没有子View正在滚动,左侧面板处于可见,手指按下的坐标X值是在手柄(B视图的当前可见部分)的宽度范围内,则down事件到此处理结束。在move代码块里同样判断用户当前手指按下的坐标X值是在手柄宽度范围内的,则move事件也到此结束。在手指抬起后,up事件里判断在down是事件里设置的标识,发现条件都满足,响应用户在手柄上的单击事件请求。也就是说父View把用户的请求已响应了,不用传递到子View。子View也不知道发生过这件事。(大概意思就是这样)

触控区域太大的问题,每次都判断用户手指按下的坐标x值,是否在手柄宽度范围内(不管左侧面板是否可见)。灵敏度的问题,其实就是响应滚动子View的临界值的大小问题,值越小灵敏都越高。

ScrollerContainer类,修改后的代码如下:

package com.everyone.android.widget;import android.content.Context;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.widget.RelativeLayout;
import android.widget.Scroller;/*** 功能描述:手指在屏幕上左右滑动时,该类的实例负责让其子View根据用户的手势左右偏移(滚动)* * @author android_ls*/
public class ScrollerContainer extends RelativeLayout {private static final String TAG = "ScrollerContainer";private Scroller mScroller;private VelocityTracker mVelocityTracker;/*** 手柄(手把)的宽度*/private int mHandlebarWidth;/*** 在偏移过程中,动画持续的时间*/private static final int ANIMATION_DURATION_TIME = 300;/*** 记录当前的滑动结束后的状态,左侧面板是否可见* true  向右滑动(左侧面板处于可见)* false 向左滑动(左侧面板处于不可见)*/private boolean mPanelInvisible;/*** 是否已滑动结束*/private boolean mFinished;/*** 是否允许滚动* 满足的条件:*     左侧面板可见,当前手指按下的坐标x值 ,是在手柄宽度范围内;*     左侧面板不可见,当前手指按下的坐标x值 < 手柄宽度*/private boolean mAllowScroll;/*** 是否满足响应单击事件的条件* 满足的条件:左侧面板可见,当前手指按下的坐标x值 ,是在手柄宽度范围内*/private boolean isClick;public ScrollerContainer(Context context) {super(context);mScroller = new Scroller(context);mHandlebarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 70, getResources().getDisplayMetrics());}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.e(TAG, "dispatchTouchEvent()");switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:Log.i(TAG, "dispatchTouchEvent():  ACTION_DOWN");mFinished = mScroller.isFinished();if(mFinished){int x = (int) ev.getX();int width = getWidth();if(mPanelInvisible)// 左侧面板可见{if(x > (width - mHandlebarWidth)){ // 当前手指按下的坐标x值 ,是在手柄宽度范围内isClick = true;mAllowScroll = true;return true;} else {isClick = false;mAllowScroll = false;}} else { // 左侧面板不可见if(x < mHandlebarWidth ){ // 当前手指按下的坐标x值 < 手柄宽度 (也就是说在手柄宽度范围内,是可以相应用户的向右滑动手势)mAllowScroll = true;}else{mAllowScroll = false;}}} else {// 当前正在滚动子View,其它的事不响应return false;}break;case MotionEvent.ACTION_MOVE:Log.i(TAG, "dispatchTouchEvent():  ACTION_MOVE");int margin = getWidth() - (int) ev.getX();if (margin < mHandlebarWidth && mAllowScroll) {Log.e(TAG, "dispatchTouchEvent ACTION_MOVE margin = " + margin + "\t mHandlebarWidth = " + mHandlebarWidth);return true;}break;case MotionEvent.ACTION_UP:Log.i(TAG, "dispatchTouchEvent():  ACTION_UP");if (isClick && mPanelInvisible && mAllowScroll) {isClick = false;mPanelInvisible = false;int scrollX = getChildAt(1).getScrollX();mScroller.startScroll(scrollX, 0, -scrollX, 0, ANIMATION_DURATION_TIME);invalidate();return true;}break;case MotionEvent.ACTION_CANCEL:Log.i(TAG, "dispatchTouchEvent():  ACTION_CANCEL");break;default:break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.e(TAG, "onInterceptTouchEvent()");switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:Log.i(TAG, "onInterceptTouchEvent():  ACTION_DOWN");mFinished = mScroller.isFinished();if(!mFinished){return false;}break;case MotionEvent.ACTION_MOVE:Log.i(TAG, "onInterceptTouchEvent():  ACTION_MOVE");mVelocityTracker = VelocityTracker.obtain();mVelocityTracker.addMovement(ev);// 一秒时间内移动了多少个像素mVelocityTracker.computeCurrentVelocity(1000, ViewConfiguration.getMaximumFlingVelocity());float velocityValue = Math.abs(mVelocityTracker.getXVelocity()) ;Log.d(TAG, "onInterceptTouchEvent():  mVelocityValue = " + velocityValue);if (velocityValue > 300 && mAllowScroll) {return true;}break;case MotionEvent.ACTION_UP:Log.i(TAG, "onInterceptTouchEvent():  ACTION_UP");if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}break;case MotionEvent.ACTION_CANCEL:Log.i(TAG, "onInterceptTouchEvent():  ACTION_CANCEL");break;default:break;}return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.e(TAG, "onTouchEvent()");float x = event.getX();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:Log.i(TAG, "onTouchEvent():  ACTION_DOWN");mFinished = mScroller.isFinished();if(!mFinished){return false;}break;case MotionEvent.ACTION_MOVE:Log.i(TAG, "onTouchEvent():  ACTION_MOVE");getChildAt(1).scrollTo(-(int)x, 0);break;case MotionEvent.ACTION_UP:Log.i(TAG, "onTouchEvent():  ACTION_UP");if(!mAllowScroll){break;}float width = getWidth();// 响应滚动子View的临界值,若觉得响应过于灵敏,可以将只改大些。// 比如:criticalWidth = width / 3或criticalWidth = width / 2,看情况而定,呵呵。float criticalWidth = width / 5;Log.i(TAG, "onTouchEvent():  ACTION_UP x = " + x + "\t criticalWidth = " + criticalWidth);int scrollX = getChildAt(1).getScrollX();if ( x < criticalWidth) {Log.i(TAG, "onTouchEvent():  ACTION_UP 向左滑动");mPanelInvisible = false;mScroller.startScroll(scrollX, 0, -scrollX, 0, ANIMATION_DURATION_TIME);invalidate();} else if ( x > criticalWidth){Log.i(TAG, "onTouchEvent():  ACTION_UP 向右滑动");mPanelInvisible = true;int toX = (int)(width - Math.abs(scrollX) - mHandlebarWidth);mScroller.startScroll(scrollX, 0, -toX, 0, ANIMATION_DURATION_TIME);invalidate();}break;case MotionEvent.ACTION_CANCEL:Log.i(TAG, "onTouchEvent():  ACTION_CANCEL");break;default:break;}return super.onTouchEvent(event);}@Overridepublic void computeScroll() {// super.computeScroll();if(mScroller.computeScrollOffset()){this.getChildAt(1).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());this.postInvalidate();}}/*** 向右滑动View,让左侧操作面饭可见*/public void slideToRight() {mFinished = mScroller.isFinished();if(mFinished && !mPanelInvisible){mPanelInvisible = true;float width = getWidth();int scrollX = getChildAt(1).getScrollX();int toX = (int)(width - Math.abs(scrollX) - mHandlebarWidth);mScroller.startScroll(scrollX, 0, -toX, 0, ANIMATION_DURATION_TIME);invalidate();}}/*** View滑动事件监听器* @author android_ls*/public interface OnSlideListener {/*** 向左滑动子View*/public abstract void toLeft();/*** 向右滑动子View*/public abstract void toRight();}}

主应用界面源码:

package com.everyone.android.ui;import android.os.Bundle;
import android.view.ViewGroup.LayoutParams;import com.everyone.android.AppBaseActivity;
import com.everyone.android.widget.FreshNewsLayout;
import com.everyone.android.widget.LeftPanelLayout;
import com.everyone.android.widget.ScrollerContainer;
import com.everyone.android.widget.ScrollerContainer.OnSlideListener;/*** 功能描述:应用主界面* @author android_ls**/
public class EveryoneActivity extends AppBaseActivity implements OnSlideListener {/*** 滚动(滑动)容器*/private ScrollerContainer mSlideContainer;/*** 左侧面板*/private LeftPanelLayout mLeftPanelLayout;/*** 新鲜事*/private FreshNewsLayout mFreshNewsLayout;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(mSlideContainer);}@Overrideprotected int getLayoutId() {return 0;}@Overrideprotected void setupView() {mSlideContainer = new ScrollerContainer(mContext);mLeftPanelLayout = new LeftPanelLayout(mContext);mFreshNewsLayout = new FreshNewsLayout(mContext);mFreshNewsLayout.setOnSlideListener(this);LayoutParams layoutParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);mSlideContainer.addView(mLeftPanelLayout, layoutParams);mSlideContainer.addView(mFreshNewsLayout, layoutParams);}@Overrideprotected void initialized() {// TODO Auto-generated method stub}@Overridepublic void toLeft() {// TODO Auto-generated method stub}@Overridepublic void toRight() {mSlideContainer.slideToRight();}}

AppBaseActivity类的修改部分,在onCreate里添加的处理:

      int layoutId = getLayoutId();if(layoutId != 0){setContentView(getLayoutId());}

有关滑动菜单的到这里就完了,后面在使用过程中遇到什么问题,再处理。

看下效果图,还和上一篇一样,静态的图片看不出来优化后的效果,不过还是上传几张,有图有真相。

向右滑动或点击顶部箭头后

Android仿人人客户端(v5.7.1)——采用RelativeLayout做父容器,实现左侧滑动菜单(二)...相关推荐

  1. Android仿人人客户端(v5.7.1)——采用RelativeLayout做父容器,实现左侧滑动菜单(二)

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/8758943 上一篇在Android仿人人客户端(v5.7.1)--应用主界面之滑动效 ...

  2. Android仿人人客户端(v5.7.1)——采用RelativeLayout做父容器,实现左侧滑动菜单(一)...

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/8756059 一.滑动效果的实现原理: 1.采用RelativeLayout作为父容器 ...

  3. Android仿人人客户端(v5.7.1)——采用ViewGroup做父容器,实现左侧滑动菜单(三)...

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/8761410 前面已实现以滑动的方式显示或隐藏左侧菜单,采用的父容器是自定义类继承自R ...

  4. Android仿人人客户端(v5.7.1)——项目框架新做的调整描述(项目中基类java源码)...

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/8909068 声明:没看过仿人人android客户端系列博文,前面的相关文章的朋友,请 ...

  5. Android仿人人客户端(v5.7.1)——对从服务器端(网络)获取的图片进行本地双缓存处理(编码实现)...

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/8797740 这篇是基于上一篇Android仿人人客户端(v5.7.1)--对从服务器 ...

  6. Android仿人人客户端(v5.7.1)——通过HTTPS协议的POST方式获取用户的基本信息

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/8770537 一.扩展之前的网络模块 基于Android仿人人客户端(v5.7.1)- ...

  7. Android仿人人客户端(v5.7.1)——网络模块处理的架构

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/8732427 声明:仿人人项目,所用所有图片资源都来源于官方人人android客户端, ...

  8. Android仿人人客户端(v5.7.1)——新鲜事之下拉列表(过滤项列表)

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/8884335 声明:仿人人项目,所用所有图片资源都来源于其它Android移动应用,编 ...

  9. Android仿人人客户端(v5.7.1)——点击左侧菜单栏中的Item切换视图

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/8765193 在前面几讲中,左侧菜单(左侧面板).满足滑动或点击子View的方式,打开 ...

最新文章

  1. Java数组的基本知识点
  2. 在SAP WebClient UI里使用AJAX进行异步数据读取
  3. leetcode1253. 重构 2 行二进制矩阵(贪心算法)
  4. JAVA中文件指针复位到文件开头
  5. Encoder与Decoder
  6. 华为笔试题库及性格测试
  7. js 实现文件上传 php,JS+php后台实现文件上传功能详解
  8. 使用YUI3创建Popup弹出层
  9. ARM处理器对比分析
  10. CIR 工业自动化雷达
  11. uva 509 RAID!(磁盘数据)
  12. 【C4D】材质+渲染自学宝典(纯干货)
  13. TensorFlow2.8.0报错TypeError: Descriptors cannot not be created directly.
  14. STM32-SPI资料整理
  15. 《Linux C编程从入门到精通》——1.3 Shell的使用基础
  16. phpStrom 连接数据库时报错:Uncaught Error: Class ‘mysqli‘ not found in
  17. 【算法浅析NO.00004】递归算法浅析(un-accomplished version) by arttnba3
  18. Cadence全家桶Capture+Allegro流程-1-创建原理图库和元器件库
  19. 永恒之蓝漏洞分析与防护技术
  20. P1209 [USACO1.3]修理牛棚 Barn Repair 的详解

热门文章

  1. 第五人格皮肤测试软件,第五人格皮肤美化软件2018
  2. 无锡东亭计算机培训班,锡山区东亭办公自动化培训、电脑培训班有哪些?
  3. 湘教云实名服务平台怎样认证_【i通知】小贝喊你来校园一卡通微信支付实名认证!...
  4. js原生ajax跨域请求,封装一个原生js的ajax请求,支持IE9CORS跨域请求
  5. c+安装+mysql+服务器端_centos7安装JDK1.7+tomcat7+mysql5.5
  6. 语言阿克曼函数_函数式的动态规划
  7. Python按行读取txt文件
  8. 对C语言实验报告的建议,c语言实验报告.docx
  9. QT4C-Windows自动化测试框架正式开源
  10. 访问服务器显示无法访问目标主机,发送4接收4无法访问目标主机