ScrollView与ListView冲突解决

时间:2024-06-22 20:06:08

正 常来说,在ScrollView添加一个ListView后在真机上只会显示ListView的一行多一点,我也不理解为什么会这样,后来我把 ListView的layout_height改成400dip,而不是用match_parent和wrap_content,我发现这样的话 ListView就显示的多了很多。所以就产生了把ListView所有的item的高度算出来给ListView设置的想法。下面是代码:

源码:http://www.jinhusns.com/Products/Download/?type=xcj

Java代码  ScrollView与ListView冲突解决

  1. public void setListViewHeightBasedOnChildren(ListView listView) {

  2. ListAdapter listAdapter = listView.getAdapter();

  3. if (listAdapter == null) {

  4. return;

  5. }

  6. int totalHeight = 0;

  7. for (int i = 0; i < listAdapter.getCount(); i++) {

  8. View listItem = listAdapter.getView(i, null, listView);

  9. listItem.measure(0, 0);

  10. totalHeight += listItem.getMeasuredHeight();

  11. }

  12. ViewGroup.LayoutParams params = listView.getLayoutParams();

  13. params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));

  14. params.height += 5;//if without this statement,the listview will be a little short

  15. listView.setLayoutParams(params);

  16. }

在代码的倒数第二行二我又给加了5个像素,这是因为我在listview的属性里面添加了padding=5dip。 
然后每次ListView的数据一有变化就用这个函数设置一下就好了,不过这样总感觉效率很低,希望有达人给指点一下。

简单来说就是把layout_height写死,这种办法也很适用于GridView(如果能估计得出GridView的高度的话)。

listview与ScrollView老问题的另类解法 
http://www.eoeandroid.com/thread-42893-1-1.html 

几天一直被listview怎么合理的放进scorllview中的问题困扰,尝试过把listview放入scorllview中的朋友都知
道,被放入的listview显示是有问题的,无论怎么设置layout都只显示大概2行的高度,看起来很郁闷,更别说美观了,后来上网查询了一下,解决

方法有的是用linearlayout替换listview,还有修改onmeasure的,我比较懒个人感觉很麻烦不喜欢,终于想出了一个还算和谐的解
决方法:xml中的textlist设置如下:

Java代码  ScrollView与ListView冲突解决

  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  3. android:layout_width="fill_parent"

  4. android:layout_height="wrap_content"

  5. android:orientation="vertical"

  6. android:background="#44444444">

  7. <ScrollView

  8. android:layout_width="fill_parent"

  9. android:layout_height="wrap_content">

  10. <LinearLayout

  11. android:id="@+id/ll1"

  12. android:layout_width="fill_parent"

  13. android:layout_height="wrap_content"

  14. android:scrollbars="vertical"

  15. android:orientation="vertical"

  16. android:paddingLeft="15dp"

  17. android:paddingRight="15dp"

  18. android:paddingTop="30dp"

  19. android:paddingBottom="30dp"

  20. android:background="#ff888888">

  21. <TextView

  22. android:text="あ"

  23. android:textColor="#ffeeeeee"

  24. android:textSize="18sp"

  25. android:layout_width="fill_parent"

  26. android:layout_height="wrap_content"></TextView>

  27. <ListView

  28. android:scrollbars="none"

  29. android:stackFromBottom="true"

  30. android:id="@+id/lv0"

  31. android:layout_width="fill_parent"

  32. android:layout_height="20dp"></ListView>

  33. </LinearLayout>

  34. </ScrollView>

  35. </LinearLayout>

其中的textview是我做的东西要用到的,和方法无关可以不看,然后就是在java中重新设置listview的高度了,目的是把listview“撑”开: 
LinearLayout.LayoutParams  lp5 =new LinearLayout.LayoutParam(LayoutParams.FILL_PARENT, listItem.size()*51-1); 

中第一个属性不必说了,第二个是为了计算listview要设置的总高度用的,51是我事先设置好的一行的高度(50)+每行之间的间隔(1)
而得来的,listItem.size()是我要显示的行数,用.setLayoutParams(lp5);来重新设置高度,其他别的设置跟以前一样,
想要源码我整理完之后贴出来

如果不想写死 ,Android 解决ListView 和 ScrollView 共存冲突的问题 
http://labs.chinamobile.com/mblog/532767_72693?wralxianxrnx 
http://blog.liaoxiaoqi.com/?p=503

