




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
【移動應用開發(fā)技術(shù)】Android觸摸事件的應用
常見的滑動沖突場景常見的滑動沖突場景可以簡單分為以下三種:場景1:外部滑動方向和內(nèi)部滑動方向不一致場景2:外部滑動方向和內(nèi)部滑動方向一致場景3:上面兩種情況的嵌套如圖:場景1,主要是將ViewPager和Fragment配合使用所組成的頁面滑動效果,主流應用幾乎都會使用這個效果。在這個效果中可以通過左右滑動來切換頁面,而每個頁面內(nèi)部往往又是一個ListView,所以就造成了滑動沖突,但是在ViewPager內(nèi)部處理了這種滑動沖突,因此在采用ViewPager時我們就無須關(guān)注這個問題,而如果把ViewPager換成ScrollView,那就必須自己手動處理,不然造成的結(jié)果就是內(nèi)外兩層只能一層能夠滑動。場景2,就復雜一點,當內(nèi)外兩層都在同一個方向可以滑動的時候,顯然存在邏輯問題。因為當手指開始滑動的時候,系統(tǒng)無法知道用戶到底是想讓哪一層滑動,所以當手指滑動的時候就會出現(xiàn)問題,要么只有一層滑動,要么就是內(nèi)外兩層都滑動但很卡頓。場景3,是場景1和場景2兩種情況的嵌套,顯得更復雜了。比如外部有一個SlideMenu效果,內(nèi)部有一個ViewPager,ViewPager的每一個頁面中又是一個ListView。雖然場景3滑動沖突看起來很復雜,但都是幾個單一的滑動沖突的疊加,因此需要一一拆解開來即可。滑動沖突的處理規(guī)則一般來說,不管滑動沖突有多么復雜,它都有既定的規(guī)則,根據(jù)這些規(guī)則我們就可以選擇合適的方法去處理。對于場景1,它的處理規(guī)則就是:當用戶左右滑動時,需要讓外部的View攔截點擊事件,當用戶上下滑動,需要讓內(nèi)部View攔截點擊事件。具體來說就是根據(jù)滑動是水平滑動還是豎直滑動來判斷到底是由誰來攔截事件。如圖:簡單來說,就是根據(jù)水平方向和豎直方向的距離差來判斷,如果是Dx>Dy,那么則是水平滑動,如果是Dy>Dx,那么則是豎直滑動。場景2,則是比較特殊,它無法根據(jù)滑動的角度,距離差以及速度差來做判斷。這個時候就需要從業(yè)務上找到突破點,比如,當處于某種狀態(tài)時需要外部View響應用戶的滑動,而處于另外一種狀態(tài)時需要內(nèi)部View來響應View的滑動對于場景3的話,它的滑動規(guī)則也更復雜,和場景2一樣,同樣是從業(yè)務上找到突破點。外部攔截法外部攔截法是指點擊事件都是先經(jīng)過父容器的攔截處理,如果父容器需要此事件就攔截,如果不需要此事件,就不攔截了,這樣就可以解決滑動沖突的問題,外部攔截法需要重寫父容器的onInterceptTouchEvent方法,在內(nèi)部做相應的攔截即可,偽代碼如下:
@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
(父容器需要點擊當前事件)
{
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;
//
分別記錄上次滑動的坐標
private
int
mLastX
=
0;
private
int
mLastY
=
0;
//
分別記錄上次滑動的坐標(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();
}
}這個情況的攔截條件就是父容器在滑動過程中水平距離差比垂直距離差大,那么就進行攔截,否則就不攔截,繼續(xù)傳遞事件。內(nèi)部攔截法內(nèi)部攔截法是指父容器不攔截任何事件,所有的事件都傳遞給子元素,如果子元素需要此事件就直接消耗掉,否則就交給父容器進行處理,這種方法和Android中的事件分發(fā)機制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起來較外部攔截法復雜。偽代碼如下:
@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);
}當子元素調(diào)用requestDisallowInterceptTouchEvent(false)方法時,父元素才能繼續(xù)攔截所需的事件。前面是用自定義類似的ViewPager,現(xiàn)在重寫一個ListView,我們可以自定義一個ListView,叫做ListViewEx,然后對內(nèi)部攔截法的模板代碼進行修改即可。public
class
ListViewEx
extends
ListView
{
private
static
final
String
TAG
=
"ListViewEx";
private
HorizontalScrollViewEx2
mHorizontalScrollViewEx2;
//
分別記錄上次滑動的坐標
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外部布局進行修改,在onInterceptTouchEvent事件上不進行攔截public
class
HorizontalScrollViewEx2
extends
ViewGroup
{
private
static
final
String
TAG
=
"HorizontalScrollViewEx2";
private
int
mChildrenSize;
private
int
mChildWidth;
private
int
mChildIndex;
//
分別記錄上次滑動的坐標
private
int
mLastX
=
0;
private
int
mLastY
=
0;
//
分別記錄上次滑動的坐標(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)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- DB3709T 039-2025 泰山靈芝-羊肚菌周年輪作栽培技術(shù)規(guī)程
- 福建裝配式鋼板倉施工方案
- 進入自然保護區(qū)施工方案
- 氧氣管道脫脂施工方案
- 采光井加陽光房施工方案
- 街道巷口硬化施工方案
- 吉林展會裝潢施工方案
- 耐高溫超輕硅酸鈣隔熱保濕材料項目風險識別與評估綜合報告
- 馬鞍山打地熱井施工方案
- 智研咨詢發(fā)布:中國城市礦產(chǎn)行業(yè)市場現(xiàn)狀及投資前景分析報告
- 機電控制與可編程序控制器課程設(shè)計
- 布朗德戰(zhàn)略導向的薪酬管理體系
- SOP標準作業(yè)指導書樣板
- 食品經(jīng)營餐飲操作流程(共1頁)
- JTS 144-1-2010 港口工程荷載規(guī)范
- 產(chǎn)液剖面介紹
- 彎矩二次分配法EXCEL計算
- 美國UNF和unc螺紋標準
- 童話故事《老鼠搬雞蛋》.ppt
- 河北省省直行政事業(yè)單位資產(chǎn)(房屋)租賃合同書(共7頁)
- 220kV、110kV設(shè)備基礎(chǔ)施工方案
評論
0/150
提交評論