ListView布局之View复用原理举例

1.简单介绍:

ListView是android开发中经常使用的控件,系统自带的那些样式,我就不列举了。

今天主要看一下。一个模仿系统历史通话记录的ListView。

效果例如以下:

上面ListView的样式还能够更复杂。首先看一下这个简单的ListView的Item的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/contacts_items"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ffffff"
    android:orientation="vertical" >

    <View
        android:id="@+id/topLine"
        android:layout_width="fill_parent"
        android:layout_height="1dp"
        android:background="#ff474745" />

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="60dp"
        android:gravity="center_vertical"
        android:paddingRight="1.0dip" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:gravity="center_vertical"
            android:orientation="horizontal" >

            <ImageView
                android:id="@+id/imgHead"
                android:layout_width="wrap_content"
                android:layout_height="fill_parent"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="10dp"
                android:contentDescription="" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="fill_parent"
                android:gravity="center_vertical"
                android:orientation="vertical" >

                <TextView
                    android:id="@+id/tvName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:ellipsize="end"
                    android:singleLine="true"
                    android:textSize="14.0sp" />

                <TextView
                    android:id="@+id/tvTelephone"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:layout_marginTop="4.0dip"
                    android:ellipsize="end"
                    android:singleLine="true"
                    android:textColor="#ffcccccc"
                    android:textSize="12sp" />

                <TextView
                    android:id="@+id/tvDate"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="0.2dip"
                    android:layout_marginTop="0dip"
                    android:ellipsize="end"
                    android:singleLine="true"
                    android:textColor="#ffcccccc"
                    android:textSize="12sp" />
            </LinearLayout>
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:gravity="center_vertical"
            android:orientation="horizontal" >

            <Button
                android:id="@+id/btnCall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="0.2dip"
                android:layout_marginTop="0dip"
                android:ellipsize="end"
                android:singleLine="true"
                android:textColor="#ff0000ff"
                android:focusable="false"
                android:textSize="12sp" />
        </LinearLayout>
    </RelativeLayout>

    <View
        android:id="@+id/bottomLine"
        android:layout_width="fill_parent"
        android:layout_height="1dp"
        android:background="#ff1c1c1b" />

    <View
        android:id="@+id/lastLine"
        android:layout_width="fill_parent"
        android:layout_height="1dp"
        android:background="#ff474745"
        android:visibility="gone" />

</LinearLayout>

没什么问题吧?可是你一定要注意Button的一个属性:android:focusable="false",假设不加这个属性,会使得ListView的OnItemClick被屏蔽。

因为是模仿通话记录,那么Item里面的这个属性。我们还是封装到一个类里面吧。

/*
 * $filename: Model.java,v $
 * $Date: 2014-4-27  $
 * Copyright (C) ZhengHaibo, Inc. All rights reserved.
 * This software is Made by Zhenghaibo.
 */
package com.example.testaa;

import org.androidannotations.annotations.EBean;

/*
 *@author: ZhengHaibo
 *web:     http://blog.csdn.net/nuptboyzhb
 *mail:    [email protected]
 *2014-4-27  Nanjing,njupt,China
 */