下面是我的一个实现 步骤:

  • 1、继承LinearLayout,既然会冲突那就不用ListView 改成线性布局做动态布局效果

  • 2、继承BaseAdapter ,可以参照一下Android app源码中 Widget 目录下的SimpleAdapter 为前面扩展的LinearLayout做数据。

  • 3、模拟数据填充扩展后的BaseAdapter 为扩展后的LinearLayout 加载数据

第一步:新建LinearLayoutForListView 类使其扩展LinearLayout重写以下两个方法:

Java代码  ScrollView与ListView冲突解决

  1. public LinearLayoutForListView(Context context) {

  2. super(context);

  3. }

  4. public LinearLayoutForListView(Context context, AttributeSet attrs) {

  5. super(context, attrs);

  6. }

这两个方法可选,不过建议都写上,第一个方法可以让我们通过 编程的方式 实例化出来,第二个方法可以允许我们通过 XML的方式注册 控件,可以在第二个方法里面为扩展的复合组件加属性。为其添加get / set 方法

Java代码  ScrollView与ListView冲突解决

  1. /**

  2. * 获取Adapter

  3. *

  4. * @return adapter

  5. */

  6. public AdapterForLinearLayout getAdpater() {

  7. return adapter;

  8. }

  9. /**

  10. * 设置数据

  11. *

  12. * @param adpater

  13. */

  14. public void setAdapter(AdapterForLinearLayout adpater) {

  15. this.adapter = adpater;

  16. bindLinearLayout();

  17. }

  18. /**

  19. * 获取点击事件

  20. *

  21. * @return

  22. */

  23. public OnClickListener getOnclickListner() {

  24. return onClickListener;

  25. }

  26. /**

  27. * 设置点击事件

  28. *

  29. * @param onClickListener

  30. */

  31. public void setOnclickLinstener(OnClickListener onClickListener) {

  32. this.onClickListener = onClickListener;

  33. }

  34. sp;

第二步:新建AdapterForLinearLayout 类继承自BaseAdapter,并为其添加构造函数

Java代码  ScrollView与ListView冲突解决

  1. private LayoutInflater mInflater;

  2. private int resource;

  3. private List<? extends Map<String, ?>> data;

  4. private String[] from;

  5. private int[] to;

  6. public AdapterForLinearLayout(Context context,

  7. List<? extends Map<String, ?>> data, int resouce, String[] from,

  8. int[] to) {

  9. this.data = data;

  10. this.resource = resouce;

  11. this.data = data;

  12. this.from = from;

  13. this.to = to;

  14. this.mInflater = (LayoutInflater) context

  15. .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

  16. }


构造函数模仿 simpleAdapter 通过传进来的resouce 为布局设置数据。通过继承BaseAdapter
重要的实现方法在下面getView ,此方法判断通过传进来的 String[] from 与 int[] to 为分别查找出View
并为View 设置相应的Text,代码如下:

Java代码  ScrollView与ListView冲突解决

  1. @Override

  2. public View getView(int position, View convertView, ViewGroup parent) {

  3. // TODO Auto-generated method stub

  4. convertView = mInflater.inflate(resource, null);

  5. Map<String, ?> item = data.get(position);

  6. int count = to.length;

  7. for (int i = 0; i < count; i++) {

  8. View v = convertView.findViewById(to[i]);

  9. bindView(v, item, from[i]);

  10. }

  11. convertView.setTag(position);

  12. return convertView;

  13. }

  14. /**

  15. * 绑定视图

  16. * @param view

  17. * @param item

  18. * @param from

  19. */

  20. private void bindView(View view, Map<String, ?> item, String from) {

  21. Object data = item.get(from);

  22. if (view instanceof TextView) {

  23. ((TextView) view).setText(data == null ? "" : data.toString());

  24. }

  25. }

Tip:

  • BindView 方法是一个自定义方法,在方法体内可以为通过判断使本类更具灵活性,如上,你不仅可以判断是TextView 并且可以传入任何你想要的View 只要在方法体内加入相应判断即可,数据可以通过data 做相应处理,具体如何操作读者可另行测试。

  • convertView.setTag(position); 此句代码为View 设置tag 在以后我们可以通过 getTag 找出下标,后文有介绍如何通过下标操作数据。

下面是两个类的全部代码,读者可以无须更改直接使用:

