zoukankan      html  css  js  c++  java
  • android多种布局的列表实现

      最近有一个列表效果,需要一个列表有多种布局,最终效果如下:

      

      这个我也问了同事以及开发群里的朋友,居然都没得到最优的实现方式的回答,看来这种复杂列表的需求还是比较少的,我自己也走了一些弯路,把我几个实现的方式整理下,希望对于还不了解的朋友有所帮助。

    实现方式1:(每次getView时重新inflate itemView,convertView没有复用,性能低,运行没问题

    private class MyAdapter extends BaseAdapter{
    
            private List<Object> datas = Collections.EMPTY_LIST;
    
            public void setDatas(List<Object> datas) {
                if(datas == null){
                    datas = Collections.EMPTY_LIST;
                }
                this.datas = datas;
                notifyDataSetChanged();
            }
    
            @Override
            public int getCount() {
                return datas.size();
            }
    
            @Override
            public Object getItem(int position) {
                return datas.get(position);
            }
    
            @Override
            public long getItemId(int position) {
                return 0;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                Object data = getItem(position);
    
                if(data instanceof Folder){
                    FolderViewHolder holder = null;
                    if(convertView != null && convertView.getTag() instanceof FolderViewHolder){
                        //View与数据类型一致
                        holder = (FolderViewHolder) convertView.getTag();
                    }else{
                        convertView = mInflater.inflate(R.layout.listitem1, null);
                        holder = new FolderViewHolder(convertView);
                        convertView.setTag(holder);
                    }
                    holder.setData((Folder)data);
                }else{
                    FileViewHolder holder = null;
                    if(convertView != null && convertView.getTag() instanceof FileViewHolder){
                        //View与数据类型一致
                        holder = (FileViewHolder) convertView.getTag();
                    }else{
                        convertView = mInflater.inflate(R.layout.listitem2, null);
                        holder = new FileViewHolder(convertView);
                        convertView.setTag(holder);
                    }
                    holder.setData((File)data);
                }
    
                return convertView;
            }
        }
    
        private class FolderViewHolder{
            public TextView tvName;
    
            public FolderViewHolder(View itemView){
                tvName = (TextView) itemView.findViewById(R.id.tvName);
            }
    
            public void setData(Folder data) {
                tvName.setText(data.name);
            }
        }
    
        private class FileViewHolder{
            public TextView tvName;
    
            public FileViewHolder(View itemView){
                tvName = (TextView) itemView.findViewById(R.id.tvName);
            }
    
            public void setData(File data) {
                tvName.setText(data.name);
            }
        }

    实现方式2:(因为方式1不断inflate view,影响性能,于是考虑是否能尽可能重用已经inflate的view,于是添加了一个缓存,不过实际测试快速滑动或切换数据会显示异常,应该是AbsListView#RecycleBin缓存的原因,具体原因我后面理清了再添加,看别人的代码最痛苦了。。。)

    private class MyAdapter extends BaseAdapter{
    
            private List<View> folderViewCaches = new ArrayList<View>(5);
            private List<View> fileViewCaches = new ArrayList<View>(5);
    
            private List<Object> datas = Collections.EMPTY_LIST;
    
            public void setDatas(List<Object> datas) {
                if(datas == null){
                    datas = Collections.EMPTY_LIST;
                }
                this.datas = datas;
                notifyDataSetChanged();
            }
    
            @Override
            public int getCount() {
                return datas.size();
            }
    
            @Override
            public Object getItem(int position) {
                return datas.get(position);
            }
    
            @Override
            public long getItemId(int position) {
                return 0;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                Object data = getItem(position);
    
                if(data instanceof Folder){
                    //文件夹,应该返回R.layout.listitem1对应的View
                    FolderViewHolder holder = null;
                    if(convertView != null && convertView.getTag() instanceof FolderViewHolder){
                        //View与数据类型一致
                        holder = (FolderViewHolder) convertView.getTag();
                    }else{
                        if(convertView != null){
                            //缓存到文件列表
                            fileViewCaches.add(convertView);
                            convertView = null;
                        }
    
                        //从缓存里面取已从ListView移除的缓存(注释掉此部分代码显示正常)
                        if(!folderViewCaches.isEmpty()){
                            for(View cache : folderViewCaches){
                                if(cache.getParent() == null){
                                    //缓存的View已从listView里面移除
                                    convertView = cache;
                                    holder = (FolderViewHolder) convertView.getTag();
                                    folderViewCaches.remove(cache);
                                    break;
                                }
                            }
                        }
    
                        //还是没有,重新inflate
                        if(convertView == null){
                            convertView = mInflater.inflate(R.layout.listitem1, null);
                            holder = new FolderViewHolder(convertView);
                            convertView.setTag(holder);
                        }
                    }
    
                    holder.setData((Folder) data);
    
                }else{
                    //文件,应该返回R.layout.listitem2对应的View
                    FileViewHolder holder = null;
                    if(convertView != null && convertView.getTag() instanceof FileViewHolder){
                        //View与数据类型一致
                        holder = (FileViewHolder) convertView.getTag();
                    }else{
                        if(convertView != null){
                            //缓存到文件夹列表
                            folderViewCaches.add(convertView);
                            convertView = null;
                        }
    
                        //从缓存里面取已从ListView移除的缓存(注释掉此部分代码显示正常)
                        if(!fileViewCaches.isEmpty()){
                            for(View cache : fileViewCaches){
                                if(cache.getParent() == null){
                                    //缓存的View已从listView里面移除
                                    convertView = cache;
                                    holder = (FileViewHolder) convertView.getTag();
                                    fileViewCaches.remove(cache);
                                    break;
                                }
                            }
                        }
    
                        //还是没有,重新inflate
                        if(convertView == null){
                            convertView = mInflater.inflate(R.layout.listitem2, null);
                            holder = new FileViewHolder(convertView);
                            convertView.setTag(holder);
                        }
                    }
    
                    holder.setData((File) data);
                }
    
                return convertView;
            }
        }

    实现方式3:(最佳实现,运行正常

    后面仔细阅读ListView相关源码,才发现Adapter本身就支持不同的布局了,而且AbsListView#RecycleBin也支持不同类型的布局的缓存策略,RecycleBin.mViewTypeCount标示有多少种View类型。

    我们需要做的就是重写Adapter的下面3个方法:

    1.getViewTypeCount:

         /**
             * 有多少种不同布局的View
             */
            @Override
            public int getViewTypeCount() {
                return 2;
            }

    2.getItemViewType

            /**
             * 相应position对应的View类型
             */
            @Override
            public int getItemViewType(int position) {
                if(getItem(position) instanceof Folder){
                    return TYPE_FOLDER;
                }else{
                    return TYPE_FILE;
                }
            }    

    3.getView,通过判断对应position的类型,返回相应类型的view:

         @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                Object data = getItem(position);
    
                if(data instanceof Folder){
                    //TYPE_FOLDER,文件夹,应该返回R.layout.listitem1对应的View
                    FolderViewHolder holder = null;
                    if(convertView != null){
                        holder = (FolderViewHolder) convertView.getTag();
                    }else{
                        convertView = mInflater.inflate(R.layout.listitem1, null);
                        holder = new FolderViewHolder(convertView);
                        convertView.setTag(holder);
                    }
    
                    holder.setData((Folder) data);
    
                }else{
                    //TYPE_FILE,文件,应该返回R.layout.listitem2对应的View
                    FileViewHolder holder = null;
                    if(convertView != null){
                        holder = (FileViewHolder) convertView.getTag();
                    }else{
                        convertView = mInflater.inflate(R.layout.listitem2, null);
                        holder = new FileViewHolder(convertView);
                        convertView.setTag(holder);
                    }
    
                    holder.setData((File) data);
                }
    
                return convertView;
            }

    此demo的github源码地址:

    https://github.com/John-Chen/BlogSamples/tree/master/MultipleListTest

    apk下载地址:

    https://github.com/John-Chen/BlogSamples/blob/master/MultipleListTest/MultipleListTest.apk

    如果写的有问题的地方,欢迎指教!

  • 相关阅读:
    web前端要学哪些?
    angularjs factory,service,provider 自定义服务的不同
    PSP软件开发过程
    Eclipse连接sqlserver体验过程
    在线制作GIF图片项目愿景与范围
    C# Hashtable vs Dictionary 学习笔记
    Unity 工作经历+近期面试经历
    配置文件加载的一个坑
    rabbitmq exchange type
    centos7下nginx添加到自定义系统服务中提示Access denied
  • 原文地址:https://www.cnblogs.com/John-Chen/p/4307394.html
Copyright © 2011-2022 走看看