Scrollview如何自适应Listview/GridView

来源:互联网 发布:jira切换数据库 编辑:程序博客网 时间:2024/06/16 20:47

前言

前段时间做了个项目是在一个搜索界面上方显示历史搜索记录,下方显示的是搜索出来的物品,需求是物品数量不确定所以需要使用ListView,可以滑动显示,又不可以只滑动物品,也要相应可以将上方的历史记录共同滑动隐藏,所以采用了ScrollView嵌套ListView的办法,结果就遇到坑了,并没有达到想要的效果,没有办法滑动,于是在网上查询各种资料,有数限制高度,有说自适应的,资料部分是从@张辉skywish,这里获取加上从朋友那得到的办法整理了一下


首先先介绍一下ScrollView

1.ScrollView 和HorizontalScrollView是为控件或者布局添加滚动条

2.上述的两个控件只能有一个孩子,但是它并不是传统上的容器

3.上述两个控件可以互相嵌套

4.ScrollView用于设置垂直滚动条,HorizontalScrollView用于设置水平滚动条,需要注意的是,有一个属性是scrollbars可以设置滚动条的方向但是ScrollView设置成horizontal是和设置成none效果相同的,HorizontalScrollView设置成vertical和none的效果相同.


                                                                                                                                                                                                                                                public class ScrollerActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) {  // TODO Auto-generated method stub  super.onCreate(savedInstanceState);  setContentView(R.layout.main5); }}

以上就是ScrollView的简单用法,那怎么去解决ScrollView嵌套ListView呢?请往下看,GridView与ListView类似就不多阐述了


问题解决方案:

1.手动在XML/代码中设置ListView高度(在xml设置不推荐),除非ListView的数据是不可变的,但要是不可变也没有必要非这么折腾了对吧 - - 

/***动态设置ListView的高度*/public staticvoidsetListViewHeightBaseOnChildren(ListView listView){
if(listView ==null)return ;
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.getMeasureHeight();
}
     ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight +(listView.getDividerHeight()* (listAdapter.getCount()-1));
listView.setLayoutParams(params);
}


上面的这个方法就是设定ListView的高度了,在为ListView设置了Adapter之后使用就可以解决问题,但是要注意两个问题:

a.是Adapter中getView方法返回的View必须有LinearLayout组成,因为只用LinearLayout才有measure()方法,如果使用其他的布局如RelativeLayout,在调用listItem.measure(0,0);时就会抛异常,因为除LinearLayout外的其他布局的这个方法就是抛了异常的

b.是需要手动把ScrollView滚动至最顶端,因为使用这个方法,默认在ScrollView顶端的项是ListView

2、使用单个ListView取代ScrollView中所有内容
    就是说,把整个需要放在ScrollView中的内容,统统放在ListView中,原ListView上方的数据和下方数据,都作为现ListView的一个itemView,和原ListView中的单条数据是平级的关系。
    xml布局方面十分简单:

  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">    <ListView  
  2.         android:id="@+id/act_solution_2_lv"  
  3.         android:layout_width="fill_parent"  
  4.         android:layout_height="wrap_content">  
  5.   
  6.     </ListView>  
  7. </span>  
   一个单独的ListView就可以了。
    原ListView上方数据和下方数据,都写进两个xml布局文件中:

    Java代码方面,需要自定义一个Adapter,在Adapter中的getView方法中进行position值的判断,根据position值来决定inflate哪个布局:
[java] view plain copy
 print?
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">    public View getView(int position, View convertView, ViewGroup parent) {  
  2.                 //列表第一项  
  3.         if(position == 0){  
  4.            convertView = inflater.inflate(R.layout.item_solution2_top, null);  
  5.             return convertView;  
  6.         }  
  7.                 //列表最后一项  
  8.         else if(position == 21){  
  9.             convertView = inflater.inflate(R.layout.item_solution2_bottom, null);  
  10.             return convertView;  
  11.         }  
  12.   
  13.                 //普通列表项  
  14.         ViewHolder h = null;  
  15.         if(convertView == null || convertView.getTag() == null){  
  16.             convertView = inflater.inflate(R.layout.item_listview_data, null);  
  17.             h = new ViewHolder();  
  18.             h.tv = (TextView) convertView.findViewById(R.id.item_listview_data_tv);  
  19.             convertView.setTag(h);  
  20.         }else{  
  21.             h = (ViewHolder) convertView.getTag();  
  22.         }  
  23.   
  24.         h.tv.setText("第"+ position + "条数据");  
  25.   
  26.         return convertView;  
  27.     }  
  28. </span>  
    在Activty中,只需要直接为ListView设置自定义的Adapter就行了。
[java] view plain copy
 print?
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">    lv = (ListView) findViewById(R.id.act_solution_2_lv);  
  2.     adapter = new AdapterForListView2(this);  
  3.     lv.setAdapter(adapter);</span>  
