當前位置:成語大全網 - 書法字典 - Android scrollview嵌套listview頁面有額外的空白空間。

Android scrollview嵌套listview頁面有額外的空白空間。

以下文章轉自@安卓泡面。

在工作中,我多次遇到過ScrollView視圖嵌套ListView的問題。有許多在線解決方案,但它們很雜且不完整。我試過很多方法,每種方法都有自己的優缺點。

在這裏,我將從使用ScrollView嵌套ListView結構的原因、該結構遇到的問題、幾種解決方案以及優缺點比較四個方面進行闡述、分析和總結。

事實上,不僅是ListView,從AbsListView繼承的其他類也是如此,包括ExpandableListView、GridView等。為方便解釋,以下均以ListView表示。

首先,為什麽使用ScrollView來嵌套ListView的奇怪結構?

ScrollView和ListView都是滾動結構。從理論上講,UI上這兩個控件的功能是相同的,但是請看下面的設計:

這是天貓商城的確認頁面。ExpandableListView嵌套在ScrollView中,上面有壹些固定控件,下面也有壹些固定控件,所以整體上應該是可滾動的。列表數據應該嵌入固定數據中,並作為壹個整體滾動在壹起。有了這樣的設計需求,就有了ScrollView嵌套ListView的奇怪結構。

其次,在ScrollView和ListView的嵌套結構中遇到的問題

話不多說,只看失敗例子:

& lt滾動視圖

Android:id =“@+id/act _ solution _ 1 _ SV“

Android:layout _ width =“fill _ parent“

Android:layout _ height =“fill _ parent“& gt;

& lt線性布局

Android:layout _ width =“fill _ parent“

Android:layout _ height =“wrap _ content“

Android:orientation =“vertical“& gt;

& lt文本視圖

Android:layout _ width =“fill _ parent“

Android:layout _ height =“wrap _ content“

Android:text =“\ n列表查看以上數據“/& gt;

& lt列表視圖

Android:id =“@+id/act _ solution _ 1 _ LV“

Android:layout _ width =“fill _ parent“

Android:layout _ height =“wrap _ content“& gt;

& lt/ListView>

& lt文本視圖

Android:layout _ width =“fill _ parent“

Android:layout _ height =“wrap _ content“

Android:text =“\ n下面的列表視圖數據\ n“/& gt;

& lt/linear layout & gt;

& lt/scroll view & gt;

ScrollView中只能放置壹個控件,通常放置LinearLayout,orientation屬性的值為vertical。將需要呈現的內容放到LinearLayout中。ListView也在其中,ListView的高度被設置為適合自己的內容(wrap_content)。乍壹看,應該沒有問題。但是看看下面的實際效果圖:

圖中的黑框是ListView,包含20條數據,但只顯示了1條。

控件的屬性設置沒有問題,但是為什麽我沒有按照我的想法去做呢?

請看下圖:

是不是有點清楚了?原因是滾動事件的消耗處理和ListView控件的高度設置。

雖然我讀過很多源代碼,但我不知道如何開始談論它。我大概知道原因,但不知道怎麽徹底理清。求高手指點…

第三,解決問題的方法

1.手動設置ListView的高度。

經過測試發現,直接在xml中指定ListView的高度可以解決這個問題,但ListView中的數據是可變的,需要測量實際高度。於是通過手動代碼設置ListView高度的方法誕生了。

/**

*動態設置ListView的高度。

* @param列表視圖

*/

