Android 自定义流式布局 实现淘宝搜索记录

效果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
废话不多说

实现代码:

attrs.xml

<declare-styleable name="TagFlowLayout">
        <!--最大选择数量-->
        <attr name="max_select" format="integer"/>
        <!--最大可显示行数-->
        <attr name="limit_line_count" format="integer"/>
        <!--是否设置多行隐藏-->
        <attr name="is_limit" format="boolean"/>
        <attr name="tag_gravity">
            <enum name="left" value="-1"/>
            <enum name="center" value="0"/>
            <enum name="right" value="1"/>
        </attr>
    </declare-styleable>

TagFlowLayout .java

public class TagFlowLayout extends FlowLayout
        implements TagAdapter.OnDataChangedListener {

    private static final String TAG = "TagFlowLayout";
    private TagAdapter mTagAdapter;
    private int mSelectedMax = -1;//-1为不限制数量

    private Set<Integer> mSelectedView = new HashSet<Integer>();

    private OnSelectListener mOnSelectListener;
    private OnTagClickListener mOnTagClickListener;
    private OnLongClickListener mOnLongClickListener;

    public interface OnSelectListener {
        void onSelected(Set<Integer> selectPosSet);
    }

    public interface OnTagClickListener {
        void onTagClick(View view, int position, FlowLayout parent);
    }

    public interface OnLongClickListener {
        void onLongClick(View view, int position);
    }

    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 setOnLongClickListener(OnLongClickListener onLongClickListener) {
        mOnLongClickListener = onLongClickListener;
    }

    public void setAdapter(TagAdapter adapter) {
        mTagAdapter = adapter;
        mTagAdapter.setOnDataChangedListener(this);
        mSelectedView.clear();
        changeAdapter();
    }

    @SuppressWarnings("ResourceType")
    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 {
                MarginLayoutParams lp = new MarginLayoutParams(
                        LayoutParams.WRAP_CONTENT,
                        LayoutParams.WRAP_CONTENT);
                lp.setMargins(dip2px(getContext(), 5),
                        dip2px(getContext(), 5),
                        dip2px(getContext(), 5),
                        dip2px(getContext(), 5));
                tagViewContainer.setLayoutParams(lp);
            }
            LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 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);
                    }
                }
            });

            tagViewContainer.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    if (mOnLongClickListener != null) {
                        mOnLongClickListener.onLongClick(finalTagViewContainer, position);
                        //消费事件,不让事件继续下去
                        return true;
                    }
                    return false;
                }
            });
        }
        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());

        String selectPos = "";
        if (mSelectedView.size() > 0) {
            for (int key : mSelectedView) {
                selectPos += key + "|";
            }
            selectPos = selectPos.substring(0, selectPos.length() - 1);
        }
        bundle.putString(KEY_CHOOSE_POS, selectPos);
        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);
    }
}

TagView

public class TagView extends FrameLayout implements Checkable
{
    private boolean isChecked;
    private static final int[] CHECK_STATE = new int[]{android.R.attr.state_checked};

    public TagView(Context context)
    {
        super(context);
    }

    public View getTagView()
    {
        return getChildAt(0);
    }

    @Override
    public int[] onCreateDrawableState(int extraSpace)
    {
        int[] states = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked())
        {
            mergeDrawableStates(states, CHECK_STATE);
        }
        return states;
    }


    /**
     * Change the checked state of the view
     *
     * @param checked The new checked state
     */
    @Override
    public void setChecked(boolean checked)
    {
        if (this.isChecked != checked)
        {
            this.isChecked = checked;
            refreshDrawableState();
        }
    }

    /**
     * @return The current checked state of the view
     */
    @Override
    public boolean isChecked()
    {
        return isChecked;
    }

    /**
     * Change the checked state of the view to the inverse of its current state
     */
    @Override
    public void toggle()
    {
        setChecked(!isChecked);
    }


}

