学习谷歌的小弟自定义View01--常用工具介绍

谷歌的小弟说 先看看这几个东西 懵逼啊 坚持看下去吧

Configuration

ViewConfiguration

GestureDetector

VelocityTracker

Scroller

ViewDragHelper

嗯哼,它们都已经躺在这里了,我们就来挨个瞅瞅

Configuration

This class describes all device configuration information that can impact the resources the application retrieves.

Configuration用来描述设备的配置信息。

比如用户的配置信息:locale和scaling等等

比如设备的相关信息:输入模式,屏幕大小, 屏幕方向等等

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

Configuration configuration=getResources().getConfiguration();

//获取国家码

int countryCode=configuration.mcc;

//获取网络码

int networkCode=configuration.mnc;

//判断横竖屏

if(configuration.orientation==Configuration.ORIENTATION_PORTRAIT){ 

 }else{

}

ViewConfiguration

看完Configuration再来瞅ViewConfiguration。这两者的名字有些像,差了一个View;咋一看,还以为它俩是继承关系,其实不然。

官方对于ViewConfiguration的描述是:

Contains methods to standard constants used in the UI for timeouts,sizes, and distances.

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();

ViewConfiguration

还提供了一些非常有用的静态方法,比如:

//双击间隔时间.在该时间内是双击,否则是单击

int doubleTapTimeout=ViewConfiguration.getDoubleTapTimeout();

//按住状态转变为长按状态需要的时间

int longPressTimeout=ViewConfiguration.getLongPressTimeout();

//重复按键的时间

int keyRepeatTimeout=ViewConfiguration.getKeyRepeatTimeout();

GestureDetector

大家都知道,我们可以在onTouchEvent()中自己处理手势。其实Android系统也给我们提供了一个手势处理的工具,这就是GestureDetector手势监听类。利用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,floatvelocityX,floatvelocityY) { 

System.out.println("---> 手势中的onFling方法");

return false; 

 }

//手指长按屏幕时均会调用该方法

@Override

public void onLongPress(MotionEvent e) {

  System.out.println("---> 手势中的onLongPress方法"); 

  }

//手指在屏幕上滚动时会调用该方法

@Override

