Android使用RecyclerView+RadioGroup实现选择添加标签的效果

目录

一丶概述

二丶Layout布局

三丶Activity

四丶最重要的Adapter了

五丶说点其他的


一丶概述

最近遇到一个需求,如下面的图所示。

                 

Android使用RecyclerView+RadioGroup实现选择添加标签的效果_第1张图片

功能可能并不难,就是实现一个添加标签,涉及到流式布局,很多人都知道怎么做,但是作为刚入行不久的我还是稍微有点难度,所以决定写来记录下自己的实现方式,其实连这都是请教了朋友才完成的,他帮我完成了一大部分。这里除了记录还得感谢我的这位朋友。当然,肯定有很多很多更好的方式有待我日后慢慢去学习。小生知识微薄,所以只能使用最笨的方式。还请见谅。不足之处,还望大家多多指点。

二丶Layout布局

因为需求是怕以后会有改动,所以我们要做成动态的列表去展示。根据图来可以看成有n个类型,没个类型里面有很多元素。并且最后一项是其他标签,可以多选,其他只能单选。博主其实刚开始想用三方FlowTagLayout来做的,可是做完过后发现有很多地方并不满足,所以就放弃了,可能是因为博主学艺不精吧。

最后博主决定使用RecyclerView来做,RecyclerView的item使用自定义RedioGroup,然后addview就好了。不废话了代码:



    
    
    
        
    
    

外层布局,其实没有什么,就是一个RecyclerView。

接下里是item的布局:



    
        
        
    
    

item的布局我就使用了一个自定义的FlowRadioGroup,代码在下面。

FlowRadioGroup:里面注视已经写好了。

public class FlowRadioGroup extends RadioGroup {
    public FlowRadioGroup(Context context) {
        super(context);
    }

    public FlowRadioGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 储存所有的view 按行记录
     */
    private List> mAllViews = new ArrayList>();
    /**
     * 记录每一行的高度
     */
    private List mLineHeight = new ArrayList();
    private String TAG = "TAG";

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 置空 view 容器 和 lineHeight 容器  重新赋值
        //因为OnMeasure方法会走两次,第一次是实例化这个对象的时候高度和宽度都是0
        //之后走了OnSizeChange()方法后 又走了一次OnMeasure,所以要把第一次加进去的数据清空。
        mAllViews.clear();
        mLineHeight.clear();
        //得到上级容器为其推荐的宽高和计算模式
        int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int specHeighMode = MeasureSpec.getMode(heightMeasureSpec);
        int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);
        int specHeighSize = MeasureSpec.getSize(heightMeasureSpec);
        // 计算出所有的 child 的 宽和高
