android群英传笔记——ListView常用优化技巧(一、使用ViewHolder模式提高效率)
2016-10-28 19:29
633 查看
一段时间没有更新博客了,最近开始了JSP的课程,同时也要学习Android,所以进度有所下降,但是不影响笔者Android群英传的学习。
一、使用ViewHolder模式提高效率
使用ViewHolder模式可以提高50%以上的效率,其使用方法也是比较简单的。只要在自定义的Adapter中定义一个内部类,再将子项的控件获取到并存储进内部类即可。其作用就是优化代码,减少加载控件的时间。 其内部类的定义方法如下所示:
/** * 用于缓存已经加载过的子项控件 */ public final class ViewHolder { public ImageView img; public TextView title; }
以下是测试过程: 程序运行如图所示:
笔者在自定义Adapter内执行ViewHolder时,添加日志输出,以下是第一次运行时的log日志:
10-28 11:58:30.561 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.634 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.669 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.689 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.707 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.712 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存!
读者们可以看出其中的关系吗?对,log缓存次数其实就是屏幕上加载的子项数量,可以看出第一次加载时的速度并没有太大提升,因为都需要进行相同的加载次数。 于是笔者向下滑动了ListView,日志如下所示:
10-28 11:58:30.561 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.634 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.669 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.689 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.707 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存! 10-28 11:58:30.712 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存!
10-28 11:58:33.006 14886-14886/com.example.listviewcontrol3 I/HolderAdapter: HolderAdapterGetView: 执行缓存!
其实笔者滑到了底部,笔者在ListView中设置了10个子项,但是log日志只加载了7次,不是10次吗?不是,在加载完屏幕的子项后,只加载了一次,也就意味着除了屏幕上的加载以外,未显示的子项在第一次加载后,不进行重复加载。也就是说直接调用了ViewHolder内存储的已加载的控件,以此优化了ListView。 笔者在ListView的上方加上了删除和添加子项的按钮,以此测试当ListView添加新的子项时是否直接调用缓存好的子项控件。结果发现日志没有显示执行缓存,意味着在后期添加子项时都无需再加载,直接调用。可见,在子项比较多的时候,此方法可大幅度优化ListView的滑动。
代码
以下是测试代码: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:orientation="vertical" tools:context="com.example.listviewcontrol3.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="50dp" android:orientation="horizontal"> <!--用于添加子项的按钮--> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/btn_add" android:id="@+id/btn_add"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/btn_div" android:id="@+id/btn_div" /> </LinearLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!--可以通过 android:divider="@null" 来设置透明分割线--> <!--android:scrollbars="none" 可以设置隐藏滚动条--> <!--android:listSelector="@android:color/transparent" 用来取消item的点击效果--> <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:divider="@color/red" android:dividerHeight="3dp" android:scrollbars="none" android:listSelector="@android:color/transparent" android:id="@+id/list_view" /> <include layout="@layout/empty_list_view" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/empty_view"/> </FrameLayout> </LinearLayout>
viewholder_item.xml
<?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:layout_width="100dp" android:layout_height="100dp" android:id="@+id/imageView"/> <TextView android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginStart="20dp" android:textSize="24sp" android:id="@+id/textView"/> </LinearLayout>
empty_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"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/list_item_empty" android:gravity="center" android:textSize="30sp"/> </LinearLayout>
MainActivity.java
package com.example.listviewcontrol3; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.AbsListView; import android.widget.Button; import android.widget.ListView; import android.widget.Toast; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private ArrayList<String> list; private HolderAdapter adapter; private ListView listView; private String TAG = "HolderAdapter"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 随意加入数据,在适配器内已经设置了图片,故只用向适配器传递一个字符串集合即可 list = new ArrayList<>(); for(int i = 0; i < 10; i++){ list.add(getResources().getString(R.string.list_item_text)); } adapter = new HolderAdapter(this, list); listView = (ListView) findViewById(R.id.list_view); // 假设控件存在 assert listView != null : "未找到listVie!!!"; listView.setAdapter(adapter); // 给listView 设置没有数据时显示的视图 listView.setEmptyView(findViewById(R.id.empty_view)); //listView.setOnTouchListener(new OnTouchListViewListener()); listView.setOnScrollListener(new OnListScrollListener()); Button btnAdd = (Button) findViewById(R.id.btn_add); assert btnAdd != null : "未找到btn_add按钮!!!"; btnAdd.setOnClickListener(new OnAddClickListener()); Button btnDiv = (Button) findViewById(R.id.btn_div); assert btnDiv != null : "未找到btn_div按钮!!!"; btnDiv.setOnClickListener(new OnDivClickListener()); } /** * 添加add按钮监听事件类 */ private class OnAddClickListener implements View.OnClickListener { @Override public void onClick(View v) { // 给数据源添加数据 list.add("new!!!"); // 刷新界面 adapter.notifyDataSetChanged(); // 设置初始时显示的位置 listView.setSelection(list.size() - 1); Log.i(TAG, "btnAddOnClick: 添加成功!"); } } /** * 添加div按钮监听事件类 */ private class OnDivClickListener implements View.OnClickListener { @Override public void onClick(View v) { if(list.size() > 0) { // 删除数据源中的数据 list.remove(list.size() - 1); // 刷新界面 adapter.notifyDataSetChanged(); // 设置初始时显示的位置 listView.setSelection(list.size() - 1); Log.i(TAG, "btnDivOnClick: 删除成功!"); } else { Toast.makeText(MainActivity.this,R.string.item_not_clear, Toast.LENGTH_SHORT).show(); Log.i(TAG, "btnDivOnClick: list为空!"); } } } /** * listView的触摸监听类 */ private class OnTouchListViewListener implements View.OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: // 触碰到时的操作 Log.i(TAG, "onTouch: ACTION_DOWN!"); break; case MotionEvent.ACTION_MOVE: // 移动时的操作 Log.i(TAG, "onTouch: ACTION_MOVE!"); break; case MotionEvent.ACTION_UP: // 离开时操作 Log.i(TAG, "onTouch: ACTION_UP!"); break; } return false; } } /** * 滑动事件监听类 */ private class OnListScrollListener implements AbsListView.OnScrollListener { /** * 监听滑动状态 * @param view 监听的控件 * @param scrollState 监听控件的状态 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState){ case OnListScrollListener.SCROLL_STATE_IDLE: // 滑动停止时 Log.d(TAG, "onScrollStateChanged: scrollStop!"); break; case OnListScrollListener.SCROLL_STATE_TOUCH_SCROLL: // 正在滑动 Log.d(TAG, "onScrollStateChanged: scrolling!"); break; case OnListScrollListener.SCROLL_STATE_FLING: // 手指抛动时,即手指用力滑动在离开后ListView由于惯性继续滑动 Log.d(TAG, "onScrollStateChanged: scrollFling!"); break; } } /** * 监听listView的滚动状态 * @param view 当前view * @param firstVisibleItem 第一个显示的子项id * @param visibleItemCount 可见的子项总数,包括显示部分的子项 * @param totalItemCount 子项的总数 */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // 滚动时一直调用 Log.d(TAG, "onScroll: onScroll!!!"); if(firstVisibleItem+visibleItemCount==totalItemCount && totalItemCount>0){ // 滚动到最后一行 Log.d(TAG, "onScroll: OnLastItem!"); } } } }
HolderAdapter.java
package com.example.listviewcontrol3;
import android.content.Context;
import android.util.Log;
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;
/**
* 利用ViewHolder提高效率
* Created by shize on 2016/10/24.
*/
public class HolderAdapter extends BaseAdapter {
private List<String> mData;
private LayoutInflater mInflater;
private String TAG = "HolderAdapter";
public HolderAdapter(Context context, List<String> 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 View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
// 判断是否缓存,若控件已经加载过了,就跳过加载,直接对控件进行样式参数的设置
if (convertView == null) {
holder = new ViewHolder();
// 通过LayoutInflater实例化布局
convertView = mInflater.inflate(R.layout.viewholder_item, null);
// 加载控件,缓存进holder内
holder.img = (ImageView) convertView.findViewById(R.id.imageView);
holder.title = (TextView) convertView.findViewById(R.id.textView);
convertView.setTag(holder);
Log.i(TAG, "HolderAdapterGetView: 执行缓存!");
} else {
// 通过tag找到缓存的布局
holder = (ViewHolder) convertView.getTag();
}
// 设置子项控件的图片和文字
holder.img.setImageResource(R.drawable.ic_launcher);
holder.title.setText(mData.get(position));
return convertView;
}
/** * 用于缓存已经加载过的子项控件 */ public final class ViewHolder { public ImageView img; public TextView title; }
}
感谢阅读,学习重在坚持,贵在坚持,那么下次再见。
相关文章推荐
- ListView使用技巧之ViewHolder模式提高效率
- ListView使用ViewHolder模式提高效率
- 【Android UI】ListView使用ViewHolder模式提高效率
- Android列表组件ListView使用详解之ViewHolder提高效率
- 使用ViewHolder模式优化ListView
- 使用ViewHolder模式优化ListView
- 使用ViewHolder提高ListView的效率
- android基础笔记——使用viewholder模式优化ListView
- 使用ViewHolder模式来提高自定义adapter的效率
- Android探究--使用ViewHolder提高ListView效率
- ListView之BaseAdapter的基本使用以及ViewHolder模式
- Android中优化ListView的ViewHolder模式
- Iwfu-ListView(一) - ListView使用ViewHolder优化以及ListView的其他
- ListView之BaseAdapter的基本使用以及ViewHolder模式
- 关于ListView使用ConvertView和ViewHolder实现性能优化
- ListView之BaseAdapter的基本使用以及ViewHolder模式
- ListView优化--ViewHolder的使用
- 使用ViewHolder优化ListView
- ListView之BaseAdapter的基本使用以及ViewHolder模式
- ListView的使用用ViewHolder提升效率