zoukankan      html  css  js  c++  java
  • App优化(一)通用ViewHolder

    App优化(一)通用ViewHolder

    一直都是用歇菜方式写的Adapter,这种方式每次加载view,都要建立很多view对象,如果超过一定数量这种加载方式肯定要歇菜。在应用上架后,修正了用户提交的Bug后,我打算系统的对App做优化。第一步就是优化Adapter,那么就从ViewHolder开始。

    优化目标

    不光是要让效率变高,代码也要好看,而且要增加可重用性,为以后的开发打好基础。下面是我的目标:

    • 让代码变得更加效率
    • 让代码好看一些
    • 为以后的App开发速度做下良好的知识储备

    知识

    很幸运我直接就搜到了hyman老师的视频。

    创建一个ViewHolder

    在ViewHolder里,用SparseArray来存储一个View,我们首先来定义变量

    private SparseArray<View> mViews;
    private int mPosition;
    private View mConvertView;
    

    然后我们在定义一个静态方法get()来返回这个ViewHolder

    public static ViewHolder get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
        if(convertView == null) {
            return new ViewHolder(context,parent,layoutId,position);
        } else {
            ViewHolder holder = (ViewHolder)convertView.getTag();
            holder.mPosition = position;
            return holder;
        }
    }
    

    在这个方法里在 converView == null 的时候我们才创建一个ViewHolder。构造函数为

    public ViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
        this.mPosition = position;
        this.mViews = new SparseArray<View>();
        mConvertView = LayoutInflater.from(context).inflate(layoutId,parent,false);
        mConvertView.setTag(this);
    }
    

    使用 convertView.getTag()convertView.setTag() 来关联ViewHolder之后,在来改变ViewHolder都是可以的,前提是先要关联。

    然后我们需要一个方法来加入和读取控件。这个方法用一个泛型来实现的

    /***
     * 通过viewId返回View
     * @param viewId
     * @param <T>
     * @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;
    }
    

    在这个方法里通过空间id来获得控件,如果没有就从convertView里面找出来。还有一个方法是返回这个convertView

    public View getConvertView() {
        return mConvertView;
    }
    

    初步使用这个ViewHolder

    我创建了一个Adapter类为 RiftExAdapter 扩展至 BaseAdapter,在没有使用ViewHolder以前代码大概是这个样子的

       public View getView(int position, View convertView, ViewGroup parent) {
            RiftInfo bean = mDatas.get(position);
            if(null == convertView) {
                convertView = ((LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
                        .inflate(R.layout.item_rift, null);
            }
            TextView rankRift = (TextView)convertView.findViewById(R.id.rankRift);
            TextView battletagRift = (TextView) convertView.findViewById(R.id.battletagRift);
            TextView riftLevelRift = (TextView) convertView.findViewById(R.id.riftLevelRift);
            TextView riftTimeRift = (TextView) convertView.findViewById(R.id.riftTimeRift);
            rankRift.setText(bean.getRank());
            battletagRift.setText(bean.getBattleTag());
            riftLevelRift.setText(bean.getRiftLevel());
            riftTimeRift.setText(bean.getRiftTime());
            return null;
        }
    

    首先加入ViewHolder后代码初步修改为这个样子

    public View getView(int position, View convertView, ViewGroup parent) {
        RiftInfo bean = mDatas.get(position);
        ViewHolder holder = ViewHolder.get(mContext, convertView, parent, R.layout.item_rift, position);
        ((TextView)holder.getView(R.id.rankRift)).setText(bean.getRank());
        ((TextView)holder.getView(R.id.battletagRift)).setText(bean.getBattleTag());
        ((TextView)holder.getView(R.id.riftLevelRift)).setText(bean.getRiftLevel());
        ((TextView)holder.getView(R.id.riftTimeRift)).setText(bean.getRiftTime());       
        return holder.getConvertView();
    }
    

    运行一下看看结果

    (如图1.1)

    初步使用小结

    以后使用ViewHolder只需要3步

    1. 使用get()方法得到holder
    2. 使用getView()来得到空间
    3. 返回一个holder.getConvertView()

    可以节省大量的代码,尤其是ListView多了之后。

    稍微复杂一点的情况

    实际的App界面中还涉及到一个图片。当然如果是资源id的图片那跟直接使用setText是没有区别的。如果使用的是网络图片的话,我使用了
    com.android.volley.toolbox.ImageLoader;

    那么首先要申明一个变量

    ImageLoader mImageLoader;

    然后在getView() 中是这样使用的

    if (position > 0) {
            mImageLoader = MySingleton.getInstance(mContext).getImageLoader();
            mImageLoader.get(bean.getSrc(), ImageLoader.getImageListener((ImageView)holder.getView(R.id.battletagImageRift),
                    R.drawable.def_image, R.drawable.err_image));
        }
    

    在这里我使用了Volley的ImageLoader给一个ImageView异步读取了一个网络图片。

    MySingleton是官方提供的一个管理Volley队列的类。

    小结

    封装后的ViewHolder并不影响正常的使用。

  • 相关阅读:
    应用JConsole学习Java GC
    删除MySQL重复数据
    Linux后台运行程序
    Jvm基础(2)-Java内存模型
    一个word合并项目的分布式架构设计
    Jvm基础(1)-Java运行时数据区
    【JPA】01 快速上手
    【Ubuntu】下载安装 20.04.版本 桌面端
    【Ubuntu】下载安装 12.04.5版本 桌面端
    【CentOS】tar包安装Tomcat
  • 原文地址:https://www.cnblogs.com/canglin/p/4575246.html
Copyright © 2011-2022 走看看