android开发之SearchView

需求

用户点击搜索Action跳转搜索界面,根据用户输入文字,拦截软键盘最后一个键进行搜索,并添加到 历史纪录,搜索编辑框根据文字显示一键删除文字内容按钮,历史纪录列表提供一键清空功能,历史纪录支持item点击搜索,删除单条历史纪录。根据搜索结果展示列表,支持刷新和分页加载更多

实现效果图

分析

根据需求做了一个大概的分析草图(看不清楚还请见谅,了解个大概结构就好),搜索activity嵌入两个碎片HistoryFragment.SearchLisFragment,和一个进度相关的LoadFragment

android开发之SearchView_第1张图片

从实现来说头部头部SearchView控件是一个自定义的LinearLayout,左侧一个ImageView,右侧一个TextView,中间的搜索EditText需要自定义,在自定义EditText时候遇到的第一个坑:无法接受键盘事件,解决方案如下:

setFocusableInTouchMode(true);
setFocusable(true);

根据EditText文字长度改变是否显示delete图标,addTextChangedListener可以满足我们需求,只需要在afterTextChanged方法控制drawable visibility

    //设置删除图片
    private void setDrawable() {
        if(length() < 1)
            setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
        else
            setCompoundDrawablesWithIntrinsicBounds(null, null, deleteDrawable, null);
    }

删除图标的显示搞定了,面临的问题点击删除按钮清楚EditText内容,这里可以根据touch范围判断触摸区域选择拦截触摸事件


    // 处理删除事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (deleteDrawable != null && event.getAction() == MotionEvent.ACTION_UP) {
            int eventX = (int) event.getRawX();
            int eventY = (int) event.getRawY();
            Rect rect = new Rect();
            getGlobalVisibleRect(rect);
            rect.left = rect.right - deleteDrawable.getIntrinsicWidth();
            if(rect.contains(eventX, eventY)) {
                setText("");
                return true;
            }
        }
        return super.onTouchEvent(event);
    }

一生二,二生三生万物,透过该控件我们可以学到很多东西,比如密码输入框,带眼的图标点击可以切换密码显示类型,也就是一个InputType的changed,再比如用户账号弹出下拉框选择曾用名,当然这里就不是在空间内部处理事件,需要一个interface回调到Activity里面处理弹出dialog.对于这一块的我们可以整理出一个EditText系列的库,从抽象CustomEditText 到具体的Impl,以便于我们敏捷开发。

扯远了一点,现在回到正题,SearchView的组装又遇到一个问题,软键盘输入完成后监听事件,需求:要显示label搜索,点击后直接搜索,这里用到的知识主要是EditorInfo(EditorInfo更的资料自己百度)

setImeActionLabel("搜索", EditorInfo.IME_ACTION_SEARCH);
setImeOptions(EditorInfo.IME_ACTION_SEARCH);

跟踪源码发现监听事件setOnEditorActionListener方法,根据actionId 选择拦截

searchEditText.setOnEditorActionListener(new OnEditorActionListener() {

            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_SEARCH) {

                    String result = searchEditText.getText().toString().trim();
                    if(result==null||result.equals("")||result.equals("null")){
                        Toast.makeText(getContext(), "搜索内容不能为空!", Toast.LENGTH_SHORT).show();
                    }else if(getOnHisterListener()!=null){
                        searchEditText.setText("");
                        getOnHisterListener().onSearch(result);
                    }
                }
                return false;
            }
        });

对于搜索我们需要涉及到的功能主要有以下几点:

  • 添加一条历史数据(优先级)
  • 删除一条历史数据
  • 查询所有历史数据
  • 头部返回
  • 搜索服务器网络数据

定义核心接口OnHisterListener


    public interface OnHisterListener{

        void deleteItem(String searchName);

        void clearCacheHistoryRecord();

        void onSearch(String searchName);

        void addHistoryRecord(String searchName);

        void onSearchBack();

    }

根据界面展示,历史纪录界面布局ScrollView+ListView(禁止滑动),ForbidScrollListview 诞生


public class ForbidScrollListview extends ListView{

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


    public ForbidScrollListview(Context context, AttributeSet attrs,
            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    public ForbidScrollListview(Context context) {
        this(context,null,0);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }
}

至于进度的实现我采用官方推荐DialogFragment,转圈进度采用github开源项目CircularProgressView,DialogFragment简单说一下基本的参数配置:

  setCancelable(false);//外部系统返回键屏蔽
  getDialog().setContentView(converView);
  getDialog().setCanceledOnTouchOutside(false);//界面外部区域触摸dialog无法消失
  getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));//背景边框透明

源代码下载



结语

这个demo 只是一个参考,具体问题具体分析,网络数据请求根据自己需求改写,adapter适配也是如此,另外我的接口设计也有不合理之处,缓存操作和网络请求接口应该分开,不过惫懒的我只能纸上谈兵,EditTextCustom库系列控件没完成是我的遗憾,我也就是几分钟的热度,了解了具体实现原理后就没了激情,如果你有开源类似的库,欢迎推荐,在此鸣谢!

你可能感兴趣的:(Android)