1.View的基礎(chǔ)知識(shí)
(1)什么是View
View是Android中所有控件的基類谤绳,不管是簡(jiǎn)單的TextView還是復(fù)雜的RelativeLayout和ListView抛姑,它們共同的基類都是View。
ViewGroup:內(nèi)部包含了多個(gè)控件驶赏,即一組View院喜。
在Android中,ViewGroup也繼承View证芭,這就意味著View本身就可以是單個(gè)控件也可以是由多個(gè)控件組成的一組控件瞳浦。
(2)View的位置參數(shù)
View的四個(gè)屬性:top、left废士、right叫潦、bottom,其中top是左上角縱坐標(biāo)官硝,left是左上角橫坐標(biāo)矗蕊,right是右下角橫坐標(biāo),bottom是右下角縱坐標(biāo)氢架。
//這些坐標(biāo)都是相對(duì)于view父容器的
//view 左上角的坐標(biāo)(mTop傻咖,mLeft)
//view 右上角坐標(biāo)(mBottom,mRight)
mTop = mBtView.getTop();
mLeft = mBtView.getLeft();
mBottom = mBtView.getBottom();
mRight = mBtView.getRight();
//得出當(dāng)前view的寬和高 單位是像素 px
int width = mRight - mLeft;
int height = mBottom - mTop;
從Android3.0開始岖研,View增加了額外的幾個(gè)參數(shù):x卿操、y、translationX孙援、translationY害淤。
其中x和y是View左上角的坐標(biāo),translationX和translationY是View左上角相對(duì)于父容器的偏移量
x = left+translationX
y = top+translationY
(3)MotionEvent和TouchSlop
MotionEvent
在手指接觸屏幕后所產(chǎn)生的一系列事件中拓售,典型的事件類型有以下幾種:
- ACTION_DOWN:手指剛接觸屏幕
- ACTION_MOVE:手指在屏幕上移動(dòng)
- ACTION_UP: 手指在屏幕上松開
正常情況下窥摄,一次手指接觸屏幕的行為會(huì)觸發(fā)一些列點(diǎn)擊事件
- 點(diǎn)擊屏幕后離開松開,事件序列為:DOWN->UP
- 點(diǎn)擊屏幕滑動(dòng)一會(huì)再松開邻辉,事件序列為:DOWN->MOVE->.....>MOVE->UP
通過MotionEvent對(duì)象我們可以得到點(diǎn)擊事件發(fā)生的x和y的坐標(biāo)溪王。
getX和getY:返回的是相對(duì)于當(dāng)前View左上角的x和y坐標(biāo)
getRawX和getRawY:返回到是相當(dāng)于手機(jī)屏幕左上角的x和y坐標(biāo)
TouchSlop
是系統(tǒng)所能識(shí)別出的被認(rèn)為是滑動(dòng)的最小距離腮鞍,是個(gè)常量,與設(shè)備有關(guān)莹菱。
可以通過
ViewConfiguration.get(getContext()).getScaledTouchSlop()獲取
(4)VelocityTracker移国、GestureDetector和Scroller
VelocityTracker
速度追蹤,用于追蹤手指在滑動(dòng)過程中的速度道伟,包括水平和豎直方向的速度迹缀,首先在View的onTouchEvent方法中追蹤當(dāng)前單擊事件的速度
VelocityTracker mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(event);
獲取當(dāng)前的滑動(dòng)速度
mVelocityTracker.computeCurrentVelocity(1000);
//x方向的速度
int xVelocity = (int) mVelocityTracker.getXVelocity();
//y方向的速度
int yVelocity = (int) mVelocityTracker.getYVelocity();
注意:獲取速度之前必須先加速速度,即在獲取速度之前要先調(diào)用computeCurrentVelocity方法
最后蜜徽,當(dāng)不在使用的時(shí)候祝懂,需要調(diào)用clear方法來重置并回收內(nèi)存
mVelocityTracker.clear();
mVelocityTracker.recycle();
GestureDetector
手勢(shì)檢測(cè),用戶輔助檢測(cè)用戶的點(diǎn)擊拘鞋、滑動(dòng)砚蓬、長(zhǎng)按、雙擊等行為盆色。
首先灰蛙,創(chuàng)建一個(gè)GestureDetector對(duì)象并實(shí)現(xiàn)OnGestureListener接口
GestureDetector mGestureDetector = new GestureDetector(MainActivity.this);
//解決長(zhǎng)按屏幕后無法拖動(dòng)的現(xiàn)象
mGestureDetector.setIsLongpressEnabled(false);
接著,接管目標(biāo)View的onTouchEvent方法隔躲,在待監(jiān)聽的onTouchEvent方法中添加如下實(shí)現(xiàn)
@Override
public boolean onTouchEvent(MotionEvent event)
{
boolean mBoolean = mMGestureDetector.onTouchEvent(event);
return mBoolean;
}
然后摩梧,可以有選擇的實(shí)現(xiàn)OnGestureListener和OnDoubleTabListener
OnGestureListener中要實(shí)現(xiàn)方法
/**
* 手指輕輕觸摸屏幕的一瞬間
* @param event
* @return
*/
@Override
public boolean onDown(MotionEvent event) {
return false;
}
/**
* 手指輕輕觸摸屏幕,尚未松開或拖動(dòng)
* @param event
*/
@Override
public void onShowPress(MotionEvent event) {
}
/**
* 用戶單擊
* @param event
* @return
*/
@Override
public boolean onSingleTapUp(MotionEvent event) {
return false;
}
/**
* 手指滑動(dòng)
* @param event
* @param event1
* @param v
* @param v1
* @return
*/
@Override
public boolean onScroll(MotionEvent event, MotionEvent event1, float v, float v1) {
return false;
}
/**
* 長(zhǎng)按
* @param event
*/
@Override
public void onLongPress(MotionEvent event) {
}
/**
* 快速滑動(dòng)
* @param event
* @param event1
* @param v
* @param v1
* @return
*/
@Override
public boolean onFling(MotionEvent event, MotionEvent event1, float v, float v1) {
return false;
}
Scroller
彈性滑動(dòng)對(duì)象宣旱,用于實(shí)現(xiàn)View的彈性滑動(dòng)仅父。
scrollTo/scrollBy方法來進(jìn)行滑動(dòng),其過程是瞬間完成的浑吟。
Scroller本身無法讓View彈性滑動(dòng)笙纤,它需要和View的computeScroll方法配合使用才能共同完成這個(gè)功能。
2.View的滑動(dòng)
三種方式實(shí)現(xiàn)View滑動(dòng):
- 通過View本身提供的scrollTo/scrollBy方法來實(shí)現(xiàn)滑動(dòng)
- 通過動(dòng)畫給View添加平移效果來實(shí)現(xiàn)滑動(dòng)
- 通過改變View的LayoutParams使得View重新布局而實(shí)現(xiàn)滑動(dòng)
使用scrollTo/scrollBy
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
從上面源碼可以看出scrollBy實(shí)際上是調(diào)用了scrollTo方法买置。
注意:scrollTo和scrollBy只能改變View的內(nèi)容位置而不能改變View在布局中的位置粪糙。
使用動(dòng)畫
可以采用View動(dòng)畫
也可以采用屬性動(dòng)畫,為了兼容3.0以前的版本忿项,可以采用開源動(dòng)畫庫nineoldandroids
nineoldandroids實(shí)現(xiàn)的屬性動(dòng)畫本質(zhì)上仍然是View動(dòng)畫蓉冈。
注意:View動(dòng)畫并不能改變View的位置,比如把一個(gè)Button右移100px轩触,但是點(diǎn)擊事件還在原來的位置寞酿。
改變布局參數(shù)
即改變LayoutParams,比如想把一個(gè)Button右移100px脱柱,我們只需要把這個(gè)Button的LayoutParams里的marginLeft參數(shù)的值增加100px即可
MarginLayoutParams params = (MarginLayoutParams)mButton.getLayoutParams();
params.leftMargin +=100;
mButton.requestLayout;
各種滑動(dòng)方式對(duì)比
scrollTo和scrollBy:操作簡(jiǎn)單伐弹,只能改變View的內(nèi)容位置而不能改變View在布局中的位置。不影響內(nèi)部元素的點(diǎn)擊事件
動(dòng)畫:操作簡(jiǎn)單榨为,主要用于沒有交互的View和實(shí)現(xiàn)復(fù)雜的動(dòng)畫效果惨好,影響點(diǎn)擊事件
改變布局:操作復(fù)雜煌茴,適用于有交互的view
3.彈性滑動(dòng)
- 使用Scroller
- 通過動(dòng)畫
- 使用延時(shí)策略
View的事件分發(fā)機(jī)制
事件的分發(fā)機(jī)制主要是有三個(gè)重要方法共同完成
dispatchTouchEvent:用于分發(fā)事件
onInterceptTouchEvent:是否攔截事件
onTouchEvent:處理點(diǎn)擊事件
分發(fā)機(jī)制:對(duì)于一個(gè)ViewGroup來說,點(diǎn)擊事件產(chǎn)生后日川,首先會(huì)傳遞給它蔓腐,這時(shí)它的dispatchTouchEvent就會(huì)被調(diào)用。
如果dispatchTouchEvent返回true就表示它要攔截當(dāng)前事件龄句,這個(gè)事件就會(huì)交給這個(gè)ViewGroup來處理回论,那么它的onTouchEvent就會(huì)被調(diào)用
如果dispatchTouchEvent返回false就表示它不攔截當(dāng)前事件,當(dāng)前事件就會(huì)繼續(xù)傳遞給它的子元素分歇,接著子元素的dispatchTouchEvent方法就會(huì)被調(diào)用傀蓉,如此反復(fù)直到事件被最終處理。