@EBean
public class Model {
	private int imgHead;//头像资源ID
	private String name;//姓名
	private String telephone;//电话号码
	private String date;//日期
	public int getImgHead() {
		return imgHead;
	}
	public void setImgHead(int imgHead) {
		this.imgHead = imgHead;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getTelephone() {
		return telephone;
	}
	public void setTelephone(String telephone) {
		this.telephone = telephone;
	}
	public String getDate() {
		return date;
	}
	public void setDate(String date) {
		this.date = date;
	}

}

接下来。思路非常清晰。就是继承BaseAdapter类,重写它的几个重要方法:

@Override
			public View getView(int position, View convertView, ViewGroup parent) {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public long getItemId(int position) {
				// TODO Auto-generated method stub
				return 0;
			}

			@Override
			public Object getItem(int position) {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public int getCount() {
				// TODO Auto-generated method stub
				return 0;
			}

依照我们的需求,我们必须在getView类中,为Item布局中的每个View进行关联。设置对应的參数。而对于Button,还要设置对应的事件监听器。我们必须注意的是:在设置事件监听器的时候。我们必须将当前的Item的位置信息position传递给监听器。否则的话,onClick方法无法知道当前按下的是哪个button。因此,我们写了一个内部类,实现OnClickListener接口,这个类的须要有一个属性来保存Item的位置。

因此,我们的BaseAdapter1代码例如以下:

/*
 * $filename: BaseAdapter1.java,v $
 * $Date: 2014-4-27  $
 * Copyright (C) ZhengHaibo, Inc. All rights reserved.
 * This software is Made by Zhenghaibo.
 */
package com.example.testaa;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

/*
 *@author: ZhengHaibo
 *web:     http://blog.csdn.net/nuptboyzhb
 *mail:    [email protected]
 *2014-4-27  Nanjing,njupt,China
 */
public class BaseAdapter1 extends BaseAdapter {

	private Context context;

	private List<Model> listViewData;

	private int layoutResId;//ListView每个Item的布局文件

	public BaseAdapter1(Context context,int layoutResId) {
		this.context = context;
		this.layoutResId = layoutResId;
		listViewData = new ArrayList<Model>();
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
        convertView = LayoutInflater.from(context).inflate(layoutResId,null);
        Model model = listViewData.get(position);
        ImageView imageView = (ImageView)convertView.findViewById(R.id.imgHead);
        imageView.setImageBitmap(BitmapFactory.decodeResource(context.getResources(), model.getImgHead()));
        TextView tvName = (TextView)convertView.findViewById(R.id.tvName);
        tvName.setText(model.getName());
        TextView tvTelephone = (TextView)convertView.findViewById(R.id.tvTelephone);
        tvTelephone.setText(model.getTelephone());
        TextView tvDate = (TextView)convertView.findViewById(R.id.tvDate);
        tvDate.setText(model.getDate());
        Button btnCall = (Button) convertView.findViewById(R.id.btnCall);
        btnCall.setText("拨打电话");
        btnCall.setOnClickListener(new ListViewButtonOnClickListener(position) );
		return convertView;
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return listViewData.get(position);
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		if(null == listViewData){
			return 0;
		}
		return listViewData.size();
	}

	/**
	 * 加入一条记录
	 * @param model
	 */
	public void addModel(Model model){
		listViewData.add(model);
	}
	/**
	 * 获取一条记录
	 * @param i
	 * @return
	 */
	public Model getModel(int i){
		if(i<0||i>listViewData.size()-1){
			return null;
		}
		return listViewData.get(i);
	}
	/**
	 * 清除全部数据
	 */
	public void clear(){
		listViewData.clear();
	}

	class ListViewButtonOnClickListener implements OnClickListener{
		private int position;//记录ListView中Button所在的Item的位置
		public ListViewButtonOnClickListener(int position) {
			this.position = position;
		}
		@Override
		public void onClick(View v) {
			Toast.makeText(context,listViewData.get(position).getTelephone(), Toast.LENGTH_SHORT).show();
		}
	}
}

由此可见,假设我们须要定制Item的布局,我们仅仅须要改动的地方除了Item的布局文件以外,还要将Adapter里的getView方法进行对应的改动。

接下来看一下Activity的測试代码

package com.example.testaa;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.ViewById;

import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
@EActivity(R.layout.activity_list_1)
public class ActivityList1 extends Activity implements OnItemClickListener{

	@ViewById
	ListView listView;
	//ListView的设配器
	private BaseAdapter1 baseAdapter1;
	@AfterViews
	void afterViewInitList(){
		baseAdapter1 = new BaseAdapter1(this,R.layout.listview1);
		listView.setAdapter(baseAdapter1);
		listView.setOnItemClickListener(this);
		for(int i=0;i<10;i++){
			Model model = new Model();
			model.setImgHead(R.drawable.ic_launcher);
			model.setName("Name"+i);
			model.setTelephone("手机  1311111111"+i);
			model.setDate(new SimpleDateFormat().format(new Date()).toString());
			baseAdapter1.addModel(model);
		}
		baseAdapter1.notifyDataSetChanged();

	}
	@Override
	public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
		// TODO Auto-generated method stub
		Log.d("ItemClick", "pos="+position);
		String string = "clicked item"+position+"content="+baseAdapter1.getModel(position).getTelephone();
		Toast.makeText(this, string, Toast.LENGTH_SHORT).show();
	}
}

此时。就完毕了我们想要的功能。

Item的点击事件和Button的点击事件互不冲突。

问题二:

向微信,QQ,易信等聊天界面的ListView则有所不同。

我们上述的样例是:Item仅仅有一个布局。而聊天界面其中ListView的Item布局有多种,比方显示文字的布局,显示图片的布局,显示语音的布局等等。除此之外。我们还要依据消息的发送者,将其左右分开。在这里,仅仅演示左右的文本。原理都是都是一样的。

先看一下布局文件

左右文本的布局文件:

<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"
     >

    <ImageView
        android:id="@+id/imgHead"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_margin="5dip"
        android:src="@drawable/ic_launcher"
         />

    <Button
        android:id="@+id/btn_left_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/imgHead"
        android:layout_marginTop="5dip"
        android:layout_marginBottom="5dip"
        android:textColor="#404040"
        android:textSize="16sp"
        android:gravity="center"
        android:focusable="false"
        android:background="@drawable/chatfrom_bg_normal"
        />

</RelativeLayout>

右边的布局文件:

<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"
     >

    <ImageView
        android:id="@+id/imgHead"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_margin="5dip"
        android:src="@drawable/ic_launcher"
         />

    <Button
        android:id="@+id/btn_right_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@+id/imgHead"
        android:layout_marginTop="5dip"
        android:layout_marginBottom="5dip"
        android:textColor="#404040"
        android:textSize="16sp"
        android:gravity="center"
        android:focusable="false"
        android:background="@drawable/chatto_bg_normal"
        />

</RelativeLayout>

再看一下适配器,基本和上一个样例一样,不一样无非就是getView方法的差异。

/*
 * $filename: BaseAdapter1.java,v $
 * $Date: 2014-4-27  $
 * Copyright (C) ZhengHaibo, Inc. All rights reserved.
 * This software is Made by Zhenghaibo.
 */
package com.example.testaa;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

/*
 *@author: ZhengHaibo
 *web:     http://blog.csdn.net/nuptboyzhb
 *mail:    [email protected]
 *2014-4-27  Nanjing,njupt,China
 */
public class ChatBaseAdapter extends BaseAdapter {

	private Context context;

	private List<Msg> listViewData;

	public ChatBaseAdapter(Context context) {
		this.context = context;
		listViewData = new ArrayList<Msg>();
	}

	/**
	 * 依据发送消息的类型进行分类,不同的消息类型不同的布局
	 */
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Msg msg = listViewData.get(position);
		if(msg.isSelf()){//自己发送的消息
			convertView = LayoutInflater.from(context).inflate(R.layout.list_item_right_text,null);
			ImageView imgHead =(ImageView) convertView.findViewById(R.id.imgHead);
			imgHead.setImageBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher));
			Button btn = (Button)convertView.findViewById(R.id.btn_right_text);
			btn.setText(msg.getContent());
			btn.setOnClickListener(new ListViewButtonOnClickListener(position));
		}else {//对方发送的消息
			convertView = LayoutInflater.from(context).inflate(R.layout.list_item_left_text,null);
			ImageView imgHead =(ImageView) convertView.findViewById(R.id.imgHead);
			imgHead.setImageBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher));
			Button btn = (Button)convertView.findViewById(R.id.btn_left_text);
			btn.setText(msg.getContent());
			btn.setOnClickListener(new ListViewButtonOnClickListener(position));
		}
		return convertView;
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return listViewData.get(position);
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		if(null == listViewData){
			return 0;
		}
		return listViewData.size();
	}

	/**
	 * 加入一条记录
	 * @param Msg
	 */
	public void addMsg(Msg Msg){
		listViewData.add(Msg);
	}
	/**
	 * 获取一条记录
	 * @param i
	 * @return
	 */
	public Msg getMsg(int i){
		if(i<0||i>listViewData.size()-1){
			return null;
		}
		return listViewData.get(i);
	}
	/**
	 * 清除全部数据
	 */
	public void clear(){
		listViewData.clear();
	}

	class ListViewButtonOnClickListener implements OnClickListener{
		private int position;//记录ListView中Button所在的Item的位置
		public ListViewButtonOnClickListener(int position) {
			this.position = position;
		}
		@Override
		public void onClick(View v) {
			Toast.makeText(context,listViewData.get(position).getContent(), Toast.LENGTH_SHORT).show();
		}
	}
}

