ListView 与 ScrollView 同在一界面会导致ListView 显示变形,ListView只显示出了一个条目的高度,本文我们来谈谈如何解决Android应用中ListView 和 ScrollView 共生的问题。
一开始就想着用一个ScrollView把主要内容和评论区的ListView包起来,然后添加各个控件的内容即可,但是写出来之后发现ListView只显示出了一个条目的高度,并且不能滑动,网上搜了一下发现原因是ScrollView和ListView都是可滑动的,把它们放在一块会有冲突,最后还是ScrollView获得了焦点,ListView不能滑动。网上的解决方法最多的是在加载ListView时用getMeasure计算每个条目和分割线的高度,然后相加,把结果设置为ListView控件的高度,不过貌似是只适用于ListView每个条目高度都一样的情况(没有试过,很奇怪为什么会这样)。要么就是自定义一个继承自ListView的控件,也是事先设置好ListView的高度,但这样总归比较麻烦,而且准确度不如由系统自己构造好。
懒癌发作实在不想自己去做这些事情,于是便想试一下比较投机的方法,就是在ListView的Adapter的getView方法中根据position构造不同的界面,即如果position是0,则用原来主要信息的xml文件取inflate convertView,否则就用评论条目的xml去inflate,经试验果然可行。之后不死心想看下有没有更好的实现方法,去overflow上找了一下,发现有人推荐的方法和我的差不多,所以认为这种方法是比较好的,不需要做额外的工作,只需要把inflate的工作由主Activity放在Adapter里就可以了。
getView方法
@Override public View getView(int position, View convertView, ViewGroup parent) { /*主信息界面*/ if(0 == position) { MainHolder holder = null; convertView = inflater.inflate(R.layout.info, parent, false); holder = new MainHolder(); convertView.setTag(holder); ・・・・・・ ・・・・・・ } /*评论界面*/ else { ItemHolder holder = null; convertView = inflater.inflate(R.layout.item, parent, false); holder = new ItemHolder(); convertView.setTag(holder); ・・・・・・ ・・・・・・ return convertView; } }
另一个方法解决
Android 解决ListView 和 ScrollView 共存冲突的问题
ListView 与 ScrollView 同在一界面会导致ListView 显示变形,因为ListView 也有自带的滚动事件,故无法与ScrollView 相容,可能造成的现象是ListView 只能显示一行或者两行,其他数据在那一点儿宽的地方做滚动,甚不雅观。
下面是我的一个实现 步骤:
1、继承LinearLayout,既然会冲突那就不用ListView 改成线性布局做动态布局效果
2、继承BaseAdapter ,可以参照一下Android app源码中 Widget 目录下的SimpleAdapter 为前面扩展的LinearLayout做数据。
3、模拟数据填充扩展后的BaseAdapter 为扩展后的LinearLayout 加载数据
第一步:新建LinearLayoutForListView 类使其扩展LinearLayout重写以下两个方法:
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public LinearLayoutForListView(Context context) { super(context); } public LinearLayoutForListView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub }
这两个方法可选,不过建议都写上,第一个方法可以让我们通过 编程的方式 实例化出来,第二个方法可以允许我们通过 XML的方式注册 控件,可以在第二个方法里面为扩展的复合组件加属性。
为其添加get / set 方法
/** * 获取Adapter * * @return adapter */ public AdapterForLinearLayout getAdpater() { return adapter; } /** * 设置数据 * * @param adpater */ public void setAdapter(AdapterForLinearLayout adpater) { this.adapter = adpater; bindLinearLayout(); } /** * 获取点击事件 * * @return */ public OnClickListener getOnclickListner() { return onClickListener; } /** * 设置点击事件 * * @param onClickListener */ public void setOnclickLinstener(OnClickListener onClickListener) { this.onClickListener = onClickListener; }
第二步:新建AdapterForLinearLayout 类继承自BaseAdapter,并为其添加构造函数
private LayoutInflater mInflater; private int resource; private List extends Map> data; private String[] from; private int[] to; public AdapterForLinearLayout(Context context, List extends Map > data, int resouce, String[] from, int[] to) { this.data = data; this.resource = resouce; this.data = data; this.from = from; this.to = to; this.mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); }
此构造函数模仿 simpleAdapter 通过传进来的resouce 为布局设置数据。通过继承BaseAdapter 重要的实现方法在下面getView ,此方法判断通过传进来的 String[] from 与 int[] to 为分别查找出View 并为View 设置相应的Text,代码如下:
@Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub convertView = mInflater.inflate(resource, null); Mapitem = data.get(position); int count = to.length; for (int i = 0; i < count; i++) { View v = convertView.findViewById(to[i]); bindView(v, item, from[i]); } convertView.setTag(position); return convertView; } /** * 绑定视图 * @param view * @param item * @param from */ private void bindView(View view, Map item, String from) { Object data = item.get(from); if (view instanceof TextView) { ((TextView) view).setText(data == null ? "" : data.toString()); } }
Tip:
BindView 方法是一个自定义方法,在方法体内可以为通过判断使本类更具灵活性,如上,你不仅可以判断是TextView 并且可以传入任何你想要的View 只要在方法体内加入相应判断即可,数据可以通过data 做相应处理,具体如何操作读者可另行测试。
convertView.setTag(position); 此句代码为View 设置tag 在以后我们可以通过 getTag 找出下标,后文有介绍如何通过下标操作数据。
下面是两个类的全部代码,读者可以无须更改直接使用:
LinearLayoutForListView package com.terry.widget; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.LinearLayout; public class LinearLayoutForListView extends LinearLayout { private AdapterForLinearLayout adapter; private OnClickListener onClickListener = null; /** * 绑定布局 */ public void bindLinearLayout() { int count = adapter.getCount(); for (int i = 0; i < count; i++) { View v = adapter.getView(i, null, null); v.setOnClickListener(this.onClickListener); if (i == count - 1) { LinearLayout ly = (LinearLayout) v; ly.removeViewAt(2); } addView(v, i); } Log.v("countTAG", "" + count); } public LinearLayoutForListView(Context context) { super(context); } public LinearLayoutForListView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } /** * 获取Adapter * * @return adapter */ public AdapterForLinearLayout getAdpater() { return adapter; } /** * 设置数据 * * @param adpater */ public void setAdapter(AdapterForLinearLayout adpater) { this.adapter = adpater; bindLinearLayout(); } /** * 获取点击事件 * * @return */ public OnClickListener getOnclickListner() { return onClickListener; } /** * 设置点击事件 * * @param onClickListener */ public void setOnclickLinstener(OnClickListener onClickListener) { this.onClickListener = onClickListener; } }
AdapterForLinearLayout package com.terry.widget; import java.util.List; import java.util.Map; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; public class AdapterForLinearLayout extends BaseAdapter { private LayoutInflater mInflater; private int resource; private List extends Map> data; private String[] from; private int[] to; public AdapterForLinearLayout(Context context, List extends Map > data, int resouce, String[] from, int[] to) { this.data = data; this.resource = resouce; this.data = data; this.from = from; this.to = to; this.mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public int getCount() { // TODO Auto-generated method stub return data.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return data.get(position); } @SuppressWarnings("unchecked") public String get(int position, Object key) { Map map = (Map ) getItem(position); return map.get(key).toString(); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub convertView = mInflater.inflate(resource, null); Map item = data.get(position); int count = to.length; for (int i = 0; i < count; i++) { View v = convertView.findViewById(to[i]); bindView(v, item, from[i]); } convertView.setTag(position); return convertView; } /** * 绑定视图 * @param view * @param item * @param from */ private void bindView(View view, Map item, String from) { Object data = item.get(from); if (view instanceof TextView) { ((TextView) view).setText(data == null ? "" : data.toString()); } } }
对应的XML 如下:
第三步:主页面使用控件并为其设置数据
XML如下:
加载数据如下:
lv = (LinearLayoutForListView) findViewById(R.id.ListView01); for (int i = 0; i < 10; i++) { HashMapmap = new HashMap (); map.put("key_name", "name" + i); map.put("value_name", "value" + i); list.add(map); } final AdapterForLinearLayout Layoutadpater = new AdapterForLinearLayout( this, list, R.layout.test, new String[] { "key_name", "value_name" }, new int[] { R.id.TextView01, R.id.TextView02 });
事件操作,并通过下标得到数据源:
lv.setOnclickLinstener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Toast.makeText( BlueToothActivity.this, Layoutadpater.get(Integer.parseInt(v.getTag() .toString()), "key_name"), 1000).show(); } }); lv.setAdapter(Layoutadpater);
Tip:get方法是在Layoutadpater 封装的一个通过下标获取相应数据的方法请参考上文。
至此完成。有碰到这个问题的朋友可以试试。