ListView的Item对应多布局,滚动错位

来源:互联网 发布:假期综合症 锻炼 知乎 编辑:程序博客网 时间:2024/05/27 14:12

我相信肯定有不少人和我一样需要做到这样一个效果,一个ListView需要对用多个布局,就是类似与QQ的聊天页面一样,是对应的两种布局,这样的情况下能想到的当然就是在Adapter里的getView方法的地方通过判断来选择使用哪一种布局,这种想法是没有错的,但是实际应用的时候却会出现一定问题,比如我滚动ListView的时候原来应该用1布局的那个Item结果用的布局2,造成ListView上的数据错位

下面先看看正常的写法,以及会造成的现象。


新建一个项目叫TestListViewDemo,然后将MainActivity对应的布局文件修改如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity" >    <ListView        android:id="@+id/MainActivity_ListView"        android:layout_width="match_parent"        android:layout_height="match_parent" >    </ListView></RelativeLayout>

可以看到只有一个ListView,然后这个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:gravity="center|left"    android:orientation="horizontal"    android:paddingBottom="20dp"    android:paddingTop="20dp" >    <ImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:src="@drawable/ic_launcher" />    <TextView        android:id="@+id/listviwe_item_txt"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="这个是TextView" /></LinearLayout>

这个是类似QQ的左边的布局,对应的效果:


下面的是右边的布局,和左边的一样也是一个ImageView和TextView只是位置和摆放的地方不一样 :

<?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:gravity="center|right"    android:orientation="horizontal"    android:paddingBottom="20dp"    android:paddingTop="20dp" >    <TextView        android:id="@+id/listviwe_item_txt"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="这个是TextView" />    <ImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:src="@drawable/ic_launcher" /></LinearLayout>


对应的效果图如下:




这样我们的布局就都有了,然后修改MainActivity如下:

package com.testlistview.yl;import java.util.ArrayList;import android.app.Activity;import android.content.Context;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ListView;import android.widget.TextView;public class MainActivity extends Activity {/** ListView **/private ListView MainActivity_ListView;/** 用来标记左边的常量 0 **/private static final int LEFT = 0;/** 用来标记右边的常量 1 **/private static final int RIGHT = 1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//获取控件MainActivity_ListView = (ListView) findViewById(R.id.MainActivity_ListView);//装数据,这里ArrayList里面中了20个数字,顺序是一个0一个1ArrayList<Integer> listData = addData();//实例化我们的AdapterMyAdapter adapter = new MyAdapter(this, listData);//将Adapter给ListViewMainActivity_ListView.setAdapter(adapter);}/** * 装填假的数据,里面只有数字0和1 *  * @return */private ArrayList<Integer> addData() {ArrayList<Integer> listData = new ArrayList<Integer>();listData.add(LEFT);listData.add(RIGHT);listData.add(LEFT);listData.add(RIGHT);listData.add(LEFT);listData.add(RIGHT);listData.add(LEFT);listData.add(RIGHT);listData.add(LEFT);listData.add(RIGHT);listData.add(LEFT);listData.add(RIGHT);listData.add(LEFT);listData.add(RIGHT);listData.add(LEFT);listData.add(RIGHT);listData.add(LEFT);listData.add(RIGHT);listData.add(LEFT);listData.add(RIGHT);return listData;}class MyAdapter extends BaseAdapter {private Context context;private ArrayList<Integer> listData;private LayoutInflater mInflater;public MyAdapter(Context context, ArrayList<Integer> listData) {this.listData = listData;this.context = context;this.mInflater = LayoutInflater.from(context);}@Overridepublic int getCount() {return listData.size();}@Overridepublic Object getItem(int position) {return listData.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if (convertView == null) {holder = new ViewHolder();if (listData.get(position) == LEFT) {//左边显示左边布局convertView = mInflater.inflate(R.layout.list_item_left, null);} else if (listData.get(position) == RIGHT) {//右边显示右边布局convertView = mInflater.inflate(R.layout.list_item_right, null);}//查找每个ViewItem中,各个子View,放进holder中     holder.listviwe_item_txt = (TextView) convertView.findViewById(R.id.listviwe_item_txt);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}//将position显示在TextView上holder.listviwe_item_txt.setText("position:" + position);return convertView;}private class ViewHolder {TextView listviwe_item_txt;}}}

可以看到ArrayList中只有20个数字,而且是顺序的01相间排序的,然后在Adapter的getView方法中根据0和1去选择对应的布局,然后我们运行项目,加载的第一屏数据是没有任何问题的。但是我们滚动ListView以后就会出现如下的清空:




我们的ListView中的数字0和1是顺序出现的,所以显示的结果也应该是一个左一个右顺序往下的,然后事实并非如此,拖动ListView以后就会出现这种错位

然后网上搜了一下解决方案,看到的基本上都是说是因为用convertView == null这种写法导致的,我们用这样的写法是为了复用缓存,也就是因为这个原因导致加载的时候会复用已经用了的缓存,就会出现这种错位的清空,而给出的解决方案就是不要去判断,讲getView方法修改如下:

public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;//if (convertView == null) {holder = new ViewHolder();if (listData.get(position) == LEFT) {//左边显示左边布局convertView = mInflater.inflate(R.layout.list_item_left, null);} else if (listData.get(position) == RIGHT) {//右边显示右边布局convertView = mInflater.inflate(R.layout.list_item_right, null);}//查找每个ViewItem中,各个子View,放进holder中     holder.listviwe_item_txt = (TextView) convertView.findViewById(R.id.listviwe_item_txt);convertView.setTag(holder);//} else {//holder = (ViewHolder) convertView.getTag();//}//将position显示在TextView上holder.listviwe_item_txt.setText("position:" + position);return convertView;}

然后我们运行项目发现这样修改了以后确实不会在出现错位的情况了,但是这样每次都创建新的对象,不是很好。。

接下来就是本文的重点,介绍另外一个解决方案:

其实我们只要多重写BaseAdapte的两个方法就行了,这两个方法就是:getItemViewType() 和 getViewTypeCount() ,这里我们重写这两个方法如下:

//重写下面两个方法就能使得滑动的时候不错位@Overridepublic int getItemViewType(int position) {if (listData.get(position) == LEFT) {return LEFT;} else {return RIGHT;}}@Overridepublic int getViewTypeCount() {// TODO Auto-generated method stubreturn 2;}

并且将getView方法中注释掉的取消注释,运行项目发现无论怎么滚动都不会出现错误了,这样问题就解决了。效果图:



最后附上TestListViewDemo的源码下载地址 http://download.csdn.net/detail/kshw123/7106659,看过本文觉得有帮助或者有问题的请留言,谢谢


1 0