![【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第1頁](http://file4.renrendoc.com/view/c97e542ea0aee46cc6409167568b0786/c97e542ea0aee46cc6409167568b07861.gif)
![【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第2頁](http://file4.renrendoc.com/view/c97e542ea0aee46cc6409167568b0786/c97e542ea0aee46cc6409167568b07862.gif)
![【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第3頁](http://file4.renrendoc.com/view/c97e542ea0aee46cc6409167568b0786/c97e542ea0aee46cc6409167568b07863.gif)
![【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第4頁](http://file4.renrendoc.com/view/c97e542ea0aee46cc6409167568b0786/c97e542ea0aee46cc6409167568b07864.gif)
![【移動應(yīng)用開發(fā)技術(shù)】Android觸摸事件的應(yīng)用_第5頁](http://file4.renrendoc.com/view/c97e542ea0aee46cc6409167568b0786/c97e542ea0aee46cc6409167568b07865.gif)
版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 一年級上冊數(shù)學(xué)聽評課記錄《7.3 有幾瓶牛奶(4)》北師大版
- 蘇教版小學(xué)數(shù)學(xué)二年級上乘法口算試題
- 公司廚師聘用合同范本
- 任務(wù)二貿(mào)易合同范本
- 2022年新課標(biāo)八年級上冊歷史第一單元中國開始淪為半殖民地半封建社會1-3課共3課時聽課評課記錄
- 2025年度股權(quán)增資擴(kuò)股協(xié)議-創(chuàng)新科技研發(fā)合作
- 2025年度返點合作協(xié)議版:人力資源服務(wù)銷售返利合作方案
- 2025年度污水管安裝工程進(jìn)度與結(jié)算合同
- 2025年度股東對公司無息借款及財務(wù)支持合同
- 2025年度老式摩托車俱樂部會員權(quán)益續(xù)費合同
- 自然辯證法概論(新)課件
- 基層醫(yī)療機(jī)構(gòu)基本情況調(diào)查報告
- 六西格瑪(6Sigma)詳解及實際案例分析
- 機(jī)械制造技術(shù)-成都工業(yè)學(xué)院中國大學(xué)mooc課后章節(jié)答案期末考試題庫2023年
- 住院患者發(fā)生管路非計劃性拔管應(yīng)急預(yù)案及處理流程應(yīng)急預(yù)案
- 電解槽檢修施工方案
- 正常分娩 分娩機(jī)制 助產(chǎn)學(xué)課件
- 廣東縣級農(nóng)商銀行聯(lián)社高管候選人公開競聘筆試有關(guān)事項上岸提分題庫3套【500題帶答案含詳解】
- 中國成人住院患者高血糖管理目標(biāo)專家共識課件
- 讀書分享-精力管理課件
- 新上崗干部的90天轉(zhuǎn)身計劃課件
評論
0/150
提交評論