老规矩,先上图:

功能描述:①用户搜索功能,存本地历史记录;②采用流式布局

存储方式: 采用SharedPreferences

优点:避免使用本地数据库导致项目更新版本升级版本号问题,避免sql常规操作失误引发的bug问题。简单实用

一 布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/white"android:fitsSystemWindows="false"android:orientation="vertical"><!--搜索界面false--><RelativeLayoutandroid:id="@+id/rl_yellow_search"android:layout_width="match_parent"android:layout_height="83dp"android:background="@drawable/bg_title_base"><!--搜索--><RelativeLayoutandroid:id="@+id/rl_search"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_marginBottom="@dimen/ds_10dp"android:layout_marginLeft="@dimen/ds_12dp"android:layout_toLeftOf="@+id/tv_search_cancel"android:background="@drawable/bg_white_round_small"android:paddingBottom="@dimen/ds_8dp"android:paddingTop="@dimen/ds_8dp"tools:ignore="RtlHardcoded"><ImageButtonandroid:id="@+id/btn_gray_search"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:layout_gravity="center_vertical"android:layout_marginLeft="@dimen/ds_16dp"android:background="@drawable/icon_xiaoxi_sousuo"tools:ignore="ContentDescription,RtlHardcoded" /><com.yc.stscf.widget.LastInputEditTextandroid:id="@+id/ce_search"android:layout_width="match_parent"android:layout_height="@dimen/ds_30dp"android:layout_centerHorizontal="true"android:layout_centerVertical="true"android:layout_gravity="center_vertical"android:layout_marginRight="@dimen/ds_36dp"android:layout_toRightOf="@+id/btn_gray_search"android:background="@null"android:focusable="false"android:focusableInTouchMode="false"android:inputType="text|number"android:gravity="left|center_horizontal|center_vertical"android:hint="请输入要搜索的关键词"android:imeOptions="actionSearch"android:textColor="@color/cs_999999"android:textColorHint="@color/cs_999999"android:textSize="@dimen/ds_14sp"tools:ignore="HardcodedText,InefficientWeight,NestedWeights,RelativeOverlap,RtlHardcoded,SmallSp" /><ImageButtonandroid:id="@+id/btn_gray_delete"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:visibility="gone"android:layout_gravity="center_vertical"android:layout_marginLeft="@dimen/ds_16dp"android:layout_marginRight="@dimen/ds_6dp"android:background="@drawable/icon_xiaoxi_sousuo_delete"android:padding="@dimen/ds_10dp"tools:ignore="ContentDescription,RtlHardcoded" /></RelativeLayout><!--取消--><TextViewandroid:id="@+id/tv_search_cancel"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginBottom="@dimen/ds_20dp"android:layout_marginLeft="@dimen/ds_13dp"android:layout_marginRight="@dimen/ds_16dp"android:paddingBottom="@dimen/ds_8dp"android:paddingTop="@dimen/ds_8dp"android:text="取消"android:textColor="@color/white"android:textSize="@dimen/ds_15sp"tools:ignore="HardcodedText,RtlHardcoded" /></RelativeLayout><!--无数据--><RelativeLayoutandroid:id="@+id/rl_no_date"android:layout_width="match_parent"android:layout_height="match_parent"android:visibility="gone"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:drawablePadding="@dimen/ds_30dp"android:drawableTop="@drawable/bd_zanwuxiangguannsousuo"android:gravity="center"android:text="暂无相关搜索"android:textColor="@color/cs_ff000000"android:textSize="@dimen/ds_14sp"tools:ignore="HardcodedText" /></RelativeLayout><!--1有数据本地历史的--><LinearLayoutandroid:id="@+id/ll_history"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><!--History搜索--><LinearLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/ds_40dp"android:background="@color/white"android:orientation="horizontal"tools:ignore="UseCompoundDrawables"><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginLeft="@dimen/ds_16dp"android:layout_weight="1"android:text="历史搜索"android:textColor="@color/cs_4a4a4a"android:textSize="@dimen/ds_13sp"tools:ignore="HardcodedText,PxUsage,RtlHardcoded" /><ImageButtonandroid:id="@+id/btn_clean"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginRight="@dimen/ds_16dp"android:background="@drawable/icon_xiaoxi_sousuo_shanchu"tools:ignore="ContentDescription,RtlHardcoded" /></LinearLayout><com.yc.stscf.widget.TagFlowLayoutandroid:id="@+id/flow_history"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="@dimen/ds_18dp"android:layout_marginLeft="@dimen/ds_13dp"android:layout_marginRight="@dimen/ds_13dp" /></LinearLayout><!--2有数据服务器--><com.handmark.pulltorefresh.library.PullToRefreshScrollViewandroid:id="@+id/pr_scroll_view"android:layout_width="match_parent"android:layout_height="match_parent"android:divider="@null"android:background="@color/white"android:scrollbars="none"app:ptrAnimationStyle="flip"app:ptrMode="both"><android.support.v7.widget.RecyclerViewandroid:id="@+id/rv_myRecycler"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/white"android:nestedScrollingEnabled="false" /></com.handmark.pulltorefresh.library.PullToRefreshScrollView></LinearLayout>

