View基礎(chǔ)知識(shí)
- View本身可以是單個(gè)控件氯迂,也可以是多個(gè)控件組成的一組控件
- ViewGroup也繼承了View
View的位置參數(shù)
top:左上角縱坐標(biāo)
left:左上角橫坐標(biāo)
bottom:右下角縱坐標(biāo)
right:右下角橫坐標(biāo)
x县袱,y:左上角坐標(biāo)
translatioX,translationY:左上角相對(duì)于父容器的坐標(biāo)偏移量辉阶,默認(rèn)值為0
x = left + translationX;
y = top + translationY;
MotionEvent和TouchSlop
- MotionEvent
在手指接觸到屏幕澈驼,觸發(fā)的典型事件類型:
- ACTION_DOWN:手指剛接觸屏幕
- ACTION_MOVE:手指在屏幕上移動(dòng)
- ACTION_UP:手指離開(kāi)屏幕
通過(guò)MotionEvent對(duì)象可得點(diǎn)擊事件發(fā)生的x讼庇,y坐標(biāo)
- getX/getY:獲得相對(duì)于View左上角x想邦,y的坐標(biāo)
- getRowX/getRowY:獲得相對(duì)于手機(jī)屏幕左上角的x峡捡,y坐標(biāo)
- TouchSlop
系統(tǒng)所能識(shí)別滑動(dòng)的最小距離,是個(gè)常量么鹤,也就是滑動(dòng)的距離小于這個(gè)常量系統(tǒng)就認(rèn)為沒(méi)有進(jìn)行滑動(dòng)操作
獲得常量方式:ViewConfiguration.get(getContext()).getScaledTouchSlop()
VelocityTracker终娃、GestureDetector和Scroller
- VelocityTracker:速度追蹤
用于追蹤手指在滑動(dòng)過(guò)程中的速度,包含水平和垂直上的速度
- 首先在View的onTouchEvent方法中追蹤當(dāng)前單擊事件的速度
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
- 接著蒸甜,計(jì)算速度,獲取速度之前要計(jì)算速度余佛,這里的速度指一段時(shí)間手指所劃過(guò)的像素?cái)?shù)
velocityTracker.computeCurrentVelocity(1000);//計(jì)算速度
int xVelocity = (int)velocityTracker.getXVelocity();
int yVelocity = (int)velocityTracker.getYVelocity();
- 最后柠新,調(diào)用clear方法重置并回收內(nèi)存
velocityTracker.clear();
velocityTracker.recycle();
- GestureDetector:手勢(shì)檢測(cè)
用來(lái)輔助檢測(cè)用戶的單擊、滑動(dòng)辉巡、長(zhǎng)按恨憎、雙擊等行為
- Scroller:彈性滑動(dòng),實(shí)現(xiàn)有過(guò)渡效果的滑動(dòng)
View的滑動(dòng)
方式:
- 通過(guò)View的ScrollTo/ScrollBy方法
- 通過(guò)動(dòng)畫(huà)給View施加平移效果來(lái)實(shí)現(xiàn)滑動(dòng)
- 通過(guò)改變View的LayoutParams使得View重新布局從而實(shí)現(xiàn)滑動(dòng)
使用scrollTo/scrollBy
scrollBy最終也是使用scrollTo來(lái)實(shí)現(xiàn)的郊楣,他們都只能該變View內(nèi)容的位置憔恳,不能該變View的布局位置,從左往右滑净蚤,mScrollX為正钥组,反之為負(fù),從下往上滑mScrollY為正今瀑,反之為負(fù)
使用動(dòng)畫(huà)方式(屬性動(dòng)畫(huà))
例:將一個(gè)View在100ms內(nèi)從原始位置向右平移100像素
ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start();
View動(dòng)畫(huà)是對(duì)View的影像做操作程梦,并沒(méi)有該變View的位置參數(shù),要使動(dòng)畫(huà)的狀態(tài)得以保存橘荠,要將屬性fillAfter設(shè)置為true屿附,否則動(dòng)畫(huà)完成后結(jié)果就會(huì)消失,屬性動(dòng)畫(huà)不會(huì)出現(xiàn)此問(wèn)題
改變布局參數(shù)
即改變LayoutParams哥童,例如通過(guò)marginLeft參數(shù)可以達(dá)到向右平移的效果挺份,還可以預(yù)先設(shè)置一個(gè)空View等
3種滑動(dòng)方式的比較
- ScrollTo/ScrollBy:操作簡(jiǎn)單,適合對(duì)View內(nèi)容的滑動(dòng)贮懈,不影響內(nèi)部元素的單擊事件匀泊,缺點(diǎn):只能滑動(dòng)View的內(nèi)容优训,不能滑動(dòng)View本身
- 動(dòng)畫(huà):不能該變View本身屬性,適合不與用戶交互的View和實(shí)現(xiàn)復(fù)雜動(dòng)畫(huà)
- 該變布局:操作復(fù)雜探赫,適用于與用戶交互的View
彈性滑動(dòng)
即漸進(jìn)式滑動(dòng)型宙,主要思想是將一次很大的滑動(dòng)分成若干個(gè)小的滑動(dòng),并且在一段時(shí)間內(nèi)完成
實(shí)現(xiàn)方式:Scroller伦吠、Handler.postDelayed妆兑、Thread.sleep
Scroller
典型使用方法:
Scroller scroller = new Scroller(mContext);
//緩慢移動(dòng)到指定位置
private void smoothScrollerTo(int destX, int destY) {
int scrollX = getScrollX();
int destX = destX - scrollX;
//1s內(nèi)滑動(dòng)destX
mSroller.startScroll(scrollX, 0, deltaX, 0, 1000);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mSroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
注意:這里的滑動(dòng)是指內(nèi)容上的滑動(dòng),View本身是沒(méi)有改變的毛仪,在上述中搁嗓,僅僅調(diào)用startScroll方法是無(wú)法讓View進(jìn)行滑動(dòng)的,它的內(nèi)部沒(méi)有做滑動(dòng)相關(guān)的事情箱靴,只是保存了些數(shù)據(jù)而已腺逛,真正讓View滑動(dòng)的是invalidate方法,這個(gè)方法導(dǎo)致View重繪衡怀,在View重繪的draw方法中又會(huì)去調(diào)用computeScroll方法棍矛,這個(gè)方法又會(huì)去向Scroller獲取當(dāng)前的scrollX和scrollY,然后通過(guò)scrollTo方法實(shí)現(xiàn)滑動(dòng)
computeScrollOffset方法返回的是true表示滑動(dòng)沒(méi)有結(jié)束抛杨,返回false够委,表示滑動(dòng)結(jié)束
Scroller本身不能實(shí)現(xiàn)View滑動(dòng),要配合View的computeScroll方法才能完成彈性滑動(dòng)的效果
View事件分發(fā)機(jī)制
點(diǎn)擊事件傳遞規(guī)則
點(diǎn)擊事件分析的對(duì)象是MotionEvent怖现,點(diǎn)擊事件分發(fā)過(guò)程是由:dispatchTouchEvent茁帽、onInterceptTouchEvent、onTouchEvent方法共同完成的
- dispatchTouchEvent:用于事件的分發(fā)屈嗤,如果事件傳遞到當(dāng)前View潘拨,那么此方法一定會(huì)被調(diào)用,返回結(jié)果受當(dāng)前View的onTouchEvent和下級(jí)View的dispatchTouchEvent影響
- onInterceptTouchEvent:在上述方法內(nèi)部調(diào)用饶号,判斷是否攔截此事件铁追,true為攔截
- onTouchEvent:在dispatchTouchEvent方法中調(diào)用,用來(lái)處理點(diǎn)擊事件
對(duì)于ViewGroup來(lái)說(shuō)讨韭,點(diǎn)擊事件后脂信,調(diào)用dispatchTouchEvent方法,此時(shí)ViewGroup的onInterceptTouchEvent方法被調(diào)用透硝,返回true代表攔截狰闪,接著調(diào)用onTouchEvent處理事件,如果返回false濒生,分發(fā)給子元素
當(dāng)View要處理事件時(shí)埋泵,如果設(shè)置了onTouchListener,此時(shí)onTouch方法會(huì)被回調(diào),返回false丽声,view的ouTouchEvent會(huì)被調(diào)用礁蔗,返回true,不會(huì)調(diào)用雁社。onTouchListener優(yōu)先級(jí)比onTouchEvent優(yōu)先級(jí)高浴井,onClickListener優(yōu)先級(jí)最低
事件點(diǎn)擊后傳遞順序:Acticity,window,view,如果view不處理事件就會(huì)向上傳遞處理
總結(jié):
- 同一個(gè)事件序列指手指觸摸屏幕到離開(kāi)這一過(guò)程事件
- 正常情況霉撵,一個(gè)事件序列只能給一個(gè)View處理
- 一旦一個(gè)View攔截了一個(gè)事件磺浙,那么它的同一個(gè)事件序列都直接交給它處理,并且它的onInterceptTouchEvent方法不再調(diào)用
- 某個(gè)View一旦開(kāi)始處理事件徒坡,如果不消耗ACTION_DOWN事件的話撕氧,那么其它同一系列事件它也不會(huì)消耗,并且重新交給父元素處理
- 如果View不消耗ACTION_DOWN以外的其他事件喇完,那么這個(gè)點(diǎn)擊事件就會(huì)消失伦泥,并且父元素的onTouchEvent方法也不會(huì)調(diào)用,View可以持續(xù)收到后續(xù)的事件锦溪,最終這些事件都會(huì)交由Activity來(lái)處理
- ViewGroup默認(rèn)不攔截任何事件
- View沒(méi)有onInterceptTouchEvent方法不脯,一旦有事件傳遞給它,他就調(diào)用onTouchEvent方法
- View的onTouchEvent方法都默認(rèn)會(huì)消耗事件的刻诊,除非它是不可點(diǎn)擊的
- View的enable屬性不影響onTouchEvent的返回值
- 事件的傳遞方向是從外向內(nèi)的跨新,總是由父元素傳遞給子元素,子元素再傳遞給View
- onClick發(fā)生的前提是View可點(diǎn)擊并且收到了down和up事件