浅谈android中的ListView合集系列之解决ScrollView和ListView嵌套冲突
Posted 熊喵先生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈android中的ListView合集系列之解决ScrollView和ListView嵌套冲突相关的知识,希望对你有一定的参考价值。
相信大家都已经可以熟练使用ListView和GridView,大神们估计都在使用RecyclerView了。如果还在使用ListView,你肯定有这样的一个深刻的感受,那就是在做一个APP的时候使用ListView和GridView很频繁,并且经常会遇到一个页面中除了有ListView或GridView可能还有一些其他的内容,但是可能内容很多,你第一时间就会想到让它整体滑动即可,那就是在总的布局外面包裹一个ScrollView。也就是出现了ScrollView中嵌套一个ListView的场景,或者你的ScrollView嵌套多个ListView或者GridView的时候。我们自认为出现场景应该是整体内容会滑动,但是你会惊讶的发现并不是这样的,你会发现如果是嵌套了一个ListView或者GridView的时候,ListView只会显示一个Item项,GridView也会只显示一行。看来我们还是错了,具体是什么样子的下面我们通过一个demo来看看,然后分析一下为什么会出现这样的结果,以及最后解决的办法。
我就简单写了一个ListView只要能说明问题即可,至于ListView的优化等问题不是本篇博客的重点,将会在后续的博客出现。
布局文件(注意:仔细看清楚ListView的layout高度的属性是warp_content)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include
android:id="@+id/include_header_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="@layout/header_home" />
<ScrollView
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="380dp"
android:scaleType="fitXY"
android:src="@drawable/center_image" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF"
android:orientation="horizontal" >
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="商家分类" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:id="@+id/test"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="智能排序" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#77000000" />
<TextView
android:id="@+id/wei_express"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/slip_off_img_bd_express"
android:gravity="center"
android:padding="15dp"
android:text="微送" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.1dp"
android:background="#44000000" />
</LinearLayout>
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#33000000"
android:dividerHeight="0.1dp" >
</ListView>
</LinearLayout>
</ScrollView>
</LinearLayout>
MainActivity.java
package com.mikyou.listviewtest;
import com.mikyou.utils.SystemStatusManager;
import android.app.Activity;
import android.database.DataSetObserver;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setTranslucentStatus();
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initView() {
mListView=(ListView) findViewById(R.id.listview);
mListView.setAdapter(new MyAdapter());
}
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return 7;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
return view;
}
}
private void initData() {
}
private void setTranslucentStatus() {//沉浸标题栏效果
// TODO Auto-generated method stub
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
Window win=getWindow();
WindowManager.LayoutParams winParams=win.getAttributes();
final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
winParams.flags |=bits;
win.setAttributes(winParams);
}
SystemStatusManager tintManager = new SystemStatusManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(0);
tintManager.setNavigationBarTintEnabled(true);
}
}
运行的结果:
注意:你会发现只会显示一个,而我的设置adapter的时候明明设置的是7个,为什么只显示一个呢??这是为什么,这就是意味ListView和ScrollView的嵌套带来的问题,因为ListView和GridView本身就是继承于ScrollView,而ScrollView中再次嵌套一个ScrollVewi就会出现高度测量的问题,我们都知道ListView也可以上下滑动,当ListView中的内容很多的时候,屏幕不足以显示的时候才会滑动显示。那为什么只会显示一项呢?其实仔细分析一下很简单就是ListView高度不够,如果高度达到一定的话,就会出现其他的Item项,注意看我们的布局ListView的Wrap_content,也就是测量里面的内容的高度来得到的,也就对应着android中的VIew测量的模式中的AT_MOST,这种测量模式实际上系统是不会给你真正的测的,而是根据测量其内部的内容来给出一个适合的尺寸,是系统只会测EXACTLY模式即为对应match_parent和指定明确的尺寸。我们还知道ListView每个Item加载模式通过getView方法一个一个的加载出来的,也就是当你第一个Item加载完后,它只测量第一个Item,所以只显示第一个Item,因为高度不够,只能显示第一个。如果不信,我们可以作如下的两个实验,第一打印出此时ListVIew的高度看看是不是此时ListView的高度是不是等于第一个Item的高度,第二就是我不指定warp_content,我指定一个明确尺寸看看。
第一个实验:比较第一个Item高度和ListVIew总高度,来说明此时ScrollView测量第一个Item来作为整个ListView的高度。
通过给ListView添加getViewTreeObserver().addOnGlobalLayoutListener事件,然后在回调方法中去得到高度,注意:因为我们直接或者间接在Activity中的OnCreate方法中去得到高度是为0,因为此时Activity中的View窗体树并没有绘制完毕,该方法就是通过监听整个View的树绘制完毕后才会回调,所以在该方法才能得到高度
package com.mikyou.listviewtest;
import com.mikyou.utils.SystemStatusManager;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setTranslucentStatus();
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initView() {
mListView=(ListView) findViewById(R.id.listview);
mListView.setAdapter(new MyAdapter());
//
mListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
View firstItemView=mListView.getAdapter().getView(0, null, mListView);//得到第一个Item
firstItemView.measure(0, 0);
System.out.println("第一个Item的高度:"+firstItemView.getMeasuredHeight());
System.out.println("mListView的高度:"+mListView.getHeight());
}
});
}
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return 7;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
return view;
}
}
private void initData() {
}
private void setTranslucentStatus() {//沉浸标题栏效果
// TODO Auto-generated method stub
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
Window win=getWindow();
WindowManager.LayoutParams winParams=win.getAttributes();
final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
winParams.flags |=bits;
win.setAttributes(winParams);
}
SystemStatusManager tintManager = new SystemStatusManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(0);
tintManager.setNavigationBarTintEnabled(true);
}
}
在Logcat中打印出来的结果是:
所以第一个实验证明咱们的观点是正确的
那么接下进行第二个实验:通过修改布局文件中的ListView的Layout_Height属性,我们指定明确尺寸看看会发生什么?
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include
android:id="@+id/include_header_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="@layout/header_home" />
<ScrollView
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="380dp"
android:scaleType="fitXY"
android:src="@drawable/center_image" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF"
android:orientation="horizontal" >
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="商家分类" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:id="@+id/test"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="智能排序" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#77000000" />
<TextView
android:id="@+id/wei_express"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/slip_off_img_bd_express"
android:gravity="center"
android:padding="15dp"
android:text="微送" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.1dp"
android:background="#44000000" />
</LinearLayout>
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="300dp"
android:divider="#33000000"
android:dividerHeight="0.1dp" >
</ListView>
</LinearLayout>
</ScrollView>
</LinearLayout>
运行结果和Logcat打印的结果:
大家会看到这时候就出现了2个Item,但是最后一个Item的还是显示不够完全,这因为我们的高度还是不够,我们在布局文件指定了ListView的高度为300dp,会发现logcat 输出的高度600,这个就是另外的知识了,因为最后打印出来以及View绘制的尺寸都是Px为单位的,但是这个dp实际上就是逻辑单位,这也是为了兼容和适配Android中众多不同手机分辨率。由于在不同的手机分辨率中PX和DP换算率也不一样,由于我的模拟器是1280*768的尺寸,他们换算率为1:2,也就是1dp等于2px,我们设置了300dp,所以打印出来就是600,这样能解释为什么这第二Item显示不完全,因为一个Item需要321px,两个就是641px,而我们设置300dp也就是600px小于641px所以肯定显示不完全了。
通过如下分析,本以为可以找到一个解决办法,它也确实可以显示出其他的Item,但是有个很大的问题就是你知道每个Item的高度吗?由于每个不同ListView中的Item的样式不一样,高度自然就不一样,难道我们每次都去写死它的高度吗?而且如果从网络中获取数据,Item的个数也是在变得,整个ListVIew的高度也是在变的。那么这个方法就断了吗?其实不然,我们再顺着这个思路想想,这个思路就是“只要去修改ListView的高度就能实现显示其他的Item”,但是现在问题就是如何去得到一个合适的ListView的高度呢,其实最后的思路就是这样的:“动态的测量ListView的高度然后动态的去修改ListView的高度就能实现显示其他的Item”。
测量的大致思路如下:
就是通过ListView对象去getAdapter去得到适配器中的每个Item的然后通过一个循环遍历地去测量每个Item的高度,然后通过累加这些Item的高度,最后得到总的高度,再把这个高度设置给ListView即可,这里我将这个功能的实现封装一个工具方法,只要简单地将ListVIew对象传入即可。
测量高度的方法:
package com.mikyou.utils;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ListAdapter;
import android.widget.ListView;
public class MikyouMetricListViewHeightUtils {
public static void setListViewHeight(ListView lv){
if (lv==null) {
return ;
}
ListAdapter adapter=lv.getAdapter();
if (adapter==null) {
return ;
}
int totalHeight=0;
for (int i = 0; i < adapter.getCount(); i++) {
View listItem=adapter.getView(i, null, lv);
listItem.measure(0, 0);
totalHeight+=listItem.getMeasuredHeight();
}
LayoutParams params=lv.getLayoutParams();
params.height=totalHeight+(lv.getDividerHeight()*(lv.getCount()-1));//这里还将分割线的高度考虑进去了,统计出所有分割线占有的高度和
lv.setLayoutParams(params);
}
}
通过使用这个方法后直接将布局文件写成Warp_content即可,现在不需要直接指定了明确尺寸了,看看最后的实现效果:
package com.mikyou.listviewtest;
import com.mikyou.utils.MikyouMetricListViewHeightUtils;
import com.mikyou.utils.SystemStatusManager;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setTranslucentStatus();
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initView() {
mListView=(ListView) findViewById(R.id.listview);
mListView.setAdapter(new MyAdapter());
/**
* @author zhongqihong
* 解决ScrollView与ListView冲突的问题
* 该方法需要在setAdapter之后调用
* */
MikyouMetricListViewHeightUtils.setListViewHeight(mListView);
mListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
View firstItemView=mListView.getAdapter().getView(0, null, mListView);//得到第一个Item
firstItemView.measure(0, 0);
System.out.println("第一个Item的高度:"+firstItemView.getMeasuredHeight());
System.out.println("mListView的高度:"+mListView.getHeight());
}
});
}
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return 7;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
return view;
}
}
private void initData() {
}
private void setTranslucentStatus() {//沉浸标题栏效果
// TODO Auto-generated method stub
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
Window win=getWindow();
WindowManager.LayoutParams winParams=win.getAttributes();
final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
winParams.flags |=bits;
win.setAttributes(winParams);
}
SystemStatusManager tintManager = new SystemStatusManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(0);
tintManager.setNavigationBarTintEnabled(true);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include
android:id="@+id/include_header_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="@layout/header_home" />
<ScrollView
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="380dp"
android:scaleType="fitXY"
android:src="@drawable/center_image" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF"
android:orientation="horizontal" >
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="商家分类" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:id="@+id/test"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="智能排序" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#77000000" />
<TextView
android:id="@+id/wei_express"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/slip_off_img_bd_express"
android:gravity="center"
android:padding="15dp"
android:text="微送" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.1dp"
android:background="#44000000" />
</LinearLayout>
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#33000000"
android:dividerHeight="0.1dp" >
</ListView>
</LinearLayout>
</ScrollView>
</LinearLayout>
最后实现的结果:
注意:最后分析一下结果,我们可以看到我们的7个Item全部完全显示,并且Logcat显示的ListVIew的高度为2253px,一个Item为321px,有7个Item就是2247px,但是我们还算了分割线一个分割线占的高度为1px,7个Item占有6个分割线,所以最后的高度:2247px+6px=2253px.
OK,大功告成,以后大家可以使用我这个工具类可以方便测量ListView的高度,并且下次遇到这种问题就可以很轻松的解决了。
但是,有的人就反映说上面的方法效率不高,不过确实效率不高,因为一旦拿到的数据有很多的话,还有手动地去测量每个ListView的Item的高度,所以后来又找到两个方法,方法一:就是通过自定一个类然后就去继承ListView然后去重写OnMeasure方法,然后去调用一个makeMeasureSpec方法,实际上和我们上面的方法思想一样,因为这样还是修改它的测量方法,还是动态测量高度从而得到真实高度,从而解决冲突。但是这个方法有一个Bug就是不能支持嵌套布局除了LinearLayout之外的类似RelativeLayout等其他布局,源码是这样说的:
/**
* Creates a measure specification based on the supplied size and mode.
*
* The mode must always be one of the following:
* <ul>
* <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
* <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
* <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
* </ul>
*
* <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's
* implementation was such that the order of arguments did not matter
* and overflow in either value could impact the resulting MeasureSpec.
* {@link android.widget.RelativeLayout} was affected by this bug.
* Apps targeting API levels greater than 17 will get the fixed, more strict
* behavior.</p>
*
* @param size the size of the measure specification
* @param mode the mode of the measure specification
* @return the measure specification based on size and mode
*/
它大致的意思:就是这个方法的作用就是通过传入的指定的测量的尺寸和测量的模式来得到一个明确的测量方式,但是传入的模式只支持UNSPECIFIED模式和EXACTLY模式
AT_MOST模式,并且需要注意的是在API17以及以下更低的版本中,传入的参数的顺序不重要,但是传入的值溢出的话则会影响实际的测量,其中的相对布局就会出现这种Bug
package com.mikyou.myview;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;
public class MyListView extends ListView{
public MyListView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
/**
* 重写OnMeasure方法
* onMeasure方法是测量view和它的内容,决定measured width和measured height的,
* 这个方法由 measure(int, int)方法唤起,子类可以覆写onMeasure来提供更加准确和有效的测量。
其中两个输入参数:
widthMeasureSpec
heightMeasureSpec
分别是parent提出的水平和垂直的空间要求。
这两个要求是按照View.MeasureSpec类来进行编码的。
参见View.MeasureSpec这个类的说明:这个类包装了从parent传递下来的布局要求,传递给这个child。
每一个MeasureSpec代表了对宽度或者高度的一个要求。
每一个MeasureSpec有一个尺寸(size)和一个模式(mode)构成。
MeasureSpecs这个类提供了把一个的元组包装进一个int型的方法,从而减少对象分配。当然也提供了逆向的解析方法,从int值中解出size和mode。
*
* */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec( //这个makeMeasureSpec方法表示就是针对明确的测量模式,来得到相应的尺寸
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); //第一个参数就是传入的尺寸size,表示Integer表示最大空间值进行2位移位运算,第二参数指定为AT_MOST表示是"warp_content"的测量模式
super.onMeasure(widthMeasureSpec, expandSpec);//指定了我们明确的测量尺寸
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include
android:id="@+id/include_header_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="@layout/header_home" />
<ScrollView
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="380dp"
android:scaleType="fitXY"
android:src="@drawable/center_image" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF"
android:orientation="horizontal" >
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="商家分类" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:id="@+id/test"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="智能排序" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#77000000" />
<TextView
android:id="@+id/wei_express"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/slip_off_img_bd_express"
android:gravity="center"
android:padding="15dp"
android:text="微送" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.1dp"
android:background="#44000000" />
</LinearLayout>
<com.mikyou.myview.MyListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#33000000"
android:dividerHeight="0.1dp"
></com.mikyou.myview.MyListView>
</LinearLayout>
</ScrollView>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include
android:id="@+id/include_header_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="@layout/header_home" />
<ScrollView
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="380dp"
android:scaleType="fitXY"
android:src="@drawable/center_image" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF"
android:orientation="horizontal" >
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="商家分类" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:id="@+id/test"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="智能排序" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#77000000" />
<TextView
android:id="@+id/wei_express"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/slip_off_img_bd_express"
android:gravity="center"
android:padding="15dp"
android:text="微送" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.1dp"
android:background="#44000000" />
</LinearLayout>
<com.mikyou.myview.MyListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#33000000"
android:dividerHeight="0.1dp"
></com.mikyou.myview.MyListView>
</LinearLayout>
</ScrollView>
</LinearLayout>
package com.mikyou.listviewtest;
import com.mikyou.myview.MyListView;
import com.mikyou.utils.MikyouMetricListViewHeightUtils;
import com.mikyou.utils.SystemStatusManager;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
private MyListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setTranslucentStatus();
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initView() {
mListView=(MyListView) findViewById(R.id.listview);
mListView.setAdapter(new MyAdapter());
/**
* @author zhongqihong
* 解决ScrollView与ListView冲突的问题
* 该方法需要在setAdapter之后调用
* */
//MikyouMetricListViewHeightUtils.setListViewHeight(mListView);
mListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
View firstItemView=mListView.getAdapter().getView(0, null, mListView);//得到第一个Item
firstItemView.measure(0, 0);
System.out.println("第一个Item的高度:"+firstItemView.getMeasuredHeight());
System.out.println("mListView的高度:"+mListView.getHeight());
}
});
}
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return 7;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
return view;
}
}
private void initData() {
}
private void setTranslucentStatus() {//沉浸标题栏效果
// TODO Auto-generated method stub
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
Window win=getWindow();
WindowManager.LayoutParams winParams=win.getAttributes();
final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
winParams.flags |=bits;
win.setAttributes(winParams);
}
SystemStatusManager tintManager = new SystemStatusManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(0);
tintManager.setNavigationBarTintEnabled(true);
}
}
运行结果和上面的一样:
但是,说到这里,我想说的是我们之前所做的努力实际上都是自己自作自受,没事闲的蛋疼而已,实际上在真正开发中人家Goole发话了,不会在ScrollView中去放一个ListView的不信来看看Google官方的原话:
Google员工 Roman Guy早已回复:“There is no need to put a ListView in a ScrollView since ListView already supports scrolling. Do NOT put a ListView inside a ScrollView. ListView already handles scrolling, you’re only going to run into trouble. “ 更详细的,大家可以看看他在 google i/o 上的ListView视频讲解。大家不要抱着侥幸的心理,要用正确的方法去做正确的事情。大家是不是顿时就懵逼了,是不是心中涌出一万个草泥马。但是如果不嵌套的话怎么去实现这样的一个效果呢,实际上在设计ListView的时候就可以实现对不同样式的显示,也就是整个界面就只用一个ListView来显示,不搞那么多嵌套那么麻烦,意味我们需要用ListView中的第一个Item的位置来显示我们其他内容,所以整体思路就出来,实际上就是通过对ListView中的position的控制,让那些其他内容占据ListView第一个Item的位置即[position为0的位置,然后接下来其他的位置显示真正每个Item的信息下面我们还是通过这个Demo来实现下吧:
首先我们把那些顶部其他的内容抽取成一个布局文件,然后我的主布局只有一个ListView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="380dp"
android:scaleType="fitXY"
android:src="@drawable/center_image" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF"
android:orientation="horizontal" >
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="商家分类" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#44000000" />
<TextView
android:id="@+id/test"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/all_category_arrow_down"
android:gravity="center"
android:padding="15dp"
android:text="智能排序" />
<View
android:layout_width="0.1dp"
android:layout_height="match_parent"
android:background="#77000000" />
<TextView
android:id="@+id/wei_express"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/slip_off_img_bd_express"
android:gravity="center"
android:padding="15dp"
android:text="微送" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.1dp"
android:background="#44000000" />
</LinearLayout>
</LinearLayout>
主布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include
android:id="@+id/include_header_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="@layout/header_home" />
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#33000000"
android:dividerHeight="0.1dp" >
</ListView>
</LinearLayout>
MainActivity
package com.mikyou.listviewtest;
import com.mikyou.myview.MyListView;
import com.mikyou.utils.MikyouMetricListViewHeightUtils;
import com.mikyou.utils.SystemStatusManager;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setTranslucentStatus();
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initView() {
mListView=(ListView) findViewById(R.id.listview);
mListView.setAdapter(new MyAdapter());
/**
* @author zhongqihong
* 解决ScrollView与ListView冲突的问题
* 该方法需要在setAdapter之后调用
* */
//MikyouMetricListViewHeightUtils.setListViewHeight(mListView);
mListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
View firstItemView=mListView.getAdapter().getView(0, null, mListView);//得到第一个Item
firstItemView.measure(0, 0);
System.out.println("第一个Item的高度:"+firstItemView.getMeasuredHeight());
System.out.println("mListView的高度:"+mListView.getHeight());
}
});
}
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return (1+7);//为什么是1+7呢??因为我要把ListView的其中一个Item空间作为那些顶部内容占用,我们这是把第一个Item给它用,所以就相当于在原来基础上添加一个Item
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (position==0) {//如果position为0即第一项的时候,所以加载显示顶部内容
View view2=View.inflate(MainActivity.this, R.layout.top_listview, null);
/**
* @author zhongqihong
* //并且要return,为什么呢??因为我们都知道每个Item的加载出来都要去调用一次getView的方法,
* 所以当我已经加载出第一个Item即我们顶部内容时候,
* 说明getView此次加载的使命已经完成了,所以要return掉,以终止执行防止它执行接下来其他的方法
* */
return view2;
}else {//表示从第二个开始显示正常的ListView的Item
View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
return view;
}
}
}
private void initData() {
}
private void setTranslucentStatus() {//沉浸标题栏效果
// TODO Auto-generated method stub
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
Window win=getWindow();
WindowManager.LayoutParams winParams=win.getAttributes();
final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
winParams.flags |=bits;
win.setAttributes(winParams);
}
SystemStatusManager tintManager = new SystemStatusManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(0);
tintManager.setNavigationBarTintEnabled(true);
}
}
最后实现效果:
注意:其实最后一种方法才是最正确的最高效率的方法,根本就不需要考虑所谓嵌套冲突的问题。建议以后大家还是采用这种方法来实现。
以上是关于浅谈android中的ListView合集系列之解决ScrollView和ListView嵌套冲突的主要内容,如果未能解决你的问题,请参考以下文章
Android项目实战(二十):浅谈ListView悬浮头部展现效果
浅谈ScrollView嵌套ListView及ListView嵌套的高度计算