Android PopupWindow+RecyclerView 实现二级联动筛选

前言

这篇文章主要的功能是利用 PopupWindow 和  RecyclerView 实现条件筛选包括二级联动筛选,主要是仿小红书里的筛选功能而写的一个 Demo 效果如下,代码通俗易懂,保姆级教程

Android PopupWindow+RecyclerView 实现二级联动筛选_第1张图片


一、使用步骤

1.引入库

api 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.46'
api 'com.google.code.gson:gson:2.8.6'
api 'com.alibaba:fastjson:1.2.61'

2. 模拟API数据

这里我模拟实际接口返回的数据而准备的数据源,在工程目录下新建 assets 资源文件,在新建一个JsonData.json 文件,复制数据源到此文件里

{
    "code": "200",
    "data": {
        "result": [
            {
                "id": 1,
                "list": [
                    {
                        "name": "全部分类"
                    }
                ],
                "name": "全部分类"
            },
            {
                "id": 2,
                "list": [
                    {
                        "name": "天安门"
                    },
                    {
                        "name": "故宫"
                    },
                    {
                        "name": "颐和园"
                    },
                    {
                        "name": "圆明园"
                    },
                    {
                        "name": "北海公园"
                    },
                    {
                        "name": "雍和宫"
                    }
                ],
                "name": "景点"
            },
            {
                "id": 2,
                "list": [
                    {
                        "name": "海鲜"
                    },
                    {
                        "name": "烧烤"
                    },
                    {
                        "name": "火锅"
                    },
                    {
                        "name": "烤肉"
                    },
                    {
                        "name": "西餐"
                    },
                    {
                        "name": "日料"
                    }
                ],
                "name": "餐饮"
            },
            {
                "id": 2,
                "list": [
                    {
                        "name": "西单"
                    },
                    {
                        "name": "SKP"
                    },
                    {
                        "name": "荟聚"
                    },
                    {
                        "name": "凯德茂"
                    },
                    {
                        "name": "奥特莱斯"
                    },
                    {
                        "name": "老佛爷"
                    }
                ],
                "name": "购物地"
            },
            {
                "id": 2,
                "list": [
                    {
                        "name": "电影"
                    },
                    {
                        "name": "SPA"
                    },
                    {
                        "name": "密室逃脱"
                    },
                    {
                        "name": "酒吧"
                    },
                    {
                        "name": "KTV"
                    },
                    {
                        "name": "足疗"
                    },
                    {
                        "name": "汗蒸/洗浴"
                    }
                ],
                "name": "休闲娱乐"
            },
            {
                "id": 2,
                "list": [
                    {
                        "name": "希岸"
                    },
                    {
                        "name": "云朵"
                    },
                    {
                        "name": "橘子"
                    },
                    {
                        "name": "兰朵"
                    },
                    {
                        "name": "汉庭"
                    },
                    {
                        "name": "如家"
                    },
                    {
                        "name": "维也纳"
                    }
                ],
                "name": "酒店"
            }
        ]
    },
    "message": "success"
}

3. 布局文件

主页面  activity_main.xml




    

        

            

            
        

        

            

            
        


        

            

            
        


    

一级 AcolumnAdapter 适配器 item 文件

a_column_layout.xml




    



二级 BcolumnAdapter 适配器 item 文件

b_column_layout.xml




    

    



单选 GeneAdapter 适配器 item 文件

item_listview_popwin.xml




    

        

        
    

这里是 popupWindow 实现二级联动 布局文件,

popwin_supplier_list.xml





    

        

            

                

                    

                    


                

            
        
    

4. 自定义 MyScrollView

在 popupWindow 布局里自定义了 MyScrollView,重写 onMeasure() 方法可以实现自定义测量规则。其中,使用 getMeasuredHeight() 方法和 getMeasuredWidth() 方法可以获取 ScrollView 的测量高度和测量宽度。通过 setMaxHeight 来设置 ScrollView 的最大高度

public class MyScrollView extends ScrollView {
    public static final String TAG = "MyScrollView";
    private int maxHeight = -1;

    public MyScrollView(Context context) {
        super(context);
    }

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