FlowLayout

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;

    private int limitLineCount; //默认显示3行 断词条显示3行,长词条显示2行
    private boolean isLimit; //是否有行限制
    private boolean isOverFlow; //是否溢出2行

    private int mGravity;
    protected List<List<View>> mAllViews = new ArrayList<List<View>>();
    protected List<Integer> mLineHeight = new ArrayList<Integer>();
    protected List<Integer> mLineWidth = new ArrayList<Integer>();
    private List<View> lineViews = new ArrayList<>();

    public boolean isOverFlow() {
        return isOverFlow;
    }

    private void setOverFlow(boolean overFlow) {
        isOverFlow = overFlow;
    }

    public boolean isLimit() {
        return isLimit;
    }

    public void setLimit(boolean limit) {
        if (!limit) {
            setOverFlow(false);
        }
        isLimit = limit;
    }

    public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout);
        mGravity = ta.getInt(R.styleable.TagFlowLayout_tag_gravity, LEFT);
        limitLineCount = ta.getInt(R.styleable.TagFlowLayout_limit_line_count, 3);
        isLimit = ta.getBoolean(R.styleable.TagFlowLayout_is_limit, false);
        int layoutDirection = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault());
        if (layoutDirection == LayoutDirection.RTL) {
            if (mGravity == LEFT) {
                mGravity = RIGHT;
            } else {
                mGravity = 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);

        // wrap_content
        int width = 0;
        int height = 0;

        int lineWidth = 0;
        int lineHeight = 0;

        //在每一次换行之后记录,是否超过了行数
        int lineCount = 0;//记录当前的行数

        int cCount = getChildCount();

        for (int i = 0; i < cCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                if (i == cCount - 1) {
                    if (isLimit) {
                        if (lineCount == limitLineCount) {
                            setOverFlow(true);
                            break;
                        } else {
                            setOverFlow(false);
                        }
                    }

                    width = Math.max(lineWidth, width);
                    height += lineHeight;
                    lineCount++;
                }
                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()) {
                if (isLimit) {
                    if (lineCount == limitLineCount) {
                        setOverFlow(true);
                        break;
                    } else {
                        setOverFlow(false);
                    }
                }
                width = Math.max(width, lineWidth);
                lineWidth = childWidth;
                height += lineHeight;
                lineHeight = childHeight;
                lineCount++;
            } else {
                lineWidth += childWidth;
                lineHeight = Math.max(lineHeight, childHeight);
            }
            if (i == cCount - 1) {
                if (isLimit) {
                    if (lineCount == limitLineCount) {
                        setOverFlow(true);
                        break;
                    }  else {
                        setOverFlow(false);
                    }
                }
                width = Math.max(lineWidth, width);
                height += lineHeight;
                lineCount++;
            }
        }
        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 lineCount = 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()) {
                if (isLimit) {
                    if (lineCount == limitLineCount) {
                        break;
                    }
                }

                mLineHeight.add(lineHeight);
                mAllViews.add(lineViews);
                mLineWidth.add(lineWidth);

                lineWidth = 0;
                lineHeight = childHeight + lp.topMargin + lp.bottomMargin;
                lineViews = new ArrayList<View>();
                lineCount++;
            }
            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:
                    //  适配了rtl,需要补偿一个padding值
                    left = width - (currentLineWidth + getPaddingLeft()) - getPaddingRight();
                    //  适配了rtl,需要把lineViews里面的数组倒序排
                    Collections.reverse(lineViews);
                    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);
    }
}

TagAdapter

public abstract class TagAdapter<T> {
    private List<T> mTagDatas;
    private OnDataChangedListener mOnDataChangedListener;
    @Deprecated
    private HashSet<Integer> mCheckedPosList = new HashSet<Integer>();

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

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

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

    interface OnDataChangedListener {
        void onChanged();
    }

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

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

RecordsDao

public class RecordsDao {
    private final String TABLE_NAME = "records";
    private SQLiteDatabase recordsDb;
    private RecordSQLiteOpenHelper recordHelper;
    private NotifyDataChanged mNotifyDataChanged;
    private String mUsername;

