在自定義View的時候,經常會用到一些Android系統提供的工具。這些工具封裝了咱們常常會用到的方法,好比拖拽View,計算滑動速度,View的滾動,手勢處理等等。若是咱們本身去實現這些方法會比較繁瑣,並且容易出一些bug。因此瞭解熟悉這些經常使用的工具,對咱們後續的學習和工做有很大幫助。android
Configuration:網絡
Configuration用來描述設備的配置信息。好比用戶的配置信息:locale和scaling等等 ,好比設備的相關信息:輸入模式,屏幕大小, 屏幕方向等等。ide
咱們能夠採用以下方式來獲取須要的相關信息:工具
Configuration configuration=getResources().getConfiguration(); //獲取國家碼 int countryCode=configuration.mcc; //獲取網絡碼 int networkCode=configuration.mnc; //判斷橫豎屏 if(configuration.orientation==Configuration.ORIENTATION_PORTRAIT){ } else { }
ViewConfiguration:佈局
ViewConfiguration提供了一些自定義控件用到的標準常量,好比尺寸大小,滑動距離,敏感度等等。學習
能夠利用ViewConfiguration的靜態方法獲取一個實例this
ViewConfiguration viewConfiguration=ViewConfiguration.get(context);.net
介紹ViewConfiguration的幾個對象方法:code
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自動返回到原位等複雜操做。