Android開發中,如何有效解決ScrollView和ListView衝突?

還有ViewPager GridView 感覺資料很少 而且大多是用了無效的。。

——————————————————————————————

謝謝各位熱心的答主解答!我確實有點沒描述清楚,其實主要是滑動的衝突,雖然是看過一些事件分發的處理,但項目遇到問題感覺還是解決不了。拿我最近的一個項目來說吧,進到主頁以後是可以左右滑動的,大家肯定都會想到側滑菜單SlidingMenu,那麼問題來了,產品要求左右的頁面是可以全屏的。。而通過我初步的使用以及去網上查資料,SlidingMenu的左右菜單全屏之後就不能滑動了,那麼我只能是監聽左右頁面的onTouch事件,問題又來了。。因為右邊的頁面又有一個ScrollView,是一些功能的菜單,點擊之後要跳轉到對應的界面,那麼肯定要有onClick事件,然而右界面已經有了onTouch且返回值是true,onClick又掛掉了。。改了一會onClick能用了,ScrollView又不能上下滑動了。。各種蛋疼


這個很常見,比如出門左轉打開企鵝,QQ聊天記錄列表,縱滑翻過不同好記錄,橫滑刪除記錄。但兩者並不衝突。

一句話概括核心思想:有一個方法,在當前view中獲得ViewParent,並請求父容器(臨時)不要攔截觸摸事件,當某個觸摸事件處理完後,再恢復父容器能攔截。

這個方法是(需要自己在子View中實現):

當然,接下來需要重寫下listView中onTouchEvent()方法。

舉個例子:如果子View要檢測橫滑,父容器檢測縱滑,很分裂對吧?

解決方案是:

當子容器檢測到橫滑的時候,禁止本該要豎滑的父容器,等子容器的橫滑事件結束,再恢復回來即可(反過來也成立)。

光說不貼代碼是耍流氓,這裡我這寫了個小例子:

http://blog.csdn.net/oyyj42/article/details/47333443


仔細看下這個方法


事實證明,還是有地方用的到的

~~我是分割線~~

雖然onTouchEvent可以解決,不過我真的不知道除了教學視頻以外哪裡還會遇到類似的嵌套情況


不要拿 ScrollView 嵌套 ListView 啊。你為什麼要這麼做呢?


監聽scroll,配合好onIntercept


題主只需要看一下Android的touch事件分發和攔截機制即可


恕我直言,感覺樓主的標題和問題描述牛頭不對馬嘴啊,身為程序員,樓主的邏輯思維和整理能力有待加強。。

仔細看了下你的描述,這個問題應該也不算什麼特麻煩的事兒,自定義右側頁面根布局的ViewGroup,重寫onInterceptTouchEvent方法,如果是橫向滑動這個事件就攔截掉自己處理,否則super.onIn....


ListView 和 ScrollView 的衝突真的是一個大問題。包括使用RecyclerView 也一樣會遇到這個問題。我們的 Android開發者納米學位的第二個項目,就要實現電影詳情信息的展示。 應用需要在一個 Activity 中展示電影劇情,若干個預告片,以及若干個評論。如下圖所示。

於是問題就來了,若干個要告片,若干個評論,如何展示在一個可以豎直滑動的界面裡面呢?

下面是我們的一個學員 AL-Arx7 經過自己的研究以後,發出的總結帖。特別分享給大家。

TL;DR: 重寫ListView 的 onMeasure方法,使Listview以為自己的高度很高很高,從而永遠不會滑動。 缺點:Listview 過長是非常影響性能。

---------------------------------------------------------------------------------------------

因為在電影的詳細界面中,內容太多導致無法,除了電影本身的信息外,還用到了兩個listview來顯示預告片與評論,所以最開始考慮在外層加入了一個ScrollView來顯示全部內容。 然後,抬頭就發現自己已經在一個大坑中。

明明感覺挺常用的格式居然有各種問題,ScrollView和listview 存在衝突。雖然ScrollView能讓我拉動屏幕看到更多,如但果在ScrollView中使用listview,就算listview中用adapter塞入了多份數據 也就只會顯示一份。 stackoverflow上各種吐槽和解決方法,就是不存在myListView.scrollEnabled = false;這樣簡單的方式。

消耗了我不少時間來找到一種合適的方法。作為紀念就把我找到,並且測試過的幾種方法大致說明下。

1.手動設置ListView的高度layout_height

如果手動設置 ListView的高度使之大於等於內容所需的高度就不會被收縮,符合我的要求,但是問題是如何自適應內容的高度。 stackoverflow上有人給出這樣的解決方法,調用靜態函數計算內容數量手動設置ListView的高度。

好處:簡單,不需要做XML上的修改。

壞處:對布局之類的限制較大只能 LinearLayout

注意事項: 雖然按照給出方法的大神要求:在設置Adapter之後調用;子控制項的布局為LinearLayout;但是 listAdapter.getCount會是0.因為還沒有數據。 因此我嘗試在onLoaderFinish 的swap之後調用,確實能實現,能開心的拉動了。 但是可能是高度計算上有缺陷 如果Listview中如果是TextView 且為多行,還是可能會出現被收縮的情況。如果哪位知道怎麼做請告訴我下

public class Utility {
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}

int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();

for (int i = 0; i &< listAdapter.getCount(); i++) { View listItem = listAdapter.getView(i, null, listView); if (listItem instanceof ViewGroup) { listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); } listItem.measure(0, 0); totalHeight += listItem.getMeasuredHeight(); } ViewGroup.LayoutParams params = listView.getLayoutParams(); params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); listView.setLayoutParams(params); } }

