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

老规矩,先上图:

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

存储方式: 采用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-->
    <RelativeLayout
        android:id="@+id/rl_yellow_search"
        android:layout_width="match_parent"
        android:layout_height="83dp"
        android:background="@drawable/bg_title_base">
        <!--搜索-->
        <RelativeLayout
            android: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">

            <ImageButton
                android: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.LastInputEditText
                android: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" />

            <ImageButton
                android: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>
        <!--取消-->
        <TextView
            android: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>
    <!--无数据-->
    <RelativeLayout
        android:id="@+id/rl_no_date"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone">

        <TextView
            android: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有数据本地历史的-->
    <LinearLayout
        android:id="@+id/ll_history"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <!--History搜索-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/ds_40dp"
            android:background="@color/white"
            android:orientation="horizontal"
            tools:ignore="UseCompoundDrawables">

            <TextView
                android: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" />

            <ImageButton
                android: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.TagFlowLayout
            android: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.PullToRefreshScrollView
        android: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.RecyclerView
            android: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);
    }

    @Override
    protected 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);
    }


    @Override
    protected 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() {
                @Override
                public 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";


    @Override
    protected 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;
    }

    @Override
    protected 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);
    }


    @Override
    public 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);
    }

    @Override
    protected 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()
        );

    }


    @Override
    protected 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 gravity
            int 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;
        }

    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

    @Override
    protected 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;
    @Deprecated
    private HashSet<Integer> mCheckedPosList = new HashSet<Integer>();

    public TagAdapter(List<T> datas) {
        mTagDatas = datas;
    }

    @Deprecated
    public TagAdapter(T[] datas) {
        mTagDatas = new ArrayList<T>(Arrays.asList(datas));
    }

    public interface OnDataChangedListener {
        void onChanged();
    }

    public void setOnDataChangedListener(OnDataChangedListener listener) {
        mOnDataChangedListener = listener;
    }

    /**
     * 清除选中的tab
     **/
    @Deprecated
    public void setSelectedList(int... poses) {
        Set<Integer> set = new HashSet<>();
        for (int pos : poses) {
            set.add(pos);
        }
        setSelectedList(set);
    }

    @Deprecated
    public void setSelectedList(Set<Integer> set) {
        mCheckedPosList.clear();
        if (set != null) {
            mCheckedPosList.addAll(set);
        }
        notifyDataChanged();
    }

    @Deprecated
    public 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;

    @Override
    public void initRootView() {
        setContentView(R.layout.activity_search);
    }

    @Override
    public 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() {
                    @Override
                    public 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;
                    }
                }
        });
    }

    @Override
    public 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")
    @Override
    public void initListener() {
        btnClean.setOnClickListener(this);
        btnGrayDelete.setOnClickListener(this);
        tvSearchCancel.setOnClickListener(this);

        //搜索框的触摸事件(不写无法唤起焦点)
        ceSearch.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //点击输入框显示搜索并获取焦点
                ceSearch.setFocusable(true);
                ceSearch.setFocusableInTouchMode(true);
                return false;
            }
        });

        //换行改为搜索按钮
        ceSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public 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() {
            @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) {
                if (ceSearch.getText().toString().trim().length() > 0) {
                    btnGrayDelete.setVisibility(View.VISIBLE);
                } else {
                    btnGrayDelete.setVisibility(View.GONE);
                }
            }
        };
        ceSearch.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public 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) {
            @Override
            public 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() {
            @Override
            public boolean onTagClick(View view, int position, FlowLayout parent) {
                Toast.makeText(mContext, history.get(position), Toast.LENGTH_SHORT).show();
                return true;
            }
        });
    }

    @Override
    public 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方法时候会被包含。

 

 

 

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土狗的想法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值