Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用

Android (28) 2024-02-26 10:12

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用,希望能够帮助你!!!。

1.首先去定义该控件:MPopListView

package com.maddox.mmrrr1;

import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Maddox
 * 2021-11-30
 */
public class MPopListView extends LinearLayout {

    private Context mContext;

    private MFlowLayout flowLayout;

    private ImageView iv_arrow;

    private List<DataModel> allDatas;


    public MPopListView(Context context) {
        this(context, null);
    }

    public MPopListView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MPopListView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        LayoutInflater.from(mContext).inflate(R.layout.layout_pop_list_view, this, true);
        initView();
    }

    private void initView() {
        allDatas = new ArrayList<>();
        flowLayout = findViewById(R.id.mflowlayout);
        iv_arrow = findViewById(R.id.iv_arrow);
        iv_arrow.setOnClickListener(v -> {
            showPopWindow(this);
        });
        flowLayout.setOnItemClickListener(new MFlowLayout.OnItemClickListener() {
            @Override
            public void onItemClick(View itemView, int position) {
                String removeName = flowLayout.removeItem(position);
                for (DataModel allData : allDatas) {
                    if (allData.getText().equals(removeName)) {
                        allData.setSelected(false);
                    }
                }
            }
        });

    }

    /**
     * 外部调用
     * 初始化数据
     * 设置所有待展示的数据
     *
     * @param list 展示的数据
     */
    public void setInitList(List<DataModel> list) {
        allDatas.clear();
        allDatas.addAll(list);
    }

    /**
     * 外部调用
     * 返回已选择的所有数据
     *
     * @return
     */
    public List<DataModel> getSelectedList() {
        List<DataModel> selectedList = new ArrayList<>();
        for (DataModel data : allDatas) {
            if (data.getSelected()) {
                selectedList.add(data);
            }
        }
        return selectedList;
    }


    /**
     * 外部调用
     * 设置展示的子条目文本颜色
     *
     * @param color 颜色值
     */
    public void setItemTextColor(int color) {
        flowLayout.setItemTextColor(color);
    }

    /**
     * 外部调用
     * 设置展示的子条目背景颜色
     *
     * @param drawable 自定义的Drawable类型
     */
    public void setItemBackgroundDrawable(Drawable drawable) {
        flowLayout.setItemBackgroundDrawable(drawable);
    }

    /**
     * 外部调用
     * 设置展示的子条目文本右侧小图标,默认是x
     *
     * @param imgRes 图片资源id
     */
    public void setItemTextIcon(int imgRes) {
        flowLayout.setItemTextIcon(imgRes);
    }

    /**
     * 外部调用
     * 设置调出弹窗的图片
     *
     * @param imgRes 图片资源
     */
    public void setArrowImage(int imgRes) {
        iv_arrow.setImageResource(imgRes);
    }


    private void setSelectDataList(List<DataModel> list) {

        flowLayout.setData(list);
        //更新所有数据中已选中的状态
        for (DataModel model : list) {
            if (allDatas.contains(model)) {
                int index = allDatas.indexOf(model);
                allDatas.get(index).setSelected(model.getSelected());
            }
        }
    }


    private void showPopWindow(MPopListView popListView) {

        View contentView = LayoutInflater.from(mContext).inflate(R.layout.layout_pop_list_view_fly_content, null, false);
        GridView gridView = contentView.findViewById(R.id.fly_grid_view);
        TextView cancleBtn = contentView.findViewById(R.id.tv_dialog_cancle);
        TextView confirmBtn = contentView.findViewById(R.id.tv_dialog_confirm);
        ListItemAdapter itemAdapter = new ListItemAdapter(mContext, allDatas, R.layout.layout_pop_list_view_fly_content_item);
        gridView.setAdapter(itemAdapter);
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(mContext, allDatas.get(position).getText(), Toast.LENGTH_SHORT).show();
                DataModel item = itemAdapter.getItem(position);
                item.setSelected(!item.getSelected());
                itemAdapter.notifyDataSetChanged();

            }
        });

        AlertDialog alertDialog = new AlertDialog.Builder(mContext)
                .setView(contentView)
                .create();
        cancleBtn.setOnClickListener(v -> {
            if (alertDialog != null && alertDialog.isShowing()) {
                alertDialog.dismiss();
            }
        });

        confirmBtn.setOnClickListener(v -> {
            if (alertDialog != null && alertDialog.isShowing()) {
                alertDialog.dismiss();
                //获取已选择的
                popListView.setSelectDataList(itemAdapter.getSelectedList());

            }
        });

        alertDialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
        WindowManager.LayoutParams lp = alertDialog.getWindow().getAttributes();
        lp.width = LayoutParams.WRAP_CONTENT;
        lp.height = LayoutParams.WRAP_CONTENT;
        alertDialog.getWindow().setAttributes(lp);
        alertDialog.show();


    }

    private class ListItemAdapter extends CommonAdapter<DataModel> {


        public ListItemAdapter(Context mContext, List<DataModel> datas, int layoutId) {
            super(mContext, datas, layoutId);
        }

        @Override
        public void bindView(ViewHolder holder, DataModel dataModel, int position) {
            holder.setText(R.id.item_text, dataModel.getText());
            CheckBox checkBox = holder.getView(R.id.item_checkbox);
            if (dataModel.getSelected()) {
                checkBox.setChecked(true);
            } else {
                checkBox.setChecked(false);
            }

        }

        public List<DataModel> getSelectedList() {
            List<DataModel> datas = getDatas();
            List<DataModel> haveChoose = new ArrayList<>();
            for (DataModel data : datas) {
                if (data.getSelected()) {
                    haveChoose.add(data);
                }
            }
            return haveChoose;
        }
    }


}


Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第1张