2.用 listview/recyclerview 代替ScrollView

將整個界面分成數塊。 以這個詳細頁面為例,電影的標題,日期,概述之類做一個XML, R.layout.Detailitem1; 預告片列表做一個XML R.layout.Detailitem2; 評論列表做一個XML R.layout.Detail_item3; 之後就類似Sunshine應用中 給ListView做adapter時候,根據類別導入不同的布局。

在 adapter中添加類別

private static final int VIEW_TYPE_Item1 = 1;
private static final int VIEW_TYPE_Item2 = 2;
private static final int VIEW_TYPE_Item3 = 3;
private static final int VIEW_TYPE_Count = 3; //類型數

實現 getItemViewType getViewTypeCount cursorAdapter中就是在newView方法中new方法中根據類別 LayoutInflater.from(context).inflate( R.layout.Detail_itemX, parent, false); 即可

好處:迴避了坑人的衝突問題。

壞處:好好的一個界面被拆成了好多塊,adapter的複雜度高了不少。

3.自定義ListView

stackoverflow上找到的另外種迴避的方法,原先的 ListView既然會衝突,那就自定義一個屏蔽衝突的。 重寫了 onMeasure 方法,修改了原版傳遞進入的heightMeasureSpec參數。 用 MeasureSpec.makeMeasureSpec 方法,以 AT_MOST 模式獲取了一個相當大的值,保證子類都能顯示出來。

好處:簡單,非常簡單。無論基於代碼量,還是修改難度。如果沒出現 myListView.scrollEnabled = false這樣的功能我應該就用這招了。

壞處:額,找到太遲浪費不少時間

MeasureSpec.AT_MOST:子視圖的大小最多是specSize中指定的值,也就是說不建議子視圖的大小超過specSize中給定的值。

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

public class UnScrollListView extends ListView {
public UnScrollListView(Context context) {
super(context);
}

public UnScrollListView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public UnScrollListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE &>&> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}

4.LinearLayout代替listview

按照@Walker 導師建議,做了下這個方法的測試。找到兩種方式實現。

方法1:

手動添加

LinearLayout linearLayout = (LinearLayout) findViewById(...);
LayoutInflater inflater = LayoutInflater.from(this);
for (item in arrayList) {
View view = inflater.inflate(R.layout.row, linearLayout, false);
// set item content in view
linearLayout.addView(view)}

這種方法只適合非常簡單的情況。看著有點蠢。

方法2:

給LinearLayout 一個adapter 我想找一個實現了CursorAdapter的LinearLayout 網上幾乎沒有。 下面的程序最多算是實驗性質,完全不能用問題很多。 看了下android的源碼,這個不僅沒有實現recycling,連OnItemClickListener都沒有實現。 使用後顯示一份數據,效果也有問題。 查看了源碼中的ItemClick事件也在別的類中實現。感覺要實現一個cursorAdapter需要實現不少東西的樣子。 不能多個繼承多個類好難受。花了這麼多時間搞不定,不開心啊。暫時繼續使用方法三自定義listView的方法處理。

參考資料:

Stack Overflow 上的解決方案

四種方案解決ScrollView嵌套ListView問題


去掉ScrollView,把其他布局都放在ListView的header和footer中。


還在用ScrollView和ListView?

NestedScrollView 和 RecyclerView 不更好?


也經常會看到這樣的提問,我的第一感覺是你的布局寫錯了,我從來沒有遇到過scrollview和listview嵌套的問題,因為不需要這樣的界面,要麼用scrollview要麼用listview要麼用recyclerview,除非方向不同,否則沒有共存的需要!


不要嵌套。


我記得stackoverflow上有個比較絕好的解決方案,那就是用linearlayout代替listview,上github上搜索linearlistview


slidingmenu的bug我已解決,歡迎查看我的博客,衝突的話自定義一個listview就可以啦

附上github地址,有詳細的博客加以講解 https://github.com/Mr-wangyong/slidingMenu.git


[Android]ScrollView,ListView,ExpandableListView,ViewPager各種嵌套demo


自定義listview /重寫on measure等方法 百度幾行代碼幫你搞定


一開始我看成布局衝突了,按題主的意思應該是事件衝突,子類試試調用這個方法:
requestDisallowInterceptTouchEvent(boolean b);
-------原回答
重寫ListView或GridView的onMeasure方法,在調用父類之前加上這一句,就可以放到ScrollView裡面去了。
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE &>&> 2, MeasureSpec.AT_MOST);


@sbbic 說的android的touch事件分發機制,這裡有一些資料講得都很詳細了。

Android ViewGroup攔截觸摸事件詳解

@hi大頭鬼hi 說的可以參看一個下拉刷新的例子,關於scroll和onIntercept中的一些相關應用

打造通用的Android下拉刷新組件(適用於ListView、GridView等各類View)

可能還會有一些ListView在Scrollview中無法顯示全的問題,和 @清沙 說的相關,在stackoverflow上有個解決方案,是把ListView中的所有Item的高度計算出來,不過這樣性能不好。

android - How can I put a ListView into a ScrollView without it collapsing?

這些是當時我在做項目時幫助我解決關於這個問題的資料,應該可以解決大部分題主會遇到的問題吧。


推薦閱讀:

關於 Android,用多個 activity,還是單 activity 配合 fragment?
怎麼可以快速的學會並掌握Android開發?
有哪些開源的採用 Material Design 的 Android 程序呢?
移動APP切圖標準?
學習android架構的步驟?

TAG:Android開發 | Android |