样式解析,部分图片资源什么的,大家自己替换下即将

布局中用到的自定义控件:(PullToRefreshScrollView就不提供了,这块大家注释掉吧)

自定义的edit,可以让光标一直显示在后面。

package com.yc.stscf.widget;import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;/*** ================================================** @author :vip* @version :V 1.0.0* @date :2019/6/6 14:56* 描    述:光标在后的editView* 修订历史:* ================================================*/
@SuppressLint("AppCompatCustomView")
public class LastInputEditText extends EditText {public LastInputEditText(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public LastInputEditText(Context context, AttributeSet attrs) {super(context, attrs);}public LastInputEditText(Context context) {super(context);}@Overrideprotected void onSelectionChanged(int selStart, int selEnd) {super.onSelectionChanged(selStart, selEnd);//保证光标始终在最后面,//防止不能多选if (selStart == selEnd) {setSelection(getText().length());}}
}

流式布局三部曲之一,全粘贴即可

package com.yc.stscf.widget;import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;import com.yc.stscf.R;
import com.yc.stscf.adapter.TagAdapter;import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;/*** ================================================** @author:vip 版    本:V 1.0.0* 创建日期:2018/12/05* 描    述:搜索自定义布局* 修订历史:* ================================================*/
public class TagFlowLayout extends FlowLayout implements TagAdapter.OnDataChangedListener {private TagAdapter mTagAdapter;/*** -1为不限制数量**/private int mSelectedMax = -1;private static final String TAG = "TagFlowLayout";private Set<Integer> mSelectedView = new HashSet<Integer>();private OnSelectListener mOnSelectListener;private OnTagClickListener mOnTagClickListener;public interface OnSelectListener {void onSelected(Set<Integer> selectPosSet);}public interface OnTagClickListener {boolean onTagClick(View view, int position, FlowLayout parent);}public TagFlowLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout);mSelectedMax = ta.getInt(R.styleable.TagFlowLayout_max_select, -1);ta.recycle();}public TagFlowLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public TagFlowLayout(Context context) {this(context, null);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int cCount = getChildCount();for (int i = 0; i < cCount; i++) {TagView tagView = (TagView) getChildAt(i);if (tagView.getVisibility() == View.GONE) {continue;}if (tagView.getTagView().getVisibility() == View.GONE) {tagView.setVisibility(View.GONE);}}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}public void setOnSelectListener(OnSelectListener onSelectListener) {mOnSelectListener = onSelectListener;}public void setOnTagClickListener(OnTagClickListener onTagClickListener) {mOnTagClickListener = onTagClickListener;}public void setAdapter(TagAdapter adapter) {mTagAdapter = adapter;mTagAdapter.setOnDataChangedListener(this);mSelectedView.clear();changeAdapter();}private void changeAdapter() {removeAllViews();TagAdapter adapter = mTagAdapter;TagView tagViewContainer = null;HashSet preCheckedList = mTagAdapter.getPreCheckedList();for (int i = 0; i < adapter.getCount(); i++) {View tagView = adapter.getView(this, i, adapter.getItem(i));tagViewContainer = new TagView(getContext());tagView.setDuplicateParentStateEnabled(true);if (tagView.getLayoutParams() != null) {tagViewContainer.setLayoutParams(tagView.getLayoutParams());} else {ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);lp.setMargins(dip2px(getContext(), 5),dip2px(getContext(), 5),dip2px(getContext(), 5),dip2px(getContext(), 5));tagViewContainer.setLayoutParams(lp);}ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);tagView.setLayoutParams(lp);tagViewContainer.addView(tagView);addView(tagViewContainer);if (preCheckedList.contains(i)) {setChildChecked(i, tagViewContainer);}if (mTagAdapter.setSelected(i, adapter.getItem(i))) {setChildChecked(i, tagViewContainer);}tagView.setClickable(false);final TagView finalTagViewContainer = tagViewContainer;final int position = i;tagViewContainer.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {doSelect(finalTagViewContainer, position);if (mOnTagClickListener != null) {mOnTagClickListener.onTagClick(finalTagViewContainer, position,TagFlowLayout.this);}}});}mSelectedView.addAll(preCheckedList);}public void setMaxSelectCount(int count) {if (mSelectedView.size() > count) {Log.w(TAG, "you has already select more than " + count + " views , so it will be clear .");mSelectedView.clear();}mSelectedMax = count;}public Set<Integer> getSelectedList() {return new HashSet<Integer>(mSelectedView);}private void setChildChecked(int position, TagView view) {view.setChecked(true);mTagAdapter.onSelected(position, view.getTagView());}private void setChildUnChecked(int position, TagView view) {view.setChecked(false);mTagAdapter.unSelected(position, view.getTagView());}private void doSelect(TagView child, int position) {if (!child.isChecked()) {//处理max_select=1的情况if (mSelectedMax == 1 && mSelectedView.size() == 1) {Iterator<Integer> iterator = mSelectedView.iterator();Integer preIndex = iterator.next();TagView pre = (TagView) getChildAt(preIndex);setChildUnChecked(preIndex, pre);setChildChecked(position, child);mSelectedView.remove(preIndex);mSelectedView.add(position);} else {if (mSelectedMax > 0 && mSelectedView.size() >= mSelectedMax) {return;}setChildChecked(position, child);mSelectedView.add(position);}} else {setChildUnChecked(position, child);mSelectedView.remove(position);}if (mOnSelectListener != null) {mOnSelectListener.onSelected(new HashSet<Integer>(mSelectedView));}}public TagAdapter getAdapter() {return mTagAdapter;}private static final String KEY_CHOOSE_POS = "key_choose_pos";private static final String KEY_DEFAULT = "key_default";@Overrideprotected Parcelable onSaveInstanceState() {Bundle bundle = new Bundle();bundle.putParcelable(KEY_DEFAULT, super.onSaveInstanceState());StringBuilder selectPos = new StringBuilder();if (mSelectedView.size() > 0) {for (int key : mSelectedView) {selectPos.append(key).append("|");}selectPos = new StringBuilder(selectPos.substring(0, selectPos.length() - 1));}bundle.putString(KEY_CHOOSE_POS, selectPos.toString());return bundle;}@Overrideprotected void onRestoreInstanceState(Parcelable state) {if (state instanceof Bundle) {Bundle bundle = (Bundle) state;String mSelectPos = bundle.getString(KEY_CHOOSE_POS);if (!TextUtils.isEmpty(mSelectPos)) {String[] split = mSelectPos.split("\\|");for (String pos : split) {int index = Integer.parseInt(pos);mSelectedView.add(index);TagView tagView = (TagView) getChildAt(index);if (tagView != null) {setChildChecked(index, tagView);}}}super.onRestoreInstanceState(bundle.getParcelable(KEY_DEFAULT));return;}super.onRestoreInstanceState(state);}@Overridepublic void onChanged() {mSelectedView.clear();changeAdapter();}public static int dip2px(Context context, float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}
}

流式布局三部曲之二,全粘贴即可

package com.yc.stscf.widget;import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;import com.yc.stscf.R;import java.util.ArrayList;
import java.util.List;/*** ================================================** @author :Vip* @version :V 1.0.0* @date :2019/7/8 14:12* 描    述:搜索自定义控件* 修订历史:* ================================================*/
public class FlowLayout extends ViewGroup {private static final String TAG = "FlowLayout";private static final int LEFT = -1;private static final int CENTER = 0;private static final int RIGHT = 1;protected List<List<View>> mAllViews = new ArrayList<>();protected List<Integer> mLineHeight = new ArrayList<>();protected List<Integer> mLineWidth = new ArrayList<>();private int mGravity;private List<View> lineViews = new ArrayList<>();public FlowLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);@SuppressLint("CustomViewStyleable") TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout);mGravity = ta.getInt(R.styleable.TagFlowLayout_tag_gravity, LEFT);ta.recycle();}public FlowLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public FlowLayout(Context context) {this(context, null);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);int modeWidth = MeasureSpec.getMode(widthMeasureSpec);int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);int modeHeight = MeasureSpec.getMode(heightMeasureSpec);int width = 0;int height = 0;int lineWidth = 0;int lineHeight = 0;int cCount = getChildCount();for (int i = 0; i < cCount; i++) {View child = getChildAt(i);if (child.getVisibility() == View.GONE) {if (i == cCount - 1) {width = Math.max(lineWidth, width);height += lineHeight;}continue;}measureChild(child, widthMeasureSpec, heightMeasureSpec);MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth() + lp.leftMargin+ lp.rightMargin;int childHeight = child.getMeasuredHeight() + lp.topMargin+ lp.bottomMargin;if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) {width = Math.max(width, lineWidth);lineWidth = childWidth;height += lineHeight;lineHeight = childHeight;} else {lineWidth += childWidth;lineHeight = Math.max(lineHeight, childHeight);}if (i == cCount - 1) {width = Math.max(lineWidth, width);height += lineHeight;}}setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(),modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom());}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {mAllViews.clear();mLineHeight.clear();mLineWidth.clear();lineViews.clear();int width = getWidth();int lineWidth = 0;int lineHeight = 0;int cCount = getChildCount();for (int i = 0; i < cCount; i++) {View child = getChildAt(i);if (child.getVisibility() == View.GONE) {continue;}MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth();int childHeight = child.getMeasuredHeight();if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width - getPaddingLeft() - getPaddingRight()) {mLineHeight.add(lineHeight);mAllViews.add(lineViews);mLineWidth.add(lineWidth);lineWidth = 0;lineHeight = childHeight + lp.topMargin + lp.bottomMargin;lineViews = new ArrayList<>();}lineWidth += childWidth + lp.leftMargin + lp.rightMargin;lineHeight = Math.max(lineHeight, childHeight + lp.topMargin+ lp.bottomMargin);lineViews.add(child);}mLineHeight.add(lineHeight);mLineWidth.add(lineWidth);mAllViews.add(lineViews);int left = getPaddingLeft();int top = getPaddingTop();int lineNum = mAllViews.size();for (int i = 0; i < lineNum; i++) {lineViews = mAllViews.get(i);lineHeight = mLineHeight.get(i);// set gravityint currentLineWidth = this.mLineWidth.get(i);switch (this.mGravity) {case LEFT:left = getPaddingLeft();break;case CENTER:left = (width - currentLineWidth) / 2 + getPaddingLeft();break;case RIGHT:left = width - currentLineWidth + getPaddingLeft();break;default:break;}for (int j = 0; j < lineViews.size(); j++) {View child = lineViews.get(j);if (child.getVisibility() == View.GONE) {continue;}MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int lc = left + lp.leftMargin;int tc = top + lp.topMargin;int rc = lc + child.getMeasuredWidth();int bc = tc + child.getMeasuredHeight();child.layout(lc, tc, rc, bc);left += child.getMeasuredWidth() + lp.leftMargin+ lp.rightMargin;}top += lineHeight;}}@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {return new MarginLayoutParams(getContext(), attrs);}@Overrideprotected LayoutParams generateDefaultLayoutParams() {return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);}@Overrideprotected LayoutParams generateLayoutParams(LayoutParams p) {return new MarginLayoutParams(p);}
}