public boolean onScroll(MotionEvent e1,MotionEvent e2,floatdistanceX,floatdistanceY) {

 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(newOnTouchListener() {

@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 = Velocity Tracker.obtain(); 

    }   

  mVelocityTracker.addMovement(event); 

}


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

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

privateintgetScrollVelocity() {

// 设置VelocityTracker单位.1000表示1秒时间内运动的像素mVelocityTracker.computeCurrentVelocity(1000);

// 获取在1秒内X方向所滑动像素值

int xVelocity = (int) mVelocityTracker.getXVelocity();

returnMath.abs(xVelocity);     

}

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

第三步:解除速度追踪

private void stopVelocityTracker() {

if(mVelocityTracker !=null) {   

        mVelocityTracker.recycle();       

    mVelocityTracker =null;     

       }

  }

以上就是VelocityTracker的常用使用方式。

Scroller

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

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

先看scrollBy( )的源码

publicvoidscrollBy(intx,inty) {          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)和我们的理解相反

scrollBy(int x,int y)方法与上类似,不再多说了.

ViewDragHelper

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

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

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

packagecom.tencent.test.utils;

importandroid.content.Context;

importandroid.support.v4.widget.ViewDragHelper;

importandroid.util.AttributeSet;

importandroid.view.MotionEvent;

importandroid.view.View;

importandroid.widget.LinearLayout;

public classIPlayGameextendsLinearLayout {

privateViewDragHelpermViewDragHelper;

publicIPlayGame(Context context,AttributeSet attrs) {

super(context,attrs);

initViewDragHelper();

}

//初始化ViewDragHelper

private voidinitViewDragHelper() {

mViewDragHelper= ViewDragHelper.create(this,1.0f, newViewDragHelper.Callback() {

@Override

public booleantryCaptureView(View child, intpointerId) {

return true;

}

//处理水平方向的越界

@Override

public intclampViewPositionHorizontal(View child, intleft, intdx) {

intfixedLeft;

View parent = (View) child.getParent();

intleftBound = parent.getPaddingLeft();

intrightBound = parent.getWidth() - child.getWidth() - parent.getPaddingRight();

if(left < leftBound) {

fixedLeft = leftBound;

}else if(left > rightBound) {

fixedLeft = rightBound;

}else{

fixedLeft = left;

}

returnfixedLeft;

}

//处理垂直方向的越界

@Override

public intclampViewPositionVertical(View child, inttop, intdy) {

intfixedTop;

View parent = (View) child.getParent();

inttopBound = getPaddingTop();

intbottomBound = getHeight() - child.getHeight() - parent.getPaddingBottom();

if(top < topBound) {

fixedTop = topBound;

}else if(top > bottomBound) {

fixedTop = bottomBound;

}else{

fixedTop = top;

}

returnfixedTop;

}

//监听拖动状态的改变

@Override

public voidonViewDragStateChanged(intstate) {

super.onViewDragStateChanged(state);

switch(state) {

caseViewDragHelper.STATE_DRAGGING:

System.out.println("STATE_DRAGGING");

break;

caseViewDragHelper.STATE_IDLE:

System.out.println("STATE_IDLE");

break;

caseViewDragHelper.STATE_SETTLING:

System.out.println("STATE_SETTLING");

break;

}

}

}

//捕获View

@Override

public voidonViewCaptured(View capturedChild, intactivePointerId) {

super.onViewCaptured(capturedChild,activePointerId);

System.out.println("ViewCaptured");

}

//释放View

@Override

public voidonViewReleased(View releasedChild, floatxvel, floatyvel) {

super.onViewReleased(releasedChild,xvel,yvel);

System.out.println("ViewReleased");

}

});

}

//将事件拦截交给ViewDragHelper处理

@Override

public booleanonInterceptTouchEvent(MotionEvent ev) {

returnmViewDragHelper.shouldInterceptTouchEvent(ev);

}

//将Touch事件交给ViewDragHelper处理

@Override

public booleanonTouchEvent(MotionEvent ev) {

mViewDragHelper.processTouchEvent(ev);

return true;

}


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

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

(1) ViewDragHelper接管了ViewGroup的事件拦截,请参见代码第91-94行

(2) ViewDragHelper接管了ViewGroup的Touch事件,请参见代码第98-102行

(3) ViewDragHelper处理了拖拽子View时的边界越界,请参见代码第22-55行

(4) ViewDragHelper监听拖拽子View时的状态变化,请参见代码第58-72行

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

好了,了解完这些非常有用的工具,我们就正式进入自定义View。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,406评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,976评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,302评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,366评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,372评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,457评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,872评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,521评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,717评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,523评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,590评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,299评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,859评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,883评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,127评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,760评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,290评论 2 342

推荐阅读更多精彩内容

  • 在自定义View的时候,常常会用到一些Android系统提供的工具。这些工具封装了我们经常会用到的方法,比如拖拽V...
    SnowDragonYY阅读 535评论 0 5
  • layout: postdate: 2016-01-08title: Android开发艺术探索-第三章-View...
    KuTear阅读 1,879评论 2 18
  • 本以为今日是阴雨天气,没想到起床后旭日东升,于是立马吆喝儿子起床去玩沙。 沙和水是伴随孩子成长过程中必备的游戏,可...
    丫丫笑语阅读 346评论 4 4
  • 高冷喵现在全是常见的宠物了,我也是十分喜欢的!其实小时候真的没见过,我们家的第一只猫还是我刚上初中时,我爸从...
    筱瞳冰心913阅读 391评论 0 3
  • 每次想起自己还有书单没有清理,看看时间只有一个小时,时间太短了吧,就不看书了,也看不了多少是不是?很多时候我不得已...
    我叫小屁孩阅读 100评论 0 0