一、适配器模式
参考
Android中Adapter的学习与思考
Android源码之ListView的适配器模式
我们知道Adapter就是适配器的意思。在GOF设计模式中存在一种设计模式,即是适配器模式(Adapter)。对设计模式的学习使我们知道:适配器模式能够将一个接口转换为客户所期望的另一个接口,使得原来由与接口不兼容而不能一切工作的类可以一起工作。举个简单例子:大家都知道笔记本的电源插头一般是三孔的,假定你家里没有三孔的插座,而只有两孔的怎么办。解决方法很简单,就是去买一个带三孔和两孔的插板,并且插板的插头应该是两孔的。这样问题就解决了嘛。这种解决的方法就是一种适配器模式,而插板就是适配器。
作为最重要的View,ListView需要能够显示各式各样的视图,每个人需要的显示效果各不相同,显示的数据类型、数量等也千变万化。那么如何隔离这种变化尤为重要。Android的做法是增加一个Adapter层来应对变化,将ListView需要的接口抽象到Adapter对象中,这样只要用户实现了Adapter的接口,ListView就可以按照用户设定的显示效果、数量、数据来显示特定的Item View。
通过代理数据集来告知ListView数据的个数( getCount函数 )以及每个数据的类型( getItem函数 ),最重要的是要解决Item View的输出。Item View千变万化,但终究它都是View类型,Adapter统一将Item View输出为View ( getView函数 ),这样就很好的应对了Item View的可变性。简单的说Adapter就是AdapterView视图与数据之间的桥梁,Adapter提供对数据项的访问,同时也负责为每一项数据产生一个View.
由上述适配图就可以看出其实Android中的Adapter与设计模式中的Adapter特点都是一样的,虽然ListView需要的数据接口与Data Source并不兼容,但是通过Adater却可以让ListView使用Data Source。这与java中适配器模式的理念不谋而合!
二、ArrayAdapter
Android ArrayAdapter 详解
使用详解及源码解析Android中的Adapter、BaseAdapter、ArrayAdapter、SimpleAdapter和SimpleCursorAdapter
1.简单显示文本
//activity_main.xml:
<LinearLayout...>
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
//MainActivity.java
public class MainActivity extends Activity{
private String[] data = {"Apple","Banana","Orange"};
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//ArrayAdapter可以通过泛型来指定适配的数据类型,本例数据为String
//simple_list_item_1是android内置布局文件,里面只有一个textview
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this,android.R.layout.simple_list_item_1,data);
ListView listview = (ListView) findViewById(R.id.list_view);
listview.setAdapter(adapter);
}
}
2.简单显示文本,另外同时显示对应图片
//新建一个实体类Fruit
public class Fruit{
private String name;
private int imageId;
public Fruit(String name, int imageId){
this.name = name;
this.imageId = imageId;
}
public String getName(){
return name;
}
public int getImageId(){
return imageId;
}
}
//layout目录下新建fruit_item.xml
<LinearLayout...>
<ImageView fruit_image>
<TextView fruit_name>
</LinearLayout>
//自定义适配器
public class FruitAdapter extends ArrayAdapter<Fruit>{
private int resourceId;
//构造方法中指定了泛型Fruit
public FruitAdapter(Context context,
int textViewResourceId, List<Fruit> objects){
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
//getView方法在每个子项被滚动到屏幕内的时候调用
@override
public View getView(int postion, View convertView, ViewGroup parent){
Fruit fruit = getItem(postion);
//加载布局
View view = LayoutInflater.from(getContext()).inflate(resourceId,null);
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
//返回布局
return view;
}
}
//MainActivity.java
private List<Fruit> fruitList = new ArrayList<Fruit>();
private void initFruits(){
Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
fruitList.add(apple);
...
}
protected void onCreate(){
...
FruitAdapter adapter = new FruitAdapter
(MainActivity.this,R.layout.fruit_item,fruitList);
...
listview.setAdapter(adapter);
}
3.优化getView
getView每次都把布局重新加载了一遍,观察一下getView(int postion, View convertView, ViewGroup parent)
,contentView参数用于将之前加载好的布局进行缓存,以便之后可以重用。所以可以这样:
View view;
if(converView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId,null);
}else{
view = convertView;//直接对convertView进行重用
}
不过我们可以继续优化,把那些findViewById也给缓存起来
View view;
ViewHolder viewHolder;
if(converView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId,null);
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);//缓存起来
}else{
view = convertView;//直接对convertView进行重用
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
class ViewHolder{
ImageView fruitImage;
TextView fruitName;
}
4.点击事件
listView.setOnItemClickListener(new OnItemClickListener(){
@override
public void onItemClick(AdapterView<?> parent,
View view,int position, longid){
Fruit fruit = fruitList.get(position);
Toast.makeText(MainActivity.this,fruit.getName(),
Toast.LENGTH_SHORT).show();
}
});
三、SimpleAdaper
参考
使用详解及源码解析Android中的Adapter、BaseAdapter、ArrayAdapter、SimpleAdapter和SimpleCursorAdapter
SimpleAdaper的作用是方便地将数据与XML文件定义的各种View绑定起来,从而创建复杂的UI。SimpleAdapter有很强的扩展性,可以自定义出各种效果。
<pre>
package com.ispring.adapter;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView)findViewById(R.id.listView);
final String[] names = {"Windows","Mac OS","Linux","Android","Chrome OS"};
final String[] descriptions = {
"Windows是微软公司的操作系统",
"Mac OS是苹果公司的操作系统",
"Linux是开源免费操作系统",
"Android是Google公司的智能手机操作系统",
"Chrome OS是Google公司的Web操作系统"
};
final int[] icons = {
R.drawable.windows,
R.drawable.mac,
R.drawable.linux,
R.drawable.android,
R.drawable.chrome
};
List<Map<String, Object>> list = new ArrayList<>();
for(int i = 0; i < names.length; i++){
HashMap<String, Object> map = new HashMap<>();
map.put("name", names[i]);
map.put("description", descriptions[i]);
map.put("icon", icons[i]);
list.add(map);
}
//每个数据项对应一个Map,from表示的是Map中key的数组
String[] from = {"name", "description", "icon"};
//数据项Map中的每个key都在layout中有对应的View,
//to表示数据项对应的View的ID数组
int[] to = {R.id.name, R.id.description, R.id.icon};
//R.layout.item表示数据项UI所对应的layout文件
SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.item, from, to);
listView.setAdapter(adapter);
}
}
</pre>
其中R.layout.item表示数据项UI所对应的layout文件,如下所示:
<pre>
<?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="horizontal"
android:paddingTop="5dp"
android:paddingBottom="5dp">
<ImageView android:id="@+id/icon"
android:layout_width="100dp"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="10dp">
<TextView android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/defaultFontSize" />
<TextView android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/defaultFontSize"
android:layout_marginTop="10dp"/>
</LinearLayout>
</LinearLayout>
</pre>
SimpleAdapter只有一个构造函数,签名如下所示:
public SimpleAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)
- data表示的是List数据源,其中List中的元素都是Map类型,并且Map的key是String类型,Map的value可以是任意类型,我们一般使用HashMap<String, Object>作为List中的数据项。
- resource表示数据项UI所对应的layout文件,在本例中即R.layout.item。在本例中,每条数据项都要包含图片、名称、描述三条信息,所以我们在item.xml中定义了一个ImageView表示图片,两个TextView分别表示名称和描述,并且都设置了ID值。
- 每个数据项对应一个Map,from表示的是Map中key的数组。
- 数据项Map中的每个key都在layout中有对应的View,to表示数据项对应的View的ID数组。
四、SimpleCursorAdapter
SimpleCursorAdapter可以从数据库中读取数据显示在列表上。
五、自定义baseAdapter
参考Android中万能的BaseAdapter的使用来一个未优化版本:
<pre>
package com.tutor.baseadapter;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
public class BaseAdapterDemo extends Activity {
private Spinner mSpinner;
private ListView mListView;
private GridView mGridView;
private MyAdapter mMyAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setupViews();
}
public void setupViews(){
mMyAdapter = new MyAdapter();
mSpinner = (Spinner)findViewById(R.id.spinner);
mSpinner.setAdapter(mMyAdapter);
mListView = (ListView)findViewById(R.id.listview);
mListView.setAdapter(mMyAdapter);
mGridView = (GridView)findViewById(R.id.gridview);
mGridView.setAdapter(mMyAdapter);
mGridView.setNumColumns(2);
}
//定义自己的适配器,注意getCount和getView方法
private class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
// 这里我就返回10了,也就是一共有10项数据项
return 10;
}
@Override
public Object getItem(int arg0) {
return arg0;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// position就是位置从0开始,convertView是Spinner,ListView中每一项要显示的view
//通常return 的view也就是convertView
//parent就是父窗体了,也就是Spinner,ListView,GridView了.
//TextView mTextView = new TextView(getApplicationContext());
//mTextView.setText("BaseAdapterDemo");
//mTextView.setTextColor(Color.RED);
convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.baseadapter_provider,null);
TextView mTextView = (TextView)convertView.findViewById(R.id.textview);
mTextView.setText("BaseAdapterDemo" + position);
mTextView.setTextColor(Color.RED);
return mTextView;
}
}
}
</pre>
优化思路:通过convertView+ViewHolder来实现,使用ViewHolder这个静态类的好处是缓存了显示数据的View,加快了UI的响应速度。当我们判断convertView == null,若为空,就会根据设计好的布局文件布局,并未convertView赋值,并且生成一个viewHolder来绑定convertView的各个View控件。再用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。
参考
Android--Adapter深入理解及ListView优化
android代码优化----ListView中自定义adapter的封装
package com.example.listview_baseadapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity {
private ListView listView = null;
private List<Map<String, Object>> data = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.list);// 取得控件
data = getData();//获取数据
MyAdapter adapter = new MyAdapter(this);
listView.setAdapter(adapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
// 得到数据
public List<Map<String, Object>> getData() {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Map<String, Object> map;
for(int i=0;i<10;i++)
{
map = new HashMap<String, Object>();
map.put("img", R.drawable.bgs);
map.put("title", "Coder");
map.put("content", "简单Coding,快乐生活~~~~");
list.add(map);
}
return list;
}
// ViewHolder静态类
static class ViewHolder {
public ImageView img;
public TextView title;
public TextView content;
}
public class MyAdapter extends BaseAdapter {
private LayoutInflater mInflater = null;
private MyAdapter(Context context) {
// 根据context上下文加载布局
this.mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
// 在此适配器中所代表的数据集中的条目数
return data.size();
}
@Override
public Object getItem(int position) {
// 获取数据集中与指定索引对应的数据项
return position;
}
@Override
public long getItemId(int position) {
// 获取在列表中与指定索引对应的行id
return position;
}
// 获取一个在数据集中指定索引的视图来显示数据
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
// 如果缓存convertView为空,则需要创建View
if (convertView == null) {
holder = new ViewHolder();
// 根据自定义的Item布局加载布局
convertView = mInflater.inflate(R.layout.list_item, null);
holder.img = (ImageView) convertView.findViewById(R.id.img);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.content = (TextView) convertView.findViewById(R.id.content);
// 将设置好的布局保存到缓存中,并将其设置在Tag里,以便后面方便取出Tag
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.img.setBackgroundResource((Integer) data.get(position).get("img"));
holder.title.setText((String) data.get(position).get("title"));
holder.content.setText((String)data.get(position).get("content"));
return convertView;
}
}
}
注意这段代码:
// ViewHolder静态类
static class ViewHolder {
public ImageView img;
public TextView title;
public TextView content;
}
参考android listview 声明ViewHolder内部类时,为什么建议使用static关键字
这个问题也是我每次面试别人必问的问题之一。其实这个是考静态内部类和非静态内部类的主要区别之一。非静态内部类会隐式持有外部类的引用,就像大家经常将自定义的adapter在Activity类里,然后在adapter类里面是可以随意调用外部activity的方法的。当你将内部类定义为static时,你就调用不了外部类的实例方法了,因为这时候静态内部类是不持有外部类的引用的。声明ViewHolder静态内部类,可以将ViewHolder和外部类解引用。大家会说一般ViewHolder都很简单,不定义为static也没事吧。确实如此,但是如果你将它定义为static的,说明你懂这些含义。万一有一天你在这个ViewHolder加入一些复杂逻辑,做了一些耗时工作,那么如果ViewHolder是非静态内部类的话,就很容易出现内存泄露。如果是静态的话,你就不能直接引用外部类,迫使你关注如何避免相互引用。 所以将 ViewHolder内部类 定义为静态的,是一种好习惯.
另外,使用使用静态内部类实现单例模式,这种方法也是《Effective Java》上所推荐的
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。
六、使用技巧
参考《安卓群英传》P68
1.设置项目分隔线
android:divider="@android:color/darker_gray"
android:dividerHeight="10dp"
2.隐藏滚动条
android:scrollbars="none"
3.取消点击效果,可以使用安卓自带透明色
android:listSelector="@android:color/transparent"
4.设置显示在第几项
默认显示第一项,可以使用listView.setSelection(N)
设置显示第N项。
当然这个方法类似scrollTo,瞬间完成移动。缓动可以使用:
mListView.smoothScrollBy(distance,duration);
mListView.smoothScrollbyOffset(offset);
mListView.smoothScrollToPosition(index);
5.动态修改
mData.add("new");
mAdapter.notifyDataSetChanged();
mListView.setSelection(mData.size() - 1);
6.遍历
for(int i = 0; i < mListView.getChildCount();i ++){
View view = mListView.getChildAt(i);
}
7.设置空数据时如何显示
listView.setEmptyView(findViewById(R.id.empty_view));
8.点击
listView.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id){
Fruit fruit = fruitList.get(position);//通过position判断用户点击的是哪一个子项
Toast.makeText...
}
);
9.onTouchListener
10.onScrollListener
//通过不同状态来设置一些flag,来区分不同的滑动状态,供其他方法处理
mListView.setOnScrollListener(new OnScrollListener(){
public void onScrollStateChanged(AbsListView view, int scrollState){
switch(scrollState){
case OnScrollListener.SCROLL_STATE_IDLE:
//滑动停止时
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
//正在滚动
break;
case OnScrollListener.SCROLL_STATE_FLING:
//手指抛动时 离开listview后由于惯性继续滑动
break;
}
}
});
//onScroll在listview滚动时会一直回调
public void onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount){
//firstVisibleItem 当前能看见的第一个Item的ID,从0开始
//visibleItemCount 当前能看见的Item总数
//totalItemCount 整个ListView的Item总数(包括没有显示完整的Item)
if(firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0){
//滚动到最后一行
}
if(firstVisibleItem > lastVisibleItemPosition){
//上滑
}else if(firstVisibleItem < lastVisibleItemPosition){
//下滑
}
lastVisibleItemPosition = firstVisibleItem;
//获取可视区域内最后一个Item的id
mListView.getLastVisiblePosition()
//获取可视区域内第一个Item的id
mListView.getFirstVisiblePosition()
}
11.优化卡顿-参考《安卓开发艺术探索》
首先,不要在getView中执行耗时操作。比如加载图片,必须要用异步方式处理。
其次,控制异步操作的执行频率。如果用户刻意频繁上下滑动,会在一瞬间产生上百个异步任务,这会造成线程池拥堵并随即带来大量的UI更新,这是没有意义的。可以在列表滑动时停止加载图片,静止时再加载。
public void onScrollStateChanged(AbsListView view,int scrollState){
if(scrollState == OnScrollListener.SCROLL_STATE_IDLE){
mIsGridViewIdle = true;
mImageAdapter.notifyDataSetChanged();
}else{
mIsGridViewIdle = false;
}
}
//在getView方法中,仅当列表静止时才加载图片:
if(mIsGridViewIdle && mCanGetBitmapFromNetWrok){
imageView.setTage(uri);
mImageLoader.bindBitmap(uri,imageView,mImageWidth,mImageWidth);
}
另外,可以开启硬件加速来解决莫名的卡顿问题。通过设置android:hardwareAccelerated = "true"
即可为Activity开启硬件加速。
七、常用扩展
1.使用maxOverScrollY属性,让列表滚动到底端或顶端后,继续向下或向上滑动一段距离。
private void initView() {
//让不同分辨率弹性滑动距离基本一致
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
float density = metrics.density;
mMaxOverDistance = (int) (density * mMaxOverDistance);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY,
scrollX, scrollY,
scrollRangeX, scrollRangeY,
maxOverScrollX, mMaxOverDistance,
isTouchEvent);
}
2.向下滑动时,标题栏和悬浮按钮自动消失,让用户有更大空间阅读。
package com.imooc.myapplication;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class ScrollHideListView extends Activity {
private Toolbar mToolbar;
private ListView mListView;
private String[] mStr = new String[20];
private int mTouchSlop;
private float mFirstY;
private float mCurrentY;
private int direction;
private ObjectAnimator mAnimator;
private boolean mShow = true;
View.OnTouchListener myTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mFirstY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
mCurrentY = event.getY();
if (mCurrentY - mFirstY > mTouchSlop) {
direction = 0;// down
} else if (mFirstY - mCurrentY > mTouchSlop) {
direction = 1;// up
}
if (direction == 1) {
if (mShow) {
toolbarAnim(1);//show
mShow = !mShow;
}
} else if (direction == 0) {
if (!mShow) {
toolbarAnim(0);//hide
mShow = !mShow;
}
}
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scroll_hide);
//系统认为最低滑动距离,超过此值就是滑动状态
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mListView = (ListView) findViewById(R.id.listview);
for (int i = 0; i < mStr.length; i++) {
mStr[i] = "Item " + i;
}
View header = new View(this);
header.setLayoutParams(new AbsListView.LayoutParams(
AbsListView.LayoutParams.MATCH_PARENT,
(int) getResources().getDimension(
R.dimen.abc_action_bar_default_height_material)));
mListView.addHeaderView(header);
mListView.setAdapter(new ArrayAdapter<String>(
ScrollHideListView.this,
android.R.layout.simple_expandable_list_item_1,
mStr));
mListView.setOnTouchListener(myTouchListener);
}
private void toolbarAnim(int flag) {
//位移属性动画
if (mAnimator != null && mAnimator.isRunning()) {
mAnimator.cancel();
}
if (flag == 0) {
mAnimator = ObjectAnimator.ofFloat(mToolbar,
"translationY", mToolbar.getTranslationY(), 0);
} else {
mAnimator = ObjectAnimator.ofFloat(mToolbar,
"translationY", mToolbar.getTranslationY(),
-mToolbar.getHeight());
}
mAnimator.start();
}
}
3.像微信聊天,区分收到消息和发送消息的两种布局
package com.imooc.myapplication;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class ChatItemListViewAdapter extends BaseAdapter {
private List<ChatItemListViewBean> mData;
private LayoutInflater mInflater;
public ChatItemListViewAdapter(Context context,
List<ChatItemListViewBean> data) {
this.mData = data;
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
//用来返回第position个Item是何类型
ChatItemListViewBean bean = mData.get(position);
return bean.getType();
}
@Override
public int getViewTypeCount() {
//用来返回不同布局的总数
return 2;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
//根据类型生成不同的布局
if (getItemViewType(position) == 0) {
holder = new ViewHolder();
convertView = mInflater.inflate(
R.layout.chat_item_itemin, null);
holder.icon = (ImageView) convertView.findViewById(
R.id.icon_in);
holder.text = (TextView) convertView.findViewById(
R.id.text_in);
} else {
holder = new ViewHolder();
convertView = mInflater.inflate(
R.layout.chat_item_itemout, null);
holder.icon = (ImageView) convertView.findViewById(
R.id.icon_out);
holder.text = (TextView) convertView.findViewById(
R.id.text_out);
}
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.icon.setImageBitmap(mData.get(position).getIcon());
holder.text.setText(mData.get(position).getText());
return convertView;
}
public final class ViewHolder {
public ImageView icon;
public TextView text;
}
}