【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第1頁
【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第2頁
【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第3頁
【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第4頁
【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用

常見的滑動沖突場景常見的滑動沖突場景可以簡單分為以下三種:場景1:外部滑動方向和內(nèi)部滑動方向不一致場景2:外部滑動方向和內(nèi)部滑動方向一致場景3:上面兩種情況的嵌套如圖:場景1,主要是將ViewPager和Fragment配合使用所組成的頁面滑動效果,主流應(yīng)用幾乎都會使用這個效果。在這個效果中可以通過左右滑動來切換頁面,而每個頁面內(nèi)部往往又是一個ListView,所以就造成了滑動沖突,但是在ViewPager內(nèi)部處理了這種滑動沖突,因此在采用ViewPager時我們就無須關(guān)注這個問題,而如果把ViewPager換成ScrollView,那就必須自己手動處理,不然造成的結(jié)果就是內(nèi)外兩層只能一層能夠滑動。場景2,就復(fù)雜一點,當(dāng)內(nèi)外兩層都在同一個方向可以滑動的時候,顯然存在邏輯問題。因為當(dāng)手指開始滑動的時候,系統(tǒng)無法知道用戶到底是想讓哪一層滑動,所以當(dāng)手指滑動的時候就會出現(xiàn)問題,要么只有一層滑動,要么就是內(nèi)外兩層都滑動但很卡頓。場景3,是場景1和場景2兩種情況的嵌套,顯得更復(fù)雜了。比如外部有一個SlideMenu效果,內(nèi)部有一個ViewPager,ViewPager的每一個頁面中又是一個ListView。雖然場景3滑動沖突看起來很復(fù)雜,但都是幾個單一的滑動沖突的疊加,因此需要一一拆解開來即可?;瑒記_突的處理規(guī)則一般來說,不管滑動沖突有多么復(fù)雜,它都有既定的規(guī)則,根據(jù)這些規(guī)則我們就可以選擇合適的方法去處理。對于場景1,它的處理規(guī)則就是:當(dāng)用戶左右滑動時,需要讓外部的View攔截點擊事件,當(dāng)用戶上下滑動,需要讓內(nèi)部View攔截點擊事件。具體來說就是根據(jù)滑動是水平滑動還是豎直滑動來判斷到底是由誰來攔截事件。如圖:簡單來說,就是根據(jù)水平方向和豎直方向的距離差來判斷,如果是Dx>Dy,那么則是水平滑動,如果是Dy>Dx,那么則是豎直滑動。場景2,則是比較特殊,它無法根據(jù)滑動的角度,距離差以及速度差來做判斷。這個時候就需要從業(yè)務(wù)上找到突破點,比如,當(dāng)處于某種狀態(tài)時需要外部View響應(yīng)用戶的滑動,而處于另外一種狀態(tài)時需要內(nèi)部View來響應(yīng)View的滑動對于場景3的話,它的滑動規(guī)則也更復(fù)雜,和場景2一樣,同樣是從業(yè)務(wù)上找到突破點。外部攔截法外部攔截法是指點擊事件都是先經(jīng)過父容器的攔截處理,如果父容器需要此事件就攔截,如果不需要此事件,就不攔截了,這樣就可以解決滑動沖突的問題,外部攔截法需要重寫父容器的onInterceptTouchEvent方法,在內(nèi)部做相應(yīng)的攔截即可,偽代碼如下:

@Override

public

boolean

onInterceptTouchEvent(MotionEvent

event)

{

boolean

intercepted

=

false;

int

x

=

(int)

event.getX();

int

y

=

(int)

event.getY();

switch

(event.getAction())

{

case

MotionEvent.ACTION_DOWN:

{

intercepted

=

false;

break;

}

case

MotionEvent.ACTION_MOVE:

{

if

(父容器需要點擊當(dāng)前事件)

{

intercepted

=

true;

}

else

{

intercepted

=

false;

}

break;

}

case

MotionEvent.ACTION_UP:

{

intercepted

=

false;

break;

}

default:

break;

}

mLastXIntercept

=

x;

mLastYIntercept

=

y;

return

intercepted;

}首先ACTION_DOWN這個事件,父容器必須返回false,這樣保證后續(xù)move和up的事件可以傳遞給子View,根據(jù)move事件來決定是否攔截,如果父容器攔截就返回true,否則返回false。實現(xiàn)一個自定義類似ViewPager的控件,嵌套ListView的效果,源代碼如下:public

class

HorizontalScrollViewEx

extends

ViewGroup