    public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int height = getMeasuredHeight();
        int width = getMeasuredWidth();
        if (maxHeight > 0 && height > maxHeight) {
            setMeasuredDimension(width, maxHeight);
        }
    }

    public void setMaxHeight(int height) {
        this.maxHeight = height;
    }
}

5.读取本地资源文件工具类

public class ToolsUtils {

    /**
     * 读取本地资源文件
     *
     * @param context  上下文
     * @param fileName 本地数据文件名
     * @return
     */
    public static String getFromAssets(Context context, String fileName) {
        InputStreamReader inputReader = null;
        BufferedReader bufReader = null;
        try {
            inputReader = new InputStreamReader(context.getResources().getAssets().open(fileName));
            bufReader = new BufferedReader(inputReader);
            String line = "";
            StringBuilder result = new StringBuilder();
            while ((line = bufReader.readLine()) != null)
                result.append(line);
            return result.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputReader != null) {
                    inputReader.close();
                }
                if (bufReader != null) {
                    bufReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "";
    }
}

6.数据实体类 JsonBean

public class JsonBean {


    private String code;
    private String message;
    private DataBean data;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public DataBean getData() {
        return data;
    }

    public void setData(DataBean data) {
        this.data = data;
    }

    public static class DataBean {
        private List result;

        public List getResult() {
            return result;
        }

        public void setResult(List result) {
            this.result = result;
        }

        public static class ResultBean {
            private int id;
            private List list;
            private String name;

            public int getId() {
                return id;
            }

            public void setId(int id) {
                this.id = id;
            }

            public List getList() {
                return list;
            }

            public void setList(List list) {
                this.list = list;
            }

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public static class ListBean {
                private String name;

                public String getName() {
                    return name;
                }

                public void setName(String name) {
                    this.name = name;
                }
            }
        }
    }
}

7.一级适配器 AcolumnAdapter

public class AcolumnAdapter extends BaseQuickAdapter {
    private int position;


    public AcolumnAdapter(int layoutResId, @Nullable List resultList) {
        super(layoutResId, resultList);
    }

    @Override
    protected void convert(BaseViewHolder helper, JsonBean.DataBean.ResultBean item) {
        helper.setTextColor(R.id.tv_class_name, helper.getLayoutPosition() == position ? Color.parseColor("#00d8a0") : Color.parseColor("#363636"));
        helper.setText(R.id.tv_class_name, item.getName());
    }

    public void setSelection(int pos) {
        this.position = pos;
        notifyDataSetChanged();
    }

}

8.二级适配器 BcolumnAdapter

public class BcolumnAdapter extends BaseQuickAdapter {

    private BcolumnAdapter.OnCallBackData onCallBackData;

    public BcolumnAdapter(int layoutResId, @Nullable List resultList) {
        super(layoutResId, resultList);
    }

    @Override
    protected void convert(BaseViewHolder helper, JsonBean.DataBean.ResultBean.ListBean item) {
        helper.addOnClickListener(R.id.item_layout);
        helper.setText(R.id.tv_name, item.getName() + "");
        onCallBackData.convertView(helper, item);
    }

    public void setOnCallBackData(BcolumnAdapter.OnCallBackData onCallBackData) {
        this.onCallBackData = onCallBackData;
    }


    public interface OnCallBackData {
        void convertView(BaseViewHolder holder, T item);
    }
}

9.单选适配器 GeneAdapter

public class GeneAdapter extends BaseQuickAdapter {

    public GeneAdapter(@LayoutRes int layoutResId, @Nullable List data) {
        super(layoutResId, data);
    }

    public GeneAdapter(@Nullable List data) {
        super(data);
    }

    public GeneAdapter(@LayoutRes int layoutResId) {
        super(layoutResId);
    }


    private OnCallBackData onCallBackData;

    @Override
    protected void convert(BaseViewHolder holder, T item) {
        if (onCallBackData != null) {
            onCallBackData.convertView(holder, item);
        }
    }

    public void setOnCallBackData(OnCallBackData onCallBackData) {
        this.onCallBackData = onCallBackData;
    }