2.里面用到的类:

package com.maddox.mmrrr1;

import android.app.ActionBar;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;



import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 流式布局
 *
 * @author Maddox
 */
public class MFlowLayout extends ViewGroup {
    private Context mContext;
    private int textColor = Color.WHITE;//默认展示的文本颜色
    private Drawable textDrawable;//展示的文本背景色

    //记录所有行的View列表
    private List<List<View>> mViews = new ArrayList<>();
    //记录所有行的各自最大高度
    private List<Integer> mLineHeight = new ArrayList<>();
    //记录当前绘制的实体数据
    private List<DataModel> itemList = new ArrayList<>();
    private int textIcon;//文本右侧小图片


    public MFlowLayout(Context context) {
        this(context, null);
    }

    public MFlowLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;

    }


    public String removeItem(int position) {
        String name = itemList.get(position).getText();
        itemList.remove(position);
        List<DataModel> newList = new ArrayList<>();
        newList.addAll(itemList);
        setData(newList);
        return name;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //当前布局的最大宽度和高度
        int width = 0;
        int height = 0;
        //每一行子view的宽高
        int lineWidth = 0;
        int lineHeight = 0;
        int count = getChildCount();

        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            //获取每个子View的宽度
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            //获取每个子View的高度
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            //如果当前子View与当前行的已使用宽度超出了父布局的宽度大小,则进入下一行
            if (lineWidth + childWidth > widthSize - getPaddingLeft() - getPaddingRight()) {
                //超出一行,先记录当前行的宽度,再去新建一行
                width = Math.max(width, lineWidth);
                //新的一行宽度等于当前子View的宽度
                lineWidth = childWidth;
                //记录行总高度
                height += lineHeight;
                //记录新一行的高度
                lineHeight = childHeight;
            } else {
                //如果没有换行,则累加当前子View的宽度到当前行宽度
                lineWidth += childWidth;
                //得到行子View的最大行高度
                lineHeight = Math.max(lineHeight, childHeight);
            }
            //处理最后一个子View的情况
            if (i == count - 1) {
                width = Math.max(lineWidth, width);
                height += lineHeight;
            }

        }
        //当父布局的宽高为具体值时,就使用设置的具体值,否则使用子View的总体宽高
        int resultW = widthMode == MeasureSpec.EXACTLY ? widthSize : width + getPaddingLeft() + getPaddingRight();
        int resultH = heightMode == MeasureSpec.EXACTLY ? heightSize : height + getPaddingTop() + getPaddingBottom();
        //设置父布局的大小
        setMeasuredDimension(resultW, resultH);


    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        mViews.clear();
        mLineHeight.clear();
        //获取当前ViewGroup的宽度
        int width = getWidth();
        int lineWidth = 0;
        int lingHeight = 0;
        //记录当前行的View
        List<View> lineViews = new ArrayList<>();
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();
            if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width - getPaddingLeft() - getPaddingRight()) {
                mLineHeight.add(lingHeight);
                mViews.add(lineViews);
                lineWidth = 0;
                lingHeight = childHeight + lp.topMargin + lp.bottomMargin;
                lineViews = new ArrayList<>();
            }
            lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
            lingHeight = Math.max(lingHeight, childHeight + lp.topMargin + lp.bottomMargin);
            lineViews.add(child);
        }
        //记录每行高度,为后面设置子View单独top做准备,比如 gravity
        mLineHeight.add(lingHeight);
        mViews.add(lineViews);

        int left = getPaddingLeft();
        int top = getPaddingTop();
        int lineCount = mViews.size();
        for (int i = 0; i < lineCount; i++) {
            lineViews = mViews.get(i);
            lingHeight = mLineHeight.get(i);
            int lineViewCount = lineViews.size();

            for (int j = 0; j < lineViewCount; j++) {
                View child = lineViews.get(j);
                if (child.getVisibility() == GONE) {
                    continue;
                }
                MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                int cLeft = left + lp.leftMargin;
                int cTop = top + lp.topMargin;
                int cRight = child.getMeasuredWidth() + cLeft;
                int cBottom = cTop + child.getMeasuredHeight();
                child.layout(cLeft, cTop, cRight, cBottom);
                left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;


            }

            left = 0;
            top += lingHeight;
        }
    }

    /**
     * 设置填充数据
     *
     * @param data
     */
    public void setData(List<DataModel> data) {
        removeAllViews();
        if (data == null || data.size() <= 0) {
            return;
        }
        itemList.clear();
        itemList.addAll(data);
        MarginLayoutParams lp = new MarginLayoutParams(LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT);
        lp.leftMargin = 5;
        lp.rightMargin = 5;
        lp.topMargin = 5;
        lp.bottomMargin = 5;
        //初始化布局加载器
        for (int i = 0; i < data.size(); i++) {
            View itemView = LayoutInflater.from(mContext).inflate(R.layout.layout_flow_item, null, false);
            TextView itemText = itemView.findViewById(R.id.tv_item_name);
            ImageView itemIcon = itemView.findViewById(R.id.iv_item_icon);
            itemText.setText(data.get(i).getText());
            itemText.setTextColor(textColor);
            if (textDrawable != null) {
                itemView.setBackground(textDrawable);
            }
            if (textIcon != 0) {
                itemIcon.setImageResource(textIcon);
            }
            addView(itemView, lp);
        }
        //因为是新创建的View,需要重新加载监听器
        if (clickListener != null) {
            setOnItemClickListener(clickListener);
        }
    }

    private int randomColor() {
        Random random = new Random();
        return Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256));
    }

    private OnItemClickListener clickListener;

    /**
     * 设置点击事件
     *
     * @param clickListener 点击回调
     */
    public void setOnItemClickListener(OnItemClickListener clickListener) {
        if (clickListener == null)
            return;
        this.clickListener = clickListener;
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            int index = i;
            child.setOnClickListener(v -> {
                this.clickListener.onItemClick(child, index);
            });
        }


    }

    public List<DataModel> getItemList() {
        return itemList;
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    public void setItemTextColor(int color) {
        textColor = color;
    }

    public void setItemBackgroundDrawable(Drawable drawable) {
        textDrawable = drawable;
    }

    public void setItemTextIcon(int imgRes) {
        textIcon = imgRes;
    }


    public static interface OnItemClickListener {
        void onItemClick(View itemView, int position);
    }

}

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第2张