    public RecordsDao(Context context, String username) {
        recordHelper = new RecordSQLiteOpenHelper(context);
        mUsername = username;
    }

    public interface NotifyDataChanged {
        void notifyDataChanged();
    }

    /**
     * 设置数据变化监听
     */
    public void setNotifyDataChanged(NotifyDataChanged notifyDataChanged) {
        mNotifyDataChanged = notifyDataChanged;
    }

    /**
     * 移除数据变化监听
     */
    public void removeNotifyDataChanged() {
        if (mNotifyDataChanged != null) {
            mNotifyDataChanged = null;
        }
    }

    private synchronized SQLiteDatabase getWritableDatabase() {
        return recordHelper.getWritableDatabase();
    }

    private synchronized SQLiteDatabase getReadableDatabase() {
        return recordHelper.getReadableDatabase();
    }

    /**
     * 如果考虑操作频繁可以到最后不用数据库时关闭
     * <p>
     * 关闭数据库
     */
    public void closeDatabase() {
        if (recordsDb != null) {
            recordsDb.close();
        }
    }

    /**
     * 添加搜索记录
     *
     * @param record 记录
     */
    public void addRecords(String record) {
        //如果这条记录没有则添加,有则更新时间
        int recordId = getRecordId(record);
        try {
            recordsDb = getReadableDatabase();
            if (-1 == recordId) {
                ContentValues values = new ContentValues();
                values.put("username", mUsername);
                values.put("keyword", record);
                //添加搜索记录
                recordsDb.insert(TABLE_NAME, null, values);
            } else {
                Date d = new Date();
                @SuppressLint("SimpleDateFormat") SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                //更新搜索历史数据时间
                ContentValues values = new ContentValues();
                values.put("time", sdf.format(d));
                recordsDb.update(TABLE_NAME, values, "_id = ?", new String[]{Integer.toString(recordId)});
            }
            if (mNotifyDataChanged != null) {
                mNotifyDataChanged.notifyDataChanged();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 判断是否含有该搜索记录
     *
     * @param record 记录
     * @return true | false
     */
    public boolean isHasRecord(String record) {
        boolean isHasRecord = false;
        Cursor cursor = null;
        try {
            recordsDb = getReadableDatabase();
            cursor = recordsDb.query(TABLE_NAME, null, "username = ?", new String[]{mUsername}, null, null, null);
            while (cursor.moveToNext()) {
                if (record.equals(cursor.getString(cursor.getColumnIndexOrThrow("keyword")))) {
                    isHasRecord = true;
                }
            }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                //关闭游标
                cursor.close();
            }
        }
        return isHasRecord;
    }

    /**
     * 判断是否含有该搜索记录
     *
     * @param record 记录
     * @return id
     */
    public int getRecordId(String record) {
        int isHasRecord = -1;
        Cursor cursor = null;
        try {
            recordsDb = getReadableDatabase();
            cursor = recordsDb.query(TABLE_NAME, null, "username = ?", new String[]{mUsername}, null, null, null);
            while (cursor.moveToNext()) {
                if (record.equals(cursor.getString(cursor.getColumnIndexOrThrow("keyword")))) {
                    isHasRecord = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
                }
            }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                //关闭游标
                cursor.close();
            }
        }
        return isHasRecord;
    }

    /**
     * 获取当前用户全部搜索记录
     *
     * @return 记录集合
     */
    public List<String> getRecordsList() {
        List<String> recordsList = new ArrayList<>();
        Cursor cursor = null;
        try {
            recordsDb = getReadableDatabase();
            cursor = recordsDb.query(TABLE_NAME, null, "username = ?", new String[]{mUsername}, null, null, "time desc");
            while (cursor.moveToNext()) {
                String name = cursor.getString(cursor.getColumnIndexOrThrow("keyword"));
                recordsList.add(name);
            }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                //关闭游标
                cursor.close();
            }
        }
        return recordsList;
    }

