欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Android實(shí)踐之ScrollView中滑動(dòng)沖突處理解決方案

 更新時(shí)間:2016年12月17日 17:04:20   作者:王三的貓阿德  
涉及到了ViewPager,MapView,ListView,就需要ScrollView來(lái)做一下支援,這篇文章主要介紹了淺談Android實(shí)踐之ScrollView中滑動(dòng)沖突處理解決方案,有需要的可以來(lái)了解一下。

1. 前言

 在Android開發(fā)中,如果是一些簡(jiǎn)單的布局,都很容易搞定,但是一旦涉及到復(fù)雜的頁(yè)面,特別是為了兼容小屏手機(jī)而使用了ScrollView以后,就會(huì)出現(xiàn)很多點(diǎn)擊事件的沖突,最經(jīng)典的就是ScrollView中嵌套了ListView。我想大部分剛開始接觸Android的同學(xué)們都踩到過(guò)這個(gè)坑,這一篇文章就從最近做的一個(gè)項(xiàng)目講起,然后在過(guò)程中提供一些解決沖突的思路。

2. 項(xiàng)目起始

項(xiàng)目有一個(gè)頁(yè)面,涉及到了ViewPager,MapView,ListView,也就是說(shuō)在一個(gè)頁(yè)面中,會(huì)有這三個(gè)View,很明顯,屏幕無(wú)法完全顯示,需要ScrollView來(lái)做一下支援,就引入了ScrollView作為外層的容器。但是由于這個(gè)頁(yè)面的數(shù)據(jù)展示需要做到用戶手動(dòng)下拉刷新,于是又引入了官方的SwipeRefreshLayout。

于是這個(gè)頁(yè)面的布局就成了這樣子。如下圖(細(xì)節(jié)布局忽略)。

圖-1 布局圖

加入了ScrollView和SwipeRefreshLayout之后引入了新的問(wèn)題,就是各個(gè)控件之間的事件沖突,嵌套在ScrollView中的ViewPager、MapView、ListView都需要能夠正確的處理點(diǎn)擊事件,特別是ListView,需求要求它在ScrollView中可以滑動(dòng),兩種滑動(dòng)混淆在一起,不是特別好處理。

問(wèn)題提出來(lái)了,下面直接看解決思路。

3. 解決滑動(dòng)沖突的思路

在ViewGroup中有個(gè)方法叫requestDisallowInterceptTouchEvent(boolean disallowIntercept),這個(gè)方法可以用來(lái)控制該ViewGroup是否截?cái)帱c(diǎn)擊事件。我們解決滑動(dòng)沖突的時(shí)候,其實(shí)就是在某個(gè)時(shí)機(jī)去調(diào)用這個(gè)方法,讓父布局不截?cái)帱c(diǎn)擊事件,將點(diǎn)擊事件傳遞到子View,讓相關(guān)的子View去處理。 

下面就是關(guān)于在項(xiàng)目中處理各種點(diǎn)擊事件沖突的一些例子和思考。處理的方法只是提供一種思路,可能并不是最優(yōu)的方法,肯定存在其他思路的解決方案。 

以下處理滑動(dòng)沖突的方案都是在子View的OnTouchListener里面進(jìn)行處理,并沒(méi)有去復(fù)寫控件的點(diǎn)擊事件處理過(guò)程,在使用中還是比較方便的。

3.1 MapView地圖頁(yè)面滑動(dòng)沖突

MapView與ScrollView的沖突主要在于,當(dāng)用戶點(diǎn)擊到MapView地圖并且滑動(dòng)的時(shí)候,希望由地圖Map去處理點(diǎn)擊事件,并包括后續(xù)的滑動(dòng)事件、雙手指縮放地圖等等。 

在ScrollView中,是會(huì)默認(rèn)截?cái)帱c(diǎn)擊事件的,導(dǎo)致用戶點(diǎn)擊到地圖后,地圖基本是沒(méi)有反應(yīng),更別談雙手指縮放地圖了。 

用戶手指點(diǎn)擊到地圖,并且滑動(dòng)的時(shí)候,很難確定用戶是要ScrollView上下滑動(dòng)還是操控地圖內(nèi)容滑動(dòng),所以我簡(jiǎn)單的認(rèn)為,只要用戶手指點(diǎn)擊到地圖,就是要對(duì)地圖進(jìn)行操作;當(dāng)用戶手指抬起,就認(rèn)為用戶不需要操作地圖了。 

解決思路也很簡(jiǎn)單,就是在用戶點(diǎn)擊到地圖或者滑動(dòng)地圖時(shí)候,讓ScrollView不截?cái)帱c(diǎn)擊事件,并傳遞給子View處理,也就是地圖去處理點(diǎn)擊事件;當(dāng)用戶手指抬起的時(shí)候,將ScrollView的狀態(tài)恢復(fù)至之前的狀態(tài),也就是ScrollView可以截?cái)帱c(diǎn)擊事件。