    public interface OnCallBackData {
        void convertView(BaseViewHolder holder, T item);
    }

}

这里在 adapter 中 定义了一个 OnCallBackData 接口,它是一个泛型接口,表示在回调中传递数据的类型是 T。该接口包含一个 convertView() 方法,用于将数据绑定到 ViewHolder 中。同二级适配器 BcolumnAdapter 中一样  类型可以自定,其中,BaseViewHolder 是一个通用的 ViewHolder 类,用于存储 ItemView 中的 View 控件,从而避免在每次滚动列表时都重新查找控件。OnCallBackData 接口将数据绑定到 ViewHolder 中,可以通过 ViewHolder 中的控件来更新 ItemView 的内容。该接口的作用是将数据和视图分离,避免在 Activity 或 Fragment 中写过多的 UI 逻辑,同时也方便数据的管理和更新。通过实现该接口,可以将数据的获取和数据的展示分为两个部分,提高代码的可读性和可维护性。在 MainActivity 实现该接口,用于展示 选中效果

10.最后 MainActivity 代码实现

 主要是读取本地数据添加到数组,以及 popupWindow 的创建 跟点击交互

public class MainActivity extends AppCompatActivity {

    private LinearLayout ll_list_default;
    private TextView ll_list_default_txt;
    private ImageView ll_list_default_icon;

    private LinearLayout ll_list_brand;
    private TextView list_brand_txt;
    private ImageView ll_list_brand_icon;

    private LinearLayout list_list_type;
    private TextView list_list_type_txt;
    private ImageView list_list_type_icon;

    private RecyclerView recyclerView;
    private GeneAdapter popAdapter, popAdapter2;
    private List popList, popList1, popList2;
    private PopupWindow popupWindow;
    private String currentBrand, currentType;
    private MyScrollView ui_sl_container;
    private RecyclerView assRecyclerView;
    private String jsonData;
    private AcolumnAdapter acolumnAdapter;
    private BcolumnAdapter bcolumnAdapter;
    private List resultList;
    private List result1List;

    private int flag = 0;

    private boolean clickFlag = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InitView();


        String fromAssets = ToolsUtils.getFromAssets(this, "JsonData.json");
        JsonBean jsonBean = new Gson().fromJson(fromAssets, JsonBean.class);
        resultList = jsonBean.getData().getResult();

        String fromAssets1 = ToolsUtils.getFromAssets(this, "JsonData.json");
        JsonBean jsonBean1 = new Gson().fromJson(fromAssets1, JsonBean.class);
        result1List = jsonBean1.getData().getResult().get(0).getList();


        ll_list_default = findViewById(R.id.ll_list_default);
        ll_list_default_txt = findViewById(R.id.ll_list_default_txt);
        ll_list_default_icon = findViewById(R.id.ll_list_default_icon);

        ll_list_brand = findViewById(R.id.ll_list_brand);
        list_brand_txt = findViewById(R.id.list_brand_txt);
        ll_list_brand_icon = findViewById(R.id.ll_list_brand_icon);

        list_list_type = findViewById(R.id.list_list_type);
        list_list_type_txt = findViewById(R.id.list_list_type_txt);
        list_list_type_icon = findViewById(R.id.list_list_type_icon);


        View contentView = getLayoutInflater().inflate(R.layout.popwin_supplier_list, null);
        ui_sl_container = contentView.findViewById(R.id.pop_common_sl_container);
        ui_sl_container.setMaxHeight(860);
        //二级列表
        assRecyclerView = contentView.findViewById(R.id.supplier_list_lv);
        //一级列表
        recyclerView = contentView.findViewById(R.id.popwin_supplier_list_lv);

