android ListView 动态切换Item布局
2016-01-13 10:26
399 查看
需求是这样的,在列表中,第一个列表项显示一种布局,其他的列表项显示另一种布局,所以需要在列表中根据条件进行判断,动态切换布局
方法就是在定义BaseAdapter的时候,重写两个方法:
getItemViewType用来返回第position个Item是何种类型,getViewTypeCount返回不同布局的总数。
关于ListView的运行效率的提高,摘抄《第一行代码》书中的一段进行说明
3.5.3 提升 ListView 的运行效率
之所以说 ListView 这个控件很难用,就是因为它有很多的细节可以优化,其中运行效率
就是很重要的一点。 目前我们 ListView的运行效率是很低的, 因为在 FruitAdapter的 getView()
方法中每次都将布局重新加载了一遍,当 ListView 快速滚动的时候这就会成为性能的瓶颈。
仔细观察,getView()方法中还有一个 convertView 参数,这个参数用于将之前加载好的
布局进行缓存,以便之后可以进行重用。修改 FruitAdapter 中的代码,如下所示:
public class FruitAdapter extends ArrayAdapter {
……
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
} else {
view = convertView;
}
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;
}
}
可以看到,现在我们在 getView()方法中进行了判断,如果 convertView 为空,则使用
LayoutInflater 去加载布局,如果不为空则直接对 convertView 进行重用。这样就大大提高了
ListView 的运行效率,在快速滚动的时候也可以表现出更好的性能。
不过, 目前我们的这份代码还是可以继续优化的, 虽然现在已经不会再重复去加载布局,
但是每次在 getView()方法中还是会调用 View的 findViewById()方法来获取一次控件的实例。
我们可以借助一个 ViewHolder 来对这部分性能进行优化,修改 FruitAdapter 中的代码,如下
所示:
public class FruitAdapter extends ArrayAdapter {
……
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == 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); // 将ViewHolder存储在View中
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag(); // 重新获取ViewHolder
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder {
ImageView fruitImage;
TextView fruitName;
}
}
我们新增了一个内部类 ViewHolder,用于对控件的实例进行缓存。当 convertView 为空
的时候, 创建一个 ViewHolder 对象, 并将控件的实例都存放在 ViewHolder 里, 然后调用 View
的 setTag()方法,将 ViewHolder 对象存储在 View 中。当 convertView 不为空的时候则调用
View 的 getTag()方法, 把 ViewHolder 重新取出。 这样所有控件的实例都缓存在了 ViewHolder
里,就没有必要每次都通过 findViewById()方法来获取控件实例了。
两个布局my_order_top_item.xml,my_order_item.xml
my_order_top_item.xml
my_order_item.xml
Adapter:
列表我使用的是PullToRefreshListView,用于上拉下拉刷新,并且设置了列表为空进行显示的TextView
参考
Android杂谈–ListView之BaseAdapter的使用
Android类库–与Adapter相关的类
方法就是在定义BaseAdapter的时候,重写两个方法:
@Override public int getViewTypeCount() { return number; } @Override public int getItemViewType(int position) { return type; }
getItemViewType用来返回第position个Item是何种类型,getViewTypeCount返回不同布局的总数。
关于ListView的运行效率的提高,摘抄《第一行代码》书中的一段进行说明
3.5.3 提升 ListView 的运行效率
之所以说 ListView 这个控件很难用,就是因为它有很多的细节可以优化,其中运行效率
就是很重要的一点。 目前我们 ListView的运行效率是很低的, 因为在 FruitAdapter的 getView()
方法中每次都将布局重新加载了一遍,当 ListView 快速滚动的时候这就会成为性能的瓶颈。
仔细观察,getView()方法中还有一个 convertView 参数,这个参数用于将之前加载好的
布局进行缓存,以便之后可以进行重用。修改 FruitAdapter 中的代码,如下所示:
public class FruitAdapter extends ArrayAdapter {
……
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
} else {
view = convertView;
}
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;
}
}
可以看到,现在我们在 getView()方法中进行了判断,如果 convertView 为空,则使用
LayoutInflater 去加载布局,如果不为空则直接对 convertView 进行重用。这样就大大提高了
ListView 的运行效率,在快速滚动的时候也可以表现出更好的性能。
不过, 目前我们的这份代码还是可以继续优化的, 虽然现在已经不会再重复去加载布局,
但是每次在 getView()方法中还是会调用 View的 findViewById()方法来获取一次控件的实例。
我们可以借助一个 ViewHolder 来对这部分性能进行优化,修改 FruitAdapter 中的代码,如下
所示:
public class FruitAdapter extends ArrayAdapter {
……
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == 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); // 将ViewHolder存储在View中
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag(); // 重新获取ViewHolder
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder {
ImageView fruitImage;
TextView fruitName;
}
}
我们新增了一个内部类 ViewHolder,用于对控件的实例进行缓存。当 convertView 为空
的时候, 创建一个 ViewHolder 对象, 并将控件的实例都存放在 ViewHolder 里, 然后调用 View
的 setTag()方法,将 ViewHolder 对象存储在 View 中。当 convertView 不为空的时候则调用
View 的 getTag()方法, 把 ViewHolder 重新取出。 这样所有控件的实例都缓存在了 ViewHolder
里,就没有必要每次都通过 findViewById()方法来获取控件实例了。
两个布局my_order_top_item.xml,my_order_item.xml
my_order_top_item.xml
<?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="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:background="@drawable/rect_white" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@mipmap/button_green" android:gravity="center" android:orientation="horizontal"> <TextView android:id="@+id/id_textOrderTime" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_weight="1" android:drawableLeft="@mipmap/order_time1" android:drawablePadding="10dp" android:textColor="#ffffff" android:textSize="14sp" /> <ImageView android:id="@+id/id_imageOrderNavi" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:src="@mipmap/order_navi" /> </LinearLayout> <TextView android:id="@+id/id_textOrderCharge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:drawableLeft="@mipmap/order_charge" android:drawablePadding="10dp" android:textColor="#555555" android:textSize="14sp" /> <TextView android:id="@+id/id_textOrderAddress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:drawableLeft="@mipmap/order_point" android:drawablePadding="10dp" android:textColor="#555555" android:textSize="14sp" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#a2a2a2" /> <TextView android:id="@+id/id_orderTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" android:textColor="#a2a2a2" android:textSize="16sp" /> <TextView android:id="@+id/id_orderAddress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:textColor="#a2a2a2" android:textSize="16sp" /> </LinearLayout> </LinearLayout>
my_order_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_margin="10dp" android:background="@drawable/rect_white"> <TextView android:id="@+id/id_textOrderCharge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:textSize="14sp" android:textColor="#555555" android:drawableLeft="@mipmap/order_charge" android:drawablePadding="10dp"/> <TextView android:id="@+id/id_textOrderAddress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginBottom="10dp" android:textSize="14sp" android:textColor="#555555" android:drawableLeft="@mipmap/order_point" android:drawablePadding="10dp"/> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#a2a2a2"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/id_orderTime" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textSize="16sp" android:textColor="#a2a2a2" android:layout_margin="5dp"/> <TextView android:id="@+id/id_orderState" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:textColor="#a2a2a2" android:layout_margin="5dp"/> </LinearLayout> </LinearLayout> </LinearLayout>
Adapter:
private class MyOrderAdapter extends BaseAdapter { Context mContext; LayoutInflater inflater; final int TYPE_1 = 0; final int TYPE_2 = 1; //各个布局的控件资源 class ViewHolder1 { TextView textOrderTime; ImageView imageOrderNavi; TextView textOrderCharge; TextView textOrderAddress; TextView orderTime; TextView orderAddress; } class ViewHolder2 { TextView textOrderCharge; TextView textOrderAddress; TextView orderTime; TextView orderState; } public MyOrderAdapter(Context context) { mContext = context; inflater = LayoutInflater.from(mContext); } @Override public int getCount() { return 10; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public int getViewTypeCount() { return 2; } @Override public int getItemViewType(int position) { if (position == 0) { return TYPE_1; } else { return TYPE_2; } } @Override public View getView(int position, View convertView, ViewGroup parent) { int type = getItemViewType(position); ViewHolder1 viewHolder1 = null; ViewHolder2 viewHolder2 = null; if (convertView == null) { if (type == TYPE_1) { convertView = LayoutInflater.from(MyOrderActivity.this).inflate(R.layout.my_order_top_item, null); viewHolder1 = new ViewHolder1(); viewHolder1 .textOrderTime = (TextView) convertView.findViewById(R.id.id_textOrderTime); viewHolder1.imageOrderNavi = (ImageView) convertView.findViewById(R.id.id_imageOrderNavi); viewHolder1.textOrderCharge = (TextView) convertView.findViewById(R.id.id_textOrderCharge); viewHolder1.textOrderAddress = (TextView) convertView.findViewById(R.id.id_textOrderAddress); viewHolder1.orderTime = (TextView) convertView.findViewById(R.id.id_orderTime); viewHolder1.orderAddress = (TextView) convertView.findViewById(R.id.id_orderAddress); convertView.setTag(viewHolder1); } else if (type == TYPE_2) { viewHolder2 = new ViewHolder2(); convertView = LayoutInflater.from(MyOrderActivity.this).inflate(R.layout.my_order_item, null); viewHolder2.textOrderCharge = (TextView) convertView.findViewById(R.id.id_textOrderCharge); viewHolder2.textOrderAddress = (TextView) convertView.findViewById(R.id.id_textOrderAddress); viewHolder2.orderTime = (TextView) convertView.findViewById(R.id.id_orderTime); viewHolder2.orderState = (TextView) convertView.findViewById(R.id.id_orderState); convertView.setTag(viewHolder2); } } else { if (type == TYPE_1) { viewHolder1 = (ViewHolder1) convertView.getTag(); } else if (type == TYPE_2) { viewHolder2 = (ViewHolder2) convertView.getTag(); } } if (type == TYPE_1) { viewHolder1.textOrderCharge.setText("电桩号 101668"); viewHolder1.textOrderAddress.setText("充电点 无锡工业设计大厦"); viewHolder1.orderTime.setText("预约时间 2016-1-11"); viewHolder1.orderAddress.setText("充电位置 无锡工业设计大厦"); } if (type == TYPE_2) { viewHolder2.textOrderCharge.setText("电桩号 101668"); viewHolder2.textOrderAddress.setText("充电点 无锡工业设计大厦"); viewHolder2.orderTime.setText("预约时间 2016-1-11"); viewHolder2.orderState.setText("已完成"); } return convertView; } }
列表我使用的是PullToRefreshListView,用于上拉下拉刷新,并且设置了列表为空进行显示的TextView
mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.id_orderListView); mPullRefreshListView.setMode(PullToRefreshBase.Mode.BOTH); TextView textView = new TextView(MyOrderActivity.this); textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); textView.setText("没有查询到相应数据"); textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL); textView.setVisibility(View.GONE); ((ViewGroup) mPullRefreshListView.getParent()).addView(textView); mPullRefreshListView.setEmptyView(textView); final ILoadingLayout startLabels = mPullRefreshListView .getLoadingLayoutProxy(true, false); startLabels.setPullLabel("下拉刷新...");// 刚下拉时,显示的提示 startLabels.setRefreshingLabel("正在载入...");// 刷新时 startLabels.setReleaseLabel("放开刷新...");// 下来达到一定距离时,显示的提示 ILoadingLayout endLabels = mPullRefreshListView.getLoadingLayoutProxy( false, true); endLabels.setPullLabel("上拉刷新...");// 刚下拉时,显示的提示 endLabels.setRefreshingLabel("正在载入...");// 刷新时 endLabels.setReleaseLabel("放开刷新...");// 下来达到一定距离时,显示的提示 mPullRefreshListView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2<ListView>() { @Override public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) { String label = DateUtils.formatDateTime(MyOrderActivity.this, System.currentTimeMillis(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL); // Update the LastUpdatedLabel refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label); // Do work to refresh the list here. // new GetDataTask().execute(); new getDataThread().start(); } @Override public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) { String label = DateUtils.formatDateTime(MyOrderActivity.this, System.currentTimeMillis(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL); // Update the LastUpdatedLabel refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label); // Do work to refresh the list here. // new GetDataTask().execute(); new getDataThread().start(); } }); /* 如果ListView中的单个Item的view中存在checkbox,button等view,会导致ListView.setOnItemClickListener无效, 事件会被子View捕获到,ListView无法捕获处理该事件. 解决方法: 在checkbox、button对应的view处加android: focusable = "false" android: clickable = "false" android: focusableInTouchMode = "false" 其中focusable是关键 */ ListView actualListView = mPullRefreshListView.getRefreshableView(); actualListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { } }); myOrderAdapter = new MyOrderAdapter(MyOrderActivity.this); actualListView.setAdapter(myOrderAdapter); } loadUiHandler myHandler = new loadUiHandler(); private class getDataThread extends Thread { @Override public void run() { super.run(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } Message message = new Message(); message.what = 0; myHandler.sendMessage(message); } } private class loadUiHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); myOrderAdapter.notifyDataSetChanged(); // Call onRefreshComplete when the list has been refreshed. mPullRefreshListView.onRefreshComplete(); } }
参考
Android杂谈–ListView之BaseAdapter的使用
Android类库–与Adapter相关的类
相关文章推荐
- Android.mk的用法和基础【转】
- Gradle for Android
- Android Universal ImageLoader 缓存图片
- Android中可自由移动悬浮窗口的实现
- android 省略号Bug
- 360 Android 插件项目 DroidPlugin
- 关于AndroidStudio不能在xml中选择mipmap的问题
- Android Studio 1.4.x JNI开发配置
- 从源代码分析Android Universal ImageLoader的缓存处理机制
- Android 4.4 KitKat NotificationManagerService使用详解与原理分析
- Android线程管理(一)——线程通信
- 四大组件之activity活动周期
- 给 Android 开发者的 RxJava 详解
- Android studio下svn的配置
- Android抓取崩溃日志
- Android service 详解
- Android中Service 详解
- Android之ContentProvider详解
- Android中BroadCastReceiver详解
- Android广播机制