我使用的是百度地圖,直接上代碼,更容易理解。

mMapView.getChildAt(0).setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_UP){
          //允許ScrollView截?cái)帱c(diǎn)擊事件,ScrollView可滑動(dòng)
          mScrollView.requestDisallowInterceptTouchEvent(false);
        }else{
          //不允許ScrollView截?cái)帱c(diǎn)擊事件,點(diǎn)擊事件由子View處理
          mScrollView.requestDisallowInterceptTouchEvent(true);
        }
        return false;
      }
    });

3.2 ViewPager滑動(dòng)沖突解決

在這個(gè)項(xiàng)目中,ViewPager在頁(yè)面最頂層,如果只是ScrollView里面嵌套了ViewPager,因?yàn)檫@兩個(gè)控件是不同方向的滑動(dòng)事件,所以基本不會(huì)出現(xiàn)沖突。 

但是由于引入了SwipeRefreshLayout,我發(fā)現(xiàn)在滑動(dòng)ViewPager的時(shí)候,很容易就觸發(fā)了SwipeRefreshLayout的下來(lái)刷新,進(jìn)而有可能阻斷了ViewPager的左右滑動(dòng)效果,體驗(yàn)很不好。而且在滑動(dòng)ViewPager的過(guò)程中,用戶滑動(dòng)肯定不是一直水平的,會(huì)有一定程度向上向下的滑動(dòng)。 

ViewPager處理沖突和地圖處理沖突有些不同,因?yàn)楫?dāng)用戶點(diǎn)擊到ViewPager,在滑動(dòng)過(guò)程中,基本就可以猜測(cè)到用戶是想左右滑動(dòng)ViewPager還是上下滑動(dòng)ScrollView(或者下拉刷新),這就不能像地圖一樣,在點(diǎn)擊到ViewPager就禁止ScrollView截?cái)帱c(diǎn)擊事件(或者SwipeRefreshLayout下拉刷新功能),需要在滑動(dòng)過(guò)程中做出判斷。 

解決思路就是,設(shè)定一個(gè)閾值,一旦用戶在X軸也就是橫向滑動(dòng)距離超過(guò)這個(gè)閾值,我就認(rèn)為用戶是要左右滑動(dòng)ViewPager,就禁止ScrollView截?cái)帱c(diǎn)擊事件同時(shí)設(shè)置SwipeRefreshLayout不能下拉刷新。當(dāng)用戶抬起手指,就認(rèn)為用戶對(duì)ViewPager的操作已經(jīng)完畢,將ScrollView和SwipeRefreshLayout狀態(tài)恢復(fù)。

 mViewPager.setOnTouchListener(new View.OnTouchListener() {
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();

    if(action == MotionEvent.ACTION_DOWN) {
      // 記錄點(diǎn)擊到ViewPager時(shí)候,手指的X坐標(biāo)
      mLastX = event.getX();
    }
    if(action == MotionEvent.ACTION_MOVE) {
      // 超過(guò)閾值
      if(Math.abs(event.getX() - mLastX) > 60f) {
        mRefreshLayout.setEnabled(false);
        mScrollView.requestDisallowInterceptTouchEvent(true);
      }
    }
    if(action == MotionEvent.ACTION_UP) {
      // 用戶抬起手指,恢復(fù)父布局狀態(tài)
      mScrollView.requestDisallowInterceptTouchEvent(false);
      mRefreshLayout.setEnabled(true);
    }
    return false;
  }
});

用戶點(diǎn)擊到ViewPager時(shí)候,記錄下點(diǎn)擊位置的X坐標(biāo),當(dāng)用戶滑動(dòng)過(guò)程中,如果在X軸上面的滑動(dòng)超過(guò)閾值(我寫的是60f,這個(gè)可以在實(shí)際使用中自行設(shè)置最佳的閾值),就禁止ScrollView截?cái)帱c(diǎn)擊事件,同時(shí)設(shè)置不可下拉刷新。當(dāng)用戶手指離開屏幕,將ScrollView和SwipeRefreshLayout的狀態(tài)恢復(fù)。

3.3 ListView滑動(dòng)沖突解決

在ScrollView中嵌套ListView,會(huì)出現(xiàn)各種各樣奇怪的問(wèn)題。比如說(shuō)ListView顯示有問(wèn)題,可能才一兩個(gè)Item那么高,并沒(méi)有完全的展開。網(wǎng)上流傳解決這種問(wèn)題的方法會(huì)有兩種。

  • 根據(jù)展示數(shù)據(jù)的個(gè)數(shù)乘以每一個(gè)Item的高度,計(jì)算出ListView的總體高度,然后動(dòng)態(tài)的設(shè)置ListView的高度
  • 復(fù)寫ListView的onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,讓ListView完全展開