3.然后上面这个类中用到的xml layout_flow_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/selector_flow_item"
    android:padding="8dp"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/tv_item_name"
        android:layout_width="wrap_content"
        android:textSize="16dp"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:textColor="#fff"
        tools:text="北京市" />

    <ImageView
        android:id="@+id/iv_item_icon"
        android:layout_width="15dp"
        android:layout_height="15dp"
        android:layout_marginStart="5dp"
         android:src="@mipmap/icon_item_close"
        />

</LinearLayout>

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第3张

4.然后最上面那个自定义工具类中用到的xml文件 layout_pop_list_view.xml

<?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"
    android:background="@drawable/selector_stroke_theme"
    android:padding="10dp"
    android:orientation="horizontal">

    <com.maddox.mmrrr1.MFlowLayout
        android:id="@+id/mflowlayout"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content"
         />

    <ImageView
        android:id="@+id/iv_arrow"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:layout_gravity="center_vertical"
        android:layout_marginEnd="10dp"
        android:src="@mipmap/icon_select_list" />

</LinearLayout>

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第4张

5.然后就是上面xml文件中用到的小图片

icon_select_list

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第5张

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第6张

这个图标仅仅是个案例,可以自己去找心仪的.

6.然后最上面控件中用到的xml文件

layout_pop_list_view_fly_content.xml