3、使用LinearLayout取代ListView
    既然ListView不能适应ScrollView,那就换一个可以适应ScrollView的控件,干嘛非要吊死在ListView这一棵树上呢?而LinearLayout是最好的选择。但如果我仍想继续使用已经定义好的Adater呢?我们只需要自定义一个类继承自LinearLayout,为其加上对BaseAdapter的适配。
[java] view plain copy
 print?
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">    import android.content.Context;  
  2.     import android.util.AttributeSet;  
  3.     import android.util.Log;  
  4.     import android.view.View;  
  5.     import android.widget.BaseAdapter;  
  6.     import android.widget.LinearLayout;  
  7.   
  8.     /** 
  9.     * 取代ListView的LinearLayout,使之能够成功嵌套在ScrollView中 
  10.     * @author terry_龙 
  11.     */  
  12.     public class LinearLayoutForListView extends LinearLayout {  
  13.   
  14.         private BaseAdapter adapter;  
  15.         private OnClickListener onClickListener = null;  
  16.   
  17.         /** 
  18.          * 绑定布局 
  19.          */  
  20.         public void bindLinearLayout() {  
  21.             int count = adapter.getCount();  
  22.             this.removeAllViews();  
  23.             for (int i = 0; i < count; i++) {  
  24.                 View v = adapter.getView(i, nullnull);  
  25.   
  26.                 v.setOnClickListener(this.onClickListener);  
  27.                 addView(v, i);  
  28.             }  
  29.            Log.v("countTAG""" + count);  
  30.         }  
  31.   
  32.         public LinearLayoutForListView(Context context) {  
  33.             super(context);  
  34. </span>  
    上面的代码拷贝保存为LinearLayoutForListView.class,或者直接拷贝Demo中的这个类在自己的工程里。我们只需要把原来xml布局文件中的ListView替换为这个类就行了:
[html] view plain copy
 print?
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">    <pm.nestificationbetweenscrollviewandabslistview.mywidgets.LinearLayoutForListView  
  2.         android:id="@+id/act_solution_3_mylinearlayout"  
  3.         android:layout_width="fill_parent"  
  4.         android:layout_height="wrap_content"  
  5.         android:orientation="vertical" >  
  6.     </pm.nestificationbetweenscrollviewandabslistview.mywidgets.LinearLayoutForListView>  
  7.   
  8. </span>  
    在Activity中也把ListView改成LinearLayoutForListView,就能成功运行了。
[java] view plain copy
 print?
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">    mylinearlayout = (LinearLayoutForListView) findViewById(R.id.act_solution_3_mylinearlayout);  
  2.     adapter = new AdapterForListView(this);  
  3.     mylinearlayout.setAdapter(adapter);  
  4. </span>  
4、自定义可适应ScrollView的ListView
    这个方法和上面的方法是异曲同工,方法3是自定义了LinearLayout以取代ListView的功能,但如果我脾气就是倔,就是要用ListView怎么办?那就只好自定义一个类继承自ListView,通过重写其onMeasure方法,达到对ScrollView适配的效果。
    下面是继承了ListView的自定义类:
[java] view plain copy
 print?
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">    import android.content.Context;  
  2.     import android.util.AttributeSet;  
  3.     import android.widget.ListView;  
  4.   
  5.     public class ListViewForScrollView extends ListView {  
  6.         public ListViewForScrollView(Context context) {  
  7.             super(context);  
  8.         }  
  9.   
  10.         public ListViewForScrollView(Context context, AttributeSet attrs) {  
  11.             super(context, attrs);  
  12.         }  
  13.   
  14.         public ListViewForScrollView(Context context, AttributeSet attrs,  
  15.             int defStyle) {  
  16.             super(context, attrs, defStyle);  
  17.         }  
  18.   
  19.         @Override  
  20.         /** 
  21.          * 重写该方法,达到使ListView适应ScrollView的效果 
  22.          */  
  23.         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  24.             int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
  25.             MeasureSpec.AT_MOST);  
  26.             super.onMeasure(widthMeasureSpec, expandSpec);  
  27.         }  
  28.     }  
  29.     这个方法和方法1有一个同样的毛病,就是默认显示的首项是ListView,需要手动把ScrollView滚动至最顶端。  
  30. </span>  

    三个构造方法完全不用动,只要重写onMeasure方法,需要改动的地方比起方法3少了不是一点半点…
    在xml布局中和Activty中使用的ListView改成这个自定义ListView就行了。代码就省了吧…


package clb.com.tangcco038_weibo.weight;

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

/**
* Created by Administrator on 2017/3/3.
*/

public class WrapHeightGridView extends GridView {

public WrapHeightGridView(Context context) {
super(context);
}

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

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

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

//重新计算GridView条目的高度 >> 位运算符
int heightMeasureSize = MeasureSpec.makeMeasureSpec
(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);


super.onMeasure(widthMeasureSpec, heightMeasureSize);

}
}