ScrollView、RecyclerView、ScrollView嵌套ListView性能优化方案
来源:互联网 发布:Mac 不能共享文件夹 编辑:程序博客网 时间:2024/05/26 07:28
因为项目的需要我们不可避免的需要使用类似的布局方案,我之前写过的一篇文章总结ScrollView嵌套ListView的解决方法,提出了相应的解决方案。但是却陷入了一个性能的大坑:因为之前的解决方案都是以计算出ListView控件的总高度并固定,那么自然就破坏了LisView内置的特性,造成了Adapter中的 getView会被疯狂的调用。(这里就不贴代码了,用过的童鞋应该都懂)
来,让我们直接开启优化模式。
同样是得自定义View,不过我们继承的不再是ListView,而是LinearLayout。(感谢@张旭童同志的封装,文末贴出源码及源地址)
下面是自定义NestFullListView代码
<span style="font-size:12px;">/** * 介绍:完全伸展开的ListView(LinearLayout) * 作者:zhangxutong * 邮箱:zhangxutong@imcoming.com * 时间: 2016/9/9. */public class NestFullListView extends LinearLayout { private LayoutInflater mInflater; private List<NestFullViewHolder> mVHCahces;//缓存ViewHolder,按照add的顺序缓存, public NestFullListView(Context context) { this(context, null); } public NestFullListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public NestFullListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { mInflater = LayoutInflater.from(context); mVHCahces = new ArrayList<NestFullViewHolder>(); //annotate by zhangxutong 2016 09 23 for 让本控件能支持水平布局,项目的意外收获= = //setOrientation(VERTICAL); } private NestFullListViewAdapter mAdapter; /** * 外部调用 同时刷新视图 * * @param mAdapter */ public void setAdapter(NestFullListViewAdapter mAdapter) { this.mAdapter = mAdapter; updateUI(); } public void updateUI() { if (null != mAdapter) { if (null != mAdapter.getDatas() && !mAdapter.getDatas().isEmpty()) { //数据源有数据 if (mAdapter.getDatas().size() > getChildCount()) {//数据源大于现有子View不清空 } else if (mAdapter.getDatas().size() < getChildCount()) {//数据源小于现有子View,删除后面多的 removeViews(mAdapter.getDatas().size(), getChildCount() - mAdapter.getDatas().size()); //删除View也清缓存 while (mVHCahces.size() > mAdapter.getDatas().size()) { mVHCahces.remove(mVHCahces.size() - 1); } } for (int i = 0; i < mAdapter.getDatas().size(); i++) { NestFullViewHolder holder; if (mVHCahces.size() - 1 >= i) {//说明有缓存,不用inflate,否则inflate holder = mVHCahces.get(i); } else { holder = new NestFullViewHolder(getContext(), mInflater.inflate(mAdapter.getItemLayoutId(), this, false)); mVHCahces.add(holder);//inflate 出来后 add进来缓存 } mAdapter.onBind(i, holder); //如果View没有父控件 添加 if (null == holder.getConvertView().getParent()) { this.addView(holder.getConvertView()); } } } else { removeAllViews();//数据源没数据 清空视图 } } else { removeAllViews();//适配器为空 清空视图 } }}</span>
代码解析:代码增加一个变量 privateList<NestFullViewHolder>mVHCahces;//缓存ViewHolder,按照add的顺序缓存
每次updateUI()时,如果是异常情况:适配器为空 清空视图,数据源没数据 清空视图
那么数据源有数据的情况下,比较数据源的size 和现在子View(ItemView)的size:1、如果数据源大于现有子View,说明屏幕上的View不够用,当然不remove子View,也不用清缓存。
2、如果数据源小于现有子View,删除尾部多的子View,清理多余缓存的ItemView。
遍历数据源,比较i(postion)和viewCaches的size:
1、如果缓存不够就inflate一个新View。
2、如果缓存有,就取出缓存的View。
回调Adapter的onBind方法,
判断这个View有没有父控件,
如果View没有父控件 才addView()。
在上面的代码中已经尽可能的避免了View的inflate,addView()操作。可是我们都知道,findViewById()的操作也是很费时的,所以在上面的代码中
使用了ViewHolder,下面就来看看这个ViewHolder,代码有点长,作者考虑的比较细致,封装一些常用的方法,例如setText、setImageResource
等,供外部调用使用,同时还包括一些监听事件。
下面是NestFullViewHolder代码
/** * NestFullListView 的ViewHolder ,使用者无需关心 * Created by zhangxutong . * Date: 16/03/11 */public class NestFullViewHolder { private SparseArray<View> mViews; private View mConvertView; private Context mContext; public NestFullViewHolder(Context context, View view) { mContext = context; this.mViews = new SparseArray<View>(); mConvertView = view; } /** * 通过viewId获取控件 * * @param viewId * @return */ public <T extends View> T getView(int viewId) { View view = mViews.get(viewId); if (view == null) { view = mConvertView.findViewById(viewId); mViews.put(viewId, view); } return (T) view; } public View getConvertView() { return mConvertView; } public NestFullViewHolder setSelected(int viewId, boolean flag) { View v = getView(viewId); v.setSelected(flag); return this; } /** * 设置TextView的值 * * @param viewId * @param text * @return */ public NestFullViewHolder setText(int viewId, String text) { TextView tv = getView(viewId); tv.setText(text); return this; } public NestFullViewHolder setImageResource(int viewId, int resId) { ImageView view = getView(viewId); view.setImageResource(resId); return this; } public NestFullViewHolder setImageBitmap(int viewId, Bitmap bitmap) { ImageView view = getView(viewId); view.setImageBitmap(bitmap); return this; } public NestFullViewHolder setImageDrawable(int viewId, Drawable drawable) { ImageView view = getView(viewId); view.setImageDrawable(drawable); return this; } public NestFullViewHolder setBackgroundColor(int viewId, int color) { View view = getView(viewId); view.setBackgroundColor(color); return this; } public NestFullViewHolder setBackgroundRes(int viewId, int backgroundRes) { View view = getView(viewId); view.setBackgroundResource(backgroundRes); return this; } public NestFullViewHolder setTextColor(int viewId, int textColor) { TextView view = getView(viewId); view.setTextColor(textColor); return this; } public NestFullViewHolder setTextColorRes(int viewId, int textColorRes) { TextView view = getView(viewId); view.setTextColor(mContext.getResources().getColor(textColorRes)); return this; } @SuppressLint("NewApi") public NestFullViewHolder setAlpha(int viewId, float value) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { getView(viewId).setAlpha(value); } else { // Pre-honeycomb hack to set Alpha value AlphaAnimation alpha = new AlphaAnimation(value, value); alpha.setDuration(0); alpha.setFillAfter(true); getView(viewId).startAnimation(alpha); } return this; } public NestFullViewHolder setVisible(int viewId, boolean visible) { View view = getView(viewId); view.setVisibility(visible ? View.VISIBLE : View.GONE); return this; } public NestFullViewHolder linkify(int viewId) { TextView view = getView(viewId); Linkify.addLinks(view, Linkify.ALL); return this; } public NestFullViewHolder setTypeface(Typeface typeface, int... viewIds) { for (int viewId : viewIds) { TextView view = getView(viewId); view.setTypeface(typeface); view.setPaintFlags(view.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG); } return this; } public NestFullViewHolder setProgress(int viewId, int progress) { ProgressBar view = getView(viewId); view.setProgress(progress); return this; } public NestFullViewHolder setProgress(int viewId, int progress, int max) { ProgressBar view = getView(viewId); view.setMax(max); view.setProgress(progress); return this; } public NestFullViewHolder setMax(int viewId, int max) { ProgressBar view = getView(viewId); view.setMax(max); return this; } public NestFullViewHolder setRating(int viewId, float rating) { RatingBar view = getView(viewId); view.setRating(rating); return this; } public NestFullViewHolder setRating(int viewId, float rating, int max) { RatingBar view = getView(viewId); view.setMax(max); view.setRating(rating); return this; } public NestFullViewHolder setTag(int viewId, Object tag) { View view = getView(viewId); view.setTag(tag); return this; } public NestFullViewHolder setTag(int viewId, int key, Object tag) { View view = getView(viewId); view.setTag(key, tag); return this; } public NestFullViewHolder setChecked(int viewId, boolean checked) { Checkable view = (Checkable) getView(viewId); view.setChecked(checked); return this; } /** * 关于事件的 */ public NestFullViewHolder setOnClickListener(int viewId, View.OnClickListener listener) { View view = getView(viewId); view.setOnClickListener(listener); return this; } public NestFullViewHolder setOnTouchListener(int viewId, View.OnTouchListener listener) { View view = getView(viewId); view.setOnTouchListener(listener); return this; } public NestFullViewHolder setOnLongClickListener(int viewId, View.OnLongClickListener listener) { View view = getView(viewId); view.setOnLongClickListener(listener); return this; }}
代码解析:
利用privateSparseArray<View>mViews,以viewId为key,存储ItemView里的各种View。(这里简单解释一下SparseArray是Android 特有的类似于Java的HashMap的键值对存储方式,只不过SparseArray必须以<Integer, E>>类型来的使用,效率更高,本文正好可以用上)
通过public T getView(int viewId)方法,以viewId为key,获取ItemView里的各种View,
该方法是先从mViews的缓存里寻找View,如果找到了直接返回,
如果没找到就view = mConvertView.findViewById(viewId);执行findViewById,得到这个View,并放入mViews的缓存里,这样下次就不用执行findViewById方法。
好了,最后我们来构造我们的NestFullListViewAdapter
/** * 介绍:完全伸展开的ListView的适配器 * 作者:zhangxutong * 邮箱:mcxtzhang@163.com * CSDN:http://blog.csdn.net/zxt0601 * 时间: 16/09/09. */public abstract class NestFullListViewAdapter<T> { private int mItemLayoutId;//看名字 private List<T> mDatas;//数据源 public NestFullListViewAdapter(int mItemLayoutId, List<T> mDatas) { this.mItemLayoutId = mItemLayoutId; this.mDatas = mDatas; } /** * 被FullListView调用 * * @param i * @param holder */ public void onBind(int i, NestFullViewHolder holder) { //回调bind方法,多传一个data过去 onBind(i, mDatas.get(i), holder); } /** * 数据绑定方法 * * @param pos 位置 * @param t 数据 * @param holder ItemView的ViewHolder */ public abstract void onBind(int pos, T t, NestFullViewHolder holder); public int getItemLayoutId() { return mItemLayoutId; } public void setItemLayoutId(int mItemLayoutId) { this.mItemLayoutId = mItemLayoutId; } public List<T> getDatas() { return mDatas; } public void setDatas(List<T> mDatas) { this.mDatas = mDatas; }}核心代码主要就在onBind()方法中,如果对这种封装方式不是很清楚的的童鞋强烈建议有时间去看看 鸿洋大神的文章 鸿洋:打造万能适配器
好了,最重要的代码我们已经完成了剩下的就是调用了,So easy!
首先我们得在我们需要使用Xml文件中写入类似的代码(我们继承的是LinearLayout,所以理所应当线性布局的属性我们都能够使用拉!哈哈,想到这儿是不是发现还能来个orientatiion="horizonal"呢?当然拉,有兴趣的童鞋可以试试。)
<xxx.xxx.xxx.NestFullListView
android:id="@+id/cstFullShowListView"android:layout_width="match_parent"android:layout_height="wrap_content"android:showDividers="middle"/>android:orientation="vertical"
最后在我们需要使用的地方这样调用一下
nestFullListView = (NestFullListView) findViewById(R.id.cstFullShowListView);nestFullListView.setAdapter(new NestFullListViewAdapter<TestBean>(R.layout.item_lv, mDatas) { @Override public void onBind(int pos, TestBean testBean, NestFullViewHolder holder) { holder.setText(R.id.tv, testBean.getName());//按照这种方式写起来是很爽的吧A_V; }});好啦相对而言性能优化方案已经出来啦。这里有原作者的Demogithub传送门:
https://github.com/mcxtzhang/NestFullListView
复制FullListView包下三个文件(NestFullListView NestFullListViewAdapter NestFullViewHolder)即可畅快使用
Ok,小伙伴们还有什么问题可以私信我哦
1 0
- ScrollView、RecyclerView、ScrollView嵌套ListView性能优化方案
- ScrollView里面嵌套ListView,RecyclerView
- ScrollView嵌套ListView,RecyclerView问题
- ScrollView嵌套ListView问题解决方案
- ScrollView嵌套Listview/RecyclerView内容显示不全
- ScrollView与ListView RecyclerView的嵌套
- 四种方案解决ScrollView嵌套ListView
- 转四种方案解决ScrollView嵌套ListView问题
- ScrollView嵌套ListView冲突问题解决方案
- ScrollView嵌套RecyclerView
- ScrollView嵌套recyclerView问题
- android ScrollView嵌套RecyclerView
- Scrollview 嵌套 RecyclerView
- scrollview嵌套recyclerview 问题
- scrollview嵌套recyclerview
- ScrollView嵌套RecyclerView 问题
- RecyclerView和ScrollView嵌套
- android ScrollView嵌套RecyclerView
- 通过Hibernate框架搭建简单的dao层
- typedef用法
- 解决VS2008编译的程序在某些机器上运行提示“由于应用程序配置不正确,应用程序未能启动”的问题
- 小程序的swiper不显示图片
- GPRS Multislot operation
- ScrollView、RecyclerView、ScrollView嵌套ListView性能优化方案
- 为什么要用存储过程
- 2.13 Notification(通知)的使用
- node.js与express4.X实战--快速构建一个简单的API接口(翻译)
- shader ide
- Python3中使用map()结果出错的解决方法
- 题型:涉及到i++的用法,变量作用域以及闭包的概念
- AR--AR 小应用究竟是如何实现的
- 自己动手合成喜欢的编程字体