美文网首页Android开发经验谈首页投稿(暂停使用,暂停投稿)程序员
关于android中为一些ViewGroup加上适配器的一些看法

关于android中为一些ViewGroup加上适配器的一些看法

作者: a帆仔 | 来源:发表于2016-05-20 13:45 被阅读658次

    先不说别的, 上图:

    S60520-123210.jpg

    项目中,我们可能会遇到以上那种情况 ,

    颜色,品牌,尺寸,等规格,每个规格里面有长短不一,大小不一,数量不一的规格项,如果用GridView,则每一项的个数, 都会固定,实现不了这种错落排版的效果。
    <strong>怎么办?</strong>
    <strong>怎么办?</strong>
    <strong>怎么办?</strong>
    于是在github上逛了一圈, 找到了我想要的:
    https://github.com/blazsolar/FlowLayout
    感谢前人们为我们提供的方便


    到这里,还没有结束, 下面才是今天要讨论的东西。
    这个FlowLayout,一般用法是addView(view),可是,我还要嵌套在一个listview里面,要在getView里面每次

    for(xx : xx) {
     View view = View.inflate(xxxx,xxx,xx);
     Xx xx = view.findViewById(R.id.xx)
     Xx xx = ....;
     xx.setText(xx);
     ...
    }
    

    好可怕 。
    怎么办?
    android已经给我们提供指导方向了, 所以我们在使用ListView,GridView等等多条目控件的时候,会写一个适配器,把数据层给分开,那这个能加一个适配器吗,ok,老思路先分析 :

    1.首先我要给FlowLayout加个setAdapter(ListAdapter adapter);
    2.数据变动我要改动Flowlayout里面的view,看看ListView源码,要给adapter注册一个Observer,类似这样:
    adapter.registerDataSetObserver(new DataSetObserver() {
    
                @Override
                public void onChanged() {
                    changeViews();
                }
    
                @Override
                public void onInvalidated() {
                    changeViews();
                }
            });
    实现这个之后,在adapter的notifyDatasetChange的时候,onChanged方法就会回调
    3.利用adapter的getView来得到赋值好的view,并加到FlowLayout,
    4.能不能考虑下复用view,不能每次removeAllViews()再addView啊。
    

    好了,分析完了,从第一条开始上代码:

        private ListAdapter adapter;
        public void setAdapter(@NonNull ListAdapter adapter) {
            if (adapter == null) throw new NullPointerException("FlowLayout.setAdapter不能传空参数");
            this.adapter = adapter;
            changeViews(); //这个是用来给Flowlayout加view的逻辑,先忽略
            adapter.registerDataSetObserver(new DataSetObserver() {
    
                @Override
                public void onChanged() {
                    changeViews();
                }
    
                @Override
                public void onInvalidated() {
                    changeViews();
                }
            });
        }
    

    代码很简单,
    1.就是在FlowLayout里面保留一个adapter,
    2.然后从adapter里面询问view,
    3.注册观察对象,得以在adapter.notifyDataSetChanged()的时候,能感知到。

    本身很简单,一共就两个方法嘛~
    下面贴changeViews()方法:

    private void changeViews() {
            final int count = adapter.getCount();
            if (count > 0) {
                int childCount = getChildCount();
                if (childCount > count) {
                    removeViews(count, childCount - count);
                }
                for (int i = 0; i < count; i++) {
                    final View view = adapter.getView(i, getChildAt(i), this);
                    if (view.getLayoutParams() == null) {
                        LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                        lp.rightMargin  = childHorizontal;
                        lp.bottomMargin = childVerticle;
                        view.setLayoutParams(lp);
                    }
                    if(getChildAt(i) != view) {
                        addView(view);
                    }
                }
            }else{
                removeAllViews();
            }
        }
    

    这里稍微麻烦点,
    为什么呢?因为考虑到我们不能每一次都让adapter重新创建新view,这样实则是一种性能上的浪费,但是真做到ListView那种级别的复用,也是难。所以,我就简单点做,能复用就复用,不能复用再创建。

    "能复用就复用,不能复用再创建" 怎么体现出来的呢?
    
    final int count = adapter.getCount();  //先拿到要显示的view的数量
            if (count > 0) {    
                //在即将显示的数量大于0的情况下 再lookup一下flowlayout当前子view的数量
                int childCount = getChildCount();   
                if (childCount > count) { 
                    //如果说flowlayout当前的子view数量多过要显示的view数量,那就删除几个,让其数量保持一致
                    removeViews(count, childCount - count);
                }
    //上面就是我所说的‘能复用就复用,不能复用再创建‘
    
    for (int i = 0; i < count; i++) {
    //这个就比较简单了, 向adapter要view。
    //getChildAt(i) 就是adapter内getView中的第二个参数 convertView,
    //如果有就传过去 ,没有就是null,这时按正常来说,我们会重新创建一个view。
                    final View view = adapter.getView(i, getChildAt(i), this);
                    if (view.getLayoutParams() == null) {
    //好了,这时呢得到了一个view,看看有没有 布局参数,
    //没有就给一个,免得FlowLayout自动生成一个的话
    //会match_parent,这样就不合我们的要求了,
                        LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    //这些就是加加右和下margin,让view 不会粘一起,好看一点,
    //可以做成一个自定义属性嘛,从xml里面读,这个就不讨论了,
                        lp.rightMargin  = childHorizontal;
                        lp.bottomMargin = childVerticle;
                        view.setLayoutParams(lp);
                    }
      
                    if(getChildAt(i) != view) {
    //这里,为啥要判断下呢?
    //这要说到前面的蹩脚的复用了,
    //前面把getChildAt(i)传给了getView,如果FlowLayout本身就有view,
    //那么在getView里面,就只是改变一下text,image等等的数据,这时
    //getChildAt(i) 和adater返回的view肯定还是同一个view,所以不用重复加
    //----
    //但是如果不一样,那就是说,getChildAt(i)就是null , 跟本就没有,
                        addView(view);
                    }
                }
    

    ok,FlowLayout的改造已经完工了,
    回到第一张图上去,看看怎么用的吧

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:padding="@dimen/dp10"
        android:background="#fff"
        android:orientation="vertical">
    
        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="42dp"
            android:gravity="center_vertical"
            android:textColor="@color/normalText"
            android:textSize="@dimen/normal"
            />
        <demo.FlowLayout
            android:id="@+id/flowLayout"
            android:layout_marginTop="@dimen/dp10"
            app:childHorizontal="5dp"
            app:childVerticle="5dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
    
    public class SelectGuigeAdapter extends LBaseAdapter<GuigeBean, SelectGuigeAdapter.VH> {
    
        public SelectGuigeAdapter(Context context) {
            super(context);
        }
    
        @Override
        public VH createViewHolder(ViewGroup parent, int position) {
            return new VH(View.inflate(getContext(),R.layout.select_guige_list_item,null));
        }
    
    
        @Override
        public void bindViewHolder(VH holder, GuigeBean data, int position) {
            holder.txtTitle.setText(data.name);  //这里显示的是颜色,品牌等
            holder.mAdapter.setDataSource(data.items, true); //这里是具体的规格项
        }
    
        static class VH extends LBaseAdapter.ViewHolder {
    
            private TextView txtTitle;
            private FlowLayout flowLayout;
            private SelectGuigeItemAdapter mAdapter;
    
            public VH(View convertView) {
                super(convertView);
                txtTitle = get(R.id.title);
                flowLayout = get(R.id.flowLayout);
                if (mAdapter == null) {
                    mAdapter = new SelectGuigeItemAdapter(getContext());
                    flowLayout.setAdapter(mAdapter);
                }
            }
        }
    
    }
    
    

    再看FlowLayout的适配器

    public class SelectGuigeItemAdapter extends LBaseAdapter<String, LBaseAdapter.ViewHolder> {
    
        public SparseBooleanArray selectMap = new SparseBooleanArray();
    
        public SelectGuigeItemAdapter(Context context) {
            super(context);
        }
    
        @Override
        public ViewHolder createViewHolder(ViewGroup parent, int position) {
            return new ViewHolder(View.inflate(getContext(), R.layout.checkedtextview_item,null));
        }
    
        @Override
        public void bindViewHolder(ViewHolder holder, String data, final int position) {
            CheckedTextView txt = holder.get(R.id.text);
            txt.setText(data);
            txt.setChecked(selectMap.get(position,false));
            txt.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    selectMap.clear();
                    selectMap.put(position, true);
                    notifyDataSetChanged();
                }
            });
        }
    }
    

    ok,连起来了, 这个LBaseAdapter是什么鬼, 大家可以关注下我的上一遍文章
    <a href="http://www.jianshu.com/p/7b7dbbebfb4f">关于android中ListView的Adapter如何设计能通用的一些看法</a>
    嗯,先到这,写的不好,望指正

    相关文章

      网友评论

        本文标题:关于android中为一些ViewGroup加上适配器的一些看法

        本文链接:https://www.haomeiwen.com/subject/brlnrttx.html