Java代码  ScrollView与ListView冲突解决

  1. LinearLayoutForListView

  2. package  com.terry.widget;

  3. import  android.content.Context;

  4. import  android.util.AttributeSet;

  5. import  android.util.Log;

  6. import  android.view.View;

  7. import  android.widget.LinearLayout;

  8. public   class  LinearLayoutForListView  extends  LinearLayout {

  9. private  AdapterForLinearLayout adapter;

  10. private  OnClickListener onClickListener  =   null ;

  11. /**

  12. * 绑定布局

  13. */

  14. public   void  bindLinearLayout() {

  15. int  count  =  adapter.getCount();

  16. for  ( int  i  =   0 ; i  <  count; i ++ ) {

  17. View v  =  adapter.getView(i,  null ,  null );

  18. v.setOnClickListener( this .onClickListener);

  19. if  (i  ==  count  -   1 ) {

  20. LinearLayout ly  =  (LinearLayout) v;

  21. ly.removeViewAt( 2 );

  22. }

  23. addView(v, i);

  24. }

  25. Log.v( " countTAG " ,  ""   +  count);

  26. }

  27. public  LinearLayoutForListView(Context context) {

  28. super (context);

  29. }

  30. public  LinearLayoutForListView(Context context, AttributeSet attrs) {

  31. super (context, attrs);

  32. }

  33. /**

  34. * 获取Adapter

  35. *

  36. *  @ return  adapter

  37. */

  38. public  AdapterForLinearLayout getAdpater() {

  39. return  adapter;

  40. }

  41. /**

  42. * 设置数据

  43. *

  44. *  @ param  adpater

  45. */

  46. public   void  setAdapter(AdapterForLinearLayout adpater) {

  47. this .adapter  =  adpater;

  48. bindLinearLayout();

  49. }

  50. /**

  51. * 获取点击事件

  52. *

  53. *  @ return

  54. */

  55. public  OnClickListener getOnclickListner() {

  56. return  onClickListener;

  57. }

  58. /**

  59. * 设置点击事件

  60. *

  61. *  @ param  onClickListener

  62. */

  63. public   void  setOnclickLinstener(OnClickListener onClickListener) {

  64. this .onClickListener  =  onClickListener;

  65. }

  66. }

Java代码  ScrollView与ListView冲突解决

  1. AdapterForLinearLayout

  2. package  com.terry.widget;

  3. import  java.util.List;

  4. import  java.util.Map;

  5. import  android.content.Context;

  6. import  android.view.LayoutInflater;

  7. import  android.view.View;

  8. import  android.view.ViewGroup;

  9. import  android.widget.BaseAdapter;

  10. import  android.widget.TextView;

  11. public   class  AdapterForLinearLayout  extends  BaseAdapter {

  12. private  LayoutInflater mInflater;

  13. private   int  resource;

  14. private  List <?   extends  Map < String,  ?>>  data;

  15. private  String[] from;

  16. private   int [] to;

  17. public  AdapterForLinearLayout(Context context,

  18. List <?   extends  Map < String,  ?>>  data,  int  resouce, String[] from,

  19. int [] to) {

  20. this .data  =  data;

  21. this .resource  =  resouce;

  22. this .data  =  data;

  23. this .from  =  from;

  24. this .to  =  to;

  25. this .mInflater  =  (LayoutInflater) context

  26. .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

  27. }

  28. @ Override

  29. public   int  getCount() {

  30. //  TODO Auto-generated method stub

  31. return  data.size();

  32. }

  33. @ Override

  34. public  Object getItem( int  position) {

  35. //  TODO Auto-generated method stub

  36. return  data.get(position);

  37. }

  38. @ SuppressWarnings( " unchecked " )

  39. public  String get( int  position, Object key) {

  40. Map < String,  ?>  map  =  (Map < String,  ?> ) getItem(position);

  41. return  map.get(key).toString();

  42. }

  43. @ Override

  44. public   long  getItemId( int  position) {

  45. //  TODO Auto-generated method stub

  46. return  position;

  47. }

  48. @ Override

  49. public  View getView( int  position, View convertView, ViewGroup parent) {

  50. //  TODO Auto-generated method stub

  51. convertView  =  mInflater.inflate(resource,  null );

  52. Map < String,  ?>  item  =  data.get(position);

  53. int  count  =  to.length;

  54. for  ( int  i  =   0 ; i  <  count; i ++ ) {

  55. View v  =  convertView.findViewById(to[i]);

  56. bindView(v, item, from[i]);

  57. }

  58. convertView.setTag(position);

  59. return  convertView;

  60. }

  61. /**

  62. * 绑定视图

  63. *  @ param  view

  64. *  @ param  item

  65. *  @ param  from

  66. */

  67. private   void  bindView(View view, Map < String,  ?>  item, String from) {

  68. Object data  =  item.get(from);

  69. if  (view  instanceof  TextView) {

  70. ((TextView) view).setText(data  ==   null   ?   ""  : data.toString());

  71. }

  72. }

  73. }