{

private

static

final

String

TAG

=

"HorizontalScrollViewEx";

private

int

mChildrenSize;

private

int

mChildWidth;

private

int

mChildIndex;

//

分別記錄上次滑動的坐標(biāo)

private

int

mLastX

=

0;

private

int

mLastY

=

0;

//

分別記錄上次滑動的坐標(biāo)(onInterceptTouchEvent)

private

int

mLastXIntercept

=

0;

private

int

mLastYIntercept

=

0;

private

Scroller

mScroller;

//彈性滑動對象

private

VelocityTracker

mVelocityTracker;

//追蹤滑動速度

public

HorizontalScrollViewEx(Context

context)

{

super(context);

init();

}

public

HorizontalScrollViewEx(Context

context,

AttributeSet

attrs)

{

super(context,

attrs);

init();

}

public

HorizontalScrollViewEx(Context

context,

AttributeSet

attrs,

int

defStyle)

{

super(context,

attrs,

defStyle);

init();

}

private

void

init()

{

mScroller

=

new

Scroller(getContext());

mVelocityTracker

=

VelocityTracker.obtain();

}

@Override

public

boolean

onInterceptTouchEvent(MotionEvent

event)

{

boolean

intercepted

=

false;

int

x

=

(int)

event.getX();

int

y

=

(int)

event.getY();

switch

(event.getAction())

{

case

MotionEvent.ACTION_DOWN:

{

intercepted

=

false;

if

(!mScroller.isFinished())

{

mScroller.abortAnimation();

intercepted

=

true;

}

break;

}

case

MotionEvent.ACTION_MOVE:

{

int

deltaX

=

x

-

mLastXIntercept;

int

deltaY

=

y

-

mLastYIntercept;

if

(Math.abs(deltaX)

>

Math.abs(deltaY))

{

intercepted

=

true;

}

else

{

intercepted

=

false;

}

break;

}

case

MotionEvent.ACTION_UP:

{

intercepted

=

false;

break;

}

default:

break;

}

Log.d(TAG,

"intercepted="

+

intercepted);

mLastX

=

x;

mLastY

=

y;

mLastXIntercept

=

x;

mLastYIntercept

=

y;

return

intercepted;

}

@Override

public

boolean

onTouchEvent(MotionEvent

event)

{

mVelocityTracker.addMovement(event);

int

x

=

(int)

event.getX();

int

y

=

(int)

event.getY();

switch

(event.getAction())

{

case

MotionEvent.ACTION_DOWN:

{

if

(!mScroller.isFinished())

{

mScroller.abortAnimation();

}

break;

}

case

MotionEvent.ACTION_MOVE:

{

int

deltaX

=

x

-

mLastX;

scrollBy(-deltaX,

0);

break;

}

case

MotionEvent.ACTION_UP:

{

int

scrollX

=

getScrollX();

mVelocityTputeCurrentVelocity(1000);

float

xVelocity

=

mVelocityTracker.getXVelocity();

if

(Math.abs(xVelocity)

>=

50)

{

mChildIndex

=

xVelocity

>

0

?

mChildIndex

-

1

:

mChildIndex

+

1;

}

else

{

mChildIndex

=

(scrollX

+

mChildWidth

/

2)

/

mChildWidth;

}

mChildIndex

=

Math.max(0,

Math.min(mChildIndex,

mChildrenSize

-

1));

int

dx

=

mChildIndex

*

mChildWidth

-

scrollX;

smoothScrollBy(dx,

0);

mVelocityTracker.clear();

break;

}

default:

break;

}

mLastX

=

x;

mLastY

=

y;

return

true;

}

@Override

protected

void

onMeasure(int

widthMeasureSpec,

int

heightMeasureSpec)

{

super.onMeasure(widthMeasureSpec,

heightMeasureSpec);

int

measuredWidth

=

0;

int

measuredHeight

=

0;

final

int

childCount

=

getChildCount();

measureChildren(widthMeasureSpec,

heightMeasureSpec);

int

widthSpaceSize

=

MeasureSpec.getSize(widthMeasureSpec);

int

widthSpecMode

=

MeasureSpec.getMode(widthMeasureSpec);

int

heightSpaceSize

=

MeasureSpec.getSize(heightMeasureSpec);

int

heightSpecMode

=

MeasureSpec.getMode(heightMeasureSpec);

if

(childCount

==

0)

{

setMeasuredDimension(0,

0);

}

else

if

(heightSpecMode

==

MeasureSpec.AT_MOST)

{

final

View

childView

=

getChildAt(0);

measuredHeight

=

childView.getMeasuredHeight();

setMeasuredDimension(widthSpaceSize,

childView.getMeasuredHeight());

}

else

if

(widthSpecMode

==

MeasureSpec.AT_MOST)

{

final

View

childView

=

getChildAt(0);

measuredWidth

=

childView.getMeasuredWidth()

*

childCount;

setMeasuredDimension(measuredWidth,

heightSpaceSize);

}

else

{

final

View

childView

=

getChildAt(0);

measuredWidth

=

childView.getMeasuredWidth()

*

childCount;

measuredHeight

=

childView.getMeasuredHeight();

setMeasuredDimension(measuredWidth,

measuredHeight);

}

}

@Override

protected

void

onLayout(boolean

changed,

int

l,

int

t,

int

r,

int

b)

{

int

childLeft

=

0;

final

int

childCount

=

getChildCount();

mChildrenSize

=

childCount;

for

(int

i

=

0;

i

<

childCount;

i++)

{

final

View

childView

=

getChildAt(i);

if

(childView.getVisibility()

!=

View.GONE)

{

final

int

childWidth

=

childView.getMeasuredWidth();

mChildWidth

=

childWidth;

childView.layout(childLeft,

0,

childLeft

+

childWidth,

childView.getMeasuredHeight());

childLeft

+=

childWidth;

}

}

}

private

void

smoothScrollBy(int

dx,

int

dy)

{

mScroller.startScroll(getScrollX(),

0,

dx,

0,

500);

invalidate();

}

@Override

public

void

computeScroll()

{

if

(mSputeScrollOffset())

{

scrollTo(mScroller.getCurrX(),

mScroller.getCurrY());

postInvalidate();

}

}

@Override

protected

void

onDetachedFromWindow()

{

mVelocityTracker.recycle();

super.onDetachedFromWindow();

}

}這個情況的攔截條件就是父容器在滑動過程中水平距離差比垂直距離差大,那么就進(jìn)行攔截,否則就不攔截,繼續(xù)傳遞事件。內(nèi)部攔截法內(nèi)部攔截法是指父容器不攔截任何事件,所有的事件都傳遞給子元素,如果子元素需要此事件就直接消耗掉,否則就交給父容器進(jìn)行處理,這種方法和Android中的事件分發(fā)機(jī)制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起來較外部攔截法復(fù)雜。偽代碼如下:

@Override

public

boolean

dispatchTouchEvent(MotionEvent

event)

{

int

x

=

(int)

event.getX();

int

y

=

(int)

event.getY();

switch

(event.getAction())

{

case

MotionEvent.ACTION_DOWN:

{

mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);

break;

}

case

MotionEvent.ACTION_MOVE:

{

int

deltaX

=

x

-

mLastX;

int

deltaY

=

y

-

mLastY;

if

(父容器需要此類點擊事件)

{

mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(false);

}

break;

}

case

MotionEvent.ACTION_UP:

{

break;

}

default:

break;

}

mLastX

=

x;

mLastY

=

y;

return

super.dispatchTouchEvent(event);

}當(dāng)子元素調(diào)用requestDisallowInterceptTouchEvent(false)方法時,父元素才能繼續(xù)攔截所需的事件。前面是用自定義類似的ViewPager,現(xiàn)在重寫一個ListView,我們可以自定義一個ListView,叫做ListViewEx,然后對內(nèi)部攔截法的模板代碼進(jìn)行修改即可。public

class

ListViewEx

extends

ListView

{

private

static

final

String

TAG

=

"ListViewEx";

private

HorizontalScrollViewEx2

mHorizontalScrollViewEx2;

//

分別記錄上次滑動的坐標(biāo)

private

int

mLastX

=

0;

private

int

mLastY

=

0;

public

ListViewEx(Context

context)

{

super(context);

}

public

ListViewEx(Context

context,

AttributeSet

attrs)

{

super(context,

attrs);

}

public

ListViewEx(Context

context,

AttributeSet

attrs,

int

defStyle)

{

super(context,

attrs,

defStyle);

}

public

void

setHorizontalScrollViewEx2(

HorizontalScrollViewEx2

horizontalScrollViewEx2)

{

mHorizontalScrollViewEx2

=

horizontalScrollViewEx2;

}

@Override

public

boolean

dispatchTouchEvent(MotionEvent

event)

{

int

x

=

(int)

event.getX();

int

y

=

(int)

event.getY();

switch

(event.getAction())

{

case

MotionEvent.ACTION_DOWN:

{

mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);

break;

}

case

MotionEvent.ACTION_MOVE:

{

int

deltaX

=

x

-

mLastX;

int

deltaY

=

y

-

mLastY;

Log.d(TAG,

"dx:"

+

deltaX

+

"

dy:"

+

deltaY);

if

(Math.abs(deltaX)

>

Math.abs(deltaY))

{

mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(false);

}

break;

}

case

MotionEvent.ACTION_UP:

{

break;

}

default:

break;

}

mLastX

=

x;

mLastY

=

y;

return

super.dispatchTouchEvent(event);

}

}同時對于包含ListViewEx外部布局進(jìn)行修改,在onInterceptTouchEvent事件上不進(jìn)行攔截public

