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

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

时间: 2024-11-05 10:51:22

android多种布局的列表实现的相关文章

Android开发之ListView添加多种布局效果演示

在这个案例中展示的新闻列表,使用到ListView控件,然后在适配器中添加多种布局效果,这里通过重写BaseAdapter类中的 getViewType()和getItemViewType()来做判断,指定ListView列表中指定位置的item加载对应的布局,在 getView中返回对应的视图,之前由于不清楚getViewTypeCount()和getItemViewType()方法,使用得比较少,一直以 为需要添加多个适配器,现在看来当时的想法说明自己见识还不够,哈哈. 第一步:创建放置Li

Android ListView 多种布局--进阶二

Android ListView 多种布局–进阶一 中提及了这么一个需求,本博文就这个需求的实现做进一步探讨. 前面是单列,后面是双列的情况,使用ListView实现,一般的解决思路是处理getView和getCount方法,如下实现: 首先实现Adapter,处理getView和getCount方法 public class DoubleAdapter extends BaseAdapter implements OnClickListener{ private List<String> m

Android ListView Adapter的getItemViewType和getViewTypeCount多种布局

 <Android ListView Adapter的getItemViewType和getViewTypeCount多种布局> 在Android的ListView中,如果在一个ListView中要实现多种样式的ListView布局样式,则需要在ListView的适配器Adapter中用到:getItemViewType()和getViewTypeCount().getViewTypeCount()告诉ListView需要加载多少种类型的Item View,getItemViewType(

android 实现listview的adapter多种布局方式

这两天在实现某模块的排行榜功能,看了UI给的效果图和切图,感觉有点郁闷,因为平时使用listview时,子项都是只有一种布局方式,而这次却有两种.于是专门研究了下,发现重写adapter的getItemViewType()和getViewTypeCount()方法就可以实现多种布局方式,把自己的实现贴出来共享下. 步骤: 1. 重写 getViewTypeCount() – 返回你有多少个不同的布局: 2. 重写 getItemViewType(int) – 由position返回view ty

Android:布局

本文归纳Android布局中所用到的知识点 Android五大布局: LinearLayout 线性布局Relativelayout 相对布局 TableLayout表格布局FrameLayout单帧布局AbsoluteLayout绝对布局 而一个页面可以用多种布局 include:共用布局 <include layout="@layout/另外一个xml名称" /> 布局常用工具: 反编译工具 hierarchyviewer Android控件常用属性: http://w

Android五大布局对象--FrameLayout,LinearLayout,AbsoluteLayout,RelativeLayout,TableLayout

出处: http://zwkufo.blog.163.com/blog/static/2588251201011161220635/ 讲一下Android对用五大布局对象,它们分别是FrameLayout(框架布局:不知道是不是这么翻译的),LinearLayout (线性布局),AbsoluteLayout(绝对布局),RelativeLayout(相对布局),TableLayout(表格布局). FrameLayout: FrameLayout是最简单的一个布局对象.它被定制为你屏幕上的一个

android常用布局

一.线性布局LinearLayout 作用:将容器中的组件一个挨一个地排列起来,不仅可以控制各组件横向排列也可以控制各组件纵向排列(通过android:orientation属性控制) 特点:线性布局不会换行当组件一个爱一个地排列到头之后剩余的组件不会显示 属性: android:gravity   设置布局管理器内组件的对齐方式,该属性支持top,bottom,left,right,center_vertical,fill_vertical,center_horizontal,fill_hor

Android水平翻页列表,类似水平GridVIew

Android水平翻页列表,类似于水平方向的GridView,行列自定义,但要翻页切换,考虑加载性能,当Item数据很多时加载和翻页要流畅,翻页时要有动画效果,效果图如下: 实现方式: 1:翻页可用ViewPager来做,每页内多行排列Item,考虑到翻页切换数据和加载的速度不能嵌套GridView,直接使用View来显示. 2:缓存被ViewPager销毁的View,以此来提高当数据量很大时的系统资源消耗问题. 代码实现如下: 1:编写一个PagerAdapter类,用来给ViewPager适

Android Layout布局

摘自原文: http://blog.csdn.net/llping2011/article/details/9992941 一.Android布局 Android中任何可视化的控件都是从android.veiw.View继承而来的,系统提供了两种方法来设置视图: 使用XML文件来配置View的相关属性,然后在程序启动时系统根据配置文件来创建相应的View视图. 在代码中直接使用相应的类来创建视图. 使用XML文件定义视图: 每个Android项目的源码目录下都有个res/layout目录,这个目