Android 自定义 Dialog 实现列表 单选、多选、搜索功能
作者:约翰先森不喝酒
前言
在Android开发中,通过对话框让用户选择,筛选信息是很方便也很常见的操作。本文详细介绍了如何使用自定义 Dialog、RecyclerView 以及自定义搜索框 来实现选中状态和用户交互,文中大本分代码都有明确注释,主打一个简单明了,实际效果如下,可单选,全选,精准查找,选择状态变化,以及信息回调
一、Builder 模式
说到自定义 Dialog,就不得不提到 Builder模式,
Android系统中的Builder设计模式是一种创建型设计模式,它主要用于构建一个复杂对象,并将其构建过程与表示分离,Builder设计模式通过将一个复杂对象的构建过程拆解成一系列简单的步骤,使得构建过程更加灵活、可读和易于扩展。它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。
在Android开发中,Builder模式的一个常见应用是AlertDialog.Builder。AlertDialog是一个复杂的对话框对象,它包含多个属性和方法。使用AlertDialog.Builder可以方便地构建和显示对话框,而无需直接操作AlertDialog对象。例如:
AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setIcon(R.drawable.icon); builder.setTitle("头部"); builder.setMessage("内容"); builder.setPositiveButton("Button1", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // 处理点击事件 } }); builder.create().show(); // 构建并显示对话框
综上所述,Builder设计模式在Android开发中具有重要的应用价值。它可以帮助开发者构建复杂对象,提高代码的可读性和可维护性,同时支持灵活的构建过程和对象变种。
二、使用步骤
1. 自定义 SerachSelectDialog
public class SerachSelectDialog extends Dialog { private static SearchSelectAdapter sa; private static String result; private static List<String> resultList = new ArrayList<>(); private static List<String> selectedItems; private static int searchPosition; public SerachSelectDialog(Context context, int themeResId) { super(context, themeResId); } /** * 设置 Dialog的大小 * * @param x 宽比例 * @param y 高比例 */ public void setDialogWindowAttr(double x, double y, Activity activity) { if (x < 0 || x > 1 || y < 0 || y > 1) { return; } Window window = this.getWindow(); WindowManager.LayoutParams lp = window.getAttributes(); WindowManager manager = activity.getWindowManager(); DisplayMetrics outMetrics = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(outMetrics); int width = outMetrics.widthPixels; int height = outMetrics.heightPixels; lp.gravity = Gravity.BOTTOM; lp.width = (int) (width * x); lp.height = (int) (height * y); this.getWindow().setAttributes(lp); } public static class Builder { private String title; private View contentView; private String positiveButtonText; private String negativeButtonText; private List<ItemModel> listData; private View.OnClickListener positiveButtonClickListener; private View.OnClickListener negativeButtonClickListener; private View.OnClickListener singleButtonClickListener; private View layout; private Context context; private SerachSelectDialog dialog; private OnSelectedListiner selectedListiner; SearchView searchView; LinearLayout closeBtn; LinearLayout okBtn; TextView titleView; private boolean state = false; private RecyclerView itemLv; private final TextView qxTv; //初始化 public Builder(Context context) { //这里传入自定义的style,直接影响此Dialog的显示效果。style具体实现见style.xml this.context = context; dialog = new SerachSelectDialog(context, R.style.selectDialog); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); layout = inflater.inflate(R.layout.dialog_select_search, null); qxTv = layout.findViewById(R.id.qx_tv); itemLv = layout.findViewById(R.id.item_lv); searchView = layout.findViewById(R.id.searchView); closeBtn = layout.findViewById(R.id.diss_layout); okBtn = layout.findViewById(R.id.ok_layout); titleView = layout.findViewById(R.id.title_tv); dialog.addContentView(layout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } public Builder setTitle(String title) { this.title = title; return this; } public void setListData(List<ItemModel> listData) { this.listData = listData; } /** * 单按钮对话框和双按钮对话框的公共部分在这里设置 */ private SerachSelectDialog create() { GridLayoutManager gridLayoutManager = new GridLayoutManager(context, 3); sa = new SearchSelectAdapter(listData); itemLv.setLayoutManager(gridLayoutManager); itemLv.setAdapter(sa); //搜索事件 searchView.setSearchViewListener(new SearchView.onSearchViewListener() { @Override public boolean onQueryTextChange(String text) { updateLayout(searchItem(text)); return false; } }); //全选 qxTv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (sa.getSelectedItemPositions().size() == sa.getItemCount()) { sa.clearSelection(); } else { sa.selectAll(); resultList = sa.getSelectedItems(); } } }); //取消按钮 closeBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); resultList.clear(); } }); //确认按钮 okBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String json = new Gson().toJson(resultList); selectedListiner.onSelected(json); dialog.dismiss(); resultList.clear(); } }); dialog.setOnDismissListener(new OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { } }); //item点击事件 sa.setOnItemClickListener(new SearchSelectAdapter.OnItemClickListener() { @Override public void onItemClick(int position) { boolean selected = listData.get(position).isSelected(); result = listData.get(position).getItemName(); if (selected == true) { resultList.add(result); } else { resultList.remove(result); } Log.i("U--", resultList.toString() + selected + ""); } }); dialog.setContentView(layout); //用户可以点击手机Back键取消对话框显示 dialog.setCancelable(true); //用户不能通过点击对话框之外的地方取消对话框显示 dialog.setCanceledOnTouchOutside(false); return dialog; } //在数据源中查找匹配的数据 public List<ItemModel> searchItem(String name) { ArrayList<ItemModel> mSearchList = new ArrayList<ItemModel>(); for (int i = 0; i < listData.size(); i++) { int index = listData.get(i).getItemName().indexOf(name); // 存在匹配的数据 if (index != -1) { mSearchList.add(listData.get(i)); Log.i("U--", i + "搜索位置"); searchPosition = i; } } return mSearchList; } //提供匹配后的的数据进行数据回调 public void updateLayout(List<ItemModel> newList) { final SearchSelectAdapter sa = new SearchSelectAdapter(newList); GridLayoutManager gridLayoutManager = new GridLayoutManager(context, 3); itemLv.setLayoutManager(gridLayoutManager); itemLv.setAdapter(sa); //item点击事件 sa.setOnItemClickListener(new SearchSelectAdapter.OnItemClickListener() { @Override public void onItemClick(int position) { result = newList.get(position).getItemName(); boolean selected = listData.get(searchPosition).isSelected(); if (selected == true) { resultList.add(result); } else { resultList.remove(result); } Log.i("U--", resultList.toString() + selected + ""); } }); } //自定义接口进行数据点击回传 public static abstract class OnSelectedListiner { public abstract void onSelected(String String); } public void setSelectedListiner(SerachSelectDialog.Builder.OnSelectedListiner selectedListiner) { this.selectedListiner = selectedListiner; } //弹框展示 public SerachSelectDialog show() { create(); dialog.show(); return dialog; } } }
2.自定义搜索框 SearchView
UI 主要包括输入框,删除键 ,主要通过监听EditText 的文本以及输入框的变化,设置搜索回调接口来实现
public class SearchView extends LinearLayout implements View.OnClickListener { /** * 输入框 */ private EditText etInput; /** * 删除键 */ private ImageView ivDelete; /** * 上下文对象 */ private Context mContext; /** * 搜索回调接口 */ private onSearchViewListener mListener; /** * 设置搜索回调接口 * * @param listener 监听者 */ public void setSearchViewListener(onSearchViewListener listener) { mListener = listener; } public SearchView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; LayoutInflater.from(context).inflate(R.layout.view_search_layout, this); initViews(); } private void initViews() { etInput = (EditText) findViewById(R.id.et_search_text); ivDelete = (ImageView) findViewById(R.id.imb_search_clear); ivDelete.setOnClickListener(this); etInput.addTextChangedListener(new EditChangedListener()); etInput.setOnClickListener(this); } private class EditChangedListener implements TextWatcher { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { if (!"".equals(charSequence.toString())) { ivDelete.setVisibility(VISIBLE); //更新autoComplete数据 if (mListener != null) { mListener.onQueryTextChange(charSequence + ""); } } else { ivDelete.setVisibility(GONE); } } @Override public void afterTextChanged(Editable editable) { } } @Override public void onClick(View view) { switch (view.getId()) { case R.id.imb_search_clear: etInput.setText(""); if (mListener != null) { mListener.onQueryTextChange(""); } ivDelete.setVisibility(GONE); break; } } /** * search view回调方法 */ public interface onSearchViewListener { boolean onQueryTextChange(String text); } }
3.SearchSelectAdapter
主要实现条目的点击事件以及数据回调
public class SearchSelectAdapter extends RecyclerView.Adapter<SearchSelectAdapter.ViewHolder> { private List<ItemModel> itemList; private List<Integer> selectedItemPositions; //声明接口 private OnItemClickListener onItemClickListener; public SearchSelectAdapter(List<ItemModel> itemList) { this.itemList = itemList; selectedItemPositions = new ArrayList<>(); } @Override public int getItemCount() { return itemList.size(); } public void setOnItemClickListener(OnItemClickListener listener) { this.onItemClickListener = listener; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // 创建ViewHolder View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_cell_select_single, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { // 绑定数据到ViewHolder ItemModel item = itemList.get(position); holder.textView.setText(item.getItemName()); //给条目布局设置点击事件 holder.itemLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (selectedItemPositions.contains(position)) { selectedItemPositions.remove(Integer.valueOf(position)); holder.textView.setTextColor(Color.BLACK); holder.itemView.setBackgroundResource(R.drawable.item_grey_layout_bg); item.setSelected(false); } else { selectedItemPositions.add(position); holder.textView.setTextColor(Color.WHITE); holder.itemView.setBackgroundResource(R.drawable.item_blue_layout_bg); item.setSelected(true); } if (onItemClickListener != null) { onItemClickListener.onItemClick(position); } } }); if (selectedItemPositions.contains(position)) { holder.textView.setTextColor(Color.WHITE); holder.itemView.setBackgroundResource(R.drawable.item_blue_layout_bg); } else { holder.textView.setTextColor(Color.BLACK); holder.itemView.setBackgroundResource(R.drawable.item_grey_layout_bg); } } /** * 接口回调 */ public interface OnItemClickListener { void onItemClick(int position); } public void selectAll() { selectedItemPositions.clear(); for (int i = 0; i < itemList.size(); i++) { selectedItemPositions.add(i); } notifyDataSetChanged(); } public void clearSelection() { selectedItemPositions.clear(); notifyDataSetChanged(); } public List<Integer> getSelectedItemPositions() { return selectedItemPositions; } public List<String> getSelectedItems() { List<String> selectedItems = new ArrayList<>(); for (int position : selectedItemPositions) { selectedItems.add(itemList.get(position).getItemName()); } return selectedItems; } public static class ViewHolder extends RecyclerView.ViewHolder { private final TextView textView; private final LinearLayout itemLayout; public ViewHolder(@NonNull View itemView) { super(itemView); textView = itemView.findViewById(R.id.tv_select_info); itemLayout = itemView.findViewById(R.id.item_layout); } } }
4.xml 布局
dialog_select_search.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:orientation="vertical"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/item_white_layout" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp"> <TextView android:id="@+id/title_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="岗位选择" android:textColor="@color/black" /> </RelativeLayout> <com.example.dialoglistview.SearchView android:id="@+id/searchView" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/qx_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="@dimen/dp_10" android:text="全选" android:textSize="16sp" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/item_lv" android:layout_width="match_parent" android:layout_height="wrap_content" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/grey" /> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp" android:background="@color/transparent" android:gravity="center" android:orientation="horizontal"> <LinearLayout android:id="@+id/diss_layout" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="取消" android:textColor="@color/sea_blue" /> </LinearLayout> <View android:layout_width="1dp" android:layout_height="match_parent" android:background="@color/grey" /> <LinearLayout android:id="@+id/ok_layout" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="确定" android:textColor="@color/sea_blue" /> </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout>
view_search_layout.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="50dp" android:background="#ffffff" android:gravity="center" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="35dp" android:layout_marginLeft="15dp" android:layout_marginRight="15dp" android:background="@drawable/item_search_layout" android:gravity="center_vertical" android:orientation="horizontal"> <ImageButton android:id="@+id/imb_search_search" android:layout_width="20dp" android:layout_height="20dp" android:layout_marginLeft="15dp" android:background="#F0F0F0" android:scaleType="centerInside" android:src="@mipmap/im_search_back" /> <EditText android:id="@+id/et_search_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="15dp" android:layout_weight="1" android:background="@null" android:hint="请输入搜索内容" android:lines="1" android:textSize="14sp" /> <ImageButton android:id="@+id/imb_search_clear" android:layout_width="35dp" android:layout_height="35dp" android:layout_marginRight="20dp" android:background="#F0F0F0" android:padding="12.5dp" android:scaleType="centerInside" android:src="@mipmap/delet_zhaopian_1x" android:visibility="gone" /> </LinearLayout> </LinearLayout>
5.数据支持
// 创建数据列表 itemList = new ArrayList<>(); itemList.add(new ItemModel("医生", false)); itemList.add(new ItemModel("警察", false)); itemList.add(new ItemModel("护士", false)); itemList.add(new ItemModel("农民", false)); itemList.add(new ItemModel("工人", false)); itemList.add(new ItemModel("司机", false));
public class ItemModel { private String itemName; private boolean isSelected; public ItemModel(String itemName, boolean isSelected) { this.itemName = itemName; this.isSelected = isSelected; } public String getItemName() { return itemName; } public void setItemName(String itemName) { this.itemName = itemName; } public boolean isSelected() { return isSelected; } public void setSelected(boolean selected) { isSelected = selected; } }
6.实际应用
private void openSearchSelectDialog() { SerachSelectDialog.Builder alert = new SerachSelectDialog.Builder(this); alert.setListData(itemList); alert.setTitle("岗位选择"); alert.setSelectedListiner(new SerachSelectDialog.Builder.OnSelectedListiner() { @Override public void onSelected(String info) { okTv.setText(info); } }); SerachSelectDialog mDialog = alert.show(); //设置Dialog 尺寸 mDialog.setDialogWindowAttr(0.9, 0.9, this); }
三、总结
后续 Demo 会上传
到此这篇关于Android 自定义 Dialog 实现列表 单选,多选,搜索功能的文章就介绍到这了,更多相关Android 自定义 Dialog 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- Android自定义Dialog的2种常见方法
- Android自定义Dialog框样式
- Android自定义Dialog原理实例解析
- Android 自定义加载动画Dialog弹窗效果的示例代码
- Android自定义底部弹出框ButtomDialog
- android自定义Dialog弹框和背景阴影显示效果
- Android自定义Dialog实现通用圆角对话框
- Android自定义dialog 自下往上弹出的实例代码
- Android 自定义Dialog去除title导航栏的解决方法
- Android自定义Dialog实现加载对话框效果
- Android编程自定义AlertDialog样式的方法详解
- 解决Android中自定义DialogFragment解决宽度和高度问题