【大圣代的技術(shù)專欄 http://blog.csdn.net/qq_23191031 轉(zhuǎn)載煩請注明出處,尊重他人勞動成功就是對您自己的尊重】

相關(guān)文章
詳解Android控件體系與常用坐標(biāo)系
Android常用觸控類分析:MotionEvent 、 ViewConfiguration、VelocityTracker
Android View事件(二)詳解事件分發(fā)機(jī)制
一, 前言
在前面的幾篇文章,我向大家介紹的都是單一View事件,而在這篇文章中,我將向大家介紹連續(xù)的事件 —— 滑動。
在安卓設(shè)備上滑動幾乎是應(yīng)用的標(biāo)配,由于安卓手機(jī)屏幕較小,為了給用戶呈現(xiàn)更多的內(nèi)容,就需要使用滑動來隱藏和顯示一些內(nèi)容。學(xué)習(xí)View的滑動對于自定義控件的掌握、日常滑動沖突的處理都有很多裨益。為了很好的理解滑動事件,掌握Android坐標(biāo)系與觸控事件就變得格外重要,在此強(qiáng)烈建議先閱讀上面的提到的幾篇相關(guān)文章打好基礎(chǔ)。
二, View滑動產(chǎn)生的原理
從原理上講View滑動的本質(zhì)就是隨著手指的運(yùn)動不斷地改變坐標(biāo)。當(dāng)觸摸事件傳到View時,系統(tǒng)記下觸摸點(diǎn)的坐標(biāo),手指移動時系統(tǒng)記下移動后的觸摸的坐標(biāo)并算出偏移量,并通過偏移量來修改View的坐標(biāo),不斷的重復(fù)這樣的過程,從而實(shí)現(xiàn)滑動過程。
三,實(shí)現(xiàn)滑動的7種方法
在學(xué)習(xí)Android坐標(biāo)系和觸控事件之后我們就可以看看系統(tǒng)為開發(fā)者提供了那些方法來實(shí)現(xiàn)滑動效果吧!
3.0 代碼實(shí)例介紹
為了讓大家更好的理解過程,設(shè)計(jì)如下代碼實(shí)例



MainActivity中沒有任何改動,而CustomView只是繼承了View,并重寫了 onToucnEvent()方法。
可以看到代碼很簡單,這篇文章中我就不提供項(xiàng)目地址了。
3.1 layout 方法
在View繪制的時候,系統(tǒng)都會調(diào)用layout(int l, int t, int r, int b)方法來確定View的具體位置。系統(tǒng)既然是這樣來設(shè)置View的位置的,那么我們也可以通過調(diào)用layout(int l, int t, int r, int b)`方法修改left,top,right,bottom這四個屬性來控制View的位置。
// 視圖坐標(biāo)方式
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 記錄觸摸點(diǎn)坐標(biāo)
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
// 計(jì)算偏移量
int offsetX = x - lastX;
int offsetY = y - lastY;
// 在當(dāng)前l(fā)eft、top、right、bottom的基礎(chǔ)上加上偏移量
layout(getLeft() + offsetX,
getTop() + offsetY,
getRight() + offsetX,
getBottom() + offsetY);
break;
}
return true;
}
當(dāng)然使用 getX()、getY()方法和使用getRawX()、geRawtY()的效果是一樣的,只不過前者使用的是相對位置,而后者使用的是絕對位置。
但是要注意,在使用絕對坐標(biāo)系的時候,每次執(zhí)行完 ACTION_MOVE的邏輯后,一定要重新設(shè)置初始坐標(biāo),這樣才能獲得準(zhǔn)確的偏移量。
case MotionEvent.ACTION_MOVE:
// 計(jì)算偏移量
…………
//重新設(shè)置初始坐標(biāo)
x = (int)(event.getRawX());
y = (int)(event.getRawY());
break;
3.2 offsetLeftAndRight() 與 offsetTopAndroidBottom()
// 視圖坐標(biāo)方式
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 記錄觸摸點(diǎn)坐標(biāo)
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
// 計(jì)算偏移量
int offsetX = x - lastX;
int offsetY = y - lastY
//同時對于left 和 right進(jìn)行偏移
offsetLeftAndRight(offsetX);
//同時對于top 和 bottom進(jìn)行偏移
offsetTopAndBottom(offsetY);
break;
}
return true;
}
3.3 LayoutParams
LayoutParams類是子View向父View傳遞位置意圖的橋梁,告訴父View他想要變成的樣子。所以我們可以通過LayoutParams中的參數(shù)來改變View的位置。
@Override
public boolean onTouchEvent(MotionEvent event) {
int startX = (int) event.getX();
int startY = (int) event.getY();
int lastX = 0;
int lastY = 0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = startX;
lastY = startY;
Log.v(TAG, "startX " + startX + " startY " + startY);
break;
case MotionEvent.ACTION_MOVE:
Log.v(TAG, "offsetX --- " + startX + " offsetY --- " + startY);
int offsetX = startX - lastX;
int offsetY = startY - lastY;
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
break;
}
//注明消費(fèi)此事件,不然無效果
return true;
}
注意:
在獲得 LayoutParams對象的時候,需要將其轉(zhuǎn)換成直接父View(這個View的上一層布局)的類型,不然會報(bào)錯。
例如,將
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
改為錯誤的:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
報(bào)錯如下:

3.4 scrollTo與ScrollBy
在View中系統(tǒng)為開發(fā)者提供了兩個關(guān)于移動的方法: scrollTo 與 scroolBy。其實(shí)這兩個方法非常好理解,單從字面上的意思就知道
scrollTo(x,y) : 移動到 (x,y) 這個坐標(biāo)點(diǎn)
scrlollBy(dx,dy) : 移動的增量為 dx,dy。
我們對于原有代碼進(jìn)行如下更改

但是,當(dāng)我們拖動View的時候卻沒有移動?。?!這是為什么呢?
其實(shí)View是移動了地,只不過和我們設(shè)想的結(jié)果不同。scrollTo()、scrollBy()表示的是 移動當(dāng)前ViewGroup的所有子View,如果在View中使用,那么移動的就是View的內(nèi)容(content)。例如TextView它的content就是文本。這就解釋了為什么我們的代碼看不到效果了。
將原有代碼更改為如下所示:
((ViewGroup) getParent()).scrollBy(offsetX, offsetY);
這回的確是動了,但是他卻在亂動。并不是像我們想象中的那樣跟隨著手指的移動而移動。

導(dǎo)致這個的原因是什么呢? 答案請見《Scroll類源碼分析與應(yīng)用》第一節(jié) scrollTo與ScrollBy
3.5 Scroller
請見《Scroll類源碼分析與應(yīng)用》 第二節(jié)Scroller
關(guān)于屬性動畫、ViewDragHelper
二者也是實(shí)現(xiàn)View滑動的良好辦法,但是他們都有一定的復(fù)雜性直接展開不僅顯得突兀、而且篇幅較大不利于學(xué)習(xí),所以屬性動畫與ViewDragHelper我都會以單獨(dú)的篇幅展開,方便同學(xué)們更好的理解與學(xué)習(xí),敬請期待
版權(quán)聲明:
禁止一切商業(yè)行為,轉(zhuǎn)載請著名出處 http://blog.csdn.net/qq_23191031。作者: 大圣代
Copyright (c) 2017 代圣達(dá). All rights reserved.