前言
在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 会上传
如果对你有所帮助的话,不妨 点赞收藏
如果你有什么疑问的话,不妨 评论私信
青山不改,绿水长流 ,有缘江湖再见 ~