RecyclerView为什么会卡

RecyclerView作为v7包的新控件,自从推出就广受Android Developer们欢迎,实际上它已经取代了ListView和GridView两位老前辈的地位。然而不少亲们想必也已经发现了:没有优化过的Recycler性能很poor。上一篇博主使用的item也仅仅是一个图两串字而已,结果一滑动就卡的要命,不能忍!

那么why?回想在用ListView和GridView的adapter时,我们是用一种叫ViewHolder的自定义类(容器)来实现优化的,而RecyclerView的特性之一就是强制你使用它的RecyclerView.ViewHolder。可是,RecyclerView.ViewHolder要比我们写的那个单纯的容器复杂多了(源码里算上注释有大约500行),与RecyclerView.Adapter的联系也是千丝万缕。

按stackoverflow上面比较通俗的解释:RecyclerView.Adapter里面的onCreateViewHolder()方法和onBindViewHolder()方法对时间都非常敏感。类似I/O读写,Bitmap解码一类的耗时操作,最好不要在它们里面进行。

如何解决这个问题

首先当然得优化你的item,合理运用,,等标签,使布局层次尽量少——其实ListView和GridView里你也应该这么做,应该当成是一种写UI的习惯。

其次就是灵活使用各种第三方库,去完成各种耗时操作,比如通过Glide或者是Picasso加载图片。优秀的开源库在性能上往往都考虑得很仔细。

最后的问题来了,如果只想写一个小demo,不愿大张旗鼓怎么办?如果即便一般的第三方库也不好解决问题,比如上一篇那个该死的loadIcon()方法返回的是一个Drawable对象,Glide和Picasso都没法直接处理,转码又等于添了个耗时任务,那怎么办?

真正的app管理应用,应该引入UIL或者Picasso一类的加载库进行图标加载

答案就是,想法在你setAdapter之前就把任务给完成。

Demo

哟西,上代码!本文代码完全基于上一篇文,无须删减重构。

主要就是增添了一个实体bean对象,setAdapter()时要传递的数据,全部通过它预先加载到内存里!这样那俩敏感方法里只需要简单的get出来即可。

实体类AppBean.java

package com.example.jin.localapp;

import android.graphics.drawable.Drawable;

/**

* Created by Jin on 2016/11/8.

*/

public class AppBean {

private CharSequence name;

private String packageName;

private Drawable icon;

//这类代码可别逞英雄手动写哦,IDE(Android Studio和Eclipse都有的)里可以直接生成

public CharSequence getName() {

return name;

}

public void setName(CharSequence name) {

this.name = name;

}

public String getPackageName() {

return packageName;

}

public void setPackageName(String packageName) {

this.packageName = packageName;

}

public Drawable getIcon() {

return icon;

}

public void setIcon(Drawable icon) {

this.icon = icon;

}

}

主界面MainActivity.java

private List mList;//mList的泛型换成AppBean

private void initData() {//然后只需要改这个方法

mList = new ArrayList<>();

manager = getPackageManager();

List list = manager.getInstalledPackages(0);//获取已安装的全部应用

for (PackageInfo info : list) {

if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {

AppBean bean = new AppBean();

bean.setName(info.applicationInfo.loadLabel(manager));

bean.setPackageName(info.packageName);

bean.setIcon(info.applicationInfo.loadIcon(manager));

mList.add(bean);

}

}

//拿到数据再setAdapter

mainRcv.setLayoutManager(new LinearLayoutManager(this));

mainRcv.setHasFixedSize(true);

mainRcv.setAdapter(new AppAdapter(this, mList));

}

适配器AppAdapter.java

private List appList;

//同样这边的类型换过来

public AppAdapter(Context context, List appList) {

this.context = context;

this.appList = appList;

inflater = LayoutInflater.from(context);

manager = context.getPackageManager();

}

//然后也只需要改这个方法

@Override

public void onBindViewHolder(AppHolder holder, final int position) {

final AppBean bean = appList.get(position);

holder.itemIconIv.setImageDrawable(bean.getIcon());//图标

holder.itemNameTv.setText(bean.getName());//名称

holder.itemPackageTv.setText(bean.getPackageName());//包名

holder.view.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Intent intent = new Intent(manager.getLaunchIntentForPackage(bean.getPackageName()));//根据包名启动此应用

context.startActivity(intent);

}

});

}

搞定!因为博主是用手机直接录像再转gif,为了使点击看上去有效果,于是给item增添了一个背景层,这需求实战中也是很常见的哦~~

色彩资源文件colors.xml

这个粉红色其实很难看,单纯当区别用。。。。。。

实战开发如果没有美工,一定要仔细斟酌选取,尽量让自己审美好点!

#3F51B5

#303F9F

#FF4081

#ffffff

#f8bbd0

选择器item_selector.xml

条目布局item_app.xml

android:background="@drawable/item_selector"

android:layout_width="match_parent"

android:layout_height="60dp">

最终运行效果

截图已经不太能感受到卡了,真机运行更加流畅!

df9848e8b7bdbc57a990d673535b48db.gif

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