這兩種方法都可以解決ListView展示不完全的問(wèn)題,而且也可以滑動(dòng)(其實(shí)是使用ScrollView的滑動(dòng)效果),但是有一個(gè)最大的遺憾,就是ListView里面的View不能復(fù)用了。因?yàn)檫@兩種方法都是算出了ListView的全部高度,然后將ListView控件的高度設(shè)置成這個(gè)高度,這樣的話,ListView就相當(dāng)于一個(gè)LinearLayout的布局了,失去了復(fù)用View的優(yōu)勢(shì),而且在某些場(chǎng)景可能還沒(méi)有LinearLayout好用,更甚的是,如果有大量圖片的話,很容易就OOM了,這是在研發(fā)過(guò)程中最不希望看見的。 

可以參考一下美團(tuán),美團(tuán)的首頁(yè),就是一個(gè)ScrollView,下滑的時(shí)候會(huì)發(fā)現(xiàn),并不能無(wú)限向下滑動(dòng),到了底部會(huì)提醒跳轉(zhuǎn)到一個(gè)二級(jí)頁(yè)面去查看全部的團(tuán)購(gòu)信息。這是處理ScrollView里面嵌套類似ListView列表布局的時(shí)候的一種解決方案。
但是在我遇見的這個(gè)項(xiàng)目里面,并不能這樣處理。 

上面的提到的兩種解決思路很明確,如果想要ListView正常展示就需要確定ListView的高度,這個(gè)很重要。 

所以首先,我需要在布局文件中設(shè)置ListView的高度,是一個(gè)明確的數(shù)值。設(shè)置高度之后,如果ListView中的數(shù)據(jù)的Item總高度超過(guò)ListView所設(shè)置的高度,就可以復(fù)用View了。但是這只是解決了ListView的顯示問(wèn)題,ListView與ScrollView的滑動(dòng)沖突,并沒(méi)有解決。 

要解決滑動(dòng)的沖突,最主要的是確定禁止ScrollView截?cái)帱c(diǎn)擊事件的時(shí)機(jī),然后來(lái)分析有哪些時(shí)機(jī)。

  • ScrollView在未滑動(dòng)到底部時(shí)候,如果點(diǎn)擊并滑動(dòng)ListView時(shí)候,ListView是不能滑動(dòng)的,不禁止。
  • 如果ScrollView滑動(dòng)到底部,且ListView已經(jīng)到頂部,繼續(xù)下拉ListView,其實(shí)會(huì)拉動(dòng)ScrollView,不禁止。
  • 如果ScrollView滑動(dòng)到底部,用戶向上滑,ListView滑動(dòng),禁止ScrollView截?cái)帱c(diǎn)擊事件能力

很明顯,在判斷禁止ScrollView截?cái)帱c(diǎn)擊事件時(shí)機(jī)的時(shí)候,需要知道ScrollView是否滑動(dòng)到了底部。于是,重寫了ScrollView的ScrollChanged()方法,來(lái)判斷ScrollView是否滑動(dòng)到底部(SDK API 23版本中ScrollView可以設(shè)置setOnScrollChangeListener()來(lái)監(jiān)聽滑動(dòng)的變化,但是之前版本不支持,為了兼容,自己需要重寫)。

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt){
  super.onScrollChanged(l,t,oldl,oldt);
  // 滑動(dòng)的距離加上本身的高度與子View的高度對(duì)比
  if(t + getHeight() >= getChildAt(0).getMeasuredHeight()){
    // ScrollView滑動(dòng)到底部
    if(mOnScrollToBottomListener != null) {
      mOnScrollToBottomListener.onScrollToBottom();
    }
  } else {
    if(mOnScrollToBottomListener != null) {
      mOnScrollToBottomListener.onNotScrollToBottom();
    }
  }
}

public void setScrollToBottomListener(OnScrollToBottomListener listener) {
  this.mOnScrollToBottomListener = listener;
}

public interface OnScrollToBottomListener {
  void onScrollToBottom();
  void onNotScrollToBottom();
}

有了思路,而且ScrollView滑動(dòng)到底部的標(biāo)識(shí)也可以拿到,下面就可以直接來(lái)解決滑動(dòng)沖突了,直接看代碼。

mScrollView.setScrollToBottomListener(new BottomScrollView.OnScrollToBottomListener() {
  @Override
  public void onScrollToBottom() {
    isSvToBottom = true;
  }

  @Override
  public void onNotScrollToBottom() {
    isSvToBottom = false;
  }
});