流式布局三部曲之三,全粘贴即可

package com.yc.stscf.adapter;import android.util.Log;
import android.view.View;import com.yc.stscf.widget.FlowLayout;import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;/*** ================================================** @author:vip 版    本:V 1.0.0* 创建日期:2018/12/19* 描    述:搜索及评价列表使用适配器(流式布局)* 注:* 修订历史:* ================================================*/
public abstract class TagAdapter<T> {private List<T> mTagDatas;public OnDataChangedListener mOnDataChangedListener;@Deprecatedprivate HashSet<Integer> mCheckedPosList = new HashSet<Integer>();public TagAdapter(List<T> datas) {mTagDatas = datas;}@Deprecatedpublic TagAdapter(T[] datas) {mTagDatas = new ArrayList<T>(Arrays.asList(datas));}public interface OnDataChangedListener {void onChanged();}public void setOnDataChangedListener(OnDataChangedListener listener) {mOnDataChangedListener = listener;}/*** 清除选中的tab**/@Deprecatedpublic void setSelectedList(int... poses) {Set<Integer> set = new HashSet<>();for (int pos : poses) {set.add(pos);}setSelectedList(set);}@Deprecatedpublic void setSelectedList(Set<Integer> set) {mCheckedPosList.clear();if (set != null) {mCheckedPosList.addAll(set);}notifyDataChanged();}@Deprecatedpublic HashSet<Integer> getPreCheckedList() {return mCheckedPosList;}public int getCount() {return mTagDatas == null ? 0 : mTagDatas.size();}public void notifyDataChanged() {if (mOnDataChangedListener != null) {mOnDataChangedListener.onChanged();}}public T getItem(int position) {return mTagDatas.get(position);}public abstract View getView(FlowLayout parent, int position, T t);public void onSelected(int position, View view) {Log.d("zhy", "onSelected " + position);}public void unSelected(int position, View view) {Log.d("zhy", "unSelected " + position);}public boolean setSelected(int position, T t) {return false;}}

