android群英传笔记——ListView常用优化技巧(一、使用ViewHolder模式提高效率)
来源:互联网 发布:哪个fm软件好 知乎 编辑:程序博客网 时间:2024/06/05 20:17
一段时间没有更新博客了,最近开始了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; }}
感谢阅读,学习重在坚持,贵在坚持,那么下次再见。
0 0
- android群英传笔记——ListView常用优化技巧(一、使用ViewHolder模式提高效率)
- android群英传笔记——ListView常用优化技巧(二、ListView的滑动监听)
- ListView使用技巧之ViewHolder模式提高效率
- android基础笔记——使用viewholder模式优化ListView
- 【Android UI】ListView使用ViewHolder模式提高效率
- android群英传笔记--第四章--ListView使用技巧(一)
- ListView使用ViewHolder模式提高效率
- Android群英传学习笔记——ListView使用技巧
- Android群英传笔记——第四章:ListView使用技巧
- Android群英传笔记——第四章:ListView使用技巧
- ListView常用优化技巧(Android群英传)
- ListView(一)——优化技巧ViewHolder
- android 群英传读书笔记---listview使用技巧(一)
- Android群英传知识点回顾——第四章:ListView常用优化技巧
- android群英传笔记--第四章--ListView使用技巧(二)
- Android群英传——第四章ListView常用优化
- ListView使用技巧(一):ViewHolder
- Android群英传学习——第四章、ListView使用技巧
- java电商面试介绍(不完整版)
- 创造者模式
- 快速集成融云SDK– Android Studio
- 邮件发送出现错误:535 #5.7.0 Authentication failed
- OC__part11
- android群英传笔记——ListView常用优化技巧(一、使用ViewHolder模式提高效率)
- 【CodeForce 710E】Generate a String
- 模版方法模式
- ListView中多个EditText设置焦点 多次点击异常报错
- 资料整理
- Asynchronous Programming in C# 5.0 using async and await
- Codeforce 540D(概率dp)
- PHP变量在内核中的存储方式
- 数据库事务丢失修改,不可重复读,读"脏"数据的区别