mListView.setOnTouchListener(new View.OnTouchListener() {
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();

    if(action == MotionEvent.ACTION_DOWN) {
      mLastY = event.getY();
    }
    if(action == MotionEvent.ACTION_MOVE) {
      int top = mListView.getChildAt(0).getTop();
      float nowY = event.getY();
      if(!isSvToBottom) {
        // 允許scrollview攔截點(diǎn)擊事件, scrollView滑動(dòng)
        mScrollView.requestDisallowInterceptTouchEvent(false);
      } else if(top == 0 && nowY - mLastY > THRESHOLD_Y_LIST_VIEW) {
        // 允許scrollview攔截點(diǎn)擊事件, scrollView滑動(dòng)
        mScrollView.requestDisallowInterceptTouchEvent(false);
      } else {
        // 不允許scrollview攔截點(diǎn)擊事件, listView滑動(dòng)
        mScrollView.requestDisallowInterceptTouchEvent(true);
      }
    }
    return false;
  }
});

相對(duì)于其他的控件來(lái)說(shuō),ListView和ScrollView之間的滑動(dòng)沖突更難解決,但其實(shí)在實(shí)際使用中并不推薦ScrollView里面嵌套ListView,一旦業(yè)務(wù)復(fù)雜,很容易出現(xiàn)各種UI和業(yè)務(wù)邏輯沖突的錯(cuò)誤。

4. 運(yùn)行效果

由于地圖加入比較麻煩,所以在Demo中并沒(méi)有引入地圖??匆幌逻\(yùn)行效果。

圖-2 運(yùn)行效果

5. 總結(jié)

本篇文章只是提供一種解決方法的思路,在具體的場(chǎng)景下,交互往往是貼合具體業(yè)務(wù)需求的。但是不管怎么樣,找出點(diǎn)擊事件截?cái)嗪吞幚淼臅r(shí)機(jī)是最重要的,圍繞這個(gè)關(guān)鍵點(diǎn),總能找出相應(yīng)的解決方法。

附上Demo工程地址:Demo

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android使用 Retrofit 2.X 上傳多文件和多表單示例

    Android使用 Retrofit 2.X 上傳多文件和多表單示例

    本篇文章主要介紹了Android使用 Retrofit 2.X 上傳多文件和多表單示例,具有一定的參考價(jià)值,有興趣的小伙伴一起來(lái)了解一下
    2017-08-08
  • TabLayout標(biāo)題文字不顯示的解決操作

    TabLayout標(biāo)題文字不顯示的解決操作

    這篇文章主要介紹了TabLayout標(biāo)題文字不顯示的解決操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-08-08
  • Eclipse開發(fā)環(huán)境導(dǎo)入android sdk的sample中的源碼

    Eclipse開發(fā)環(huán)境導(dǎo)入android sdk的sample中的源碼

    初學(xué)Android編程,Android SDK中提供的Sample代碼自然是最好的學(xué)習(xí)材料,需要的朋友可以參考下
    2012-12-12
  • android downsample降低音頻采樣頻率代碼

    android downsample降低音頻采樣頻率代碼

    這篇文章主要介紹了android downsample降低音頻采樣頻率代碼,需要的朋友可以參考下
    2014-02-02
  • android實(shí)現(xiàn)下拉菜單三級(jí)聯(lián)動(dòng)

    android實(shí)現(xiàn)下拉菜單三級(jí)聯(lián)動(dòng)

    這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)下拉菜單三級(jí)聯(lián)動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • Android實(shí)現(xiàn)自動(dòng)截圖腳本

    Android實(shí)現(xiàn)自動(dòng)截圖腳本

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)自動(dòng)截圖腳本,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Android讀取串口數(shù)據(jù)的操作指南

    Android讀取串口數(shù)據(jù)的操作指南

    在Android系統(tǒng)上讀取串口數(shù)據(jù)是一個(gè)常見的需求,特別是當(dāng)我們需要與硬件設(shè)備進(jìn)行通信時(shí),本文給大家介紹了Android讀取串口數(shù)據(jù)的操作指南,文中有詳細(xì)的步驟和代碼示例,幫助你更好地理解和實(shí)現(xiàn)串口通信,需要的朋友可以參考下
    2024-05-05
  • Android的Touch事件處理機(jī)制介紹

    Android的Touch事件處理機(jī)制介紹

    Android的Touch事件處理機(jī)制比較復(fù)雜,特別是在考慮了多點(diǎn)觸摸以及事件攔截之后,有需求的朋友可以參考下
    2012-11-11
  • 淺談Android PathMeasure詳解和應(yīng)用

    淺談Android PathMeasure詳解和應(yīng)用

    本篇文章主要介紹了淺談Android PathMeasure詳解和應(yīng)用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • 深入解析Android中的事件傳遞

    深入解析Android中的事件傳遞

    這篇文章主要介紹了關(guān)于Android中的事件傳遞,文中介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-03-03

最新評(píng)論