Android?自定義?Dialog?實(shí)現(xiàn)列表?單選、多選、搜索功能
前言
在Android開發(fā)中,通過對話框讓用戶選擇,篩選信息是很方便也很常見的操作。本文詳細(xì)介紹了如何使用自定義 Dialog、RecyclerView 以及自定義搜索框 來實(shí)現(xiàn)選中狀態(tài)和用戶交互,文中大本分代碼都有明確注釋,主打一個簡單明了,實(shí)際效果如下,可單選,全選,精準(zhǔn)查找,選擇狀態(tài)變化,以及信息回調(diào)
一、Builder 模式
說到自定義 Dialog,就不得不提到 Builder模式,
Android系統(tǒng)中的Builder設(shè)計模式是一種創(chuàng)建型設(shè)計模式,它主要用于構(gòu)建一個復(fù)雜對象,并將其構(gòu)建過程與表示分離,Builder設(shè)計模式通過將一個復(fù)雜對象的構(gòu)建過程拆解成一系列簡單的步驟,使得構(gòu)建過程更加靈活、可讀和易于擴(kuò)展。它允許用戶在不知道內(nèi)部構(gòu)建細(xì)節(jié)的情況下,可以更精細(xì)地控制對象的構(gòu)造流程。
在Android開發(fā)中,Builder模式的一個常見應(yīng)用是AlertDialog.Builder。AlertDialog是一個復(fù)雜的對話框?qū)ο螅鄠€屬性和方法。使用AlertDialog.Builder可以方便地構(gòu)建和顯示對話框,而無需直接操作AlertDialog對象。例如:
AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setIcon(R.drawable.icon); builder.setTitle("頭部"); builder.setMessage("內(nèi)容"); builder.setPositiveButton("Button1", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // 處理點(diǎn)擊事件 } }); builder.create().show(); // 構(gòu)建并顯示對話框
綜上所述,Builder設(shè)計模式在Android開發(fā)中具有重要的應(yīng)用價值。它可以幫助開發(fā)者構(gòu)建復(fù)雜對象,提高代碼的可讀性和可維護(hù)性,同時支持靈活的構(gòu)建過程和對象變種。
二、使用步驟
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); } /** * 設(shè)置 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具體實(shí)現(xiàn)見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; } /** * 單按鈕對話框和雙按鈕對話框的公共部分在這里設(shè)置 */ 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(); } }); //確認(rèn)按鈕 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點(diǎn)擊事件 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); //用戶可以點(diǎn)擊手機(jī)Back鍵取消對話框顯示 dialog.setCancelable(true); //用戶不能通過點(diǎn)擊對話框之外的地方取消對話框顯示 dialog.setCanceledOnTouchOutside(false); return dialog; } //在數(shù)據(jù)源中查找匹配的數(shù)據(jù) 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); // 存在匹配的數(shù)據(jù) if (index != -1) { mSearchList.add(listData.get(i)); Log.i("U--", i + "搜索位置"); searchPosition = i; } } return mSearchList; } //提供匹配后的的數(shù)據(jù)進(jìn)行數(shù)據(jù)回調(diào) 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點(diǎn)擊事件 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 + ""); } }); } //自定義接口進(jìn)行數(shù)據(jù)點(diǎn)擊回傳 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 主要包括輸入框,刪除鍵 ,主要通過監(jiān)聽EditText 的文本以及輸入框的變化,設(shè)置搜索回調(diào)接口來實(shí)現(xiàn)
public class SearchView extends LinearLayout implements View.OnClickListener { /** * 輸入框 */ private EditText etInput; /** * 刪除鍵 */ private ImageView ivDelete; /** * 上下文對象 */ private Context mContext; /** * 搜索回調(diào)接口 */ private onSearchViewListener mListener; /** * 設(shè)置搜索回調(diào)接口 * * @param listener 監(jiān)聽者 */ 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數(shù)據(jù) 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回調(diào)方法 */ public interface onSearchViewListener { boolean onQueryTextChange(String text); } }
3.SearchSelectAdapter
主要實(shí)現(xiàn)條目的點(diǎn)擊事件以及數(shù)據(jù)回調(diào)
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) { // 創(chuàng)建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) { // 綁定數(shù)據(jù)到ViewHolder ItemModel item = itemList.get(position); holder.textView.setText(item.getItemName()); //給條目布局設(shè)置點(diǎn)擊事件 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); } } /** * 接口回調(diào) */ 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="請輸入搜索內(nèi)容" 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.數(shù)據(jù)支持
// 創(chuàng)建數(shù)據(jù)列表 itemList = new ArrayList<>(); itemList.add(new ItemModel("醫(yī)生", false)); itemList.add(new ItemModel("警察", false)); itemList.add(new ItemModel("護(hù)士", false)); itemList.add(new ItemModel("農(nóng)民", false)); itemList.add(new ItemModel("工人", false)); itemList.add(new ItemModel("司機(jī)", 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.實(shí)際應(yīng)用
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(); //設(shè)置Dialog 尺寸 mDialog.setDialogWindowAttr(0.9, 0.9, this); }
三、總結(jié)
后續(xù) Demo 會上傳
到此這篇關(guān)于Android 自定義 Dialog 實(shí)現(xiàn)列表 單選,多選,搜索功能的文章就介紹到這了,更多相關(guān)Android 自定義 Dialog 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android自定義Dialog的2種常見方法
- Android自定義Dialog框樣式
- Android自定義Dialog原理實(shí)例解析
- Android 自定義加載動畫Dialog彈窗效果的示例代碼
- Android自定義底部彈出框ButtomDialog
- android自定義Dialog彈框和背景陰影顯示效果
- Android自定義Dialog實(shí)現(xiàn)通用圓角對話框
- Android自定義dialog 自下往上彈出的實(shí)例代碼
- Android 自定義Dialog去除title導(dǎo)航欄的解決方法
- Android自定義Dialog實(shí)現(xiàn)加載對話框效果
- Android編程自定義AlertDialog樣式的方法詳解
- 解決Android中自定義DialogFragment解決寬度和高度問題
相關(guān)文章
Android中Fragment與Activity的生命周期對比
這篇文章主要介紹了Android中Fragment與Activity的生命周期對比,Fragment是在Activity的基礎(chǔ)之上進(jìn)行設(shè)計的,比Activity多出幾個控制生命周期的回調(diào)函數(shù),需要的朋友可以參考下2016-02-02基于Android6.0實(shí)現(xiàn)彈出Window提示框
這篇文章主要為大家詳細(xì)介紹了基于Android6.0實(shí)現(xiàn)彈出Window提示框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例
本篇文章主要介紹了Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例,非常具有實(shí)用價值,需要的朋友可以參考下2017-11-11Android studio listview實(shí)現(xiàn)列表數(shù)據(jù)顯示 數(shù)據(jù)循環(huán)顯示效果
這篇文章主要介紹了Android studio listview實(shí)現(xiàn)列表數(shù)據(jù)顯示 數(shù)據(jù)循環(huán)顯示功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04Android view更改背景資源與padding消失的問題解決辦法
這篇文章主要介紹了Android view更改背景資源與padding消失的問題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04android中實(shí)現(xiàn)手機(jī)號碼的校驗(yàn)的示例代碼
本篇文章主要介紹了android中實(shí)現(xiàn)手機(jī)號碼的校驗(yàn)的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09Android EditText限制輸入字符的方法總結(jié)
這篇文章主要介紹了 Android EditText限制輸入字符的方法總結(jié)的相關(guān)資料,這里提供了五種方法來實(shí)現(xiàn)并進(jìn)行比較,需要的朋友可以參考下2017-07-07Android使用SharedPreferences存儲數(shù)據(jù)的實(shí)現(xiàn)方法
這篇文章主要介紹了Android使用SharedPreferences存儲數(shù)據(jù)的實(shí)現(xiàn)方法,可實(shí)現(xiàn)針對短信的臨時保存功能,非常簡單實(shí)用,需要的朋友可以參考下2016-06-06