ScrollView,ListView等带滚动条控件嵌套解决方案汇总

Posted IyangcLove

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ScrollView,ListView等带滚动条控件嵌套解决方案汇总相关的知识,希望对你有一定的参考价值。


  开发中可能遇到各种需求 ViewPager+ListView(首页顶部轮播图+产品/新闻列表)是特别常见的一种需求,这里就需要用到外部的ScrollView 嵌套一个内部的ListView 进行一个显示,直接写的话 ScrollView 的滚动条会覆盖掉ListView的滚动条 使它无法滚动,下面是网上收集的一些解决方案,还有一些开发中遇到的一点小技巧,让这种需求更简单的搞定。

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

     

 // 动态设置ListView的高度
 // 此方法在对应的Adapter中定义,这里是ListAdapter。
 // 设置适配器后调用此方法传入对应的ListView就可以了
 public 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,猜测应该是ListView调用方法的时候重新设置宽高导致的一些焦点抢夺问题,对于这个问题有小技巧 下面会讲到。

2、使用单个ListView取代ScrollView中所有内容

  在我的文章android ListView显示多个类型item 和 item中控件抢夺焦点解决办法中有说到,不知道的盆友可以去看看。

3、使用LinearLayout取代ListView

  既然ListView不能适应ScrollView,那就换一个可以适应ScrollView的控件,干嘛非要吊死在ListView这一棵树上呢?而LinearLayout是最好的选择。通过addView(View v)方法 可以依次添加item 只是这种方式的适配性很差 如果item少且固定可以考虑使用。

4、自定义可适应ScrollView的ListView
    自定义一个类继承自ListView,通过重写其onMeasure方法,达到对ScrollView适配的效果。
    下面是继承了ListView的自定义类:
 

  public class ListViewForScrollView extends ListView 
    public ListViewForScrollView(Context context) 
        super(context);
    
    public ListViewForScrollView(Context context, AttributeSet attrs) 
        super(context, attrs);
    
    public ListViewForScrollView(Context context, AttributeSet attrs,
        int defStyle) 
        super(context, attrs, defStyle);
    
    @Override
    /**
     * 重写该方法,达到使ListView适应ScrollView的效果
     */
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
        MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    
 MeasureSpec属性不了解的可以参考 http://blog.csdn.net/kaixinbingju/article/details/8649218

   这个方法和方法1有一个同样的毛病,就是默认显示的首项是ListView,需要手动把ScrollView滚动至最顶端。

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

          sv.smoothScrollTo(0, 0);

      接下来讲小技巧,方法4和方法1 应该是使用得最多的吧,其实可以不用手动滚动回去的,只需要设置ListView上方控件或者父控件的一个属性

        android:focusableInTouchMode="true" //需要注意的是 有时候设置父控件属性时会不起作用,具体情况不知。

   下面给出方法4的相关代码,可直接拷贝使用

  自定义的NoScrollListView

public class NoScrollListView extends ListView 
    public NoScrollListView(Context context) 
        super(context);
    

    public NoScrollListView(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    public NoScrollListView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
    

    //MeasureSpec.EXACTLY:当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",
    //  或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
    //MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,
    // 此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
    //MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    

item_lv.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="10dp">

    <ImageView
        android:id="@+id/iv"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        android:background="#ff0000"/>
 
    <TextView
        android:id="@+id/item"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:gravity="fill_vertical"
        android:text="TextTextTestScrollView and ListView"
        android:textColor="#ff0000"
        android:textSize="30sp" />
    <!--android:textColor="#1fa67a"-->
</LinearLayout>

Activity

public class ScrollActivity extends Activity 
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.scroll_activity);
        ListView listView = (ListView) findViewById(R.id.lv);
        listView.setAdapter(new MyAdapter());
    

    class MyAdapter extends BaseAdapter 
        @Override
        public int getCount() 
            return 20;
        

        @Override
        public Object getItem(int position) 
            return null;
        

        @Override
        public long getItemId(int position) 
            return 0;
        

        @Override
        public View getView(int position, View convertView, ViewGroup parent) 
            if (convertView == null) 
                convertView = getLayoutInflater().inflate(R.layout.item_lv, null);
            
            return convertView;
        
    

scroll_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
   <ScrollView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
        >
       <!-- 在LinearLayout 或者ImageView中设置android:focusableInTouchMode属性都可以达到效果 -->
       <LinearLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:orientation="vertical"
           android:focusableInTouchMode="true" >
       <ImageView
           android:layout_width="match_parent"
           android:layout_height="200dp"
           android:src="@mipmap/ic_launcher"
           />
       <cd.com.hslistview.NoScrollListView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:id="@+id/lv"
           >
       </cd.com.hslistview.NoScrollListView>
       </LinearLayout>
   </ScrollView>
</LinearLayout>

   大家可以去掉属性测试 再添加属性进行测试,本人一直在用,是完全没问题的。 

本文参考链接:http://www.devstore.cn/essay/essayInfo/945.html

以上是关于ScrollView,ListView等带滚动条控件嵌套解决方案汇总的主要内容,如果未能解决你的问题,请参考以下文章

解决ScrollView中嵌套ListView滚动效果冲突问题

使用LinearLayout实现ListView,解决ListView和ScrollView滚动冲突

ScrollView 中的 ListView 在 Android 上不滚动

ScrollView 内不可滚动的 ListView

Scrollview 不使用 2 listview 滚动

内部滚动视图( ScrollView / RecyclerView / ListView )视频未播放。