精华内容
下载资源
问答
  • Android保存搜索历史

    2016-11-28 14:28:58
    搜索后实时将搜索内容显示到RecyclerView中,可以删除单个历史,也可以清空全部,点击其中一项显示到EditText中。将会涉及到的内容有: RecyclerView使用方法 adapter接口 SharedPerference 我们先看一下布局: R....
    在我项目中,有这样一个功能实现:先看图
    

    这里写图片描述

    搜索后实时将搜索内容显示到RecyclerView中,可以删除单个历史,也可以清空全部,点击其中一项显示到EditText中。
    
    将会涉及到的内容有:
    RecyclerView使用方法
    adapter接口
    SharedPerference
    我们先看一下布局:
    

    R.layout.activity_favo_add

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/activity_background"
        android:orientation="vertical"
        tools:context="com.mymap.practice.ui.aty.favorite.FavoAddActivity">
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/activity_favo_add_tb_toolbar"
            style="@style/toolbar_style"
            android:focusable="true"
            android:focusableInTouchMode="true">
    
            <EditText
                android:id="@+id/activity_favo_add_et_search"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginRight="50dp"
                android:background="@drawable/edit_backgroud"
                android:drawableLeft="@mipmap/search_icon"
                android:drawablePadding="@dimen/padding10"
                android:hint="@string/favo_search_hint"
                android:imeOptions="actionSearch"
                android:maxLength="30"
                android:maxLines="1"
                android:padding="@dimen/padding10"
                android:singleLine="true"
                android:textColor="@color/ios_btntext_blue"
                android:textColorHint="@color/gray"/>
        </android.support.v7.widget.Toolbar>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@+id/activity_favo_add_tv_delAll"
            android:layout_below="@id/activity_favo_add_tb_toolbar"
            android:divider="@mipmap/input_line"
            android:orientation="vertical"
            android:showDividers="middle">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="14dp"
                android:layout_marginLeft="14dp"
                android:layout_marginTop="14dp"
                android:drawableLeft="@mipmap/search_icon"
                android:drawablePadding="10dp"
                android:text="@string/see_history"
                android:textColor="@color/ios_btntext_blue"
                android:textSize="@dimen/textSize18"/>
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/activity_favo_add_rv_list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        </LinearLayout>
    
        <TextView
            android:id="@id/activity_favo_add_tv_delAll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:drawableStart="@mipmap/ic_del_all"
            android:gravity="center"
            android:padding="10dp"
            android:text="@string/del_all"/>
    
    </RelativeLayout>
    
    《!-- item布局 --》R.layout.item_favo_add
    <?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="wrap_content"
                    android:gravity="center_vertical"
                  android:orientation="horizontal">
    
        <TextView
            android:id="@+id/item_favo_add_tv_history"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:paddingLeft="30dp"
            android:textSize="@dimen/textSize18"/>
    
        <ImageView
            android:padding="10dp"
            android:id="@+id/item_favo_add_iv_del"
            android:src="@mipmap/ic_del_all"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    
    </LinearLayout>
    
    布局很简单,接下来我们先看一下Adapter吧
    
        public interface FavoAddClick
        {
            void onItemClick(View view, int position);
    
            void onDelClick(View view, int position);
        }
    
        public void setOnItemClickListener(FavoAddClick mOnItemClickListener)
        {
            this.listener = mOnItemClickListener;
        }
    
        // 删除一条
        public void removeData(int position)
        {
            list.remove(position); // 删掉对应的数据源
            notifyItemRemoved(position);
            notifyItemRangeChanged(position, getItemCount()); //刷 新
        }
    
        // 清空
        public void remoceAll()
        {
            list.clear();
        }
    
        // 添加一条数据
        public void add(String item, int position)
        {
            list.add(position, item);
            notifyItemInserted(position);
        }
    
    在接口中,定义了点击事件和删除事件。注释都写得很清楚。其他的Adapter代码:
    
        public FavoAddAdapter(Context context, List<String> list)
        {
            mInflater = LayoutInflater.from(context);
            this.context = context;
            this.list = list;
        }
    
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
        {
            MyViewHolder holder = new MyViewHolder(mInflater.inflate(
                    R.layout.item_favo_add, parent, false));
            return holder;
        }
    
        @Override
        public void onBindViewHolder(final MyViewHolder holder, final int position)
        {
            holder.tv_title.setText(list.get(position).toString());//为控件绑定数据
    
            if (null != listener)
            {
                final int pos = holder.getLayoutPosition();
    
                holder.itemView.setOnClickListener(new View.OnClickListener()
                {
                    @Override
                    public void onClick(View v)
                    {
                        listener.onItemClick(holder.itemView, pos);
                    }
                });
                holder.iv_del.setOnClickListener(new View.OnClickListener()
                {
                    @Override
                    public void onClick(View v)
                    {
                        listener.onDelClick(v, position);
                        removeData(pos);
                    }
                });
            }
    
        }
    
        @Override
        public int getItemCount()
        {
            return list == null ? 0 : list.size();
        }
    
        class MyViewHolder extends RecyclerView.ViewHolder
        {
            TextView tv_title;
            ImageView iv_del;
    
            public MyViewHolder(final View itemView)
            {
                super(itemView);
                tv_title = (TextView) itemView.findViewById(R.id.item_favo_add_tv_history);
                iv_del = (ImageView) itemView.findViewById(R.id.item_favo_add_iv_del);
            }
        }
    
    
    OK,在类中,我们要在onCreate中初始化控件:
        private Activity activity = FavoAddActivity.this;
    private SimpleDateFormat mFormat;
    private FavoAddAdapter mAdapter;
    private List<String> list; // 存放搜索内容的list
    
        /**
         * 初始化控件
         */
        private void initWidget()
        {
            //toolbar的控件
            setSupportActionBar(mToolbar);
            getSupportActionBar().setDisplayShowTitleEnabled(false); // Toolbar掉标题栏
            getSupportActionBar().setDisplayHomeAsUpEnabled(true); // 显示返回按钮
    
            rv_list.setHasFixedSize(true); // 确定了RecyclerView的高度此属性会提升性能
            rv_list.setLayoutManager(new LinearLayoutManager(this));
            rv_list.setItemAnimator(new DefaultItemAnimator());// 设置item动画
            rv_list.addItemDecoration(new DividerGridItemDecoration(this)); //分割线
            showSearchHistory(); // 显示搜索历史
        }
    
    重点就是搜索历,我们先看一下搜索历史是如何显示的:
    
        /**
         * 从SP查询搜索历史,并按时间显示
         */
        private void showSearchHistory()
        {
            Map<String, String> hisAll = (Map<String, String>) SearchHistoryUtils.getAll(activity);
            //将key排序升序
            Object[] keys = hisAll.keySet().toArray();
            Arrays.sort(keys);
            int keyLeng = keys.length;
            //这里计算 如果历史记录条数是大于 可以显示的最大条数,则用最大条数做循环条件,防止历史记录条数-最大条数为负值,数组越界
            int hisLeng = keyLeng > HISTORY_MAX ? HISTORY_MAX : keyLeng;
            list = new ArrayList<>();
            for (int i = 1; i <= hisLeng; i++)
            {
                list.add(hisAll.get(keys[keyLeng - i]));
            }
    
            mAdapter = new FavoAddAdapter(activity, list);
            rv_list.setAdapter(mAdapter);
        }
    
    其中,SearchHistoryUtils工具实现了按时间排序显示搜索历史,支持最大搜索历史条数,接下来将取到的数据填充到Adapter中。
    接下来,看看监听事件:
    
    // 搜索
            et_search.setOnEditorActionListener(new TextView.OnEditorActionListener()
            {
                @Override
                public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
                {
                    if (actionId == EditorInfo.IME_ACTION_SEARCH)
                    {
                        // 先隐藏键盘
                        KeyboardUtils.closeKeyboard(activity, et_search); // 显示键盘
                        // TODO Connect Service TO Search
    			此处做联网搜索操作
                        // 将搜索记录保存到SP中
                        saveSearchHistory(et_search.getText().toString().trim());
                        mAdapter.add( et_search.getText().toString() , 0 ); //插入数据源
                        return true;
                    }
                    return false;
                }
            });
    // 适配器点击事件
            mAdapter.setOnItemClickListener(new FavoAddAdapter.FavoAddClick()
            {
                @Override
                public void onItemClick(View view, int position) // 单击事件
                {
                    et_search.setText(list.get(position).toString());
                    et_search.requestFocus(); // 手动获得EditText焦点
                    KeyboardUtils.openKeyboard(activity, et_search); // 显示键盘
                    et_search.setSelection(list.get(position).toString().length()); // 将EditText光标移动到最后
    
                }
    
                @Override
                public void onDelClick(View view, int position) // 删除
                {
                    // 获取map中value对应的key,然后删除key对应的某条数据
                    String key = SearchHistoryUtils.valueGetKey(SearchHistoryUtils.getAll(activity), list.get(position).toString());
                    SearchHistoryUtils.remove(activity, key);
                }
            });
            // 清空
            tv_delAll.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    clearsearchHistory();
                    mAdapter.remoceAll();
                }
            });
    
    KeyboardUtils是写好的工具类,实现了弹出、隐藏键盘。文章末尾我将会提供下载地址。
    
    最后看一下对于map删除的操作过程:
    
        /**
         * 将历史记录保存至sp中,key=当前时间(20160302133455,便于排序) ,value=关键字
         *
         * @param keyWords 搜索内容
         */
        private void saveSearchHistory(String keyWords)
        {
            //保存之前要先查询sp中是否有该value的记录,有则删除.这样保证搜索历史记录不会有重复条目
            Map<String, String> historys = (Map<String, String>) SearchHistoryUtils.getAll(activity);
            for (Map.Entry<String, String> entry : historys.entrySet())
            {
                if (keyWords.equals(entry.getValue()))
                {
                    SearchHistoryUtils.remove(activity, entry.getKey());
                }
            }
            mFormat = new SimpleDateFormat("yyyyMMddHHmmss");
            // 以时间为key,保存搜索记录
            SearchHistoryUtils.put(activity, "" + mFormat.format(new Date()), keyWords);
            // 刷新适配器
            mAdapter.notifyDataSetChanged();
        }
    
    
        /**
         * 清除历史记录
         */
        private void clearsearchHistory()
        {
            SearchHistoryUtils.clear(this);
            //同时刷新历史记录显示页面
            mAdapter.notifyDataSetChanged();
        }
    /**
    * 最后是标题栏返回按钮监听事件
    **/
        @Override
        public boolean onOptionsItemSelected(MenuItem item)
        {
            switch (item.getItemId())
            {
                case android.R.id.home:
                    ActivityCollector.removeActivity(this);
                    break;
            }
            return super.onOptionsItemSelected(item);
        }
    
    哦,对了,Toolbar只有在AppCompatActivity中才可以使用哦。
    本文使用的SearchHistoryUtils是基于某同志分享的工具类基础上加了根据value得到key的方法,被很多人转载却并没有标注作者是谁,在此对作者表示感谢。
    由于时间有限并没有写成Demo而是把用到的类和布局抽了出来,在此奉上
    

    http://download.csdn.net/detail/u012552275/9695668


    展开全文
  • thinkphp5 搜索分页保留搜索条件

    千次阅读 2018-01-07 17:10:19
    在项目中,碰到模糊搜索出来的记录超过一页,翻到下一页后直接显示全部记录搜索条件丢失的情况,thinkphp5框架中,要想分页搜索附带条件,很简单就可以实现。 1、在搜索的模板页面,记住肯定用的传输方式是 get...

    在项目中,碰到模糊搜索出来的记录超过一页,翻到下一页后直接显示全部记录, 搜索条件丢失的情况,thinkphp5框架中,要想分页搜索附带条件,很简单就可以实现。

    1、在搜索的模板页面,记住肯定用的传输方式是  get;

    解释:使用 get 方法提交表单,在后台将查询条件作为 paginate() 方法的参数传入,这样查询条件就会写入分页标签的URL中。后台即可以通过表单也可以通过这个URL得到查询条件。

    这样就可以实现类似于:
    public/admin/admin/index/id/3/page/13.html 

    2、分页查询方式,传递参数:Db::name("table")->where("XXXX")->paginate(10,false,['query'=>request()->param()]); 

    在model里面也可以写成:(举个例子)


    这样就能实现分页搜索携带参数!

    展开全文
  • Android开发中,类似下图的搜索功能非常常见今天,我将手把手教大家实现一款 封装了 历史搜索记录功能 & 样式 的Android 自定义搜索框 开源库,希望你们会喜欢。 已在Github开源:地址:SearchView,欢迎 Star ! ...

    前言

    • Android开发中,类似下图的搜索功能非常常见

    搜索功能

    • 今天,我将手把手教大家实现一款 封装了 历史搜索记录功能 & 样式Android 自定义搜索框 开源库,希望你们会喜欢。

    示意图

    已在Github开源:地址:SearchView,欢迎 Star


    目录

    示意图


    1. 简介

    一款封装了 历史搜索记录功能 & 样式Android自定义搜索框

    已在Github开源:地址:SearchView,欢迎 Star

    示意图


    2. 需求场景

    • 在开始coding前, 理解好用户的需求场景 有助于我们更好地设计 & 实现功能
    • 需求场景如下

    示意图


    3. 业务流程图

    根据场景,梳理出来的功能业务流程图如下:

    示意图


    4. 功能需求

    根据功能的业务流程图,得出功能需求如下

    4.1 功能列表

    示意图

    4.2 功能原型图

    示意图

    4.3 示意图

    示意图


    5. 总体设计

    下面,将根据功能需求给出特定的技术解决方案

    5.1 总体解决方案

    示意图

    5.2 项目结构说明

    • 项目工程示意图

    示意图

    先下载Demo再阅读,效果会更好:Carson_Ho的Github地址:Search_Layout

    • 结构说明
    文件类型作用
    SearchView.java搜索框所有功能的实现
    RecordSQLiteOpenHelper.java创建、管理数据库 & 版本控制
    EditText_Clear.java自定义EdiText,丰富了自定义样式 & 一键删除
    ICallBack.java点击搜索按键后的接口回调方法
    bCallBack.java点击返回按键后的接口回调方法
    SearchListView.java解决ListView & ScrollView的嵌套冲突
    search_layout.xml搜索框的布局

    6. 功能详细设计

    下面将给出详细的功能逻辑

    6.1 关键字搜索

    • 描述:根据用户输入的搜索字段进行结果搜索
    • 原型图

    注:关键字搜索功能是因人而异的,所以本源码仅留出接口供开发者实现,不作具体实现

    示意图

    • 源码分析

    分析1:EditText_Clear.java

    • 作用:自定义EdiText,与系统自带的EdiText对比:多了左侧图片 & 右侧图片设置、一键清空EdiText内容功能
    • 具体代码如下:
    public class EditText_Clear extends android.support.v7.widget.AppCompatEditText {
        /**
         * 步骤1:定义左侧搜索图标 & 一键删除图标
         */
        private Drawable clearDrawable,searchDrawable;
    
        public EditText_Clear(Context context) {
            super(context);
            init();
            // 初始化该组件时,对EditText_Clear进行初始化 ->>步骤2
        }
    
        public EditText_Clear(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public EditText_Clear(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        /**
         * 步骤2:初始化 图标资源
         */
        private void init() {
            clearDrawable = getResources().getDrawable(R.drawable.delete);
            searchDrawable = getResources().getDrawable(R.drawable.search);
    
            setCompoundDrawablesWithIntrinsicBounds(searchDrawable, null,
                    null, null);
            // setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom)介绍
            // 作用:在EditText上、下、左、右设置图标(相当于android:drawableLeft=""  android:drawableRight="")
            // 注1:setCompoundDrawablesWithIntrinsicBounds()传入的Drawable的宽高=固有宽高(自动通过getIntrinsicWidth()& getIntrinsicHeight()获取)
            // 注2:若不想在某个地方显示,则设置为null
            // 此处设置了左侧搜索图标
    
            // 另外一个相似的方法:setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)介绍
            // 与setCompoundDrawablesWithIntrinsicBounds()的区别:可设置图标大小
              // 传入的Drawable对象必须已经setBounds(x,y,width,height),即必须设置过初始位置、宽和高等信息
              // x:组件在容器X轴上的起点 y:组件在容器Y轴上的起点 width:组件的长度 height:组件的高度
        }
    
    
        /**
         * 步骤3:通过监听复写EditText本身的方法来确定是否显示删除图标
         * 监听方法:onTextChanged() & onFocusChanged()
         * 调用时刻:当输入框内容变化时 & 焦点发生变化时
         */
    
        @Override
        protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
            super.onTextChanged(text, start, lengthBefore, lengthAfter);
            setClearIconVisible(hasFocus() && text.length() > 0);
            // hasFocus()返回是否获得EditTEXT的焦点,即是否选中
            // setClearIconVisible() = 根据传入的是否选中 & 是否有输入来判断是否显示删除图标->>关注1
        }
    
        @Override
        protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
            super.onFocusChanged(focused, direction, previouslyFocusedRect);
            setClearIconVisible(focused && length() > 0);
            // focused = 是否获得焦点
            // 同样根据setClearIconVisible()判断是否要显示删除图标
        }
    
        /**
         * 关注1
         * 作用:判断是否显示删除图标
         */
        private void setClearIconVisible(boolean visible) {
            setCompoundDrawablesWithIntrinsicBounds(searchDrawable, null,
                    visible ? clearDrawable : null, null);
        }
    
        /**
         * 步骤4:对删除图标区域设置点击事件,即"点击 = 清空搜索框内容"
         * 原理:当手指抬起的位置在删除图标的区域,即视为点击了删除图标 = 清空搜索框内容
         */
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                // 原理:当手指抬起的位置在删除图标的区域,即视为点击了删除图标 = 清空搜索框内容
                case MotionEvent.ACTION_UP:
                    Drawable drawable = clearDrawable;
                    if (drawable != null && event.getX() <= (getWidth() - getPaddingRight())
                            && event.getX() >= (getWidth() - getPaddingRight() - drawable.getBounds().width())) {
                        setText("");
                    }
                    // 判断条件说明
                    // event.getX() :抬起时的位置坐标
                    // getWidth():控件的宽度
                    // getPaddingRight():删除图标图标右边缘至EditText控件右边缘的距离
                    // 即:getWidth() - getPaddingRight() = 删除图标的右边缘坐标 = X1
                    // getWidth() - getPaddingRight() - drawable.getBounds().width() = 删除图标左边缘的坐标 = X2
                    // 所以X1与X2之间的区域 = 删除图标的区域
                    // 当手指抬起的位置在删除图标的区域(X2=<event.getX() <=X1),即视为点击了删除图标 = 清空搜索框内容
                    // 具体示意图请看下图
                    break;
            }
            return super.onTouchEvent(event);
        }
    
    
    }
    

    示意图

    对于含有一键清空功能 & 更多自定义样式的EditText自定义控件具体请看我的另外一个简单 & 好用的开源组件:Android自定义EditText:手把手教你做一款含一键删除&自定义样式的SuperEditText

    分析2:SearchListView.java

    • 作用:解决 ListView & ScrollView 的嵌套冲突
    • 具体代码如下:
    public class Search_Listview extends ListView {
        public Search_Listview(Context context) {
            super(context);
        }
    
        public Search_Listview(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public Search_Listview(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        // 通过复写其onMeasure方法,达到对ScrollView适配的效果
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                    MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
        }
    
    }
    

    分析3: 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="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:focusableInTouchMode="true"
        android:orientation="vertical">
    
        <LinearLayout
            android:id="@+id/search_block"
            android:layout_width="match_parent"
            android:layout_height="10dp"
            android:orientation="horizontal"
            android:paddingRight="10dp"
            >
    
            // 返回按钮
            <ImageView
                android:layout_width="38dp"
                android:layout_height="38dp"
                android:layout_gravity="center_vertical"
                android:padding="10dp"
                android:src="@drawable/back" />
    
            // 搜索框(采用上面写的自定义EditText
            <scut.carson_ho.searchview.EditText_Clear
                android:id="@+id/et_search"
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="264"
                android:background="@null"
                android:drawablePadding="8dp"
                android:gravity="start|center_vertical"
                android:imeOptions="actionSearch"
                android:singleLine="true"
                // 最后2行 = 更换输入键盘按钮:换行 ->>搜索
                />
            
        </LinearLayout>
    
        // 下方搜索记录布局 = ScrollView+Listview
        <ScrollView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
    
                    // Listview布局(采用上述讲解的SearchListView,解决了与ScrollView的冲突)
                    <scut.carson_ho.searchview.SearchListView
                        android:id="@+id/listView"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">
    
                    </scut.carson_ho.searchview.SearchListView>
    
                <TextView
                    android:id="@+id/tv_clear"
                    android:layout_width="match_parent"
                    android:layout_height="40dp"
                    android:background="#F6F6F6"
                    android:gravity="center"
                    android:visibility="invisible"
                    android:text="清除搜索历史" />
            </LinearLayout>
        </ScrollView>
    </LinearLayout>
    
    

    分析4:ICallBack.java、bCallBack.java

    • 作用:搜索按键、返回按键回调接口
    • 具体代码如下:
    /**
     * ICallBack.java
     */
    public interface ICallBack {
        void SearchAciton(String string);
    
    }
    
    /**
     * bCallBack.java
     */
    public interface bCallBack {
        void BackAciton();
    }
    

    分析5:SearchView.java

    • 作用:涵盖搜索框中所有功能,此处主要讲解 关键字搜索 功能实现
    • 具体代码如下:
    
    /**
       * 步骤1:初始化成员变量
       */
    
        // 搜索框组件
        private EditText et_search; // 搜索按键
        private LinearLayout search_block; // 搜索框布局
        private ImageView searchBack; // 返回按键
    
        // 回调接口
        private  ICallBack mCallBack;// 搜索按键回调接口
        private  bCallBack bCallBack; // 返回按键回调接口
    
        // ListView列表 & 适配器
        private SearchListView listView;
        private BaseAdapter adapter;
    
       /**
         * 步骤2:绑定 搜索框 组件
         */
        
        private void initView(){
            
            // 1. 绑定R.layout.search_layout作为搜索框的xml文件
            LayoutInflater.from(context).inflate(R.layout.search_layout,this);
            
            // 2. 绑定搜索框EditText
            et_search = (EditText) findViewById(R.id.et_search);
    
            // 3. 搜索框背景颜色
            search_block = (LinearLayout)findViewById(R.id.search_block);
       
            // 4. 历史搜索记录 = ListView显示
            listView = (Search_Listview) findViewById(R.id.listView);
            
            // 5. 删除历史搜索记录 按钮
            tv_clear = (TextView) findViewById(R.id.tv_clear);
            tv_clear.setVisibility(INVISIBLE); // 初始状态 = 不可见
            
        }
    
    /**
        * 步骤3
        * 监听输入键盘更换后的搜索按键
        * 调用时刻:点击键盘上的搜索键时
        */
            et_search.setOnKeyListener(new View.OnKeyListener() {
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
                        
                        // 点击搜索按键后,根据输入的搜索字段进行查询
                        // 注:由于此处需求会根据自身情况不同而不同,所以具体逻辑由开发者自己实现,此处仅留出接口
                        if (!(mCallBack == null)){
                            mCallBack.SearchAciton(et_search.getText().toString());
                        }
                        Toast.makeText(context, "需要搜索的是" + et_search.getText(), Toast.LENGTH_SHORT).show();
                        
                       
                    }
                    return false;
                }
            });
    
     /**
         * 步骤4:回调接口
         */
    
    // 搜索按键回调接口
    public interface ICallBack {
        void SearchAciton(String string);
    }
    
    // 返回按键接口回调
        public void setOnClickBack(bCallBack bCallBack){
            this.bCallBack = bCallBack;
    
        }
    

    6.2 实时显示历史搜索记录

    • 描述:包括 最近搜索记录 & 相似搜索记录
    • 原型图

    示意图

    • 源码分析

    分析1:RccordSQLiteOpenHelper.java

    • 作用:创建、管理数据库 & 版本控制

    该数据库用于存储用户的搜索历史记录

    • 具体代码如下:

    对于Android SQLlite数据库的操作请看文章:Android:SQLlite数据库操作最详细解析

    
    // 继承自SQLiteOpenHelper数据库类的子类
    public class RecordSQLiteOpenHelper extends SQLiteOpenHelper {
    
        private static String name = "temp.db";
        private static Integer version = 1;
    
        public RecordSQLiteOpenHelper(Context context) {
            super(context, name, null, version);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            // 打开数据库 & 建立一个名为records的表,里面只有一列name来存储历史记录:
            db.execSQL("create table records(id integer primary key autoincrement,name varchar(200))");
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
    
    

    分析2:SearchView.java

    • 作用:涵盖搜索框中所有功能,此处主要讲解 实时显示历史搜索记录 功能实现
    • 具体代码如下:
       /**
         * 步骤1:初始化变量
         */
        // 用于存放历史搜索记录
        private RecordSQLiteOpenHelper helper ;
        private SQLiteDatabase db;
    
        // ListView列表 & 适配器
        private SearchListView listView;
        listView = (SearchListView) findViewById(R.id.listView);
        private BaseAdapter adapter;
    
        // 实例化数据库SQLiteOpenHelper子类对象
        helper = new RecordSQLiteOpenHelper(context);
    
    /**
      * 步骤2:搜索框的文本变化实时监听
      */
            et_search.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
                }
    
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
    
                }
    
                // 输入文本后调用该方法
                @Override
                public void afterTextChanged(Editable s) {
                    // 每次输入后,模糊查询数据库 & 实时显示历史搜索记录
                    // 注:若搜索框为空,则模糊搜索空字符 = 显示所有的搜索历史
                    String tempName = et_search.getText().toString();
                    queryData(tempName); // ->>关注1
    
                }
            });
    
    /**
       * 步骤3:搜索记录列表(ListView)监听
       * 即当用户点击搜索历史里的字段后,会直接将结果当作搜索字段进行搜索
       */
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    
                    // 获取用户点击列表里的文字,并自动填充到搜索框内
                    TextView textView = (TextView) view.findViewById(android.R.id.text1);
                    String name = textView.getText().toString();
                    et_search.setText(name);
                    Toast.makeText(context, name, Toast.LENGTH_SHORT).show();
                }
            });
    
    
    /**
       * 关注1
       * 模糊查询数据 & 显示到ListView列表上
       */
        private void queryData(String tempName) {
    
            // 1. 模糊搜索
            Cursor cursor = helper.getReadableDatabase().rawQuery(
                    "select id as _id,name from records where name like '%" + tempName + "%' order by id desc ", null);
            // 2. 创建adapter适配器对象 & 装入模糊搜索的结果
            adapter = new SimpleCursorAdapter(context, android.R.layout.simple_list_item_1, cursor, new String[] { "name" },
                    new int[] { android.R.id.text1 }, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
            // 3. 设置适配器
            listView.setAdapter(adapter);
            adapter.notifyDataSetChanged();
            
            System.out.println(cursor.getCount());
            // 当输入框为空 & 数据库中有搜索记录时,显示 "删除搜索记录"按钮
            if (tempName.equals("") && cursor.getCount() != 0){
                tv_clear.setVisibility(VISIBLE);
            }
            else {
                tv_clear.setVisibility(INVISIBLE);
            };
    
        }
    

    6.3 删除历史搜索记录

    • 描述:清空所有历史搜索记录
    • 原型图

    示意图

    • 源码分析
      /**
         * 步骤1:初始化变量
         */
          private TextView tv_clear;  // 删除搜索记录按键
          tv_clear = (TextView) findViewById(R.id.tv_clear);
          tv_clear.setVisibility(INVISIBLE);// 初始状态 = 不可见
    
    /**
      * 步骤2:设置"清空搜索历史"按钮
      */
            tv_clear.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
    
                    // 清空数据库->>关注2
                    deleteData();
                    // 模糊搜索空字符 = 显示所有的搜索历史(此时是没有搜索记录的) & 显示该按钮的条件->>关注3
                    queryData("");
                }
            });
    
    /**
      * 关注2:清空数据库
      */
        private void deleteData() {
    
            db = helper.getWritableDatabase();
            db.execSQL("delete from records");
            db.close();
            tv_clear.setVisibility(INVISIBLE);
        }
    
     /**
         * 关注3
         * 模糊查询数据、显示到ListView列表上 & 确定显示 “删除历史按钮”条件
         */
        private void queryData(String tempName) {
            // 步骤1、2、3上面已经提到了,直接看步骤4
            // 1. 模糊搜索
            Cursor cursor = helper.getReadableDatabase().rawQuery(
                    "select id as _id,name from records where name like '%" + tempName + "%' order by id desc ", null);
            // 2. 创建adapter适配器对象 & 装入模糊搜索的结果
            adapter = new SimpleCursorAdapter(context, android.R.layout.simple_list_item_1, cursor, new String[] { "name" },
                    new int[] { android.R.id.text1 }, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
            // 3. 设置适配器
            listView.setAdapter(adapter);
            adapter.notifyDataSetChanged();
    
            // 4. 当输入框为空 & 数据库中有搜索记录时,才显示 "删除搜索记录"按钮
            if (tempName.equals("") && cursor.getCount() != 0){
                tv_clear.setVisibility(VISIBLE);
            }
            else {
                tv_clear.setVisibility(INVISIBLE);
            };
    
        }
    

    6.4 保存历史搜索记录

    • 描述:将用户输入的搜索字段保存到数据库中
    • 原型图

    示意图

    • 源码分析
    /**
      * 监听输入键盘更换后的搜索按键
      * 调用时刻:点击键盘上的搜索键时
      */
            et_search.setOnKeyListener(new View.OnKeyListener() {
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
                        // 步骤1已经讲解过,直接看步骤2
    
                        // 1. 点击搜索按键后,根据输入的搜索字段进行查询
                        // 注:由于此处需求会根据自身情况不同而不同,所以具体逻辑由开发者自己实现,此处仅留出接口
                        if (!(mCallBack == null)){
                         mCallBack.SearchAciton(et_search.getText().toString());
                        }
                        Toast.makeText(context, "需要搜索的是" + et_search.getText(), Toast.LENGTH_SHORT).show();
    
                        // 2. 点击搜索键后,对该搜索字段在数据库是否存在进行检查(查询)->> 关注3
                        boolean hasData = hasData(et_search.getText().toString().trim());
                        // 3. 若存在,则不保存;若不存在,则将该搜索字段保存(插入)到数据库,并作为历史搜索记录
                        if (!hasData) {
                            insertData(et_search.getText().toString().trim()); // ->>关注4
                            queryData("");
                        }
                    }
                    return false;
                }
            });
    
    /**
      * 关注3
      * 检查数据库中是否已经有该搜索记录
      */
        private boolean hasData(String tempName) {
            // 从数据库中Record表里找到name=tempName的id
            Cursor cursor = helper.getReadableDatabase().rawQuery(
                    "select id as _id,name from records where name =?", new String[]{tempName});
            //  判断是否有下一个
            return cursor.moveToNext();
        }
    
    /**
      * 关注4
      * 插入数据到数据库,即写入搜索字段到历史搜索记录
      */
        private void insertData(String tempName) {
            db = helper.getWritableDatabase();
            db.execSQL("insert into records(name) values('" + tempName + "')");
            db.close();
        }
    

    7. 具体使用

    示意图


    8. 贡献代码

    • 希望你们能和我一起完善这款简单 & 好用的SearchView控件,具体请看:贡献说明
    • 关于该开源项目的意见 & 建议可在Issue上提出。欢迎 Star

    9. 总结

    • 相信你一定会喜欢上 这款简单 & 好用的SearchView控件

    已在Github上开源:SearchView,欢迎 Star

    • 下一篇文章我将继续进行一些有趣的自定义View实例讲解,感兴趣的同学可以继续关注carson_ho的微信公众号
      示意图
      示意图

    更多简单好用的开源库:简单 & 好用的开源组件:
    自定义EditText:手把手教你做一款含一键删除&自定义样式的SuperEditText


    请 帮顶 或 评论点赞!因为你的鼓励是我写作的最大动力!

    展开全文
  • 要实现的页面效果: ...清空历史记录 思路:  1.首先显示历史记录需要一个数组searchItems,通过ng-repeat显示每一个<li> <label class="item item-input input-search"> <in...

    要实现的页面效果:

     

      1.显示历史搜索,

      2.最近搜索的排在最前,

      2.最多显示8条历史

      4.清空历史记录

    思路:

      1.首先显示历史记录需要一个数组searchItems,通过ng-repeat显示每一个<li>

     <label class="item item-input input-search">
           <input ng-focus="showSearch(i)" ng-blur="hideSearch(i)" type="text" ng-model="searchName" placeholder="">
           <button class="button button-small button-search-jd" on-tap="search()">{{'jifen.search' | i18next}}</button>
     </label> 
     <div ng-show="searchHistory" class="search-history-wrap">
         <p><i class="icon ion-ios-clock-outline"></i>&nbsp;{{'jifen.recentSearch' | i18next}}</p>
         <ul class="search-history-list">
          <li ng-repeat="sItem in searchItems" on-tap="historyName($event)">{{sItem}}</li>
           <!--<li>杯子</li>
           <li>电脑</li>
           <li>笔记本</li>
           <li>金龙鱼食用油</li>
           <li>杯子</li>
           <li>电脑</li>
           <li>笔记本</li>
           <li>金龙鱼食用油</li>-->
         </ul>
         <p class="text-right">
             <button class="button button-outline button-light del-search" on-tap="delSearch()">{{'jifen.delSearch' | i18next}}</button>
         </p>
     </div>

        注:关于搜索实现,见:angularjs+ionic的app端分页和条件

      2.js实现

    //商城列表-搜索历史弹框
            $scope.searchHistory=false;
            $scope.searchItems = [];//初始化历史列表数组
            $scope.showSearch = function(i) {
            
                $scope.searchHistory=true;
                
            };
            $scope.hideSearch = function(i) {
                
                $scope.searchHistory=false;
                
            };

       每次搜索都会想数组$scope.searchItems中添加搜索的记录searchName,并且做判断,1.最多8条记录;2.最近搜索的添加在数组头部;3如果搜索历史中已经有,则不添加,但替换到最前位置。

            //搜索事件
            $scope.search = function() {
                if($scope.searchName != undefined && $scope.searchName != "" && $scope.searchName != null) {
                    sName = $scope.searchName;
                    currentPage = 0;
    //                intData = [];
                    intData.splice(0,intData.length);//清空数组
                    $scope.loadMore();
                    
                    //搜索记录
                    if($scope.searchItems.length < 8 && $scope.searchItems.indexOf(sName) < 0){//$scope.searchItems.indexOf(sName),类似string的方法,获取索引坐标
                        $scope.searchItems.unshift(sName);//向数组的开头添加一个或更多元素,并返回新的长度
                    }else if($scope.searchItems.length >= 8 && $scope.searchItems.indexOf(sName) < 0){
                        $scope.searchItems.pop();//删除并返回数组的最后一个元素
                        $scope.searchItems.unshift(sName);
                    }else if($scope.searchItems.indexOf(sName) >= 0){
                        $scope.searchItems.splice($scope.searchItems.indexOf(sName),1);
                        $scope.searchItems.unshift(sName);
                    }
                }
            }

       点击列表中的某一个,会跳转搜索

    //历史搜索
            $scope.historyName = function(event) {
                $scope.searchName = event.target.innerHTML;
                $scope.search();
            }

        注:绑定在 li 上的方法:on-tap="historyName($event)",接受 $event 参数,你可以利用它获取当前被点击的 li

      清空搜索记录:

    //清空搜索记录
            $scope.delSearch = function() {
                $scope.searchItems.splice(0,$scope.searchItems.length);//清空数组
            }

    参考:

      JS数组的方法api:http://www.w3school.com.cn/jsref/jsref_obj_array.asp

      AngularJS如何添加的DOM元素且能绑定事件:https://segmentfault.com/q/1010000002957477

      splice()方法:http://www.jb51.net/article/33306.htm

    转载于:https://www.cnblogs.com/soul-wonder/p/8875444.html

    展开全文
  • 最近跳槽去新公司,接受的第一个任务是在 一个电商模块的搜索功能以及搜索历史记录的实现。 需求和淘宝等电商的功能大体差不多,最上面一个搜索框,下面显示搜索历史记录。在EditText里输入要搜索的关键字后,按软...
  • 今天,我将手把手教大家实现一款 封装了 历史搜索记录功能 &amp; 样式 的Android 自定义搜索框 开源库,希望你们会喜欢。     目录 示意图 1. 简介 一款封装了 历史搜索记录功能 &amp; 样式 的...
  • 在 一个电商模块的搜索功能以及搜索历史记录的实现。 需求和淘宝等电商的功能大体差不多,最上面一个搜索框,下面显示搜索历史记录。在EditText里输入要搜索的关键字后,按软键盘的搜索按键/延迟xxxxms后自动搜索。...
  • 1.浏览器缓存永久保存搜索历史数据. 2.页面初始化将数据保存到页面变量中. 3.对搜索历史记录的怎加和删除,要同步到缓存中. ----------------直接看代码---------------- *前端使用的是vue,这里只是代码片段* 1....
  • 提交时通过打印data.field 会获得最新的参数,但是搜索后就会加上原来搜索过的参数,找了好久不知道原因,只能算lauyi的一个bug? 解决方式在返回中重置where var whereNull; form.on('submit(fromQuery)', ...
  • 基于vue3的搜索框,包含存储和删除历史记录以及搜索建议列表,用户可以在配置中选择是否需要历史记录。 展示 下载 npm install bm_search_input -S 引入 import bm_search_input from 'bm_search_input' Vue....
  • vue页面刷新保持搜索条件

    千次阅读 2020-06-17 15:14:55
    vue页面刷新保持搜索条件 1.点击条件存到本地 changeBrand(brandName) { localStorage.setItem('brandName',brandName) this.brandName = localStorage.getItem('brandName') }, 2.页面打开调用 mounted(){ ...
  • 利用cookie实现搜索记录

    千次阅读 2018-03-09 19:48:59
    有时候我们需要在客户端保存用户的搜索记录,这样一来,当用户再次点击输入框,就可以自动显示用户最近的输入记录。最终效果如下:1,默认情况下,用户未输入,显示最近的几条搜索记录:2,用户输入字符,自动匹配...
  • 前言Android开发中,类似下图的搜索功能非常常见搜索功能今天,我将手把手教大家实现一款 封装了 历史搜索记录功能 &amp; 样式 的Android 自定义搜索框 开源库,希望你们会喜欢。示意图已在Github开源:地址:...
  • 原文最近跳槽去新公司,接受的第一个任务是在 一个电商模块的搜索功能以及搜索历史记录的实现。 需求和淘宝等电商的功能大体差不多,最上面一个搜索框,下面显示搜索历史记录。在EditText里输入要搜索的关键字后,按...
  • 素材有点low,软键盘右下角回车变成搜索按钮。这种实现只需要在edittext上加入Android:imeOptions="actionSearch"即可,这个也根据版本的2.3以上就必须添加android:singleLine="true",这个可以理解,因为单行为true...
  • 搜索引擎代码

    2011-09-19 08:58:09
    4.可过滤某一ip的搜索记录,避免数据库迅速膨胀 5.将广告内容的字数限制由100个字符增长到128个字符 6.升级优化客户端蜘蛛的部分功能:修正入口地址设置中屏蔽的url关键词设置后无效的问题;修正定时更新,间隔更新...
  • 如果在数据筛选时对原始数据进行操作,那么想重置该筛选条件的话,就需要重新获取...将组件input框中的数据清空, 组件改为隐藏, 修改icon颜色,触发父组件的resetChange方法 resetData () { console.log('reset')
  • // 从session中取出页面跳转前保存的查询条件 let searchObj = this.$session.get('searchObject'); if (searchObj) { let { searchQuery, isBack } = searchObj; if (!!isBack && searchQuery) {
  • 最近一直在研究sphinx的工作机制,在[搜索引擎]Sphinx的介绍和原理探索简单地介绍了其工作原理之后,还有很多问题没有弄懂,比如底层的数据结构和算法,于是更进一步地从数据结构层面了解其工作原理。在网上搜了很...
  • 接上篇博客: 先看js的第1段代码 $(document).delegate(".his-record>div","click",function...因为我们的搜索记录是通过prepend()方法动态添加的,如果用一般的事件绑定方法,会没有效果。熟悉jquery的...
  • 分别用深度优先、迭代加深、一致代价、A*搜索算法得到从起始点Arad到目标点Bucharest的一条路径,即为罗马尼亚问题的一个解,在求解的过程中记录每种算法得到的解,即输出每种解得到的条路径。   图一:...
  • 搜索引擎——原理

    2018-11-16 09:56:07
    【1】搜索引擎概述 【2】搜索引擎的基础技术 【3】搜索引擎的平台基础 【4】搜索结果的改善优化 __________________________________________________ 【1】搜索引擎概述 过去的15年间,互联网信息急剧膨胀,...
  • Span使用之利用系统Span样式实现模糊搜索,匹配变色的特殊UI效果在上一篇博客中,演示了基本的Span的使用,实现了对于字体的放大,缩小,变色等等。而这篇博客便是对于上一篇博客所讲解的东西加以利用。如果对于上一...
  • vue实现搜索显示历史搜索记录,采用插件-good-storage 安装插件 npm install good-storage -S 在本地新建cache.js文件,该文件是关于本地存储的逻辑处理(缓存到本地的数据最大缓存15条,并且新的插入在第一位,...
  • 打开的文件记录,根据以下条件过滤: Result 列是 SUCCESS ,并且 Path 不是 .dll 或者 .exe 结尾的, Operation 是 ReadFile 。在过滤的结果中的 Path 列按 CTRL+F 搜索关键字,我这里是 test.sln ,最终排查到 ...
  • 搜索引擎基本原理

    2015-12-21 19:13:52
    【1】搜索引擎概述 【2】搜索引擎的基础技术 【3】搜索引擎的平台基础 【4】搜索结果的改善优化 __________________________________________________ 【1】搜索引擎概述 过去的15年间
  • 微信小程序--历史搜索功能的实现

    千次阅读 2019-07-14 22:31:37
    最近公司在做一个内部的管理系统,是基于微信小程序的,我看了一下和Vue比较类似,布局方面有HTML和CSS基础的人都可以做,还是比较容易上手的,现在先记录一下搜索框保留搜索历史的功能的实现。 ##wxml部分  &...
  • acm.DFS深度搜索专题

    千次阅读 2014-02-04 13:06:23
    搜索被称为“通用的解题法”,在算法和人工智能方面占有非常重要的地位,特别是在各类ACM程序设计比赛中非常常见,在题目中一般位于中间位置,作为中等难度的题目出现。因此我们需要着重掌握各类的搜索算法,才能...
  • 2. 前端+搜索引擎:Xunsearch(迅搜)& Xapian. 3 3. 网页解析与数据存储:Parser.py(BeautifulSoup4).... 5 (二) 设计文档.... 6 一、 运行环境.... 6 二、 总体设计流程.... 7 ...
  • 基于layui写的一个高级搜索(筛选)功能。 效果图: 是一位萌新,所有写的有点儿乱。(放在上面,供新手们参考,也是自己做一个记录。) 代码如下: <!DOCTYPE ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,787
精华内容 9,114
关键字:

保存搜索记录清空搜索条件