package com.maddox.mmrrr1;

import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Maddox
 * 2021-11-30
 */
public class MPopListView extends LinearLayout {

    private Context mContext;

    private MFlowLayout flowLayout;

    private ImageView iv_arrow;

    private List<DataModel> allDatas;


    public MPopListView(Context context) {
        this(context, null);
    }

    public MPopListView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MPopListView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        LayoutInflater.from(mContext).inflate(R.layout.layout_pop_list_view, this, true);
        initView();
    }

    private void initView() {
        allDatas = new ArrayList<>();
        flowLayout = findViewById(R.id.mflowlayout);
        iv_arrow = findViewById(R.id.iv_arrow);
        iv_arrow.setOnClickListener(v -> {
            showPopWindow(this);
        });
        flowLayout.setOnItemClickListener(new MFlowLayout.OnItemClickListener() {
            @Override
            public void onItemClick(View itemView, int position) {
                String removeName = flowLayout.removeItem(position);
                for (DataModel allData : allDatas) {
                    if (allData.getText().equals(removeName)) {
                        allData.setSelected(false);
                    }
                }
            }
        });

    }

    /**
     * 外部调用
     * 初始化数据
     * 设置所有待展示的数据
     *
     * @param list 展示的数据
     */
    public void setInitList(List<DataModel> list) {
        allDatas.clear();
        allDatas.addAll(list);
    }

    /**
     * 外部调用
     * 返回已选择的所有数据
     *
     * @return
     */
    public List<DataModel> getSelectedList() {
        List<DataModel> selectedList = new ArrayList<>();
        for (DataModel data : allDatas) {
            if (data.getSelected()) {
                selectedList.add(data);
            }
        }
        return selectedList;
    }


    /**
     * 外部调用
     * 设置展示的子条目文本颜色
     *
     * @param color 颜色值
     */
    public void setItemTextColor(int color) {
        flowLayout.setItemTextColor(color);
    }

    /**
     * 外部调用
     * 设置展示的子条目背景颜色
     *
     * @param drawable 自定义的Drawable类型
     */
    public void setItemBackgroundDrawable(Drawable drawable) {
        flowLayout.setItemBackgroundDrawable(drawable);
    }

    /**
     * 外部调用
     * 设置展示的子条目文本右侧小图标,默认是x
     *
     * @param imgRes 图片资源id
     */
    public void setItemTextIcon(int imgRes) {
        flowLayout.setItemTextIcon(imgRes);
    }

    /**
     * 外部调用
     * 设置调出弹窗的图片
     *
     * @param imgRes 图片资源
     */
    public void setArrowImage(int imgRes) {
        iv_arrow.setImageResource(imgRes);
    }


    private void setSelectDataList(List<DataModel> list) {

        flowLayout.setData(list);
        //更新所有数据中已选中的状态
        for (DataModel model : list) {
            if (allDatas.contains(model)) {
                int index = allDatas.indexOf(model);
                allDatas.get(index).setSelected(model.getSelected());
            }
        }
    }


    private void showPopWindow(MPopListView popListView) {

        View contentView = LayoutInflater.from(mContext).inflate(R.layout.layout_pop_list_view_fly_content, null, false);
        GridView gridView = contentView.findViewById(R.id.fly_grid_view);
        TextView cancleBtn = contentView.findViewById(R.id.tv_dialog_cancle);
        TextView confirmBtn = contentView.findViewById(R.id.tv_dialog_confirm);
        ListItemAdapter itemAdapter = new ListItemAdapter(mContext, allDatas, R.layout.layout_pop_list_view_fly_content_item);
        gridView.setAdapter(itemAdapter);
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(mContext, allDatas.get(position).getText(), Toast.LENGTH_SHORT).show();
                DataModel item = itemAdapter.getItem(position);
                item.setSelected(!item.getSelected());
                itemAdapter.notifyDataSetChanged();

            }
        });

        AlertDialog alertDialog = new AlertDialog.Builder(mContext)
                .setView(contentView)
                .create();
        cancleBtn.setOnClickListener(v -> {
            if (alertDialog != null && alertDialog.isShowing()) {
                alertDialog.dismiss();
            }
        });

        confirmBtn.setOnClickListener(v -> {
            if (alertDialog != null && alertDialog.isShowing()) {
                alertDialog.dismiss();
                //获取已选择的
                popListView.setSelectDataList(itemAdapter.getSelectedList());

            }
        });

        alertDialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
        WindowManager.LayoutParams lp = alertDialog.getWindow().getAttributes();
        lp.width = LayoutParams.WRAP_CONTENT;
        lp.height = LayoutParams.WRAP_CONTENT;
        alertDialog.getWindow().setAttributes(lp);
        alertDialog.show();


    }

    private class ListItemAdapter extends CommonAdapter<DataModel> {


        public ListItemAdapter(Context mContext, List<DataModel> datas, int layoutId) {
            super(mContext, datas, layoutId);
        }

        @Override
        public void bindView(ViewHolder holder, DataModel dataModel, int position) {
            holder.setText(R.id.item_text, dataModel.getText());
            CheckBox checkBox = holder.getView(R.id.item_checkbox);
            if (dataModel.getSelected()) {
                checkBox.setChecked(true);
            } else {
                checkBox.setChecked(false);
            }

        }

        public List<DataModel> getSelectedList() {
            List<DataModel> datas = getDatas();
            List<DataModel> haveChoose = new ArrayList<>();
            for (DataModel data : datas) {
                if (data.getSelected()) {
                    haveChoose.add(data);
                }
            }
            return haveChoose;
        }
    }


}


Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第7张

7.上面xml中用到的xml shape_corner_white.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <corners android:radius="15dp" />
    <solid android:color="@android:color/white" />
</shape>

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第8张

8.最上面类中,用到的xml文件layout_pop_list_view_fly_content_item.xml

<?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="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <CheckBox
        android:id="@+id/item_checkbox"
        android:layout_width="wrap_content"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:clickable="false"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/item_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:maxLines="1"
        tools:text="选中的条目内容"
        android:textColor="#000"
        android:textSize="17dp"
        />
</LinearLayout>

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第9张

9.上面的 layout_pop_list_view_fly_content.xml 文件中用到的xml文件 selector_tv_bg1.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
 <item android:state_pressed="false">
     <shape android:shape="rectangle">
         <corners android:radius="5dp"/>
         <solid android:color="#f3f7f8"/>
     </shape>
 </item>
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <corners android:radius="5dp"/>
            <solid android:color="#884176AC"/>
        </shape>
    </item>
</selector>

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第10张

10.然后layout_pop_list_view.xml文件中用到的xml文件:selector_stroke_theme.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="false">

        <shape android:shape="rectangle">
            <corners android:radius="5dp" />
            <stroke android:width="2dp" android:color="#369484"/>

        </shape>
    </item>

    <item android:state_pressed="true">

        <shape android:shape="rectangle">
            <corners android:radius="5dp" />
            <stroke android:width="2dp" android:color="#26C6DA"/>

        </shape>
    </item>
</selector>

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第11张

11.然后,我们继续看,用到的放数据的实体类:

DataModel.java 接口

package com.maddox.mmrrr1;

/**
 * 抽象出的数据类接口,以便列表用于展现数据文本
 *  子类必须重写equals 和 hashcode方法
 *  子类必须重写equals 和 hashcode方法
 */
public interface  DataModel {


    //数据用于展示的文本
    public  String getText();
    //获取选中状态
    public boolean getSelected();
    //设置选中状态
    public void setSelected(boolean selected);

}

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第12张

12.然后

DemoModel.java 实现接口的实体类

package com.maddox.mmrrr1;

import java.util.Objects;

/**
 * @author Maddox
 * 2021-11-30
 * 展示文本的实体实现 示例
 * 必须重写equals 和 hashcode方法
 * 必须重写equals 和 hashcode方法
 * 必须重写equals 和 hashcode方法
 * 必须重写equals 和 hashcode方法
 */
public class DemoModel implements DataModel {

    public int id;//数据id
    public String content;//数据内容
    public boolean selected;//数据是否选中

    public DemoModel() {
    }

    public DemoModel(int id, String content, boolean selected) {
        this.id = id;
        this.content = content;
        this.selected = selected;
    }

    @Override
    public String getText() {
        return content;
    }

    @Override
    public boolean getSelected() {
        return selected;
    }

    @Override
    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    @Override
    public String toString() {
        return "DemoModel{" +
                "id=" + id +
                ", content='" + content + '\'' +
                ", selected=" + selected +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DemoModel demoModel = (DemoModel) o;
        return id == demoModel.id && Objects.equals(content, demoModel.content);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, content);
    }
}

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第13张

13.然后再去看适配器:

package com.maddox.mmrrr1;

import android.content.Context;
import android.graphics.Bitmap;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Maddox on 2017/11/15.
 * 通用数据适配器 Listview gridview
 */

public abstract class CommonAdapter<T> extends BaseAdapter {
    /**
     * 数据源
     */
    private List<T> datas = new ArrayList<>();
    private Context mContext;
    private int layoutId;//布局id

    public CommonAdapter(Context mContext, List<T> datas, int layoutId) {
        this.mContext = mContext;
        this.layoutId = layoutId;
        if (datas != null)
            this.datas.addAll(datas);

    }

    @Override
    public int getCount() {
        return datas.size();
    }

    @Override
    public T getItem(int position) {
        return datas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    public List<T> getDatas() {
        return datas;
    }

    /**
     * 添加一条数据
     *
     * @param t 数据
     */
    public void addOneData(T t) {
        datas.add(t);
        notifyDataSetChanged();
    }

    public void addAll(List<T> das) {
        if (das != null) {
            datas.addAll(das);
            notifyDataSetChanged();
        }
    }

    /**
     * 添加数据到指定位置
     *
     * @param t     数据
     * @param index 位置下标
     */
    public void addOneData(T t, int index) {
        datas.add(index, t);
        notifyDataSetChanged();
    }

    public void clearData() {
        datas.clear();
        notifyDataSetChanged();
    }

    /**
     * 判断已有数据中是否包含t
     *
     * @param t
     * @return
     */

    public boolean contains(T t) {
        return datas.contains(t);
    }

    /**
     * 移除一条数据
     *
     * @param t
     */
    public void removeOneData(T t) {
        datas.remove(t);
        notifyDataSetChanged();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //给每个条目绑定ViewHolder
        ViewHolder viewHolder = ViewHolder.bind(mContext, convertView, parent, layoutId, position);
        //把绑定结果反馈给调用适配器的人
        bindView(viewHolder, getItem(position), position);
        return viewHolder.getItemView();
    }

    /**
     * 把ViewHolder和对应的数据示例抽象给使用者去实现具体操作
     *
     * @param holder
     * @param t      数据
     */
    public abstract void bindView(ViewHolder holder, T t, int position);


    public static class ViewHolder {
        private Context context;
        private SparseArray<View> mViews;//缓存每个item布局中的控件
        private View itemView;//记录一条item布局
        private int position;//记录当前item的位置

        private ViewHolder(Context context, ViewGroup parent, int layoutId) {
            //初始化
            this.context = context;
            this.mViews = new SparseArray<>();
            View convertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
            convertView.setTag(this);
            this.itemView = convertView;

        }

        /**
         * 绑定当前itemView与ViewHolder
         *
         * @param context
         * @param convertView 条目布局
         * @param parent      条目view的ViewGroup
         * @param layoutId    布局资源id
         * @param position    布局位置
         * @return
         */
        public static ViewHolder bind(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
            ViewHolder holder;
            if (convertView == null) {
                holder = new ViewHolder(context, parent, layoutId);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.position = position;

            return holder;
        }

        /**
         * 根据控件的id获取该控件
         *
         * @param viewId
         * @param <T>
         * @return
         */
        public <T extends View> T getView(int viewId) {
            T t = (T) mViews.get(viewId);
            if (t == null) {
                t = itemView.findViewById(viewId);
                mViews.put(viewId, t);
            }
            return t;
        }

        /**
         * 获取当前布局
         *
         * @return
         */
        public View getItemView() {

            return itemView;
        }

        /**
         * 获取当前布局位置
         *
         * @return
         */
        public int getPosition() {
            return position;
        }

        /**
         * 给指定id的控件设置文本
         *
         * @param tvId    控件id
         * @param content 文本内容
         * @return
         */
        public ViewHolder setText(int tvId, CharSequence content) {
            View tvView = getView(tvId);

            if (tvView instanceof TextView) {

                ((TextView) tvView).setText(content);
            }

            return this;

        }

        public ViewHolder setTextColor(int tvId, int color) {
            View tvView = getView(tvId);

            if (tvView instanceof TextView) {

                ((TextView) tvView).setTextColor(color);
            }

            return this;

        }

        /**
         * 给指定id的控件设置图片或者背景
         *
         * @param ivId     控件id
         * @param imgResid 图片资源id
         * @return
         */
        public ViewHolder setImageResource(int ivId, int imgResid) {
            View imageView = getView(ivId);
            if (imageView instanceof ImageView) {
                ((ImageView) imageView).setImageResource(imgResid);
            } else {
                imageView.setBackgroundResource(imgResid);
            }
            return this;

        }

        /**
         * 给指定id的控件设置图片或者背景
         *
         * @param ivId   控件id
         * @param bitmap bitmap图片
         * @return
         */
        public ViewHolder setImageBitmap(int ivId, Bitmap bitmap) {
            View imageView = getView(ivId);
            if (imageView instanceof ImageView) {
                ((ImageView) imageView).setImageBitmap(bitmap);
            }
            return this;

        }

        /**
         * 给指定id的控件设置点击事件
         *
         * @param viewId        控件id
         * @param clickListener 监听器
         * @return
         */
        public ViewHolder setOnClickListener(int viewId, View.OnClickListener clickListener) {

            View view = getView(viewId);
            view.setOnClickListener(clickListener);
            return this;
        }

        /**
         * 指定id控件设置是否可见
         *
         * @param viewId     控件 id
         * @param visibility 可见或者隐藏
         * @return
         */
        public ViewHolder setVisibility(int viewId, int visibility) {
            View view = getView(viewId);
            view.setVisibility(visibility);
            return this;

        }

        /**
         * 指定id控件设置tag标签
         *
         * @param viewId 控件id
         * @param obj    标签
         * @return
         */
        public ViewHolder setTag(int viewId, Object obj) {
            getView(viewId).setTag(obj);

            return this;
        }


    }
}

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第14张

14.这样基本上这个控件就做好了,然后去看看怎么用:activity_main.xml 首先弄一个主的activity

对应的布局文件是:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".HomeActivity">


    <!--多选控件-->
    <com.maddox.mmrrr1.MPopListView
        android:id="@+id/mpop_list_view"

        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

    <!-- 按钮 获取已选择数据-->
    <Button
        android:id="@+id/btn_getSelect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="15dp"
        android:text="获取已选择的数据" />

    <TextView
        android:id="@+id/tv_show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center" />

</LinearLayout>

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第15张

15.可以看到上面定义了我们刚自己定义的控件.然后

package com.maddox.mmrrr1;

import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import java.util.ArrayList;
import java.util.List;

/**
 * 多选列表示例页面
 */
public class HomeActivity extends AppCompatActivity {

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

    private void initView() {
        //测试多选
        MPopListView popListView = findViewById(R.id.mpop_list_view);
        Button button = findViewById(R.id.btn_getSelect);
        TextView textView = findViewById(R.id.tv_show);


        //可选配置
        //popListView.setItemTextColor(Color.BLACK);//设置条目的文本颜色
        //popListView.setItemTextIcon(R.mipmap.ic_launcher);//设置条目的右侧小图标
        //popListView.setItemBackgroundDrawable(getDrawable(R.drawable.selector_btn_normal));//设置条目的背景色 边线 点击效果
        //popListView.setArrowImage(R.mipmap.ic_launcher);//设置调出菜单选项的图标

        //虚拟假数据
        //设置初始化数据,也就是选择数据列表
        popListView.setInitList(getTestData());

        //获取选择的数据
        button.setOnClickListener(v -> {
            List<DataModel> selectedList = popListView.getSelectedList();
            textView.setText(selectedList.toString());
        });


    }

    //假数据
    private List<DataModel> getTestData() {

        //初始化显示的假数据
        List<DataModel> list = new ArrayList<>();
        list.add(new DemoModel(0, "北京市", false));
        list.add(new DemoModel(1, "承德市 ", false));
        list.add(new DemoModel(2, "澳门半岛 ", false));
        list.add(new DemoModel(3, "台中市", false));
        list.add(new DemoModel(4, "阳江市", false));
        list.add(new DemoModel(5, "张家界", false));
        list.add(new DemoModel(6, "邵阳市", false));
        list.add(new DemoModel(7, "保山市", false));
        list.add(new DemoModel(8, "遵义市 ", false));
        list.add(new DemoModel(9, "茂名市", false));
        list.add(new DemoModel(10, "宜昌市", false));
        list.add(new DemoModel(11, "潍坊市", false));
        list.add(new DemoModel(12, "日照市", false));
        list.add(new DemoModel(13, "河池市", false));
        list.add(new DemoModel(14, "桂林市", false));
        list.add(new DemoModel(15, "海口市", false));
        return list;
    }
}

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第16张

可以看到先按照上面这样使用,设置初始化的数据,以及获取选中的数据.

16.然后再去补充一下,其他用法:

这里获取选中的,选项的key,这些key就是数据库中的数据库字典的值,用,隔开

  MPopListView layout_mdd_docreate_mpl_zhiyexiguan = findViewById(R.id.layout_mdd_docreate_mpl_zhiyexiguan);
                List<DataModel> zhiyexiguanSelectedList = layout_mdd_docreate_mpl_zhiyexiguan.getSelectedList();
                String zhiyexiguanKey = "";
                for(DataModel dataModel:zhiyexiguanSelectedList){
                    DemoModel tempDemodel = (DemoModel)dataModel;
                    zhiyexiguanKey = zhiyexiguanKey + String.valueOf(tempDemodel.getId()) +",";
                }

                if(StringUtils.isNotEmpty(zhiyexiguanKey) && -1!=zhiyexiguanKey.lastIndexOf(",")){
                    zhiyexiguanKey =  zhiyexiguanKey.substring(0, zhiyexiguanKey.lastIndexOf(","));
                }

                baseInfo.put("temp2",zhiyexiguanKey);

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第17张

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第18张

可以看到,上面把结果取出来,然后拼接成 1,2,3..这样的,用户多选的字典值的字符串了,获取以后可以传递到数据库保存起来.

17.然后初始化这个多选框,也给出个例子,上面已经有了,再写一个吧.

String workHabitsResult = YdHttpPostUtils.getDict("workHabits");
        workHabitsPair = YdCommonUtils.getDictPairs(workHabitsResult,workHabitsPair);

        MPopListView layout_mdd_docreate_mpl_zhiyexiguan  = (MPopListView)findViewById(R.id.layout_mdd_docreate_mpl_zhiyexiguan);
        List<DataModel> zhiyexiguanMpopListView = new ArrayList<DataModel>();
        for(DictPair dictPair:workHabitsPair){
            DataModel dataModel = new DemoModel(Integer.valueOf(dictPair.getKey()),dictPair.getValue(),false);
            zhiyexiguanMpopListView.add(dataModel);
        }
        layout_mdd_docreate_mpl_zhiyexiguan.setInitList(zhiyexiguanMpopListView);

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第19张

18.然后主要是这里,这里根据数据库获取的值,然后动态填充,多选框的多选情况

  List<DataModel> zhiyexiguanDatas = new ArrayList<DataModel>();
                String zhiyexiguanKey = dataObj.getString("temp2");

                if(StringUtils.isNotEmpty(zhiyexiguanKey) && !"null".equals(zhiyexiguanKey)){
                    String[] zhiyexiguanArray = zhiyexiguanKey.split(",");
                    List<String> zhiyexiguanList =  Arrays.asList(zhiyexiguanArray);

                    for (DictPair dictPair:workHabitsPair){
                        if(zhiyexiguanList.contains(dictPair.getKey())){
                            DemoModel  demoModel= new DemoModel(Integer.valueOf(dictPair.getKey()),dictPair.getValue(),true);
                            zhiyexiguanDatas.add(demoModel);
                        }
                    }
                }

                MPopListView layout_mdd_docreate_mpl_zhiyexiguan = findViewById(R.id.layout_mdd_docreate_mpl_zhiyexiguan);
                layout_mdd_docreate_mpl_zhiyexiguan.setSelectDataList(zhiyexiguanDatas);

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第20张

19.然后看一下效果:

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第21张

Android精美自定义多选控件_多选Spinner_MultiSpinner_拿来即用_https://bianchenghao6.com/blog_Android_第22张

到这里就完了,这个有个案例程序:可以下载去查看,如果看文章没有看明白.

SmartMultiSelecte.zip-Linux文档类资源-CSDN下载

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

发表回复