四种方案解决ScrollView嵌套ListView问题

一、 为什么要使用ScrollView嵌套ListView的奇怪的结构        

这是天猫商城的确认订单的页面,ScrollView中嵌套了ExpandableListView,ExpandableListView上面有固定的一些控件,下面也有固定的一些控件,整体又要能够滚动。    列表数据要嵌在固定数据中间,并且作为整体一起滚动,有了这样的设计需求,于是就有了ScrollView嵌套ListView的奇怪结构。

二、 ScrollView、ListView嵌套结构碰到的问题   

不多说,直接看失败例子: 

android:id= "@+id/act_solution_1_sv"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent" >
    
android:layout_width= "fill_parent"
android:layout_height= "wrap_content"
android:orientation= "vertical" >
         
android:layout_width= "fill_parent"
android:layout_height= "wrap_content"
android:text= "\nListView上方数据\n"  />
               
         
android:id= "@+id/act_solution_1_lv"
android:layout_width= "fill_parent"
android:layout_height= "wrap_content" >
                    
  android:layout_width= "fill_parent"
  android:layout_height= "wrap_content"
  android:text= "\nListView下方数据\n"  />
   ScrollView中只能放一个控件,一般都放LinearLayout,orientation属性值为vertical。在 LinearLayout中放需要呈现的内容。ListView也在其中,ListView的高度设为适应自身内容(wrap_content)。粗略一 看,应该没有什么问题。但是看下面的实际效果图:



图中黑框的部分就是ListView,里面放了20条数据,但是却只显示了1条。
    控件的属性设置上没有问题,但是为什么没有按照我的想法走呢?
    看看下面这个图:

 是否有点明白了呢?原因就是scroll事件的消费处理以及ListView控件的高度设定问题。
    虽然我看源码也看了不少,但是要说出来却不知到该怎么下手,我是大概知道原因,但是不知道怎么整理完全。求高手赐教…


三、问题解决方案

1、手动设置ListView高度
    经过测试发现,在xml中直接指定ListView的高度,是可以解决这个问题的,但是ListView中的数据是可变的,实际高度还需要实际测量。于是手动代码设置ListView高度的方法就诞生了。


/**
* 动态设置ListView的高度
* @param listView
*/
public  static  void  setListViewHeightBasedOnChildren(ListView listView) { 
     if (listView ==  null return ;
 
     ListAdapter listAdapter = listView.getAdapter(); 
     if  (listAdapter ==  null ) { 
         // pre-condition 
         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); 
}
 上面这个方法就是设定ListView的高度了,在为ListView设置了Adapter之后使用,就可以解决问题了。
    但是这个方法有个两个细节需要注意:
        一是Adapter中getView方法返回的View的必须由LinearLayout组成,因为只有LinearLayout才有 measure()方法,如果使用其他的布局如RelativeLayout,在调用listItem.measure(0, 0);时就会抛异常,因为除LinearLayout外的其他布局的这个方法就是直接抛异常的,没理由…。我最初使用的就是这个方法,但是因为子控件的顶 层布局是RelativeLayout,所以一直报错,不得不放弃这个方法。
        二是需要手动把ScrollView滚动至最顶端,因为使用这个方法的话,默认在ScrollView顶端的项是ListView,具体原因不了解,求大神解答…可以在Activity中设置:


sv = (ScrollView) findViewById(R.id.act_solution_1_sv);

2、使用单个ListView取代ScrollView中所有内容
    这个方法是我在试了几个方法都失败的情况下自己琢磨出来的。
    用一张图来解释这个方法的思想:


  就是说,把整个需要放在ScrollView中的内容,统统放在ListView中,原ListView上方的数据和下方数据,都作为现ListView的一个itemView,和原ListView中的单条数据是平级的关系。


    xml布局方面十分简单:

  android:id= "@+id/act_solution_2_lv"
  android:layout_width= "fill_parent"
  android:layout_height= "wrap_content" >

  一个单独的ListView就可以了。
    原ListView上方数据和下方数据,都写进两个xml布局文件中


Java代码方面,需要自定义一个Adapter,在Adapter中的getView方法中进行position值的判断,根据position值来决定inflate哪个布局:

public  View getView( int  position, View convertView, ViewGroup parent) {
             //列表第一项
     if (position ==  0 ){
        convertView = inflater.inflate(R.layout.item_solution2_top,  null );
         return  convertView;
     }
             //列表最后一项
     else  if (position ==  21 ){
         convertView = inflater.inflate(R.layout.item_solution2_bottom,  null );
         return  convertView;
     }
             
             //普通列表项
     ViewHolder h =  null ;
     if (convertView ==  null  || convertView.getTag() ==  null ){
         convertView = inflater.inflate(R.layout.item_listview_data,  null );
         h =  new  ViewHolder();
         h.tv = (TextView) convertView.findViewById(R.id.item_listview_data_tv);
         convertView.setTag(h);
     } else {
         h = (ViewHolder) convertView.getTag();
     }
             
     h.tv.setText( "第" + position +  "条数据" );
 
     return  convertView;
}
 在Activty中,只需要直接为ListView设置自定义的Adapter就行了。
lv = (ListView) findViewById(R.id.act_solution_2_lv);
adapter =  new  AdapterForListView2( this );
lv.setAdapter(adapter);


3、使用LinearLayout取代ListView
    既然ListView不能适应ScrollView,那就换一个可以适应ScrollView的控件,干嘛非要吊死在ListView这一棵树上呢?而 LinearLayout是最好的选择。但如果我仍想继续使用已经定义好的Adater呢?我们只需要自定义一个类继承自LinearLayout,为其 加上对BaseAdapter的适配。


import  android.content.Context;
import  android.util.AttributeSet;
import  android.util.Log;
import  android.view.View;
import  android.widget.BaseAdapter;
import  android.widget.LinearLayout;
 
/**
* 取代ListView的LinearLayout,使之能够成功嵌套在ScrollView中
* @author 
*/
public  class  LinearLayoutForListView  extends  LinearLayout {
 
     private  BaseAdapter adapter;
     private  OnClickListener onClickListener =  null ;
 
     /**
      * 绑定布局
      */
     public  void  bindLinearLayout() {
         int  count = adapter.getCount();
         this .removeAllViews();
         for  ( int  i =  0 ; i < count; i++) {
             View v = adapter.getView(i,  null null );
 
             v.setOnClickListener( this .onClickListener);
             addView(v, i);
         }
        Log.v( "countTAG" ""  + count);
     }
 
     public  LinearLayoutForListView(Context context) {
         super (context);
上面的代码拷贝保存为LinearLayoutForListView.class,或者直接拷贝Demo中的这个类在自己的工程里。我们只需要把原来xml布局文件中的ListView替换为这个类就行了:











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值