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

 

如果寫的有問題的地方,歡迎指教!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM