可支持快速搜索筛选的Android自定义选择控件

Android 自定义支持快速搜索筛选的选择控件使用方法,具体如下

项目中遇到选择控件选项过多,需要快速查找匹配的情况。
做了简单的Demo,效果图如下:

源码地址:https://github.com/whieenz/SearchSelect

这个控件是由Dialog+SearchView+ListView实现的。Dialog用来承载选择控件,SearchView实现输入,ListView展示结果。设计概要图如下:

一、自定义Dialog

Dialog布局文件

<?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_weight="1"
    android:background="@drawable/dialog_bg"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <RelativeLayout
      android:layout_width="match_parent"
      android:layout_height="50dp">
      <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_centerVertical="true"
        android:textSize="18sp"
        android:textColor="#000000"
        android:id="@+id/tv_dialog_select_title"/>
      <ImageButton
        android:layout_width="50dp"
        android:layout_height="match_parent"
        android:padding="8dp"
        android:layout_marginRight="10dp"
        android:layout_centerVertical="true"
        android:layout_alignParentRight="true"
        android:scaleType="centerInside"
        android:background="@color/transparent"
        android:src="@drawable/im_search_back"
        android:id="@+id/btn_dialog_select_search"/>
    </RelativeLayout>
    <com.whieenz.searchselect.DialogSearchView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:id="@+id/searchView"
      android:visibility="gone"/>
    <ListView
      android:layout_width="match_parent"
      android:layout_height="0dp"
      android:layout_weight="1"
      android:orientation="vertical"
      android:id="@+id/listview"
      android:layout_gravity="center_horizontal" />
  </LinearLayout>

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:gravity="center"
    android:background="@color/transparent">
    <ImageButton
      android:layout_width="40dp"
      android:layout_height="40dp"
      android:id="@+id/imb_dialog_select_close"
      android:scaleType="centerInside"
      android:src="@drawable/dialog_close"
      android:background="@color/transparent"/>
  </LinearLayout>

</LinearLayout>

Dialog Java文件

package com.whieenz.searchselect;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;

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

/**
 * Created by whieenz on 2017/7/18.
 */

public class SerachSelectDialog extends Dialog {

  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.CENTER;
    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 String singleButtonText;
    private List<String> 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;

    ListView listView;
    //SearchView searchView ;
    DialogSearchView searchView;
    ImageButton searchBtn;
    ImageButton closeBtn;
    TextView titleView;
    private boolean state = false;

    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);
      listView = (ListView)layout.findViewById(R.id.listview);
      //searchView = (SearchView) layout.findViewById(R.id.searchView);
      searchView = (DialogSearchView) layout.findViewById(R.id.searchView);
      searchBtn = (ImageButton) layout.findViewById(R.id.btn_dialog_select_search);
      closeBtn = (ImageButton) layout.findViewById(R.id.imb_dialog_select_close);
      titleView = (TextView) layout.findViewById(R.id.tv_dialog_select_title);
      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 Builder setContentView(View v) {
      this.contentView = v;
      return this;
    }

    public void setListData(List<String> listData) {
      this.listData = listData;
    }

    public Builder setPositiveButton(String positiveButtonText, View.OnClickListener listener) {
      this.positiveButtonText = positiveButtonText;
      this.positiveButtonClickListener = listener;
      return this;
    }

    public Builder setNegativeButton(String negativeButtonText, View.OnClickListener listener) {
      this.negativeButtonText = negativeButtonText;
      this.negativeButtonClickListener = listener;
      return this;
    }

    /**
     * 单按钮对话框和双按钮对话框的公共部分在这里设置
     */
    private SerachSelectDialog create() {
      titleView.setText(title);
      final SearchSelectAdapter sa = new SearchSelectAdapter(context,listData);
      listView.setAdapter(sa);
      listView.invalidate();
      searchBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
          if (!state){
            searchView.setVisibility(View.VISIBLE);
            state = true;
          }else {
            searchView.setVisibility(View.GONE);
            state = false;
          }
        }
      });
      searchView.setDialogSearchViewListener(new DialogSearchView.DialogSearchViewListener() {

        @Override
        public boolean onQueryTextChange(String text) {
          updateLayout(searchItem(text));
          return false;
        }
      });
      closeBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          dialog.dismiss();
        }
      });
      dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
        @Override
        public void onDismiss(DialogInterface dialog) {

        }
      });

      listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
          selectedListiner.onSelected(sa.getItem(position));
          dialog.dismiss();
        }
      });
      dialog.setContentView(layout);
      //用户可以点击手机Back键取消对话框显示
      dialog.setCancelable(true);
      //用户不能通过点击对话框之外的地方取消对话框显示
      dialog.setCanceledOnTouchOutside(false);
      return dialog;

    }
    public List<String> searchItem(String name) {
      ArrayList<String> mSearchList = new ArrayList<String>();
      for (int i = 0; i < listData.size(); i++) {
        int index = listData.get(i).indexOf(name);
        // 存在匹配的数据
        if (index != -1) {
          mSearchList.add(listData.get(i));
        }
      }
      return mSearchList;
    }

    public void updateLayout(List<String> newList) {
      final SearchSelectAdapter sa = new SearchSelectAdapter(context,newList);
      listView.setAdapter(sa);
      listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
          selectedListiner.onSelected(sa.getItem(position));
          dialog.dismiss();
        }
      });
    }

    public void setSelectedListiner(SerachSelectDialog.Builder.OnSelectedListiner selectedListiner) {
      this.selectedListiner = selectedListiner;
    }

    public static abstract class OnSelectedListiner{
      public abstract void onSelected(String String);
    }

    public SerachSelectDialog show() {
      create();
      dialog.show();
      return dialog;
    }
  }
}