Activity的代码为:

package com.example.testaa;

import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.ViewById;

import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
@EActivity(R.layout.activity_list_2)
public class ActivityList2 extends Activity implements OnItemClickListener{

	@ViewById
	ListView listView;

	private ChatBaseAdapter chatBaseAdapter;
	@AfterViews
	void afterViewInitList(){
		chatBaseAdapter = new ChatBaseAdapter(this);
		listView.setAdapter(chatBaseAdapter);
		listView.setOnItemClickListener(this);
		for(int i=0;i<10;i++){
			Msg msg = new Msg();
			if(i%2==0){
				msg.setSelf(false);
			}else {
				msg.setSelf(true);
			}
			msg.setContent("abc"+i);
			chatBaseAdapter.addMsg(msg);
		}
		chatBaseAdapter.notifyDataSetChanged();

	}
	@Override
	public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
		// TODO Auto-generated method stub
		Log.d("ItemClick", "pos="+position);
		String string = "clicked item"+position+"content="+chatBaseAdapter.getMsg(position).getContent();
		Toast.makeText(this, string, Toast.LENGTH_SHORT).show();
	}
}

效果:

关于聊天界面的很多其它内容可參考博文:http://blog.csdn.net/xyz_lmn/article/details/13745489

原理基本一样。

注意:整个项目的代码使用的是AndroidAnnotation框架,本博客的代码下载:http://download.csdn.net/detail/nuptboyzhb/7260915