    /**
     * 获取指定数量搜索记录
     *
     * @return 记录集合
     */
    public List<String> getRecordsByNumber(int recordNumber) {
        List<String> recordsList = new ArrayList<>();
        if (recordNumber < 0) {
            throw new IllegalArgumentException();
        } else if (0 == recordNumber) {
            return recordsList;
        } else {
            Cursor cursor = null;
            try {
                recordsDb = getReadableDatabase();
                cursor = recordsDb.query(TABLE_NAME, null, "username = ?", new String[]{mUsername}, null, null, "time desc limit " + recordNumber);
                while (cursor.moveToNext()) {
                    String name = cursor.getString(cursor.getColumnIndexOrThrow("keyword"));
                    recordsList.add(name);
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } finally {
                if (cursor != null) {
                    //关闭游标
                    cursor.close();
                }
            }
        }
        return recordsList;
    }

    /**
     * 模糊查询
     *
     * @param record 记录
     * @return 返回类似记录
     */
    public List<String> querySimlarRecord(String record) {
        List<String> similarRecords = new ArrayList<>();
        Cursor cursor = null;
        try {
            recordsDb = getReadableDatabase();
            cursor = recordsDb.query(TABLE_NAME, null, "username = ? and keyword like '%?%'", new String[]{mUsername, record}, null, null, "order by time desc");
            while (cursor.moveToNext()) {
                String name = cursor.getString(cursor.getColumnIndexOrThrow("keyword"));
                similarRecords.add(name);
            }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                //关闭游标
                cursor.close();
            }
        }
        return similarRecords;
    }

    /**
     * 清除指定用户的搜索记录
     */
    public void deleteUsernameAllRecords() {
        try {
            recordsDb = getWritableDatabase();
            recordsDb.delete(TABLE_NAME, "username = ?", new String[]{mUsername});
            if (mNotifyDataChanged != null) {
                mNotifyDataChanged.notifyDataChanged();
            }
        } catch (SQLException e) {
            e.printStackTrace();
            Log.e(TABLE_NAME, "清除所有历史记录失败");
        } finally {
        }
    }

    /**
     * 清空数据库所有的历史记录
     */
    public void deleteAllRecords() {
        try {
            recordsDb = getWritableDatabase();
            recordsDb.execSQL("delete from " + TABLE_NAME);
            if (mNotifyDataChanged != null) {
                mNotifyDataChanged.notifyDataChanged();
            }
        } catch (SQLException e) {
            e.printStackTrace();
            Log.e(TABLE_NAME, "清除所有历史记录失败");
        } finally {
        }
    }

    /**
     * 通过id删除记录
     *
     * @param id 记录id
     * @return 返回删除id
     */
    public int deleteRecord(int id) {
        int d = -1;
        try {
            recordsDb = getWritableDatabase();
            d = recordsDb.delete(TABLE_NAME, "_id = ?", new String[]{Integer.toString(id)});
            if (mNotifyDataChanged != null) {
                mNotifyDataChanged.notifyDataChanged();
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TABLE_NAME, "删除_id:" + id + "历史记录失败");
        }
        return d;
    }

    /**
     * 通过记录删除记录
     *
     * @param record 记录
     */
    public int deleteRecord(String record) {
        int recordId = -1;
        try {
            recordsDb = getWritableDatabase();
            recordId = recordsDb.delete(TABLE_NAME, "username = ? and keyword = ?", new String[]{mUsername, record});
            if (mNotifyDataChanged != null) {
                mNotifyDataChanged.notifyDataChanged();
            }
        } catch (SQLException e) {
            e.printStackTrace();
            Log.e(TABLE_NAME, "清除所有历史记录失败");
        }
        return recordId;
    }
}


RecordSQLiteOpenHelper

public class RecordSQLiteOpenHelper extends SQLiteOpenHelper {
    private final static String DB_NAME = "search_history.db";
    private final static int DB_VERSION = 1;

    public RecordSQLiteOpenHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String sqlStr = "CREATE TABLE IF NOT EXISTS records (_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, keyword TEXT, time NOT NULL DEFAULT (datetime('now','localtime')));";
        db.execSQL(sqlStr);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

item_background.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="#F8F8F8">
    </solid>

