Android入门项目(八)Android流式筛选弹框

一、 效果演示

在这里插入图片描述

二、实现思路

流式布局如何实现?
在onMeasure方法里,循环子view。具体思路是判断同一行的子View宽度是否大于父View的宽度(注意父View要减去padding),如果大于就换行,如果不大于就不换行。其中处理最后一个子View,需要重新赋值宽和高。这里具体实现参考demo

单选列表怎么做?
最简单的方法是用RecyclerView,每个选项对应一个item。这里因为是单选可以有两种实现方式,第一种是用CheckBox,第二种是在bean里用一个参数标记,点击之后重新渲染设置选中效果

数据bean怎么设计最好?
数据bean,最简单的设计是typeName + List< child >,其中typeName是模块名字,List< child >是集合。child里可以包含id(child的id)、value(child的文本值)、isSelected(child是否选择)

三、关键代码

适配器FlowPopRecyclerViewAdapter

/**
 * Created by zhangyan 2021/01/29
 */
public class FlowPopRecyclerViewAdapter extends RecyclerView.Adapter<FlowPopRecyclerViewAdapter.FlowPopViewHolder> {

    private Context mContext;
    private List<FiltrateBean> mData;

    public FlowPopRecyclerViewAdapter(Context context) {
        mData = new ArrayList<>();
        this.mContext = context;
    }

    @NonNull
    @Override
    public FlowPopViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        FlowPopViewHolder viewHolder = new FlowPopViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_listview_property, viewGroup, false));
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull FlowPopRecyclerViewAdapter.FlowPopViewHolder viewHolder, int i) {
        FlowPopViewHolder vh = (FlowPopViewHolder) viewHolder;
        FiltrateBean item = mData.get(i);
        vh.tvTypeName.setText(item.getTypeName());
        setFlowLayoutData(item.getChildren(), vh.layoutProperty);

    }

    @Override
    public int getItemCount() {
        return mData == null ? 0 : mData.size();
    }

    public static class FlowPopViewHolder extends RecyclerView.ViewHolder {
        private TextView tvTypeName;
        private SkuFlowLayout layoutProperty;

        public FlowPopViewHolder(@NonNull View itemView) {
            super(itemView);
            tvTypeName = itemView.findViewById(R.id.tv_type_name);
            layoutProperty = itemView.findViewById(R.id.layout_property);
        }
    }

    private void setFlowLayoutData(final List<FiltrateBean.Children> childrenList, final SkuFlowLayout flowLayout) {

        flowLayout.removeAllViews();
        for (int x = 0; x < childrenList.size(); x++) {
            CheckBox checkBox = (CheckBox) View.inflate(mContext, R.layout.item_flowlayout_bill, null);
            checkBox.setText(childrenList.get(x).getValue());

            if (childrenList.get(x).isSelected()) {
                checkBox.setChecked(true);
                childrenList.get(x).setSelected(true);
            } else {
                checkBox.setChecked(false);
                childrenList.get(x).setSelected(false);
            }

            final int finalX = x;
            checkBox.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    refreshCheckBox(flowLayout, finalX, childrenList);
                }
            });
            flowLayout.addView(checkBox);
        }
    }

    private void refreshCheckBox(SkuFlowLayout flowLayout, int finalX, List<FiltrateBean.Children> propBeenList) {
        for (int y = 0; y < flowLayout.getChildCount(); y++) {
            CheckBox radio = (CheckBox) flowLayout.getChildAt(y);
            radio.setChecked(false);
            propBeenList.get(y).setSelected(false);
            if (finalX == y) {
                radio.setChecked(true);
                propBeenList.get(y).setSelected(true);
            }
        }
    }

    /**
     * 设置数据
     * @param data
     */
    public void setData(List<FiltrateBean> data){
        mData.clear();
        mData.addAll(data);
        notifyDataSetChanged();
    }

    /**
     * 清空数据
     */
    public void clearData(){
        mData.clear();
        notifyDataSetChanged();
    }
}

流式布局的测量

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

        int width = 0;
        int height = 0;

        int lineWidth = 0;
        int lineHeight = 0;

        int cCount = getChildCount();

        for (int i = 0; i < cCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                if (i == cCount - 1) {
                    width = Math.max(lineWidth, width);
                    height += lineHeight;
                }
                continue;
            }
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            MarginLayoutParams lp = (MarginLayoutParams) child
                    .getLayoutParams();
            lp.leftMargin = 10;
            lp.rightMargin = 10;
            lp.topMargin = 10;
            lp.bottomMargin = 10;

            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) {
                width = Math.max(width, lineWidth);
                lineWidth = childWidth;
                height += lineHeight;
                lineHeight = childHeight;
            } else {
                lineWidth += childWidth;
                lineHeight = Math.max(lineHeight, childHeight);
            }
            if (i == cCount - 1) {
                width = Math.max(lineWidth, width);
                height += lineHeight;
            }
        }
        setMeasuredDimension(
                modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(),
                modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()//
        );

    }

四、完整代码

https://github.com/YanInfo/demo03

注意:
这个demo可以有很多的优化空间以适应更加复杂的需求。比如新增EditText文本框类型,新增地区联动类型等等,如果这样做就需要在bean里新增一个参数来区分类型,然后在适配器里根据不同的参数来加载不同的布局。另外demo可后续做一些提升,比如记住筛选历史,自定义弹框动画等等。其中记住历史可以用本地数据库去实现,这里点到为止,开发中可以用此为原版本进行定制。

写在最后:原创不易,觉得对自己有帮助的小老弟,欢迎点赞评论~~~

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值