时间: 2024-08-15 17:09:43

ListView布局之View复用原理举例的相关文章

ListView 的优化和优化原理

列表的显示需要三个元素: ListVeiw:  用来展示列表的View. 适配器 : 用来把数据映射到ListView上 数据:    具体的将被映射的字符串,图片,或者基本组件.             根据列表的适配器类型,列表分为三种,ArrayAdapter,SimpleAdapter和SimpleCursorAdapter,这三种适配器的使用大家可学习下官网上面的使用或者自行百度谷歌,一堆DEMO!!!其中以ArrayAdapter最为简单,只能展示一行字.SimpleAdapter有

Android——ListView布局+适配器(三)

Android--ListView布局+适配器(三) package com.example.administrator.newstop; import android.os.Bundle; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import andro

Android - View绘图原理总结

原文地址:http://blog.csdn.net/xu_fu/article/details/7829721 Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类,由此就得到了视图部分的基本结构--树形结构 View定义了绘图的基本操作 基本操作由三个函数完成:measure().layout().draw(),其内部又分别包含了onMeasure().onLayout().onDraw()三个子方法.具体操作如下:

基于“ViewHolder”技术提升Android ListView中Item View加载效率

对于提升Android中频繁使用的ListView子Item View的加载效率,基于两点基本考虑: 1,Android的View的创建比较消耗资源: 2,findViewById()也不是最快的. 所以在实际开发中经常使用的适配器中的getView()方法: public View getView(int position, View convertView, ViewGroup parent); 如果每次都在里面创建一个新View出来,将导致效率低下,比较高效率的做法是每次判断convert

在Android 窗口小组件(Widget)中显示(StackView,ListView,GridView)集合View

在Android 3.0 中引入了 Collection View Widget.用于在窗口小组件中添加了对集合View 的支持. 如下: (1)StackView 一个卡片View,以层叠的方式显示其子View. (2)ListView 和传统的ListView一样 (3)GridView 网格列表.具体用法和传统的一样. 第一步:创建Widget布局文件    (1)Wdiget的布局文件 路径:res/layout/my_widget_layout.xml <?xml version=&quo

一个ListView布局的不断演化

刚出来工作,就负责一个APP的某块功能的编写,该功能就是类似微博那样的界面.微博界面的编写实际上是非常复杂的,虽然它只是一个ListView,但要想让这个ListView滑得动,是的,在一些配置低的手机,随便一个负载内容多的Item,都有可能导致OOM...如果只是简单的为了实现了效果,可以选择将所有内容都写在xml文件,如果布局不好的话,就会出现嵌套过多的情况,同样也会出现OOM的情况...就算不会出现OOM的情况,也能滑得动,也会面临是否能够滑得快... 要想能滑得动,也能滑得快,就要动点脑

常用的优化ListView效率的方法及其原理

在Android开发中,ListView的使用频率及其高,可以说99%的应用中你都可以看到他的身影. 在日常的开发工作中,我们一般会通过以下方法来优化ListView的效率: 1.复用getView方法中的convertView 一个ListView可能有很多Item,但是限于手机屏幕非常有限,所以只能显示很少的一部分Item,比如能显示10个,那么Android会在ListView中保存多余10个的Item, 溢出屏幕的Item会被回收到一个池子中,以备后用,那么就不用从新new出View对象

ListVIew中插入view

public class MainActivity extends Activity { private ListView listview; private List<String> dataList = new ArrayList<String>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(

Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起

本文主要内容是讲解一个视图View或者一个ViewGroup对象是如何添加至应用程序窗口中的.下文中提到的窗口可泛指我们能看到的界面,包括一个Activity呈现的界面(我们可以将之理解为应用程序窗口),一个Dialog,一个Toast,一个Menu菜单等. 首先对相关类的作用进行一下简单介绍: Window 类   位于 /frameworks/base/core/java/android/view/Window.java 说明:该类是一个抽象类,提供了绘制窗口的一组通用API.可以将之理解为