//      measureChildren(specWidthSize, specHeighSize);
        // 记录如果是 warp_content 是设置的宽和高
        int width = 0;
        int height = 0;
        // 得到子view的个数
        int cCount = getChildCount();
        /**
         * 记录每一行的宽度,width不断取最大宽度
         */
        int lineWidth = 0;
        /**
         * 每一行的高度,累加至height
         */
        int lineHeight = 0;

        // 存储每一行所有的childView
        List lineViews = new ArrayList();

        for (int i = 0; i < cCount; i++) {
            // 得到每个子View
            View child = getChildAt(i);
            // 测量每个子View的宽高
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            // 当前子view的lp
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            // 子view的宽和高
            int cWidth = 0;
            int cheight = 0;
            // 当前子 view 实际占的宽
            cWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            // 当前子View 实际占的高
            cheight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            lineHeight=cheight;
            // 需要换行
            if(lineWidth + cWidth > specWidthSize){
                width = Math.max(lineWidth, cWidth);// 取最大值
                lineWidth = cWidth; // 开启新行的时候重新累加width
                // 开启新行时累加 height
//              lineHeight = cheight; // 记录下一行的高度
                mAllViews.add(lineViews);
                mLineHeight.add(cheight);
                lineViews = new ArrayList<>();
                // 换行的时候把该 view 放进 集合里
                lineViews.add(child);// 这个  view(child) 是下一行的第一个view
                height += cheight; //每个View高度是一样的,直接累加
                Log.e("需要换行", "hight--" + height);
                Log.e("onMeasure", "AllViews.size()  --  > " + mAllViews.size());
            }else {
                // 不需要换行
                lineWidth += cWidth;//
                Log.e("不需要换行","hight--"+height);
                // 不需要换行时 把子View add 进集合
                lineViews.add(child);
            }

            if(i == cCount-1){
                // 如果是最后一个view
                width = Math.max(lineWidth, cWidth);
                height += cheight;
                Log.e("最后一个view","hight--"+height);
            }
        }
        // 循环结束后 把最后一行内容add进集合中
        mLineHeight.add(lineHeight); // 记录最后一行
        mAllViews.add(lineViews);
        // MeasureSpec.EXACTLY 表示设置了精确的值
        // 如果 mode 是 MeasureSpec.EXACTLY 时候,则不是 warp_content 用计算来的值,否则则用上级布局分给它的值
        setMeasuredDimension(
                specWidthMode == MeasureSpec.EXACTLY ? specWidthSize : width,
                specHeighMode == MeasureSpec.EXACTLY ? specHeighSize : height
        );
        Log.e("onMeasure", "mAllViews.size() -- > " + mAllViews.size() + "   mLineHeight.size() -- > " + mLineHeight.size() + "Height -- > "+height);
    }

    /**
     * 所有childView的位置的布局
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 当前行的最大高度
        int lineHeight = 0;
        // 存储每一行所有的childView
        List lineViews = new ArrayList();
        int left = 0;
        int top = 0;
        // 得到总行数
        int lineNums = mAllViews.size();
        for (int i = 0; i < lineNums; i++)
        {
            // 每一行的所有的views
            lineViews = mAllViews.get(i);
            // 当前行的最大高度
            lineHeight = mLineHeight.get(i);

            Log.e("onLayout" , "第" + i + "行 :" + lineViews.size()+"-------lineHeight"+ lineHeight);

            // 遍历当前行所有的View
            for (int j = 0; j < lineViews.size(); j++)
            {
                View child = lineViews.get(j);
                if (child.getVisibility() == View.GONE)
                {
                    continue;
                }
                MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

                //计算childView的left,top,right,bottom
                int lc = left + lp.leftMargin;
                int tc = top + lp.topMargin;
                int rc =lc + child.getMeasuredWidth();
                int bc = tc + child.getMeasuredHeight();

                child.layout(lc, tc, rc, bc);

                left += child.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
            }
            left = 0;
            top += lineHeight;
        }
        Log.v("onLayout", "onLayout   mAllViews.size() -- > " + mAllViews.size() + "   mLineHeight.size() -- > "+ mLineHeight.size());
    }


    /**
     * 这个一定要设置,否则会包强转错误
     * 设置它支持 marginLayoutParams
     */
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {

        return (LayoutParams) new MarginLayoutParams(getContext(),attrs);
    }
}

三丶Activity

TagActivity 就是我的主Activity,主要是接受adapter回调选中的数据进行操作和获取数据。

public class NewTagActivity extends BaseActivity implements AddlabelView {
    private RecyclerView mTagRecyclerView;
    private AddLabelPresenter presenter;
    private NewTagRecyclerViewAdapter mTagAdapter;


    @Override
    protected int initContentView() {
        return R.layout.new_tag;
    }

    @Override
    protected void setStatus() {

    }

    @Override
    protected String getToolbarTitle() {
        return null;
    }

