前天在工作中遇到在ListView中的Item需要用ListView來展現處理后的內容,然后就遇到了一個很頭疼的問題,作為Item的ListView沒法進行滑動,而且顯示也不正常,只是顯示幾個子Item。不能將子Item全部顯示,原因是在控件繪制出來之前要對ListView的大小進行計算,要解決將子ListView全部顯示出來的問題,就是重新計算一下其大小告知系統即可。后面這個問題比較好解決,網上已經給出解決方案:
前輩們給出了一個方法,重新計算子ListView的大小,然后在設置本ListView的Adapter之后運行這個方法就好了,具體代碼如下:
/**
* 設置Listview的高度
*/
public void setListViewHeight(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
但是這個方法設置的item的Layout必須是帶有onMeasure()方法的控件,否則在計算的時候會報錯,建議使用LinearLayout。
再一個思路相同,但是,不是額外做方法來實現onMeasure()方法的計算LIstView的大小,而是自己繼承ListView,重寫ListView的onMeasure()方法,來自己計算ListView的高度,然后再xml中直接使用這個自定義的ListView就可以了。
public class MyListView extends ListView {
public MyListView (Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView (Context context) {
super(context);
}
public MyListView (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
這是解決讓作為Item的ListView顯示全部內容的方案,但是有些時候我們是想讓作為Item的ListView不用全部顯示,而是可以進行滑動,要解決這個問題就需要了解一下android對事件的分發機制了
我的解決方案是集成ListView,重寫interceptTouchEvent使其返回false來取消父ListView對觸摸事件的攔截,將觸摸事件分發到子View來處理。然后在使用的時候,將其作為父ListView使用,就可以使子ListView可以滑動了。思想來源於下面鏈接的6樓
http://www.eoeandroid.com/thread-3597-1-1.html
具體自定義父ListView代碼 :
public class ParentListView extends ListView {
public ParentListView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public ParentListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public ParentListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
//將 onInterceptTouchEvent的返回值設置為false,取消其對觸摸事件的處理,將事件分發給子view
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
return false;
}
}
xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<!-- 這里做demo用,直接使用了android中的ListActivity-->
<i.test.ParentListView android:id=" @android :id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:dividerHeight="2dip"
android:scrollbars="none"
/>
</LinearLayout>
activity代碼如下:
public class ListviewActivity extends ListActivity {
/** Called when the activity is first created. */
private ListView mLv;//這個ListView就是自定義的View
private ParentAdapter adapter;
private final static String[] array = new String[]{"1","2","3","4","5","6","7","8","9","10","11","12","13","14"};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLv = getListView();
adapter = new ParentAdapter();
mLv.setAdapter(adapter);
}
private class ParentAdapter extends BaseAdapter{
@Override
public int getCount() {
// TODO Auto-generated method stub
return Array.getLength(array);
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return array[position];
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view;
if(position == 5){
view = View.inflate(getApplicationContext(), R.layout.item, null);
ListView lv = (ListView) view.findViewById(R.id.lv);
ArrayAdapter<String> mAdapter = new ArrayAdapter<String>(ListviewActivity.this, android.R.layout.simple_list_item_1, new String[]{"a","b",
"c","d","e","f","g"});
lv.setAdapter(mAdapter);
}
else{
TextView tv = new TextView(getApplicationContext());
tv.setText(array[position]);
tv.setTextSize(30);
view = tv;
}
return view;
}
}
}
上面的方法同樣適合在ScrollView中嵌套可以滑動View的情況。
后記:2013.04.10
今天登錄oschian看到有人提到我,打開消息一看,是對這篇文字的評論,很高興我寫的東西對別人有所幫助。評論人 @jimmy.zhao ,謝謝你讓我知道,我幫助了你,這是博客寫下去的動力。
這篇文字是在我畢業之后剛入職第二天解決的問題,話說這個問題困擾前面的人有兩個月了,我來了之后就把這個坑讓我填,前輩說在網上找的所有方案都是已經有牛人給出解決方案。都這么說,但是沒有一個人說解決方案是什么,於是讓我研究了下。不能說我這篇文字是最早解決這個嵌套滑動問題的,但是如你搜一下解決滑動嵌套問題的帖子基本都在我這篇之后,先自己小驕傲下。
下面說說我在使用這種方法解決了這個問題之后遇到的問題(好像有點繞。。但攻城獅不怕哈),希望能引起后來人的注意:
問題出在一個月之后,根據項目需求,外面的ListView,即父ListView中的條目展示文本數字時需要加入對電話號碼和HTTP鏈接的識別。即:如果是手機號碼,點擊之后進入撥號盤界面。大家知道,這個很簡單,只要在TextView中設置一個簡單的屬性就好了。然后我的問題就出現,因為父ListView的觸摸事件交給了子view,如果子view中的TextView帶有這種隱式的點擊事件,就會造成父ListView的卡頓現象。而且是相當卡頓。於是在項目中,還是使用了固定子ListView大小,直接使用系統的ListView不再重寫父ListView的onInterceptTouchEvent事件。將展示更多,作為加載來處理。還有一個就是在使用TextView的時候,盡量避免使用Html.from()來讓TextView支持簡單html標簽。這個太耗性能。用MAT一看便知,不多說。希望對有時間看本文一眼的人有所幫助。
轉自http://my.oschina.net/zhibuji/blog/70892
07 |
public void setListViewHeight(ListView listView) { |
09 |
ListAdapter listAdapter = listView.getAdapter(); |
11 |
if (listAdapter == null ) { |
19 |
for ( int i = 0 ; i < listAdapter.getCount(); i++) { |
21 |
View listItem = listAdapter.getView(i, null , listView); |
23 |
listItem.measure( 0 , 0 ); |
25 |
totalHeight += listItem.getMeasuredHeight(); |
29 |
ViewGroup.LayoutParams params = listView.getLayoutParams(); |
31 |
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1 )); |
33 |
listView.setLayoutParams(params); |
但是這個方法設置的item的Layout必須是帶有onMeasure()方法的控件,否則在計算的時候會報錯,建議使用LinearLayout。
再一個思路相同,但是,不是額外做方法來實現onMeasure()方法的計算LIstView的大小,而是自己繼承ListView,重寫ListView的onMeasure()方法,來自己計算ListView的高度,然后再xml中直接使用這個自定義的ListView就可以了。
01 |
public class MyListView extends ListView { |
03 |
public MyListView (Context context, AttributeSet attrs) { |
05 |
super (context, attrs); |
09 |
public MyListView (Context context) { |
15 |
public MyListView (Context context, AttributeSet attrs, int defStyle) { |
17 |
super (context, attrs, defStyle); |
23 |
public void onMeasure( int widthMeasureSpec, int heightMeasureSpec) { |
25 |
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2 , |
29 |
super .onMeasure(widthMeasureSpec, expandSpec); |
這是解決讓作為Item的ListView顯示全部內容的方案,但是有些時候我們是想讓作為Item的ListView不用全部顯示,而是可以進行滑動,要解決這個問題就需要了解一下Android對事件的分發機制了
我的解決方案是集成ListView,重寫interceptTouchEvent使其返回false來取消父ListView對觸摸事件的攔截,將觸摸事件分發到子View來處理。然后在使用的時候,將其作為父ListView使用,就可以使子ListView可以滑動了。思想來源於下面鏈接的6樓
http://www.eoeandroid.com/thread-3597-1-1.html
具體自定義父ListView代碼
:
01 |
public class ParentListView extends ListView { |
03 |
public ParentListView(Context context) { |
11 |
public ParentListView(Context context, AttributeSet attrs, int defStyle) { |
13 |
super (context, attrs, defStyle); |
19 |
public ParentListView(Context context, AttributeSet attrs) { |
21 |
super (context, attrs); |
30 |
public boolean onInterceptTouchEvent(MotionEvent ev) { |
xml文件:
01 |
<? xml version = "1.0" encoding = "utf-8" ?> |
02 |
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" |
03 |
android:layout_width = "fill_parent" |
04 |
android:layout_height = "fill_parent" |
05 |
android:orientation = "vertical" > |
07 |
< i.test.ParentListView android:id = " @android :id/list" |
08 |
android:layout_width = "fill_parent" |
09 |
android:layout_height = "fill_parent" |
10 |
android:dividerHeight = "2dip" |
11 |
android:scrollbars = "none" |
activity代碼如下:
01 |
public class ListviewActivity extends ListActivity { |
02 |
/** Called when the activity is first created. */ |
04 |
private ParentAdapter adapter; |
05 |
private final static String[] array = new String[]{ "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "10" , "11" , "12" , "13" , "14" }; |
07 |
public void onCreate(Bundle savedInstanceState) { |
08 |
super .onCreate(savedInstanceState); |
09 |
setContentView(R.layout.main); |
11 |
adapter = new ParentAdapter(); |
12 |
mLv.setAdapter(adapter); |
15 |
private class ParentAdapter extends BaseAdapter{ |
18 |
public int getCount() { |
20 |
return Array.getLength(array); |
24 |
public Object getItem( int position) { |
26 |
return array[position]; |
30 |
public long getItemId( int position) { |
36 |
public View getView( int position, View convertView, ViewGroup parent) { |
40 |
view = View.inflate(getApplicationContext(), R.layout.item, null ); |
41 |
ListView lv = (ListView) view.findViewById(R.id.lv); |
42 |
ArrayAdapter<String> mAdapter = new ArrayAdapter<String>(ListviewActivity. this , android.R.layout.simple_list_item_1, new String[]{ "a" , "b" , |
43 |
"c" , "d" , "e" , "f" , "g" }); |
44 |
lv.setAdapter(mAdapter); |
47 |
TextView tv = new TextView(getApplicationContext()); |
48 |
tv.setText(array[position]); |
上面的方法同樣適合在ScrollView中嵌套可以滑動View的情況。
轉自http://my.oschina.net/zhibuji/blog/70892