    <corners android:radius="40dp"/>

    <padding
        android:bottom="4dp"
        android:left="12dp"
        android:right="12dp"
        android:top="4dp"/>
</shape>

search_item_background.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="#F8F8F8">
    </solid>

    <corners android:radius="40dp"/>

    <padding
        android:bottom="2dp"
        android:left="10dp"
        android:right="10dp"
        android:top="2dp"/>
</shape>

tv_history.xml

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="5dp"
          android:background="@drawable/item_background"
          android:text="搜索历史"
          android:singleLine="true"
          android:textColor="#666666"
          android:textSize="13sp">
</TextView>

activity_main.xml

<android.support.constraint.ConstraintLayout
    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="@android:color/white"
    tools:context="com.demo.www.MainActivity">

    <!--标题栏-->
    <android.support.constraint.ConstraintLayout
        android:id="@+id/cl_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/colorAccent"
        android:orientation="horizontal"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/iv_back"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:paddingLeft="@dimen/space_large"
            android:paddingRight="@dimen/space_large"
            android:src="@mipmap/home"/>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginBottom="10dp"
            android:layout_marginTop="10dp"
            android:background="@drawable/search_item_background"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:gravity="center"
            android:orientation="horizontal"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            app:layout_constraintLeft_toRightOf="@+id/iv_back"
            app:layout_constraintRight_toLeftOf="@+id/iv_search">

            <EditText
                android:id="@+id/edit_query"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@null"
                android:hint="请输入搜索关键字"
                android:imeOptions="actionSearch"
                android:singleLine="true"
                android:textSize="14sp"/>

            <ImageView
                android:id="@+id/iv_clear_search"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/space_normal"
                android:layout_marginTop="@dimen/space_normal"
                android:src="@mipmap/ic_delete"/>
        </LinearLayout>

        <TextView
            android:id="@+id/iv_search"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:paddingLeft="@dimen/space_large"
            android:paddingRight="@dimen/space_large"
            android:text="搜索"
            android:textColor="@android:color/white"
            app:layout_constraintRight_toRightOf="parent"/>
    </android.support.constraint.ConstraintLayout>

    <!--历史搜索-->
    <LinearLayout
        android:id="@+id/ll_history_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingLeft="@dimen/space_large"
        android:paddingRight="@dimen/space_large"
        android:paddingTop="@dimen/space_normal"
        app:layout_constraintTop_toBottomOf="@+id/cl_toolbar">

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/tv_history_hint"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="搜索历史"
                android:textColor="#383838"
                android:textSize="14sp"/>

            <ImageView
                android:id="@+id/clear_all_records"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:src="@mipmap/ic_delete_history"
                app:layout_constraintBottom_toTopOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toBottomOf="parent"/>
        </android.support.constraint.ConstraintLayout>

        <TagFlowLayout
            android:id="@+id/fl_search_records"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="@dimen/space_normal"
            app:is_limit="true"
            app:limit_line_count="3"
            app:max_select="1">
        </TagFlowLayout>

        <ImageView
            android:id="@+id/iv_arrow"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_arrow"
            android:visibility="gone"/>
    </LinearLayout>
</android.support.constraint.ConstraintLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private RecordsDao mRecordsDao;
    //默然展示词条个数
    private final int DEFAULT_RECORD_NUMBER = 10;
    private List<String> recordList = new ArrayList<>();
    private TagAdapter mRecordsAdapter;
    private LinearLayout mHistoryContent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //默认账号
        String username = "007";
        //初始化数据库
        mRecordsDao = new RecordsDao(this, username);

        final EditText editText = findViewById(R.id.edit_query);
        final TagFlowLayout tagFlowLayout = findViewById(R.id.fl_search_records);
        final ImageView clearAllRecords = findViewById(R.id.clear_all_records);
        final ImageView moreArrow = findViewById(R.id.iv_arrow);
        TextView search = findViewById(R.id.iv_search);
        ImageView clearSearch = findViewById(R.id.iv_clear_search);
        mHistoryContent = findViewById(R.id.ll_history_content);