    @Override
    protected void initUi() {
        mTagRecyclerView = findViewById(R.id.tag_recyclerView);
        findViewById(R.id.save_label).setOnClickListener(this);
        mTagRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mTagRecyclerView.addItemDecoration(new SpaceItemDecoration(10));
        mTagAdapter = new NewTagRecyclerViewAdapter(this);
        mTagRecyclerView.setAdapter(mTagAdapter);

        initData();
    }

    private void initData() {
        presenter = new AddLabelPresenter(this);
       // 这里是adapter定义的回调,会把选中的标签返回到acticity以便进行操作
        presenter.getLabelDataBean(this);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.save_label:
              
                saveLabel();
                break;
        }
    }
 // 点击保存后的操作
    
    private void saveLabel() {
        List allSelectedSubBean = mTagAdapter.getAllSelectedSubBean();
        Log.e("zjb", "saveLabel: "+allSelectedSubBean.size());
        for (ClickLabelDataBean.SubLabelBean subLabelBean : allSelectedSubBean) {
            Log.e("zjb", "saveLabel: "+subLabelBean.getLabel_name());
        }
    }

      // 这里是数据,我用的是mvp模式,比较懒,也就没有去搞专门的测试数据了。这个不重要
    @Override
    public void showLabelDateBean(List dataBeanList) {
         // 因为跟后台商量好了最后一个会返回其它这个类型,这里也set也是为了标记下当是最后一个集合的时候就是多选
        dataBeanList.get(dataBeanList.size() - 1).setLabel_type(1);
        mTagAdapter.setDatas(dataBeanList);
    }

   
}

四丶最重要的Adapter了

最关键的就是adapter了 不墨迹 我直接上代码。 代码里面有注视,尽力看注释吧。

public class NewTagRecyclerViewAdapter extends RecyclerView.Adapter {
    private static final int MAX_MUXSLECTE_LIMIT_SIZE = 5;
    private Context mContext;
    private int [] img = {R.mipmap.excavate_spot,R.mipmap.intention_spot,R.mipmap.other_spot,
            R.mipmap.relationship_spot,R.mipmap.trust_spot};
    private List mLabelBeanList = new ArrayList<>();
    private Map> mAllSelectedSubBean = new HashMap<>();


    public NewTagRecyclerViewAdapter(Context context) {
        this.mContext = context;
    }

    public void addDatas(List datas){
        this.mLabelBeanList.addAll(datas);
        notifyDataSetChanged();
    }

    public void clearDatas(){
        this.mLabelBeanList.clear();
        notifyDataSetChanged();
    }

    public void setDatas(List datas){
        this.mLabelBeanList.clear();
        addDatas(datas);
        Toast.makeText(mContext, datas.size(), Toast.LENGTH_SHORT).show();
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        MyViewHolder myViewHolder = (MyViewHolder) holder;
        ClickLabelDataBean subLabelBean = mLabelBeanList.get(position);

        //ui
        myViewHolder.mTvName.setText(subLabelBean.getLabel_name());
        myViewHolder.mIvName.setImageResource(img[position]);

        //暂存选中数据
        List eachLineSelectDatas = new ArrayList<>();
        String label_id = subLabelBean.getLabel_id();
        mAllSelectedSubBean.put(label_id, eachLineSelectDatas);

        final List subLabel = subLabelBean.getSubLabel();
        if(subLabelBean.getLabel_type() == 0) {// 是单选选项
            handleSubSingleItemClick(myViewHolder.mRadioGroup, eachLineSelectDatas, label_id);
            for (ClickLabelDataBean.SubLabelBean labelBean : subLabel) {
                RadioButton radioButton = new RadioButton(mContext);
                myViewHolder.mRadioGroup.addView(radioButton);
                radioButton.setId(radioButton.hashCode());
                radioButton.setText(labelBean.getLabel_name());
                radioButton.setTag(labelBean);
                if("1".equals(labelBean.getChoose())){
                    myViewHolder.mRadioGroup.check(radioButton.hashCode());
                }
            }
        }else if(subLabelBean.getLabel_type() == 1){ //多选选项
            int size = subLabel.size();
            for (int i = 0; i < size; i++) {
                ClickLabelDataBean.SubLabelBean labelBean = subLabel.get(i);
                if(i == size - 1 ){ //【添加其他标签】
                    TextView textView = new TextView(mContext);
                    textView.setText(labelBean.getLabel_name());
                    textView.setTextSize(20);
                    handlesubAddOtherTagClick(textView);
                    myViewHolder.mRadioGroup.addView(textView);
                }else {
                    CheckBox checkBox = new CheckBox(mContext);
                    handleSubMuxItemClick(checkBox, eachLineSelectDatas, label_id);
                    checkBox.setText(labelBean.getLabel_name());
                    checkBox.setTag(labelBean);
                    if("1".equals(labelBean.getChoose())){
                        checkBox.setChecked(true);
                    }
                    myViewHolder.mRadioGroup.addView(checkBox);
                }

            }
        }

    }