二、自定义SearchView

SearchView 布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:gravity="center"
  android:background="#ffffff"
  android:layout_height="50dp">
    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="35dp"
      android:orientation="horizontal"
      android:gravity="center_vertical"
      android:layout_marginLeft="15dp"
      android:layout_marginRight="15dp"
      android:background="@drawable/search_layout_bg">
      <ImageButton
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:id="@+id/imb_search_search"
        android:layout_marginLeft="15dp"
        android:scaleType="centerInside"
        android:src="@drawable/im_search_gray"
        android:background="#F0F0F0" />
      <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="15dp"
        android:id="@+id/et_search_text"
        android:layout_weight="1"
        android:lines="1"
        android:textSize="14sp"
        android:background="@null"
        android:hint="请输入搜索内容"/>

      <ImageButton
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:padding="12.5dp"
        android:id="@+id/imb_search_clear"
        android:layout_marginRight="20dp"
        android:src="@drawable/im_x"
        android:visibility="gone"
        android:scaleType="centerInside"
        android:background="#F0F0F0" />
    </LinearLayout>

</LinearLayout>

SearchView Java代码

package com.whieenz.searchselect;

import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;

/**
 * Created by whieenz on 2017/7/19.
 */

public class DialogSearchView extends LinearLayout implements View.OnClickListener {

  /**
   * 输入框
   */
  private EditText etInput;

  /**
   * 删除键
   */
  private ImageView ivDelete;

  /**
   * 上下文对象
   */
  private Context mContext;

  /**
   * 搜索回调接口
   */
  private DialogSearchViewListener mListener;

  /**
   * 设置搜索回调接口
   *
   * @param listener 监听者
   */
  public void setDialogSearchViewListener(DialogSearchViewListener listener) {
    mListener = listener;
  }

  public DialogSearchView(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 DialogSearchViewListener {
    boolean onQueryTextChange(String text);
  }
}

自定义ListView Adapter

listItem 布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="50dp"
  android:paddingLeft="10dp"
  android:paddingTop="15dp"
  android:paddingBottom="15dp"
  android:orientation="horizontal">
  <TextView
    android:id="@+id/tv_select_info"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:textSize="20sp"
    android:layout_centerInParent="true"
    android:gravity="center"
    android:lines="1"/>

</RelativeLayout>

Adapter 文件

package com.whieenz.searchselect;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.List;

public class SearchSelectAdapter extends BaseAdapter {
  private List<String> Datas;
  private Context context;
  private LayoutInflater inflater;

  public SearchSelectAdapter(Context ctx, List<String> datas){
    this.context = ctx;
    this.Datas = datas;
    this.inflater = LayoutInflater.from(ctx);
  }
  @Override
  public int getCount() {
    return Datas.size();
  }

  @Override
  public String getItem(int i) {
    return Datas.get(i);
  }

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