公共靜態void setListViewHeightBasedOnChildren(ListView ListView ){

if(listView = = null)返回;

list adapter list adapter = listview . get adapter();

if(list adapter = = null ){

//前置條件

返回;

}

int total height = 0;

for(int I = 0;我& ltlist adapter . get count();i++) {

view listItem = list adapter . getview(I,null,listView);

listitem . measure(0,0);

total height+= listitem . getmeasuredheight();

}

視圖組。layout params params = listview . getlayout params();

params . height = total height+(listview . getdividerheight()*(list adapter . get count()-1);

listview . setlayoutparams(params);

}

上面的方法是設置ListView的高度。為ListView設置適配器後,它可以解決該問題。

但是這種方法有兩個細節需要註意:

首先,適配器中getView方法返回的視圖必須由LinearLayout組成,因為只有LinearLayout具有measure()方法。如果使用其他布局,如RelativeLayout,則調用listitem . measure(0,0);妳會拋出異常,因為除了LinearLayout之外的這種布局方法就是直接拋出異常,沒有任何理由。我最初使用了這種方法,但由於子控件的頂部布局是RelativeLayout,我不斷報告錯誤,不得不放棄這種方法。

其次,您需要手動將ScrollView滾動到頂部,因為如果使用此方法,默認情況下ScrollView頂部的項目是ListView。我不知道具體原因,所以我需要向上帝尋求答案...您可以在活動中設置它:

SV =(scroll view)findViewById(r . id . act _ solution _ 1 _ SV);

2.使用單個ListView替換ScrollView中的所有內容。

這個方法是我自己在嘗試了幾種方法都失敗的情況下想出來的。

用壹張圖來解釋這種方法的思想:

也就是說,所有需要放在ScrollView中的內容都放在ListView中,原始ListView上下的數據都被視為當前ListView的壹個itemView,相當於原始ListView中的單個數據。

xml布局非常簡單:

& lt列表視圖

Android:id =“@+id/act _ solution _ 2 _ LV“

Android:layout _ width =“fill _ parent“

Android:layout _ height =“wrap _ content“& gt;

& lt/ListView>

單獨的ListView就可以了。

原始ListView上方和下方的數據被寫入兩個xml布局文件:

就Java代碼而言,您需要定制壹個適配器,判斷適配器中getView方法中的位置值,並根據位置值決定要插值的布局:

公共視圖getView(int position,View convertView,View group parent ){

//列表中的第壹項

if(position = = 0 ){

convert view = inflater . inflate(r . layout . item _ solution 2 _ top,null);

返回convertView

}

//列表中的最後壹項

else if(position = = 21 ){

convert view = inflater . inflate(r . layout . item _ solution 2 _ bottom,null);

返回convertView

}

//常用列表項

ViewHolder h = null

if(convert view = = null | | convert view . gettag()= = null ){

convert view = inflater . inflate(r . layout . item _ listview _ data,null);

h =新視圖持有者();

h . TV =(TextView)convert view . findviewbyid(r . id . item _ listview _ data _ TV);

convert view . settag(h);

}否則{

h =(view holder)convert view . gettag();

}

H.tv.setText(“文章“+位置+“數據“);

返回convertView

}

在Activty中,您只需要直接為ListView設置壹個自定義適配器。

LV =(ListView)findViewById(r . id . act _ solution _ 2 _ LV);

adapter = new adapterforlistview 2(this);

lv.setAdapter(適配器);

3.用LinearLayout替換ListView。

既然ListView不能適應ScrollView,我們為什麽要更改壹個可以適應ScrollView的控件呢?LinearLayout是最佳選擇。但是如果我仍然想使用已定義的適配器呢?我們只需要自定義壹個從LinearLayout繼承的類,並添加壹個對BaseAdapter的適配。

導入Android . content . context;

導入Android . util . attributeset;

導入Android . util . log;

導入Android . view . view;

導入Android . widget . base adapter;

導入Android . widget . linear layout;

/**

*替換ListView的LinearLayout,以便它可以成功嵌套在ScrollView中。

* @ authortry _ dragon

*/

公共類LinearLayoutForListView擴展了LinearLayout {

專用BaseAdapter適配器;

private onclick listener onclick listener = null;

/**

*裝訂布局

*/

public void bindlinelayout(){

int count = adapter . get count();

this . remove allviews();

for(int I = 0;我& lt數數;i++) {

view v = adapter . get view(I,null,null);

v . setonclicklistener(this . onclicklistener);

add view(v,I);

}

log . v(“count tag“,““+count);

}

公共LinearLayoutForListView(上下文Context ){

super(上下文);

將上面的代碼副本保存為LinearLayoutForListView.class,或者直接在您自己的項目中的Demo中復制此類。我們只需要用這個類替換原始xml布局文件中的ListView:

& ltpm . nestification between scrollviewandbslistview . my widgets . linearrayoutforlistview

Android:id =“@+id/act _ solution _ 3 _ mylinearlayout“

Android:layout _ width =“fill _ parent“

Android:layout _ height =“wrap _ content“

Android:orientation =“vertical“& gt;

& lt/pm . nestification between scrollviewandbslistview . my widgets . linearrayoutforlistview & gt;

在活動中,ListView也更改為LinearLayoutForListView,並且可以成功運行。

mylinearlayout =(linearrayoutforlistview)findViewById(r . id . act _ solution _ 3 _ mylinearlayout);

adapter = new AdapterForListView(this);

mylinearlayout.setAdapter(適配器);

4.自定義可適應滾動視圖的列表視圖。

這種方法與上述方法類似。方法3是自定義LinearLayout來代替ListView的功能,但是如果我的脾氣比較倔,想用ListView怎麽辦?然後妳必須自定義壹個類來繼承ListView,並重寫其onMeasure方法以達到適應ScrollView的效果。

以下是繼承ListView的自定義類:

導入Android . content . context;

導入Android . util . attributeset;

導入Android . widget . listview;

公共類ListViewForScrollView擴展ListView {

公共ListViewForScrollView(上下文Context ){

super(上下文);

}

公共ListViewForScrollView(上下文Context,AttributeSet attrs ){

super(上下文,attrs);

}

公共ListViewForScrollView(上下文上下文,屬性集屬性,

int def style ){

super(context、attrs、def style);

}

@覆蓋

/**

*重寫此方法以達到使ListView適應ScrollView的效果。

*/

受保護的void on measure(int width measurespec,int height measurespec ){

int expand spec = measure spec . make measure spec(整數。MAX _ VALUE & gt& gt2,

測量規格。至多);

super . on measure(widthMeasureSpec、expandSpec);

}

}

三種構造方法根本不需要動,只要重寫onMeasure方法,需要改動的地方不比方法3少壹點點…

只需將xml布局和活動中使用的ListView更改為該自定義ListView。保存代碼...

此方法和方法1有相同的問題,即默認顯示的第壹項是ListView,需要手動將ScrollView滾動到頂部。

SV =(scroll view)findViewById(r . id . act _ solution _ 4 _ SV);

SV . smooth scroll to(0,0);

5.設置ScrollView的屬性,以便ListView可以成功嵌套(無法達到預期效果)。

我在寫演示的時候發現了這個方法。我的第壹反應是為什麽我用這種方法編寫這個演示,只需在布局文件中添加壹個屬性。然而,結果是ListView的大小填滿了ScrollView的其余部分,但它無法滾動。這是壹個致命的問題…

廢話少說。在布局文件中:

& lt滾動視圖

Android:id =“@+id/act _ solution _ 5 _ SV“

Android:layout _ width =“fill _ parent“

Android:layout _ height =“fill _ parent“

Android:layout _ below =“@+id/act _ solution _ 5 _ VG _ top“

Android:fill viewport =“true“& gt;

只需將fillViewport的屬性設置為true。簡單吧?

但是不知道怎麽解決無法滾動的致命問題,繼續求大神解答…

第四,比較了幾種方法的優缺點

上面* * *給出了4中可用的親本測試方法,每種方法都有其自身的條件和復雜性。

我從幾個方面來分析幾種方法的優缺點。

方法1的優點是不需要對使用的控件進行任何更改,只需要使用壹個現成的方法即可,最大的限制是ListView的item只能由LinearLayout組成,這對於壹些復雜的布局並不適用。如果您的項目迫切需要解決這個問題,並且它滿足使用該方法的條件,即ListView的項布局很簡單,完全由LinearLayout組成,則您只需要采用setListViewHeightBased子方法。

方法2的優點是布局文件設計簡單,並且活動中的代碼很少。但是缺點是自定義適配器變得非常復雜,並且執行效率會很低,因為findViewById是壹個非常耗時的操作,而使用ViewHolder結構可以解決耗時問題(感興趣的童鞋可以搜索壹個ViewHolder結構)。然而,使用方法2會破壞這種結構。如果妳的工程設計比較簡單,ListView中的項目相對較少,ListView上下的數據很少,項目之間的交互也很少,妳可以嘗試壹下。

方法3的優點是它完全解決了在ScrollView中嵌套ListView的問題,同時代碼較少。妳甚至可以直接使用LinearLayout,並在Activity中為LinearLayout手動添加子控件。但是需要註意的是,在添加它之前需要調用它的removeAllViews方法,否則可能會發生意想不到的事情,然後就會錯過天堂中的ListView。缺點不明顯,但仍然有兩個:第壹,您無法在xml layout的圖形布局視圖中直接看到效果,因為您沒有使用系統控件;其次,不能像ListView那樣使用ViewHolder結構,並且在加載大量子項時會在findViewById中花費大量時間。如果您的列表數據較少,您不妨嘗試這種方法,只不過不能使用ViewHolder結構,該結構與ListView幾乎相同。

方法4…比方法3簡單,代碼更少。同時,它保留了ListView的所有原始方法,包括notifyDataSetChanged方法。與其他方法相比,它是最完美的方法,但需要將ScrollView設置為在活動中滾動到頂部。如果妳還在猶豫,選擇這個方法,我想我以後只會用這個方法…