    //处理【添加其他标签】的点击
    private void handlesubAddOtherTagClick(TextView textView) {
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ToastUtil.showToast(textView.getText().toString());
            }
        });
    }

    // 处理多选的点击
    private void handleSubMuxItemClick(CheckBox checkBox, List selectSingleDatas, String label_id) {
        checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                ClickLabelDataBean.SubLabelBean subLabelData = (ClickLabelDataBean.SubLabelBean) checkBox.getTag();
                if(isChecked){
                    if(selectSingleDatas.size() >= MAX_MUXSLECTE_LIMIT_SIZE){
                        ToastUtil.showToast(R.string.max_muxselcted_size);
                        checkBox.setChecked(false);
                        return;
                    }
                    selectSingleDatas.add(subLabelData);
                }else {
                    selectSingleDatas.remove(subLabelData);
                }
                mAllSelectedSubBean.put(label_id, selectSingleDatas);
            }
        });

    }

    //处理单选的点击
    private void handleSubSingleItemClick(FlowRadioGroup mRadioGroup, List selectMuxDatas, String label_id) {
        mRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                RadioButton radioButton = mRadioGroup.findViewById(checkedId);
                ClickLabelDataBean.SubLabelBean subLabelBean = (ClickLabelDataBean.SubLabelBean) radioButton.getTag();
                selectMuxDatas.clear();
                selectMuxDatas.add(subLabelBean);
                mAllSelectedSubBean.put(label_id, selectMuxDatas);
            }
        });
    }

    @Override
    public int getItemCount() {
        return mLabelBeanList.size();
    }


    static class MyViewHolder extends RecyclerView.ViewHolder{
        FlowRadioGroup mRadioGroup;
        ImageView mIvName;
        TextView mTvName;

        MyViewHolder(View itemView) {
            super(itemView);
            mRadioGroup = (FlowRadioGroup) itemView.findViewById(R.id.flowRadioGroup);
            mIvName = (ImageView) itemView.findViewById(R.id.im_name);
            mTvName = (TextView) itemView.findViewById(R.id.t_name);
        }
    }

    //提供给外部获取全部选中的结果
    List getAllSelectedSubBean() {
        List datas = new ArrayList<>();
        Set>> entries = mAllSelectedSubBean.entrySet();
        for (Map.Entry> entry : entries) {
            datas.addAll(entry.getValue());
        }
        return datas;
    }
}

五丶说点其他的

大概基本就是这样了,博主也是第一次写文章,以后会进行优化。希望大家看到的不喜勿喷。我也只是才做了一年多的小人物。只是想记录自己的一点一滴,以后肯定会有更好的实现方式,但是现在肚子里的墨水太少了。写这篇博客也只是为了提醒自己,记录一下而已。如果大家有更好的实现方式,麻烦大家倾囊相授。

祝大家工作顺利,生活愉快。

你可能感兴趣的:(Android使用RecyclerView+RadioGroup实现选择添加标签的效果)