class

HorizontalScrollViewEx2

extends

ViewGroup

{

private

static

final

String

TAG

=

"HorizontalScrollViewEx2";

private

int

mChildrenSize;

private

int

mChildWidth;

private

int

mChildIndex;

//

分別記錄上次滑動的坐標(biāo)

private

int

mLastX

=

0;

private

int

mLastY

=

0;

//

分別記錄上次滑動的坐標(biāo)(onInterceptTouchEvent)

private

int

mLastXIntercept

=

0;

private

int

mLastYIntercept

=

0;

private

Scroller

mScroller;

private

VelocityTracker

mVelocityTracker;

public

HorizontalScrollViewEx2(Context

context)

{

super(context);

init();

}

public

HorizontalScrollViewEx2(Context

context,

AttributeSet

attrs)

{

super(context,

attrs);

init();

}

public

HorizontalScrollViewEx2(Context

context,

AttributeSet

attrs,

int

defStyle)

{

super(context,

attrs,

defStyle);

init();

}

private

void

init()

{

mScroller

=

new

Scroller(getContext());

mVelocityTracker

=

VelocityTracker.obtain();

}

@Override

public

boolean

onInterceptTouchEvent(MotionEvent

event)

{

int

x

=

(int)

event.getX();

int

y

=

(int)

event.getY();

int

action

=

event.getAction();

if

(action

==

MotionEvent.ACTION_DOWN)

{

mLastX

=

x;

mLastY

=

y;

if

(!mScroller.isFinished())

{

mScroller.abortAnimation();

return

true;

}

return

false;

}

else

{

return

true;

}

}

@Override

public

boolean

onTouchEvent(MotionEvent

event)

{

Log.d(TAG,

"onTouchEvent

action:"

+

event.getAction());

mVelocityTracker.addMovement(event);

int

x

=

(int)

event.getX();

int

y

=

(int)

event.getY();

switch

(event.getAction())

{

case

MotionEvent.ACTION_DOWN:

{

if

(!mScroller.isFinished())

{

mScroller.abortAnimation();

}

break;

}

case

MotionEvent.ACTION_MOVE:

{

int

deltaX

=

x

-

mLastX;

int

deltaY

=

y

-

mLastY;

Log.d(TAG,

"move,

deltaX:"

+

deltaX

+

"

deltaY:"

+

deltaY);

scrollBy(-deltaX,

0);

break;

}

case

MotionEvent.ACTION_UP:

{

int

scrollX

=

getScrollX();

int

scrollToChildIndex

=

scrollX

/

mChildWidth;

Log.d(TAG,

"current

index:"

+

scrollToChildIndex);

mVelocityTputeCurrentVelocity(1000);

float

xVelocity

=

mVelocityTracker.getXVelocity();

if

(Math.abs(xVelocity)

>=

50)

{

mChildIndex

=

xVelocity

>

0

?

mChildIndex

-

1

:

mChildIndex

+

1;

}

else

{

mChildIndex

=

(scrollX

+

mChildWidth

/

2)

/

mChildWidth;

}

mChildIndex

=

Math.max(0,

Math.min(mChildIndex,

mChildrenSize

-

1));

int

dx

=

mChildIndex

*

mChildWidth

-

scrollX;

smoothScrollBy(dx,

0);

mVelocityTracker.clear();

Log.d(TAG,

"index:"

+

scrollToChildIndex

+

"

dx:"

+

dx);

break;

}

default:

break;

}

mLastX

=

x;

mLastY

=

y;

return

true;

}

@Override

protected

void

onMeasure(int

widthMeasureSpec,

int

heightMeasureSpec)

{

super.onMeasure(widthMeasureSpec,

heightMeasureSpec);

int

measuredWidth

=

0;

int

measuredHeight

=

0;

final

int

childCount

=

getChildCount();

measureChildren(widthMeasureSpec,

heightMeasureSpec);

int

widthSpaceSize

=

MeasureSpec.getSize(widthMeasureSpec);

int

widthSpecMode

=

MeasureSpec.getMode(widthMeasureSpec);

int

heightSpaceSize

=

MeasureSpec.getSize(heightMeasureSpec);

int

heightSpecMode

=

MeasureSpec.getMode(heightMeasureSpec);

if

(childCount

==

0)

{

setMeasuredDimension(0,

0);

}

else

if

(heightSpecMode

==

MeasureSpec.AT_MOST)

{

final

View

childView

=

getChildAt(0);

measuredHeight

=

childView.getMeasuredHeight();

setMeasuredDimension(widthSpaceSize,

childView.getMeasuredHeight());

}

else

if

(widthSpecMode

==

MeasureSpec.AT_MOST)

{

fin

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論