        initData();

        //创建历史标签适配器
        //为标签设置对应的内容
        mRecordsAdapter = new TagAdapter<String>(recordList) {

            @Override
            public View getView(FlowLayout parent, int position, String s) {
                TextView tv = (TextView) LayoutInflater.from(MainActivity.this).inflate(R.layout.tv_history,
                        tagFlowLayout, false);
                //为标签设置对应的内容
                tv.setText(s);
                return tv;
            }
        };


        tagFlowLayout.setAdapter(mRecordsAdapter);
        tagFlowLayout.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() {
            @Override
            public void onTagClick(View view, int position, FlowLayout parent) {
                //清空editText之前的数据
                editText.setText("");
                //将获取到的字符串传到搜索结果界面,点击后搜索对应条目内容
                editText.setText(recordList.get(position));
                editText.setSelection(editText.length());
            }
        });
        //删除某个条目
        tagFlowLayout.setOnLongClickListener(new TagFlowLayout.OnLongClickListener() {
            @Override
            public void onLongClick(View view, final int position) {
                showDialog("确定要删除该条历史记录?", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //删除某一条记录
                        mRecordsDao.deleteRecord(recordList.get(position));
                    }
                });
            }
        });

        //view加载完成时回调
        tagFlowLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                boolean isOverFlow = tagFlowLayout.isOverFlow();
                boolean isLimit = tagFlowLayout.isLimit();
                if (isLimit && isOverFlow) {
                    moreArrow.setVisibility(View.VISIBLE);
                } else {
                    moreArrow.setVisibility(View.GONE);
                }
            }
        });

        moreArrow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                tagFlowLayout.setLimit(false);
                mRecordsAdapter.notifyDataChanged();
            }
        });

        //清除所有记录
        clearAllRecords.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showDialog("确定要删除全部历史记录?", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        tagFlowLayout.setLimit(true);
                        //清除所有数据
                        mRecordsDao.deleteUsernameAllRecords();
                    }
                });
            }
        });

        search.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String record = editText.getText().toString();
                if (!TextUtils.isEmpty(record)) {
                    //添加数据
                    mRecordsDao.addRecords(record);
                }
            }
        });

        clearSearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //清除搜索历史
                editText.setText("");
            }
        });

        mRecordsDao.setNotifyDataChanged(new RecordsDao.NotifyDataChanged() {
            @Override
            public void notifyDataChanged() {
                initData();
            }
        });
    }

    private void showDialog(String dialogTitle, @NonNull DialogInterface.OnClickListener onClickListener) {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setMessage(dialogTitle);
        builder.setPositiveButton("确定", onClickListener);
        builder.setNegativeButton("取消", null);
        builder.create().show();
    }

    private void initData() {
        Observable.create(new ObservableOnSubscribe<List<String>>() {
            @Override
            public void subscribe(ObservableEmitter<List<String>> emitter) throws Exception {
                emitter.onNext(mRecordsDao.getRecordsByNumber(DEFAULT_RECORD_NUMBER));
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<List<String>>() {
                    @Override
                    public void accept(List<String> s) throws Exception {
                        recordList.clear();
                        recordList = s;
                        if (null == recordList || recordList.size() == 0) {
                            mHistoryContent.setVisibility(View.GONE);
                        } else {
                            mHistoryContent.setVisibility(View.VISIBLE);
                        }
                        if (mRecordsAdapter != null) {
                            mRecordsAdapter.setData(recordList);
                            mRecordsAdapter.notifyDataChanged();
                        }
                    }
                });
    }


    @Override
    protected void onDestroy() {
        mRecordsDao.closeDatabase();
        mRecordsDao.removeNotifyDataChanged();
        super.onDestroy();
    }
}

欢迎大家点赞,评论!!!

猜你喜欢

转载自blog.csdn.net/qq_42795723/article/details/107740329