您的位置:首页 > 其它

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

2016-05-23 12:40 417 查看
  开发中可能遇到各种需求 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息