博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android自定义View探索(二)—常用工具
阅读量:6328 次
发布时间:2019-06-22

本文共 8968 字,大约阅读时间需要 29 分钟。

本文转载自:

在自定义View的时候,常常会用到一些Android系统提供的工具。这些工具封装了我们经常会用到的方法,比如拖拽View,计算滑动速度,View的滚动,手势处理等等。如果我们自己去实现这些方法会比较繁琐,而且容易出一些bug。所以了解熟悉这些常用的工具,对我们后续的学习和工作有很大帮助。

Configuration:

Configuration用来描述设备的配置信息。比如用户的配置信息:locale和scaling等等 ,比如设备的相关信息:输入模式,屏幕大小, 屏幕方向等等。

我们可以采用如下方式来获取需要的相关信息:

Configuration configuration=getResources().getConfiguration();//获取国家码int countryCode=configuration.mcc;//获取网络码int networkCode=configuration.mnc;//判断横竖屏if(configuration.orientation==Configuration.ORIENTATION_PORTRAIT){   } else {}

ViewConfiguration:

ViewConfiguration提供了一些自定义控件用到的标准常量,比如尺寸大小,滑动距离,敏感度等等。

可以利用ViewConfiguration的静态方法获取一个实例

ViewConfiguration viewConfiguration=ViewConfiguration.get(context);

介绍ViewConfiguration的几个对象方法:

ViewConfiguration  viewConfiguration=ViewConfiguration.get(context);//获取touchSlop。该值表示系统所能识别出的被认为是滑动的最小距离int touchSlop = viewConfiguration.getScaledTouchSlop();//获取Fling速度的最小值和最大值int minimumVelocity = viewConfiguration.getScaledMinimumFlingVelocity();int maximumVelocity = viewConfiguration.getScaledMaximumFlingVelocity();//判断是否有物理按键boolean isHavePermanentMenuKey=viewConfiguration.hasPermanentMenuKey();//双击间隔时间.在该时间内是双击,否则是单击int doubleTapTimeout=ViewConfiguration.getDoubleTapTimeout();//按住状态转变为长按状态需要的时间int longPressTimeout=ViewConfiguration.getLongPressTimeout();//重复按键的时间int keyRepeatTimeout=ViewConfiguration.getKeyRepeatTimeout();

GestureDetector:

GestureDetector是Android给我们提供的一个手势处理的工具,利用GestureDetector可以简化许多操作,轻松实现一些常用的功能。

一起看看怎么使用的:

第一步:实现OnGestureListener

