• ListView 编程: 如何优化自定义 Adapter


    本文转自http://blog.csdn.net/androidbluetooth/article/details/6960936

    使用自定义的 Adapter,需要优化,说到底是优化我们自定义的适配器类!

    再说到底就是优化回调方法 getView 方法。

     

     ListView 编程: Adapter 何方神圣? 博客中,只是简单的介绍了如何去自定义一个适配器以及注意事项。

     

    但是.......

    如果像 ListView 编程: Adapter 何方神圣? 中的示例代码那样去写程序的话,那么估计要被老大BS的,呵呵!

     

    那么,结合 Google IO 的建议、APIDemo 代码以及 个人见解,作进一步的优化工作。

     

    说明:该博客使用的布局文件与 ListView 编程: Adapter 何方神圣? 附录中一致。

     

    整理代码,为优化做准备

     

    Activity 代码

    package mark.zhang;
    
    import java.util.ArrayList;
    
    import android.app.ListActivity;
    import android.os.Bundle;
    
    public class FileActivity extends ListActivity {
        
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            // 模擬添加數據
            ArrayList<String> arrays = new ArrayList<String>();
            for(int i=0; i<30; i++) {
                arrays.add("" + i);
            }
            // 实例化自定义 Adapter
            FileViewAdapter adapter = new FileViewAdapter(this, arrays);
            // 设置适配器
            setListAdapter(adapter);
        }
    }

    FileViewAdapter 代码

    package mark.zhang;
    
    import java.util.ArrayList;
    
    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;
    
    public class FileViewAdapter extends BaseAdapter {
        private LayoutInflater inflater = null;
        private ArrayList<String> arrays = null;
        
        
        public FileViewAdapter(Context context, ArrayList<String> arrays) {
            this.arrays = arrays;
            inflater = LayoutInflater.from(context);
        }
    
        @Override
        public int getCount() {
            Log.d("mark", "getCount() is invoked!");
            // 返回需要顯示的 item 數目
            // 這次是外界提供的數據,與上次代碼有差異
            return arrays.size();
        }
    
        @Override
        public Object getItem(int position) {
            Log.d("mark", "getItem() is invoked!");
            return position;
        }
    
        @Override
        public long getItemId(int position) {
            Log.d("mark", "getItemId() is invoked!");
            Log.d("mark", "position = " + position);
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Log.d("mark", "getView() is invoked!");
            
            View v = inflater.inflate(R.layout.custom_fileview, null);
            ((ImageView) v.findViewById(R.id.image_pic)).setImageResource(R.drawable.file);
            ((TextView) v.findViewById(R.id.text_content)).setText("fileName");
            
            return v;
        }
    }

    运行效果:

     

    ok,对代码的重新整理算是为下面的优化做准备。

     

    在接下来的优化方案里,主要是针对 FileViewAdapter 的 getView 方法进行优化,其他代码不变。

     

    说优化之前,先搞明白一个问题:getView 方法的三个参数

     

    getView 方法有三个参数,各个参数的含义可以咨询 SDK API 文档。

     

    修改代码(就是修改 Log 输出):

    @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Log.d("mark", "getView() is invoked!" + "position = " + position + ","
                    + "convertView = " + convertView + "," + "parent = " + parent);
    
            View v = inflater.inflate(R.layout.custom_fileview, null);
            ((ImageView) v.findViewById(R.id.image_pic))
                    .setImageResource(R.drawable.file);
            ((TextView) v.findViewById(R.id.text_content)).setText("fileName");
    
            return v;
        }

    运行 APP,打印信息:

    可以看出,getView 方法被调用了 16(0-15) 次,不是 30 次。


    仔细看看模拟器就可以知道当前显示的 item 

    是 16(下面还有一个显示不全,应该是 17 个,从下面的打印信息可以看出) 个.

    不是 30 个。


    那么,我们滑动当前视图中的滚动条,试一试。

    可以看出,getView 方法确实是调用了 30 次(0-29)。
    你还可以发现,(滚动)显示的打印信息中,convertView 不是 null 了。

    我们再次将滚动条滚动到顶部,发现打印信息中 convertView 也不是 null 了。


    换句话说:

    利用 convertView 这个参数,而不去在 getView 方法中重新创建一个临时的变量 View 了,

    那么可以减轻虚拟机(回收)的负担,从而提高效率。


    方案 1_ 优化代码: 使用 convertView 

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("mark", "getView() is invoked!" + "position = " + position + ","
            + "convertView = " + convertView + "," + "parent = " + parent);
        if (convertView == null) {
           convertView = inflater.inflate(R.layout.custom_fileview, null);
        }
    
    
        ((ImageView) convertView.findViewById(R.id.image_pic))
            .setImageResource(R.drawable.file);
        ((TextView) convertView.findViewById(R.id.text_content))
            .setText("fileName");
    
        return convertView;
    }

    可以看出,只有 convertView == null 为真(上面测试已经说明 convertView 何时为 null),

    才去创建 View 对象。

    如果你有兴趣的话,可以再次运行 APP 可以看出从底部再次滚动到顶部,

    反复几次,你会发现几乎很少创建 View 对象,而是重复利用原来已经存在的 View 对象。

    下面还有一种方式来优化代码,不说是最好但至少是 even better  (Google 推荐)!


    方案 2_ 优化代码:hold 一把


    在 android 提供的 APIDemo 中(List14.java)使用了 ViewHolder ,

    所以 ViewHolder 不是 android 自带的 api,也不是什么诡异的东西。

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("mark", "getView() is invoked!" + "position = " + position + ","
            + "convertView = " + convertView + "," + "parent = " + parent);
    
        ViewHolder vHolder = null;
    
        if (convertView == null) {
             convertView = inflater.inflate(R.layout.custom_fileview, null);
             // 創建 ViewHodler 對象
             vHolder = new ViewHolder();
             vHolder.pic = (ImageView) convertView.findViewById(R.id.image_pic);
             vHolder.content = (TextView) convertView
                 .findViewById(R.id.text_content);
             // 設置 Tag
            convertView.setTag(vHolder);
            } else {
                vHolder = (ViewHolder) convertView.getTag();
           }
          vHolder.pic.setImageResource(R.drawable.file);
          vHolder.content.setText("fileName");
          return convertView;
    }

    其中 ViewHolder 是 FileViewAdapter 的 一个静态内部类。

    static class ViewHolder {
        TextView content;
        ImageView pic;
    }

    使用 ViewHolder 的关键好处是缓存了显示数据的视图,加快了 UI 的响应速度。


    到目前为止,仿佛优化工作已经 ok,其实,

    还有一个小问题: ImageView 使用的图片资源需要预处理。


    FileViewAdapter 完整的代码如下:

    public class FileViewAdapter extends BaseAdapter {
        private LayoutInflater inflater = null;
        private ArrayList<String> arrays = null;
        private Bitmap showIcon = null;
    
        public FileViewAdapter(Context context, ArrayList<String> arrays) {
            this.arrays = arrays;
            inflater = LayoutInflater.from(context);
            // 處理圖片資源
            showIcon = BitmapFactory.decodeResource(context.getResources(),
                    R.drawable.file);
        }
    
        @Override
        public int getCount() {
            // Log.d("mark", "getCount() is invoked!");
            // 返回需要顯示的 item 數目
            // 這次是外界提供的數據,與上次代碼有差異
            return arrays.size();
        }
    
        @Override
        public Object getItem(int position) {
            // Log.d("mark", "getItem() is invoked!");
            return position;
        }
    
        @Override
        public long getItemId(int position) {
            // Log.d("mark", "getItemId() is invoked!");
            // Log.d("mark", "position = " + position);
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Log.d("mark", "getView() is invoked!" + "position = " + position + ","
                    + "convertView = " + convertView + "," + "parent = " + parent);
    
            ViewHolder vHolder = null;
    
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.custom_fileview, null);
                // 創建 ViewHodler 對象
                vHolder = new ViewHolder();
                vHolder.pic = (ImageView) convertView.findViewById(R.id.image_pic);
                vHolder.content = (TextView) convertView
                        .findViewById(R.id.text_content);
                // 設置 Tag
                convertView.setTag(vHolder);
            } else {
                vHolder = (ViewHolder) convertView.getTag();
            }
            // 設置位圖
            vHolder.pic.setImageBitmap(showIcon);
            vHolder.content.setText("fileName");
            return convertView;
        }
    
        static class ViewHolder {
            TextView content;
            ImageView pic;
        }
    }

    最后强调一下,要在布局文件里面将 ListView 控件的属性设置为:

        android:layout_width="fill_parent"
        android:layout_height="fill_parent"

    关于 ListView 的工作原理(Recycler)以及如何进一步优化 ListView,可以参考

    http://mzh3344258.blog.51cto.com/1823534/889879


    Google IO 文档下载

    http://download.csdn.net/detail/androidbluetooth/3783925

     

     

     

     

     

     

  • 相关阅读:
    Sphinx安装流程及配合PHP使用经验
    使用HTML5视频事件示例
    Centos6.5下编译安装mysql 5.6
    AES加密
    ab参数详解 – 压力测试
    vim 常用快捷键
    telnet操作memcache
    如何在Webstorm/Phpstorm中设置连接FTP,并快速进行文件比较,上传下载,同步等操作
    array_map 巧替 foreach
    mac brew安装mysql
  • 原文地址:https://www.cnblogs.com/sishuiliuyun/p/2739330.html
Copyright © 2020-2023  润新知