对应的XML 如下:

Java代码  ScrollView与ListView冲突解决

  1. <? xml version="1.0" encoding="UTF-8" ?>

  2. < LinearLayout  xmlns:android ="http://schemas.android.com/apk/res/android"

  3. android:orientation ="vertical"  android:layout_width ="fill_parent"

  4. android:layout_height ="fill_parent" >

  5. < TextView  android:id ="@ +id/TextView01"

  6. android:layout_marginLeft ="10px"  android:textAppearance ="?android:attr/textAppearanceLarge"

  7. android:layout_width ="wrap_content"  android:layout_height ="wrap_content" >

  8. </ TextView >

  9. < TextView  android:id ="@ +id/TextView02"  android:layout_width ="wrap_content"

  10. android:textAppearance ="?android:attr/textAppearanceSmall"

  11. android:layout_marginLeft ="10px"  android:layout_height ="wrap_content" >

  12. </ TextView >

  13. < View  android:layout_height ="1px"  android:background ="#FFFFFF"

  14. android:layout_width ="fill_parent" ></ View >

  15. </ LinearLayout >

第三步:主页面使用控件并为其设置数据

  • XML如下:

    Java代码  ScrollView与ListView冲突解决

  1. < com.terry.widget.LinearLayoutForListView

  2. android:orientation ="vertical"  android:layout_width ="450px"

  3. android:layout_height ="fill_parent"  android:id ="@ +id/ListView01" >

  4. </ com.terry.widget.LinearLayoutForListView >

  • 加载数据如下:

    Java代码  ScrollView与ListView冲突解决

    1. lv  =  (LinearLayoutForListView) findViewById(R.id.ListView01);

    2. for  ( int  i  =   0 ; i  <   10 ; i ++ ) {

    3. HashMap < String, Object >  map  =   new  HashMap < String, Object > ();

    4. map.put( " key_name " ,  " name "   +  i);

    5. map.put( " value_name " ,  " value "   +  i);

    6. list.add(map);

    7. }

    8. final  AdapterForLinearLayout Layoutadpater  =   new  AdapterForLinearLayout(

    9. this , list, R.layout.test,  new  String[] {  " key_name " ,

    10. " value_name "  },  new   int [] { R.id.TextView01,

    11. R.id.TextView02 });

  • 事件操作,并通过下标得到数据源:

    Java代码  ScrollView与ListView冲突解决

    Tip:get方法是在Layoutadpater 封装的一个通过下标获取相应数据的方法请参考上文。

    1. lv.setOnclickLinstener( new  OnClickListener() {

    2. @ Override

    3. public   void  onClick(View v) {

    4. //  TODO Auto-generated method stub

    5. Toast.makeText(

    6. BlueToothActivity. this ,

    7. Layoutadpater.get(Integer.parseInt(v.getTag()

    8. .toString()),  " key_name " ),  1000 ).show();

    9. }

    10. });

    11. lv.setAdapter(Layoutadpater);

    至此完成。有碰到这个问题的朋友可以试试。

    有人的总结如下:

    只要在设置ListView的Adapter后调用此静态方法即可让ListView正确的显示在其父ListView的ListItem中。但是要注意

    的是,子ListView的每个Item必须是LinearLayout,不能是其他的,因为其他的Layout(如RelativeLayout)没有
    重写onMeasure(),所以会在onMeasure()时抛出异常。
      在ScrollView中嵌套ListView(或者
    ScrollView)的另外一个问题就是,子ScrollView中无法滑动的(如果它没有显
    示完全的话),因为滑动事件会被父ScrollView吃掉,如果想要让子ScrollView也可以滑动,只能强行截取滑动事件,有牛人在论坛 中发过代码说可以。虽然我没有亲自试过,但估计是可行的。
     
     虽然在ScrollView中显示ScrollView在技术上的难题可以攻破,但是这样的设计却是非常差的用户体验因为用户会不容易看到和操作子
    ScrollView中的内容。比如好的设计是,父ListView的每个Item只显示概括性的描述,然后点击其Item会进入另外一个页面来详细描述
    和展示以及对这个Item的操作。
     源码:http://www.jinhusns.com/Products/Download/?type=xcj
    于是找到另外两种比较简单的方法,而且又没有影响的:
    1.在ScrollView中添加一属性 android:fillViewport="true" ,这样就可以让ListView全屏显示了
    2.指定ListView的高度 android:layout_height="420dp" ;