您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)Android應(yīng)用中的View出現(xiàn)滑動沖突如何解決,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
1、外部滑動方向和內(nèi)部滑動方向不一致
考慮這樣一種場景,開發(fā)中我們經(jīng)常使用ViewPager和Fragment配合使用所組成的頁面滑動效果,很多主流的應(yīng)用都會使用這樣的效果。在這種效果中,可以使用左右滑動來切換界面,而每一個(gè)界面里面往往又都是ListView這樣的控件。本來這種情況是存在滑動沖突的,只是ViewPager內(nèi)部處理了這種滑動沖突。如果我們不使用ViewPager而是使用ScrollView,那么滑動沖突就需要我們自己來處理,否者造成的后果就是內(nèi)外兩層只有一層能滑動。
情況1的解決思路
對于第一種情況的解決思路是這樣的:當(dāng)用戶左右滑動時(shí),需要讓外層的View攔截點(diǎn)擊事件。當(dāng)用戶上下滑動時(shí),需要讓內(nèi)部的View攔截點(diǎn)擊事件(外層的View不攔截點(diǎn)擊事件),這時(shí)候我們就可以根據(jù)它們的特性來解決滑動沖突。在這里我們可以根據(jù)滑動時(shí)水平滑動還是垂直滑動來判斷誰來攔截點(diǎn)擊事件。下面先介紹一種通用的解決滑動沖突的方法。
外部攔截法
外部攔截法是指:點(diǎn)擊事件都經(jīng)過父容器的攔截處理,如果父容器需要處理此事件就進(jìn)行攔截,否者不攔截交給子View進(jìn)行處理。這種方法比較符合點(diǎn)擊事件的分發(fā)機(jī)制。外部攔截法需要重寫父容器的onInterceptTouchEvent方法,在內(nèi)部做相應(yīng)的攔截即可。這種方法的偽代碼如下:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { int x=(int)ev.getX(); int y=(int)ev.getY(); boolean intercept=false; switch (ev.getAction()){ //按下事件不要攔截,否則后續(xù)事件都會給ViewGroup處理 case MotionEvent.ACTION_DOWN: intercept=false; break; case MotionEvent.ACTION_MOVE: //如果是橫向移動就進(jìn)行攔截,否則不攔截 int deltaX=x-mLastX; int deltaY=y-mLastY; if(父容器需要當(dāng)前點(diǎn)擊事件){ intercept=true; }else { intercept=false; } break; case MotionEvent.ACTION_UP: intercept=false; break; } mLastX = x; mLastY = y; return intercept; }
上面代碼是外部攔截法的典型邏輯,針對不同的滑動沖突,只需要修改父容器需要當(dāng)前點(diǎn)擊事件的條件即可,其他均不需要修改。我們在描述下:在onInterceptTouchEvent方法中,首先是ACTION_DOWN事件,父容器必須返回false,即不攔截ACTION_DOWN事件,這是因?yàn)橐坏└溉萜鲾r截ACTION_DOWN,那么后續(xù)的ACTION_MOVE和ACTION_UP都會直接交給父容器處理,這時(shí)候事件就沒法傳遞給子元素了;其次是ACTION_MOVE事件,這個(gè)事件可以根據(jù)需要來決定是否需要攔截。
下面來看一個(gè)具體的實(shí)例,這個(gè)實(shí)現(xiàn)模擬ViewPager的效果,我們定義一個(gè)全新的控件,名稱叫HorizontalScrollView。具體代碼如下:
1、我們先看Activity中的代碼:
public class MainActivity extends Activity{ private HorizontalScrollView mListContainer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { LayoutInflater inflater = getLayoutInflater(); mListContainer = (HorizontalScrollView) findViewById(R.id.container); final int screenWidth = MyUtils.getScreenMetrics(this).widthPixels; for (int i = 0; i < 3; i++) { ViewGroup layout = (ViewGroup) inflater.inflate( R.layout.content_layout, mListContainer, false); layout.getLayoutParams().width = screenWidth; TextView textView = (TextView) layout.findViewById(R.id.title); textView.setText("page " + (i + 1)); layout.setBackgroundColor(Color.rgb(255 / (i + 1), 255 / (i + 1), 0)); createList(layout); mListContainer.addView(layout); } } private void createList(ViewGroup layout) { ListView listView = (ListView) layout.findViewById(R.id.list); ArrayList<String> datas = new ArrayList<>(); for (int i = 0; i < 50; i++) { datas.add("name " + i); } ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.content_list_item, R.id.name, datas); listView.setAdapter(adapter); } }
在這個(gè)代碼中,我們創(chuàng)建了3個(gè)ListView然后將其添加到我們自定義控件的。這里HorizontalScrollView是父容器,ListView是子View。下面我們就使用外部攔截法來實(shí)現(xiàn)HorizontalScrollView,代碼如下:
/** * 橫向布局控件 * 模擬經(jīng)典滑動沖突 * 我們此處使用ScrollView來模擬ViewPager,那么必須手動處理滑動沖突,否則內(nèi)外兩層只能有一層滑動,那就是滑動沖突。另外內(nèi)部左右滑動,外部上下滑動也同樣屬于該類 */ public class HorizontalScrollView extends ViewGroup { //記錄上次滑動的坐標(biāo) private int mLastX = 0; private int mLastY = 0; private WindowManager wm; //子View的個(gè)數(shù) private int mChildCount; private int mScreenWidth; //自定義控件橫向?qū)挾? private int mMeasureWidth; //滑動加載下一個(gè)界面的閾值 private int mCrital; //滑動輔助類 private Scroller mScroller; //當(dāng)前展示的子View的索引 private int showViewIndex; public HorizontalScrollView(Context context){ this(context,null); } public HorizontalScrollView(Context context, AttributeSet attributeSet){ super(context,attributeSet); init(context); } /** * 初始化 * @param context */ public void init(Context context) { //讀取屏幕相關(guān)的長寬 wm = ((Activity)context).getWindowManager(); mScreenWidth = wm.getDefaultDisplay().getWidth(); mCrital=mScreenWidth/4; mScroller=new Scroller(context); showViewIndex=1; } /** * 重新事件攔截機(jī)制 * 我們分析了view的事件分發(fā),我們知道點(diǎn)擊事件的分發(fā)順序是 通過父布局分發(fā),如果父布局沒有攔截,即onInterceptTouchEvent返回false, * 才會傳遞給子View。所以我們就可以利用onInterceptTouchEvent()這個(gè)方法來進(jìn)行事件的攔截。來看一下代碼 * 此處使用外部攔截法 * @param ev * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int x=(int)ev.getX(); int y=(int)ev.getY(); boolean intercept=false; switch (ev.getAction()){ //按下事件不要攔截,否則后續(xù)事件都會給ViewGroup處理 case MotionEvent.ACTION_DOWN: intercept=false; if(!mScroller.isFinished()){ mScroller.abortAnimation(); intercept=true; } break; case MotionEvent.ACTION_MOVE: //如果是橫向移動就進(jìn)行攔截,否則不攔截 int deltaX=x-mLastX; int deltaY=y-mLastY; if(Math.abs(deltaX)>Math.abs(deltaY)){ intercept=true; }else { intercept=false; } break; case MotionEvent.ACTION_UP: intercept=false; break; } mLastX = x; mLastY = y; return intercept; } @Override public boolean onTouchEvent(MotionEvent 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; /** * scrollX是指ViewGroup的左側(cè)邊框和當(dāng)前內(nèi)容左側(cè)邊框之間的距離 */ int scrollX=getScrollX(); if(scrollX-deltaX>0 && (scrollX-deltaX)<=(mMeasureWidth-mScreenWidth)) { scrollBy(-deltaX, 0); } break; case MotionEvent.ACTION_UP: scrollX=getScrollX(); int dx; //計(jì)算滑動的差值,如果超過1/4就滑動到下一頁 int subScrollX=scrollX-((showViewIndex-1)*mScreenWidth); if(Math.abs(subScrollX)>=mCrital){ boolean next=scrollX>(showViewIndex-1)*mScreenWidth; if(showViewIndex<3 && next) { showViewIndex++; }else { showViewIndex--; } } dx=(showViewIndex - 1) * mScreenWidth - scrollX; smoothScrollByDx(dx); break; } mLastX = x; mLastY = y; return true; } /** * 緩慢滾動到指定位置 * @param dx */ private void smoothScrollByDx(int dx) { //在1000毫秒內(nèi)滑動dx距離,效果就是慢慢滑動 mScroller.startScroll(getScrollX(), 0, dx, 0, 1000); invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } }
從上面代碼中,我們看到我們只是很簡單的采用橫向滑動距離和垂直滑動距離進(jìn)行比較來判斷滑動方向。在滑動過程中,當(dāng)水平方向的距離大時(shí)就判斷為水平滑動,否者就是垂直滑動。
以上就是Android應(yīng)用中的View出現(xiàn)滑動沖突如何解決,小編相信有部分知識點(diǎn)可能是我們?nèi)粘9ぷ鲿姷交蛴玫降摹OM隳芡ㄟ^這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。