  @Override
  public View getView(int i, View view, ViewGroup viewGroup) {
    ViewHolder holder = null;
    if (view == null ) {
      view = inflater.inflate(R.layout.list_cell_select_single, null);
      holder = new ViewHolder(view);
      view.setTag(holder);
    } else {
      holder = (ViewHolder) view.getTag();
    }

    holder.info.setText(Datas.get(i));
    return view;
  }

  static class ViewHolder {
    TextView info;
    public ViewHolder(View view) {
      info = view.findViewById(R.id.tv_select_info);
    }
  }

}

MainActivity 实现

布局文件

<?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:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  android:padding="10dp"
  tools:context="com.whieenz.searchselect.MainActivity">

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="150dp"
    android:orientation="horizontal">
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textSize="20sp"
      android:gravity="left"
      android:text="选择结果:"
      />
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textSize="20sp"
      android:textColor="#ff5c5c"
      android:id="@+id/tv_result" />
  </LinearLayout>

  <Button
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:layout_marginTop="20dp"
    android:gravity="center"
    android:textSize="20sp"
    android:textColor="#ffffff"
    android:background="@drawable/btn_bg"
    android:text="打开选择器"
    android:onClick="doSelect"/>
</LinearLayout>

Java文件

package com.whieenz.searchselect;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

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

public class MainActivity extends AppCompatActivity {
 private List<String> mDatas;
 private TextView textView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  textView = (TextView) findViewById(R.id.tv_result);
  initData();
 }

 public void doSelect(View view){
  SerachSelectDialog.Builder alert = new SerachSelectDialog.Builder(this);
  alert.setListData(mDatas);
  alert.setTitle("请选择城市");
  alert.setSelectedListiner(new SerachSelectDialog.Builder.OnSelectedListiner() {
   @Override
   public void onSelected(String info) {
    textView.setText(info);
   }
  });
  SerachSelectDialog mDialog = alert.show();
  //设置Dialog 尺寸
  mDialog.setDialogWindowAttr(0.9,0.9,this);
 }
 /**
  * 初始化数据
  */
 private void initData(){
  mDatas = new ArrayList<>();
  String [] citys = {"武汉","北京","上海","深圳","兰州","成都","天津"};
  for (int i = 0; i < 10; i++) {
   for (int j = 0; j < citys.length; j++) {
    mDatas.add(citys[j]+i);
   }
  }
 }
}

其他配置

