使用 ViewHolder 的模式提高ListView的效率
ViewHolder模式充分利用了ListView的缓存机制
MyListViewAdapter
public class MyListViewAdapter extends BaseAdapter {
private List<String> mData;
private LayoutInflater mInflater;
public MyListViewAdapter(Context context,List<String> data){
this.mData = data;
this.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 View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView==null){//判断是否有缓存
holder = new ViewHolder();
//实例化布局
convertView = mInflater.inflate(R.layout.viewholder_item,null);
holder.img = (ImageView) convertView.findViewById(R.id.imageView);
holder.title = (TextView) convertView.findViewById(R.id.textView);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
//填充数据
holder.title.setText(mData.get(position));
return convertView;
}
public final class ViewHolder{
public ImageView img;
public TextView title;
}
}
vieholder_item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/imageView"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="title"/>
</LinearLayout>
Activity
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private MyListViewAdapter mAdapter;
private List<String> mData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mData = new ArrayList<>();
for(int i=0;i<20;i++){
mData.add("Title"+i);
}
mAdapter = new MyListViewAdapter(this,mData);
mListView = (ListView) findViewById(R.id.listView);
mListView.setAdapter(mAdapter);
}
}
效果图:
ListView的缓存机制
ListView设置项目之间的分隔线
隐藏ListView的滚动条
默认的ListView在滚动的时候是有滚动条的,我们可以设置 scrollbars 属性控制ListView的滚动条状态。
android:scrollbars=”none”
取消ListView的Item点击效果
当点击ListView中的一项时,系统默认会给一个点击效果,Android 5.X上是一个波纹效果,在这个版本下面则是一个改变背景颜色的效果。我们可以通过修改listSelector属性来取消掉点击后的回馈效果
android:listSelector=”#000000000”
我们也可以使用Android自带的透明色来实现这个效果
android:listSelector=”@android:color/transparent”
设置ListView需要显示在第几项
ListView 是以 Item 为单位进行显示的,默认显示在第一个 Item,当需要指定具体的 Item 时,可以通过如下代码实现:
listView.setSelection(position);
当然,这个方法类似 scrollTo ,是瞬间完成的移动。除此之外,我们可以通过如下代码实现平滑移动:
mListView.smoothScrollBy(distance,duration); distance 是像素、duration 是持续时间
mListView.smoothScrollByOffset(offset); 是偏移多少个item,正值向上滚动,负值向下滚动
mListView.smoothScrollToPosition(index); 这个方法会将 下标为 index 的 item 条目滚动到屏幕内,如果该条目已经在屏幕内就不会滚动
以上三个方法需要通过 post 方法去更新,如果我们直接去更新的话,listView 可能刚添加好条目,ui还在更新中,所以我们可以给 listView 一个延迟去更新
mListView.postDelayed(new Runnable{
mListView.smoothScrollBy(distance,duration);
mListView.smoothScrollByOffset(offset);
mListView.smoothScrollToPosition(index);
},1000);
mListView.post(new Runnable{
mListView.smoothScrollBy(distance,duration);
mListView.smoothScrollByOffset(offset);
mListView.smoothScrollToPosition(index);
});
动态修改ListView
使用如下方法需要注意的是传递到 Adapter 中的集合必须是同一个集合
mData.add("New");
mAdapter.notifyDataSetChanged();
另外这个方法会导致界面的重新绘制,虽然比较重新创建 Adapter 和重新 set比起来工作量要小的多,但当我们需要的只是更新某一条数据,就可以通过自己创建的方法实现:
public MyAdapter extends BaseAdapter{
..............
/**
* @param position 要更新条目的位置
* @param listView 要更新的ListView
*/
public void updateView(int position,ListView listView){
int visibleFirstPosition = listView.getFirstVisiblePosition();
int visibleLastPosition = listView.getLastVisiblePosition();
//如果该条目是正在显示的话就找到改条目并且更新数据
if(position >= visibleFirstPosition && position <= visibleLastPosition){
View view = listView.getChildAt(position - visibleFirstPosition);
ViewHolder viewHolder = (ViewHolder) view.getTag();
viewHolder.title.setText("new");
}else{//如果要更新的条目不是在屏幕中显示的话直接更新下数据源即可
mData.set(position,"new");
}
}
..........
}
需要注意的是:listView 的 getChildAt() 是对于 listView 目前可见范围内的条目,就是正在屏幕上展示的哪些条目
有的同学可能调试的出现没有更新的结果是因为 listView 还没有初始化完毕, visibleLastPosition 会返回-1,故而需要一定的延时策略,正常情况下也是在 listView 加载完毕后用户才需要更新某个条目
### 遍历 LivtView 的 item
getChildCount() 获取的是屏幕上展示的 item 的个数
getCount() 获取的是 listView 中的所有 item 的个数
getChildAt(position) 获取的是屏幕上展示的 item 中的某一个下标是从 0 开始的
“`java
for(int i=0;i < mListView.getChildCount();i++){
View view = mListView.getChildAt(i);//其实这个就是convertView
}
“`
### 处理空的 ListView
ListView 用于展示列表数据,但当列表中没有数据的时候, ListView 不会显示任何的数据或提示,为了更好的用户体验,这里应该给以无数据的显示。ListView 提供了一个方法——setEmptyView(),通过这个方法我们可以给 ListView 设置一个在空数据下显示的默认提示。
但是 setEmptyView()是有一些限制的,就是设置的 View 必须在当前的 View hierarchy中,亦即这个 View 需要被 add 到当前 View hierarchy 的一个节点上面,如果没有添加到节点上的话,调用 setEmpty()方法是没有任何效果的。
“`java