您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)使用Android自定義流式布局實(shí)現(xiàn)淘寶搜索記錄,文章內(nèi)容豐富且以專(zhuān)業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
效果如下:
廢話不多說(shuō)
實(shí)現(xiàn)代碼:
attrs.xml
<declare-styleable name="TagFlowLayout"> <!--最大選擇數(shù)量--> <attr name="max_select" format="integer"/> <!--最大可顯示行數(shù)--> <attr name="limit_line_count" format="integer"/> <!--是否設(shè)置多行隱藏--> <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為不限制數(shù)量 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); //消費(fèi)事件,不讓事件繼續(xù)下去 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; //默認(rèn)顯示3行 斷詞條顯示3行,長(zhǎng)詞條顯示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; //在每一次換行之后記錄,是否超過(guò)了行數(shù) int lineCount = 0;//記錄當(dāng)前的行數(shù) 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; //如果超過(guò)規(guī)定的行數(shù)則不進(jìn)行繪制 int lineCount = 0;//記錄當(dāng)前的行數(shù) 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,需要補(bǔ)償一個(gè)padding值 left = width - (currentLineWidth + getPaddingLeft()) - getPaddingRight(); // 適配了rtl,需要把lineViews里面的數(shù)組倒序排 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(); } /** * 設(shè)置數(shù)據(jù)變化監(jiān)聽(tīng) */ public void setNotifyDataChanged(NotifyDataChanged notifyDataChanged) { mNotifyDataChanged = notifyDataChanged; } /** * 移除數(shù)據(jù)變化監(jiān)聽(tīng) */ public void removeNotifyDataChanged() { if (mNotifyDataChanged != null) { mNotifyDataChanged = null; } } private synchronized SQLiteDatabase getWritableDatabase() { return recordHelper.getWritableDatabase(); } private synchronized SQLiteDatabase getReadableDatabase() { return recordHelper.getReadableDatabase(); } /** * 如果考慮操作頻繁可以到最后不用數(shù)據(jù)庫(kù)時(shí)關(guān)閉 * <p> * 關(guān)閉數(shù)據(jù)庫(kù) */ public void closeDatabase() { if (recordsDb != null) { recordsDb.close(); } } /** * 添加搜索記錄 * * @param record 記錄 */ public void addRecords(String record) { //如果這條記錄沒(méi)有則添加,有則更新時(shí)間 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"); //更新搜索歷史數(shù)據(jù)時(shí)間 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) { //關(guān)閉游標(biāo) 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) { //關(guān)閉游標(biāo) cursor.close(); } } return isHasRecord; } /** * 獲取當(dāng)前用戶(hù)全部搜索記錄 * * @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) { //關(guān)閉游標(biāo) cursor.close(); } } return recordsList; } /** * 獲取指定數(shù)量搜索記錄 * * @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) { //關(guān)閉游標(biāo) cursor.close(); } } } return recordsList; } /** * 模糊查詢(xún) * * @param record 記錄 * @return 返回類(lèi)似記錄 */ 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) { //關(guān)閉游標(biāo) cursor.close(); } } return similarRecords; } /** * 清除指定用戶(hù)的搜索記錄 */ 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 { } } /** * 清空數(shù)據(jù)庫(kù)所有的歷史記錄 */ 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 { } } /** * 通過(guò)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; } /** * 通過(guò)記錄刪除記錄 * * @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"> <!--標(biāo)題欄--> <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="請(qǐng)輸入搜索關(guān)鍵字" 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; //默然展示詞條個(gè)數(shù) 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); //默認(rèn)賬號(hào) String username = "007"; //初始化數(shù)據(jù)庫(kù) 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(); //創(chuàng)建歷史標(biāo)簽適配器 //為標(biāo)簽設(shè)置對(duì)應(yīng)的內(nèi)容 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); //為標(biāo)簽設(shè)置對(duì)應(yīng)的內(nèi)容 tv.setText(s); return tv; } }; tagFlowLayout.setAdapter(mRecordsAdapter); tagFlowLayout.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() { @Override public void onTagClick(View view, int position, FlowLayout parent) { //清空editText之前的數(shù)據(jù) editText.setText(""); //將獲取到的字符串傳到搜索結(jié)果界面,點(diǎn)擊后搜索對(duì)應(yīng)條目?jī)?nèi)容 editText.setText(recordList.get(position)); editText.setSelection(editText.length()); } }); //刪除某個(gè)條目 tagFlowLayout.setOnLongClickListener(new TagFlowLayout.OnLongClickListener() { @Override public void onLongClick(View view, final int position) { showDialog("確定要?jiǎng)h除該條歷史記錄?", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //刪除某一條記錄 mRecordsDao.deleteRecord(recordList.get(position)); } }); } }); //view加載完成時(shí)回調(diào) 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("確定要?jiǎng)h除全部歷史記錄?", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { tagFlowLayout.setLimit(true); //清除所有數(shù)據(jù) mRecordsDao.deleteUsernameAllRecords(); } }); } }); search.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String record = editText.getText().toString(); if (!TextUtils.isEmpty(record)) { //添加數(shù)據(jù) 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(); } }
上述就是小編為大家分享的使用Android自定義流式布局實(shí)現(xiàn)淘寶搜索記錄了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。