样式引用

<!--搜索-->
<declare-styleable name="TagFlowLayout"><attr name="max_select" format="integer" /><attr name="tag_gravity"><enum name="left" value="-1" /><enum name="center" value="0" /><enum name="right" value="1" /></attr>
</declare-styleable>

二 主界面逻辑

package com.yc.stscf.activity;import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.InputFilter;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;import com.handmark.pulltorefresh.library.PullToRefreshScrollView;
import com.yc.stscf.R;
import com.yc.stscf.adapter.MessageReminderAdapter;
import com.yc.stscf.adapter.TagAdapter;
import com.yc.stscf.base.BaseActivity;
import com.yc.stscf.model.MessageReminderBean;
import com.yc.stscf.storage.PreferenceCache;
import com.yc.stscf.utils.Tt;
import com.yc.stscf.widget.FlowLayout;
import com.yc.stscf.widget.LastInputEditText;
import com.yc.stscf.widget.TagFlowLayout;import java.util.ArrayList;
import java.util.List;import butterknife.BindView;/*** ================================================** @author :Vip* @version :V 1.0.0* @date :2019/7/8 13:40* 描    述:搜索界面* 修订历史:* ================================================*/public class SearchActivity extends BaseActivity implements View.OnClickListener {TagAdapter tagAdapter;@BindView(R.id.btn_gray_search)ImageButton btnGraySearch;@BindView(R.id.ce_search)LastInputEditText ceSearch;@BindView(R.id.btn_gray_delete)ImageButton btnGrayDelete;@BindView(R.id.rl_search)RelativeLayout rlSearch;@BindView(R.id.tv_search_cancel)TextView tvSearchCancel;@BindView(R.id.rl_yellow_search)RelativeLayout rlYellowSearch;@BindView(R.id.rl_no_date)RelativeLayout rlNoDate;@BindView(R.id.rv_myRecycler)RecyclerView rvMyRecycler;@BindView(R.id.pr_scroll_view)PullToRefreshScrollView prScrollView;@BindView(R.id.btn_clean)ImageButton btnClean;@BindView(R.id.flow_history)TagFlowLayout flowHistory;List<MessageReminderBean.DataBean> dataBeanList = new ArrayList<>();MessageReminderAdapter messageReminderAdapter;@BindView(R.id.ll_history)LinearLayout llHistory;private List<String> vip = new ArrayList<>();/*** 缓存为用户登录id**/public  String KEY_SEARCH_HISTORY_KEYWORD = PreferenceCache.getUsername();/*** 使用SharedPreferences记录搜索历史**/private SharedPreferences mPref;@Overridepublic void initRootView() {setContentView(R.layout.activity_search);}@Overridepublic void initView() {//换行变搜索ceSearch.setImeOptions(EditorInfo.IME_ACTION_SEARCH);ceSearch.setInputType(EditorInfo.TYPE_CLASS_TEXT);//限制名称只能输入中文和字母editLimit();//输入内容监听刷新listenerEdit();}/*** 输入文本内容类型限制**/private void editLimit() {ceSearch.setFilters(new InputFilter[]{new InputFilter() {@Overridepublic CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {for (int i = start; i < end; i++) {if (!Character.isLetterOrDigit(source.charAt(i))&& !"_".equals(Character.toString(source.charAt(i)))&& !"-".equals(Character.toString(source.charAt(i)))) {return "";}}return null;}}});}@Overridepublic void initData() {mPref = getSharedPreferences("input", Activity.MODE_PRIVATE);//初始化历搜initHistory();//服务器内容initRecycler();}/*** 初始化历史搜索**/private void initHistory() {String history = mPref.getString(KEY_SEARCH_HISTORY_KEYWORD, "");if (!TextUtils.isEmpty(history)) {List<String> list = new ArrayList<String>();for (Object o : history.split(",")) {list.add((String) o);}vip = list;historySetData(list);}}/*** 服务器内容**/private void initRecycler() {if (rvMyRecycler.getRecycledViewPool() != null) {rvMyRecycler.getRecycledViewPool().setMaxRecycledViews(0, 10);}rvMyRecycler.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false));messageReminderAdapter = new MessageReminderAdapter(R.layout.item_message_reminder, dataBeanList);rvMyRecycler.setAdapter(messageReminderAdapter);/**通知类型,读取状态,内容,日期**/MessageReminderBean.DataBean messageReminderBean = new MessageReminderBean.DataBean();messageReminderBean.setTzType("0");messageReminderBean.setContent("您的授信申请SGJK-SX-SX-201905-0001已审批完成。");messageReminderBean.setDqStatus("0");messageReminderBean.setDate("2019-10-01");MessageReminderBean.DataBean messageReminderBean1 = new MessageReminderBean.DataBean();messageReminderBean1.setTzType("1");messageReminderBean1.setContent("您的授信申请SGJK-SX-SX-201905-0001已审批完成。");messageReminderBean1.setDqStatus("1");messageReminderBean1.setDate("2019-10-01");MessageReminderBean.DataBean messageReminderBean2 = new MessageReminderBean.DataBean();messageReminderBean2.setTzType("2");messageReminderBean2.setContent("您的授信申请SGJK-SX-SX-201905-0001已审批完成。");messageReminderBean2.setDqStatus("0");messageReminderBean2.setDate("2019-10-01");MessageReminderBean.DataBean messageReminderBean3 = new MessageReminderBean.DataBean();messageReminderBean3.setTzType("0");messageReminderBean3.setContent("您的授信申请SGJK-SX-SX-201905-0001已审批完成。");messageReminderBean3.setDqStatus("0");messageReminderBean3.setDate("2019-10-01");dataBeanList.add(messageReminderBean);dataBeanList.add(messageReminderBean1);dataBeanList.add(messageReminderBean2);dataBeanList.add(messageReminderBean3);messageReminderAdapter.notifyDataSetChanged();}@SuppressLint("ClickableViewAccessibility")@Overridepublic void initListener() {btnClean.setOnClickListener(this);btnGrayDelete.setOnClickListener(this);tvSearchCancel.setOnClickListener(this);//搜索框的触摸事件(不写无法唤起焦点)ceSearch.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {//点击输入框显示搜索并获取焦点ceSearch.setFocusable(true);ceSearch.setFocusableInTouchMode(true);return false;}});//换行改为搜索按钮ceSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {@Overridepublic boolean onEditorAction(TextView v, int actionId, KeyEvent event) {if (actionId == EditorInfo.IME_ACTION_SEND || (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {clickSearchAfter();return true;}return false;}});}/*** 搜索输入按钮监听显隐删除按钮**/private void listenerEdit() {final TextWatcher textWatcher = new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {}@Overridepublic void afterTextChanged(Editable s) {if (ceSearch.getText().toString().trim().length() > 0) {btnGrayDelete.setVisibility(View.VISIBLE);} else {btnGrayDelete.setVisibility(View.GONE);}}};ceSearch.setOnFocusChangeListener(new View.OnFocusChangeListener() {@Overridepublic void onFocusChange(View v, boolean hasFocus) {if (hasFocus) {ceSearch.addTextChangedListener(textWatcher);} else {ceSearch.removeTextChangedListener(textWatcher);}}});}/*** 点击搜索按钮之后的共通逻辑* 注:丢光标不需要处理了,搜索后直接跳页面,键盘则会自动隐藏**/@SuppressLint("ApplySharedPref")private void clickSearchAfter() {if (ceSearch.getText().toString().trim().length() > 0) {//TODO  临时为了展示if ("小猫".equals(ceSearch.getText().toString().trim())) {rlNoDate.setVisibility(View.VISIBLE);} else {rlNoDate.setVisibility(View.GONE);}String text = ceSearch.getText().toString().trim();String oldText = mPref.getString(KEY_SEARCH_HISTORY_KEYWORD, "");List<String>shaList=Arrays.asList(oldText.split(","));Log.e("tag", "" + oldText);Log.e("Tag", "" + text);Log.e("Tag", "" + oldText.contains(text));if (!TextUtils.isEmpty(text) && !(shaList.contains(text))) {//Todo 限制位数
//                    if (vip != null) {
//                        if (vip.size() > 5) {
//                            //最多保存条数
//                            return;
//                        }
//                    }SharedPreferences.Editor editor = mPref.edit();editor.putString(KEY_SEARCH_HISTORY_KEYWORD, text + "," + oldText);editor.commit();vip.add(0, text);} else {Tt.showToast(mContext, "历史搜索已存在您想要输入的内容~!");}historySetData(vip);ceSearch.setFocusable(false);ceSearch.setFocusableInTouchMode(false);ceSearch.clearFocus();String searchContent = ceSearch.getText().toString().trim();Toast.makeText(mContext, "搜索内容为:" + searchContent, Toast.LENGTH_SHORT).show();//TODO  点了搜索以后,而且查到东西了,先显示列表,llHistory.setVisibility(View.GONE);prScrollView.setVisibility(View.VISIBLE);} else {prScrollView.setVisibility(View.GONE);llHistory.setVisibility(View.VISIBLE);Tt.showShort(mContext, "搜索内容不能为空");}hintKeyBoard();}/*** 清空数据*/@SuppressLint("ApplySharedPref")private void deleteData() {mPref = getSharedPreferences("input", MODE_PRIVATE);SharedPreferences.Editor editor = mPref.edit();editor.remove(KEY_SEARCH_HISTORY_KEYWORD).commit();vip.clear();historySetData(vip);Toast.makeText(mContext, "清楚搜索历史成功", Toast.LENGTH_LONG).show();}/*** 历史记录填充数据**/private void historySetData(final List<String> history) {flowHistory.setAdapter(tagAdapter = new TagAdapter<String>(history) {@Overridepublic View getView(FlowLayout parent, int position, String s) {TextView tv = new TextView(mContext);tv.setTextSize(12);tv.setPadding(40, 18, 40, 18);tv.setTextColor(getResources().getColor(R.color.cs_4a4a4a));tv.setBackgroundResource(R.drawable.bg_gray_round_small);tv.setText(s);return tv;}});flowHistory.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() {@Overridepublic boolean onTagClick(View view, int position, FlowLayout parent) {Toast.makeText(mContext, history.get(position), Toast.LENGTH_SHORT).show();return true;}});}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_clean://清空缓存deleteData();break;case R.id.btn_gray_delete:ceSearch.setText("");prScrollView.setVisibility(View.GONE);llHistory.setVisibility(View.VISIBLE);rlNoDate.setVisibility(View.GONE);break;case R.id.tv_search_cancel:finish();break;default:break;}}/*** 关闭键盘**/public void hintKeyBoard() {View view = getWindow().peekDecorView();if (view != null) {InputMethodManager inputManger = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);assert inputManger != null;inputManger.hideSoftInputFromWindow(view.getWindowToken(), 0);}}}

补充:BaseActivity 是我自己封装的界面,大家可以替换成原始的Activity,没任何影响。

三精髓剖析:

自己先建立一个缓存,存用户id,作为唯一标识,登录时候就存。我没给提供PreferenceCache这个类,大家可以先按正规操作去处理,或者临时写个:“abc”.

防止输入的edit输入了表情符号等诡异内容,因为在正规搜索的时候,我们第一是要存本地缓存,第二是将输入内容传到服务器做关联查询,防止服务器这边java后端未做处理导致的异常,我们在移动端提前预判一下。

一进到页面,查这个缓存里,有没有东西,有的话,就循环找处理,添加到最开始我们放的全局的vip这个list里,命名有点随意了,见谅。

这两个方法按注释描述执行即可,需要加,一个是增加了搜索的焦点,能让你操作edit很省心,二是唤起键盘时候,把键盘的换行按钮改写为搜索,因为有的需求没有搜索按钮。

删除按钮 的显隐,没什么大用这玩意。也是为了满足需求写的。

搜索内容不为空的情况下,图中的“小猫”是为了显示无数据时候的占位图,只是为了测试逻辑,这里你可以按自己实际需求替换,这可以理解为:当你输入了内容,点击了搜索,把输入内容传入服务,调用服务器返回了成功情况下,无查询到数据,那么显示占位图。

下方的String text 是输入的内容,oldText是从本地缓存拿到的历史内容,如果输入的内容在历史记录里有,不存并提示,否则存进去。,存这个拼接的。这个是指,你心里有没有我的方法。

清空。很简洁了。清空完了刷新数据,给吐司提示。

填充数据,常规写法。就这么去动态设置文本的间距样式吧。

总结:

没什么总结的了,肯定好用。切换用户账号是好用的,博主采用了3个手机号登录,输入了不同的搜索内容,切换账号登录均能显示对应的历史记录,清空操作也好用,清除的是单独的用户的搜索记录。有问题欢迎留言,代码写的粗糙了点,只为实现功能,等项目有了服务器数据,再二次封装优化。

重点:

          

图中 老的历史数据要转换成List之后再去比较,否则在执行 contains方法时候会被包含。

Android----搜索历史(带区分切换用户id)相关推荐

  1. android 搜索历史流布局,FlowLayout流式布局实现搜索清空历史记录

    本文实例为大家分享了FlowLayout实现搜索清空历史记录的具体代码,供大家参考,具体内容如下 效果图:点击搜索框将搜索的历史在流式布局中展示出来,清空历史记录就会将历史清空,每次搜索后都存入sp中 ...

  2. android 搜索历史流布局,Android-流布局-FlowLayout在搜索历史中的应用-0920.doc

    Android-搜索历史的实现+FlowLayout应用-(2016-9-20) 项目: SearchModel29 环境: Android Studio 1.0 项目下载地址: /s/1hrKnd5 ...

  3. android的UDC功能,Android实现搜索历史功能

    本文实例为大家分享了Android实现搜索历史的具体代码,供大家参考,具体内容如下 SharedPreferences实现本地搜索历史功能,覆盖搜索重复的文本,可清空 1. 判断搜索内容是否含表情,不 ...

  4. Android保存搜索历史

    在我项目中,有这样一个功能实现:先看图 搜索后实时将搜索内容显示到RecyclerView中,可以删除单个历史,也可以清空全部,点击其中一项显示到EditText中.将会涉及到的内容有: Recycl ...

  5. Android浏览历史sqlite功能,[Android]greendao实现搜索历史功能

    使用greendao实现搜索历史功能 ezgif-1-378e749b52.gif device-2017-08-31-103330.png 之前封装sqlite实现过这功能,不过原生封装使用sql语 ...

  6. Kinect学习(五):提取带用户ID的深度数据

    前言 在前面的一篇文章中讨论了如何从Kinect获取深度图:Kinect学习(四):提取深度数据. 这里要对其进行拓展,Kinect可以获取两种格式的深度图: 不带用户ID的深度数据,也是存储在16位 ...

  7. Linux的实际操作:查询用户id切换用户su -

    1.查询用户信息:其中 (uid是用户的id号,gid是用户组的id号,属于哪个组) id 用户名 如果没有此用户,控制台就会返回信息"no such user" 2.切换用户: ...

  8. chrome浏览器一键切换搜索引擎,一键切换谷歌和百度搜索(不需要重新输入keyword,带关键词切换引擎)

    chrome浏览器一键切换搜索引擎,一键切换谷歌和百度搜索(不需要重新输入keyword,带关键词切换引擎) 背景 有么有办法在谷歌和百度之间(或其他引擎或非引擎,如Youtube.B站.Bing等) ...

  9. android:exported =true,什么是具有相同用户ID的Android应用程序

    android:exported 是Android中的四大组件 Activity,Service,Provider,Receiver 四大组件中都会有的一个属性. 总体来说它的主要作用是:是否支持其它 ...

  10. 微信小程序 - 实现搜索界面(带热搜、搜索历史和结果页)

    GitHub Demo 地址: jh-weapp-demo 实现一些常用效果.封装通用组件和工具类 小程序码 效果图: wxml 代码: <van-search value="{{ i ...

最新文章

  1. dnf加物理攻击的卡片有哪些_DNF新版本完美附魔太贵,这些紫卡附魔也不错
  2. List、Set、Map的区别
  3. boost源码剖析之:泛型函数指针类boost::function(rev#3)
  4. poj2367 Genealogical tree
  5. 20145319 第五周学习总结
  6. oracle创建public链接,如何在oracle直接使用地址来创建database link
  7. 数据可视化【一】JavaScript学习
  8. idea 使用maven 整合ssm框架
  9. Python基础篇:用Python简简单单写个星空大战,可不能用来摸鱼啊~
  10. 《终极算法》读书笔记(二)终极算法
  11. 【FPGA的基础快速入门22-------OV7725摄像头模块】
  12. 宠物游戏系统 java
  13. 【QNX Hypervisor 2.2 用户手册】1.3 QNX hypervisor架构
  14. 自然语言处理核心期刊_中国中文信息学会
  15. 列举所有的“四叶玫瑰数”,并统计四叶玫瑰数的个数 四叶玫瑰数:是一个四位数,各个位的数字的四次方的和,为该数字本身
  16. Taro中使用WechatSI(微信同声传译)实现语音搜索
  17. 跳跃表 skipList 跳表的原理以及golang实现
  18. 解决umi 不是内部或外部命令,也不是可运行程序问题
  19. 杠杆炒股平台是不是实盘?
  20. 什么是Watchdog?

热门文章

  1. python微博情感分析,Python情感分析:鹿晗的粉丝们究竟原谅他了吗?
  2. 一款小游戏集合自动脚本
  3. java基础【十二】外存、内存、CPU、缓存
  4. vue的报错 error Trailing spaces not allowed
  5. opencv 将白色变透明
  6. Only fullscreen activities can request orientation终极解决方法
  7. protal认证的稳定性测试脚本
  8. 【SAMMY】DOS下操作隐藏文件、文件夹
  9. 自学软件测试怎么样,有前景吗?
  10. zyb的面试 字节跳动-文远知行杯”广东工业大学第十四届程序设计竞赛