        popupWindow = new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT, false);
        popupWindow.setOutsideTouchable(true);
        popupWindow.setBackgroundDrawable(new BitmapDrawable());
        popupWindow.setFocusable(true);
        popupWindow.setAnimationStyle(R.style.popwin_anim_style);
        //监听popupWindow关闭事件
        popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                ll_list_default_txt.setTextColor(Color.parseColor("#333333"));
                ll_list_default_icon.setImageResource(R.mipmap.screen_icon_normal);
                list_brand_txt.setTextColor(Color.parseColor("#333333"));
                ll_list_brand_icon.setImageResource(R.mipmap.screen_icon_normal);
                list_list_type_txt.setTextColor(Color.parseColor("#333333"));
                list_list_type_icon.setImageResource(R.mipmap.screen_icon_normal);
                clickFlag = true;
            }
        });
        ll_list_default.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (clickFlag) {
                    ll_list_default_txt.setTextColor(Color.parseColor("#00d8a0"));
                    ll_list_default_icon.setImageResource(R.mipmap.screen_icon_selected);
                    clickFlag = false;
                } else {
                    ll_list_default_txt.setTextColor(Color.parseColor("#333333"));
                    ll_list_default_icon.setImageResource(R.mipmap.screen_icon_normal);
                    clickFlag = true;
                }
                assRecyclerView.setVisibility(View.GONE);
                showPopupWindow0(v);
            }
        });

        ll_list_brand.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (clickFlag) {
                    list_brand_txt.setTextColor(Color.parseColor("#00d8a0"));
                    ll_list_brand_icon.setImageResource(R.mipmap.screen_icon_selected);
                    clickFlag = false;
                } else {
                    list_brand_txt.setTextColor(Color.parseColor("#333333"));
                    ll_list_brand_icon.setImageResource(R.mipmap.screen_icon_normal);
                    clickFlag = true;
                }

                assRecyclerView.setVisibility(View.VISIBLE);
                showPopupWindow1(v);
            }
        });

        list_list_type.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (clickFlag) {
                    list_list_type_txt.setTextColor(Color.parseColor("#00d8a0"));
                    list_list_type_icon.setImageResource(R.mipmap.screen_icon_selected);
                    clickFlag = false;
                } else {
                    list_list_type_txt.setTextColor(Color.parseColor("#333333"));
                    list_list_type_icon.setImageResource(R.mipmap.screen_icon_normal);
                    clickFlag = true;
                }

                assRecyclerView.setVisibility(View.GONE);
                showPopupWindow2(v);
            }
        });
    }


    private void showPopupWindow0(View v) {
        popupWindow.showAsDropDown(v);
        popAdapter = new GeneAdapter<>(R.layout.item_listview_popwin, popList);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(popAdapter);

        popAdapter.setOnCallBackData(new GeneAdapter.OnCallBackData() {
            @Override
            public void convertView(BaseViewHolder holder, String item) {
                ((TextView) holder.getView(R.id.listview_popwind_tv)).setText(item);
                String list_default_trim = ll_list_default_txt.getText().toString().trim();
                if (list_default_trim.equals(item)) {
                    ((TextView) holder.getView(R.id.listview_popwind_tv)).setTextColor(Color.parseColor("#00d8a0"));
                    holder.getView(R.id.iv_select_icon).setVisibility(View.VISIBLE);
                } else {
                    ((TextView) holder.getView(R.id.listview_popwind_tv)).setTextColor(Color.parseColor("#333333"));
                    holder.getView(R.id.iv_select_icon).setVisibility(View.GONE);
                }
            }
        });

        popAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
             
                popupWindow.dismiss();
                currentBrand = popList.get(position);
                ll_list_default_txt.setText(currentBrand);
                ll_list_default_txt.setTextColor(Color.parseColor("#333333"));
                ll_list_default_icon.setImageResource(R.mipmap.screen_icon_normal);
                clickFlag = true;
            }
        });

    }

    private void showPopupWindow1(View v) {
        popupWindow.showAsDropDown(v);
        acolumnAdapter = new AcolumnAdapter(R.layout.a_column_layout, resultList);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(acolumnAdapter);
        //设置默认的选取状态
        acolumnAdapter.setSelection(flag);


        bcolumnAdapter = new BcolumnAdapter(R.layout.b_column_layout, result1List);
        assRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        assRecyclerView.setAdapter(bcolumnAdapter);

        result1List.clear();
        result1List.addAll(resultList.get(flag).getList());
        bcolumnAdapter.notifyDataSetChanged();


        bcolumnAdapter.setOnCallBackData(new BcolumnAdapter.OnCallBackData() {
            @Override
            public void convertView(BaseViewHolder holder, JsonBean.DataBean.ResultBean.ListBean item) {
                ((TextView) holder.getView(R.id.tv_name)).setText(item.getName());
                String list_brand_trim = list_brand_txt.getText().toString().trim();
                if (list_brand_trim.equals(item.getName())) {
                    ((TextView) holder.getView(R.id.tv_name)).setTextColor(Color.parseColor("#00d8a0"));
                    holder.getView(R.id.iv_select_icon).setVisibility(View.VISIBLE);
                } else {
                    ((TextView) holder.getView(R.id.tv_name)).setTextColor(Color.parseColor("#333333"));
                    holder.getView(R.id.iv_select_icon).setVisibility(View.GONE);
                }
            }
        });

        //左侧列表的事件处理
        acolumnAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                acolumnAdapter.setSelection(position);
                flag = position;
                result1List.clear();
                result1List.addAll(resultList.get(position).getList());
                bcolumnAdapter.notifyDataSetChanged();
            }
        });
        //右侧列表的事件处理
        bcolumnAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                String name = result1List.get(position).getName();
                list_brand_txt.setText(name);
                acolumnAdapter.setSelection(flag);
                list_brand_txt.setTextColor(Color.parseColor("#333333"));
                ll_list_brand_icon.setImageResource(R.mipmap.screen_icon_normal);
                clickFlag = true;
                result1List.clear();
                popupWindow.dismiss();
            }
        });

    }

    private void showPopupWindow2(View v) {
        popupWindow.showAsDropDown(v);
        popAdapter2 = new GeneAdapter<>(R.layout.item_listview_popwin, popList2);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(popAdapter2);
        popAdapter2.setOnCallBackData(new GeneAdapter.OnCallBackData() {
            @Override
            public void convertView(BaseViewHolder holder, String item) {
                ((TextView) holder.getView(R.id.listview_popwind_tv)).setText(item);
                String list_list_trim = list_list_type_txt.getText().toString().trim();
                if (list_list_trim.equals(item)) {
                    ((TextView) holder.getView(R.id.listview_popwind_tv)).setTextColor(Color.parseColor("#00d8a0"));
                    holder.getView(R.id.iv_select_icon).setVisibility(View.VISIBLE);
                } else {
                    ((TextView) holder.getView(R.id.listview_popwind_tv)).setTextColor(Color.parseColor("#333333"));
                    holder.getView(R.id.iv_select_icon).setVisibility(View.GONE);
                }
            }
        });

        popAdapter2.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
               
                popupWindow.dismiss();
                currentType = popList2.get(position);
                list_list_type_txt.setText(currentType);
                list_list_type_txt.setTextColor(Color.parseColor("#333333"));
                list_list_type_icon.setImageResource(R.mipmap.screen_icon_normal);
                clickFlag = true;
            }
        });

    }

    private void InitView() {
        popList = new ArrayList<>();
        popList.add("综合排序");
        popList.add("距离优先");
        popList.add("人气优先");

        jsonData = JSON.toJSONString(popList);
        Log.e("tag", jsonData);

        popList1 = new ArrayList<>();
        popList1.add("全部分类");
        popList1.add("景点");
        popList1.add("餐饮");
        popList1.add("购物地");
        popList1.add("休闲娱乐");
        popList1.add("酒店");

        jsonData = JSON.toJSONString(popList1);
        Log.e("tag", jsonData);

        popList2 = new ArrayList<>();
        popList2.add("全部商区");
        popList2.add("热门商区");
        popList2.add("朝阳区");
        popList2.add("东城区");
        popList2.add("海淀区");
        popList2.add("西城区");
        popList2.add("丰台区");
        popList2.add("延庆区");
        popList2.add("顺义区");
        popList2.add("昌平区");
        popList2.add("密云区");
        popList2.add("怀柔区");
        popList2.add("石景山区");
        popList2.add("房山区");
        popList2.add("大兴区");
        popList2.add("通州区");

        jsonData = JSON.toJSONString(popList2);
        Log.e("tag", jsonData);

    }
}

总结

以上就是全部代码,利用 PopupWindow + RecyclerView 实现筛选二级联动功能,代码很简单,需要注意一下 PopupWindow  关闭事件 跟 控件点击事件 之间的焦点冲突问题,

如果对你有所帮助的话,不妨 Star 或 Fork,青山不改,绿水长流 有缘江湖再见 ~

源码地址:PopLinkage

你可能感兴趣的:(功能,java,android)