private class GestureListenerImpl implements GestureDetector.OnGestureListener {
//触摸屏幕时均会调用该方法 @Override public boolean onDown(MotionEvent e) { System.out.println("---> 手势中的onDown方法"); return false; } //手指在屏幕上拖动时会调用该方法 @Override public boolean onFling(MotionEvent e1,MotionEvent e2, float velocityX,float velocityY) { System.out.println("---> 手势中的onFling方法"); return false; } //手指长按屏幕时均会调用该方法 @Override public void onLongPress(MotionEvent e) { System.out.println("---> 手势中的onLongPress方法"); } //手指在屏幕上滚动时会调用该方法 @Override public boolean onScroll(MotionEvent e1,MotionEvent e2, float distanceX,float distanceY) { System.out.println("---> 手势中的onScroll方法"); return false; } //手指在屏幕上按下,且未移动和松开时调用该方法 @Override public void onShowPress(MotionEvent e) { System.out.println("---> 手势中的onShowPress方法"); } //轻击屏幕时调用该方法 @Override public boolean onSingleTapUp(MotionEvent e) { System.out.println("---> 手势中的onSingleTapUp方法"); return false; } }

第二步:生成GestureDetector对象

GestureDetector gestureDetector = new GestureDetector(context,new

GestureListenerImpl());

这里的GestureListenerImpl就是GestureListener监听器的实现。

第三步:将Touch事件交给GestureDetector处理

比如将Activity的Touch事件交给GestureDetector处理

@Override  public boolean onTouchEvent(MotionEvent event) {       return mGestureDetector.onTouchEvent(event);  }

比如将View的Touch事件交给GestureDetector处理

mButton=(Button) findViewById(R.id.button);  mButton.setOnTouchListener(new OnTouchListener() {               @Override     public boolean onTouch(View arg0, MotionEvent event) {            return mGestureDetector.onTouchEvent(event);        }  });

VelocityTracker :

VelocityTracker用于跟踪触摸屏事件(比如,Flinging及其他Gestures手势事件等)的速率。

第一步:开始速度追踪

private void startVelocityTracker(MotionEvent event) {      if (mVelocityTracker == null) {           mVelocityTracker = VelocityTracker.obtain();       }       mVelocityTracker.addMovement(event);  }

在这里我们初始化VelocityTracker,并且把要追踪的MotionEvent注册到VelocityTracker的监听中。

第二步:获取追踪到的速度

private int getScrollVelocity() {       // 设置VelocityTracker单位.1000表示1秒时间内运动的像素       mVelocityTracker.computeCurrentVelocity(1000);       // 获取在1秒内X方向所滑动像素值       int xVelocity = (int) mVelocityTracker.getXVelocity();       return Math.abs(xVelocity);      }

同理可以获取1秒内Y方向所滑动像素值

第三步:解除速度追踪

private void stopVelocityTracker() {        if (mVelocityTracker != null) {            mVelocityTracker.recycle();            mVelocityTracker = null;        }  }

Scroller:

Scroller挺常见的,用的比较多了。在此只强调几个重要的问题,别的就不再赘述了。

第一点:scrollTo()和scrollBy()的关系

先看scrollBy( )的源码

public void scrollBy(int x, int y) {          scrollTo(mScrollX + x, mScrollY + y);   }

这就是说scrollBy( )调用了scrollTo( ),最终起作用的是scrollTo( )方法。

第二点:scroll的本质

scrollTo( )和scrollBy( )移动的只是View的内容,而且View的背景是不移动的。

第三点:scrollTo( )和scrollBy( )方法的坐标说明

比如我们对于一个TextView调用scrollTo(0,25) ;那么该TextView中的content(比如显示的文字:Hello)会怎么移动呢?

向下移动25个单位?不!恰好相反!!这是为什么呢?
因为调用该方法会导致视图重绘,即会调用

public void invalidate(int l, int t, int r, int b)

此处的l,t,r,b四个参数就表示View原来的坐标.

在该方法中最终会调用:

tmpr.set(l - scrollX, t - scrollY, r - scrollX, b - scrollY);

p.invalidateChild(this, tmpr);

其中tmpr是一个Rect,this是原来的View;通过这两行代码就把View在一个Rect中重绘。

请注意第一行代码:
原来的l和r均减去了scrollX
原来的t和b均减去了scrollY
就是说scrollX如果是正值,那么重绘后的View的宽度反而减少了;反之同理
就是说scrollY如果是正值,那么重绘后的View的高度反而减少了;反之同理
所以,TextView调用scrollTo(0,25)和我们的理解相反

ViewDragHelper:

在项目中很多场景需要用户手指拖动其内部的某个View,此时就需要在onInterceptTouchEvent()和onTouchEvent()这两个方法中写不少逻辑了,比如处理:拖拽移动,越界,多手指的按下,加速度检测等等。

ViewDragHelper可以极大的帮我们简化类似的处理,它提供了一系列用于处理用户拖拽子View的辅助方法和与其相关的状态记录。比较常见的:QQ侧滑菜单,Navigation Drawer的边缘滑动,都可以由它实现。

ViewDragHelper的使用并不复杂,在此通过一个示例展示其常用的用法。

public class MyLinearLayout extends LinearLayout {
private ViewDragHelper mViewDragHelper; public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); initViewDragHelper(); } //初始化ViewDragHelper private void initViewDragHelper() { mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) { return true; } //处理水平方向的越界 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { int fixedLeft; View parent = (View) child.getParent(); int leftBound = parent.getPaddingLeft(); int rightBound = parent.getWidth() - child.getWidth() - parent.getPaddingRight(); if (left < leftBound) { fixedLeft = leftBound; } else if (left > rightBound) { fixedLeft = rightBound; } else { fixedLeft = left; } return fixedLeft; } //处理垂直方向的越界 @Override public int clampViewPositionVertical(View child, int top, int dy) { int fixedTop; View parent = (View) child.getParent(); int topBound = getPaddingTop(); int bottomBound = getHeight() - child.getHeight() - parent.getPaddingBottom(); if (top < topBound) { fixedTop = topBound; } else if (top > bottomBound) { fixedTop = bottomBound; } else { fixedTop = top; } return fixedTop; } //监听拖动状态的改变 @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); switch (state) { case ViewDragHelper.STATE_DRAGGING: System.out.println("STATE_DRAGGING"); break; case ViewDragHelper.STATE_IDLE: System.out.println("STATE_IDLE"); break; case ViewDragHelper.STATE_SETTLING: System.out.println("STATE_SETTLING"); break; } } //捕获View @Override public void onViewCaptured(View capturedChild, int activePointerId) { super.onViewCaptured(capturedChild, activePointerId); System.out.println("ViewCaptured"); } //释放View @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); System.out.println("ViewReleased"); } }); } //将事件拦截交给ViewDragHelper处理 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mViewDragHelper.shouldInterceptTouchEvent(ev); } //将Touch事件交给ViewDragHelper处理 @Override public boolean onTouchEvent(MotionEvent ev) { mViewDragHelper.processTouchEvent(ev); return true; }}

从这个例子可以看出来ViewDragHelper是作用在ViewGroup上的(比如LinearLayout)而不是直接作用到某个被拖拽的子View。其实这也不难理解,因为子View在布局中的位置是其所在的ViewGroup决定的。

在该例中ViewDragHelper做了如下主要操作:

(1) ViewDragHelper接管了ViewGroup的事件拦截

(2) ViewDragHelper接管了ViewGroup的Touch事件
(3) ViewDragHelper处理了拖拽子View时的边界越界
(4) ViewDragHelper监听拖拽子View时的状态变化

除了这些常见的操作,ViewDragHelper还可以实现:抽屉拉伸,拖拽结束松手后子View自动返回到原位等复杂操作。

接下来会带来几个自定义View的实战,下一篇文章见~~~

你可能感兴趣的文章
微服务架构介绍和RPC框架对比
查看>>
Debian下使用OpenLDAP 管理端
查看>>
泛型排序器TComparer
查看>>
9个offer,12家公司,35场面试,从微软到谷歌,应届计算机毕业生的2012求职之路...
查看>>
创建符合标准的、有语意的HTML页面——ASP.NET 2.0 CSS Friendly Control Adapters 1.0发布...
查看>>
Adobe驳斥Flash过度耗电论 称HTML5更耗电
查看>>
No!No!No! It's not fashion!
查看>>
艰困之道中学到的经验教训
查看>>
Vue 组件库 HeyUI@1.16.0 更新日志
查看>>
互联网生态建设落地五大挑战——保险科技生态建设 ...
查看>>
进行短视频app开发工作时,可以加入它来保护青少年 ...
查看>>
Rxjs 学习推荐
查看>>
25G DAC无源高速线缆和25G光模块之间的区别
查看>>
乐乐茶完成近2亿元Pre-A轮融资,祥峰投资领投
查看>>
clickhouse修改时区
查看>>
CSS_定位
查看>>
第二十四章:页面导航(六)
查看>>
百度、长沙加码自动驾驶,湖南阿波罗智行科技公司成立 ...
查看>>
Java面试笔试题大汇总一(最全+详细答案)
查看>>
10 个 Linux 中方便的 Bash 别名
查看>>