Dialog style(样式)

  <style name="selectDialog" parent="@android:style/Theme.Dialog">
    <item name="android:windowNoTitle">true</item>//无标题
    <item name="android:windowBackground">@color/transparent</item>
  </style>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android编程实现变化的双重选择框功能示例

    本文实例讲述了Android编程实现变化的双重选择框功能.分享给大家供大家参考,具体如下: 原理:定义四个RadioGroup,通过第一个RadioGroup的选择来控制其余几个radiogroup的显隐 布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&quo

  • Android滚轮选择时间控件使用详解

    滚轮选择控件 Android自带的选择时间控件有点丑,往往产品和设计都比较嫌弃,希望做成ios一样的滚轮选择,下面是我在NumberPicker的基础上自定义的选择控件,效果如下: 原理 基于NumberPicker实现 动态填充数值 联动 接口监听回调 实现滚轮效果有github上mark比较多的WheelView,但是阅读源码发现数据是一次性填入的,选择时间的话,填入10年就是10*365=3650条数据,也就是new出三千多个TextView,想想都觉得恐怖,肯定是不行的,于是便想到用Nu

  • Android组件实现列表选择框功能

    android提供的列表选择框(Spinner)相当于web端用户注册时的选择下拉框,比如注册候选择省份城市等.如下图便是一个列表选择框 下拉列表的列表选择项能够通过xml文件的android:entries属性指定,或是在java代码中导入,属性android:prompt是列表项的标题. 一    列表项数据: 实际运用当中,很多下拉列表项的数据实际是可知的,可以放在xml资源文件中.这时,开发者可以通过xml属性进行指定数据. 除了资源文件之外,开发者还能够使用适配器适配数据源.(适配器:

  • Android高仿IOS 滚轮选择控件

    最近根据项目需要,整理了一个相对比较全面的 WheelView 使用控件,借用之前看到的一句话来说,就是站在巨人肩膀上,进行了一些小调整. 这里先贴上效果图 一般常用的时间选择格式,,单项选择,以及城市联动,这里基本都可以满足了. 这里把 单项选择,和 日期时间选择 给提出到 Util 类中,代码如下: public class Util { /** * 时间选择回调 */ public interface TimerPickerCallBack { void onTimeSelect(Stri

  • Android开发实现布局中为控件添加选择器的方法

    本文实例讲述了Android开发实现布局中为控件添加选择器的方法.分享给大家供大家参考,具体如下: 在开发过程中,动态交互的一些展示效果可以通过布局中添加选择器实现,这样就可减少Activity等的代码数量,MVP开发中降低耦合性,使开发人员在写代码时只需要关注逻辑处理. 比如:一个按钮,原本背景图片为红色,字体为黑色,点击时候背景图片为黄色,字体改为白色. 这类简单效果在布局时就可以实现: <Button android:id="@+id/btn_start" android:

  • Android 实现IOS 滚轮选择控件的实例(源码下载)

     Android 实现IOS 滚轮选择控件的实例 最近根据项目需要,整理了一个相对比较全面的 WheelView 使用控件,借用之前看到的一句话来说,就是站在巨人肩膀上,进行了一些小调整. 这里先贴上效果图 一般常用的时间选择格式,,单项选择,以及城市联动,这里基本都可以满足了. 这里把 单项选择,和 日期时间选择 给提出到 Util 类中,代码如下: public class Util { /** * 时间选择回调 */ public interface TimerPickerCallBack

  • Android 列表选择框 Spinner详解及实例

    Android 列表选择框 Spinner详解及实例 Spinner 是 Android 的列表选择框,不过 spinner 并不需要显示下拉列表,而是相当于弹出一个菜单供用户选择. Spinner 属性: ● android:spinnerMode:列表显示的模式,有两个选择,为弹出列表(dialog)以及下拉列表(dropdown),如果不特别设置,为下拉列表. ● android:entries:使用<string-array.../>资源配置数据源. ● android:prompt:

  • Android控件BottomSheet实现底边弹出选择列表

    底边弹出一个选择列表这是一个比较常用的选择条件或跳转的很好的方法,可以很好的隐藏各个选项.在需要使用时在底边弹出.而BottomSheet就是这样的一个控件. 使用 1.导入build compile 'com.cocosw:bottomsheet:1.3.0' 2.在res/values/colors.xml文件中添加以下代码: <!--首页item文字颜色--> <color name="colorSubtitle">#999</color> &

  • Android自定义View实现多图片选择控件

    前言 相信很多朋友在开发中都会遇到图片上传的情况,尤其是多图上传,最经典的莫过于微信的图片选择了.所有很多情况下会使用到多图选择,所以就有了这篇文章,今天抽点时间写了个控件.  •支持自定义选择图片的样式  •支持设置图片选择数量  •支持图片预览,删除  •支持图片拍照 先来看看效果 实现分析 假如不定义控件,我们要实现这样一个功能,无非是写个GridView在item点击的时候去显示图片进行选择,在返回界面的时候进行GridView的数据刷新.我们把这些逻辑写在我们自定义的GridView中

  • Android列表选择框Spinner使用方法详解

    安卓提供的列表选择框(Spinner)相当于web端用户注册时的选择下拉框,比如注册候选择省份城市等.如下图便是一个列表选择框 下拉列表的列表选择项能够通过xml文件的android:entries属性指定,或是在java代码中导入,属性android:prompt是列表项的标题. 一 列表项数据 实际运用当中,很多下拉列表项的数据实际是可知的,可以放在xml资源文件中.这时,开发者可以通过xml属性进行指定数据. 除了资源文件之外,开发者还能够使用适配器适配数据源.(适配器:如果您的电脑不能接

  • Android Spinner列表选择框的应用

    Android  Spinner列表选择框的应用 Spinner 是 Android 的列表选择框,不过 spinner 并不需要显示下拉列表,而是相当于弹出一个菜单供用户选择. Spinner 属性: ● android:spinnerMode:列表显示的模式,有两个选择,为弹出列表(dialog)以及下拉列表(dropdown),如果不特别设置,为下拉列表. ● android:entries:使用<string-array.../>资源配置数据源. ● android:prompt:对当

随机推荐