溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

使用Android自定義流式布局實(shí)現(xiàn)淘寶搜索記錄

發(fā)布時(shí)間:2020-10-28 14:55:27 來(lái)源:億速云 閱讀:182 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)使用Android自定義流式布局實(shí)現(xiàn)淘寶搜索記錄,文章內(nèi)容豐富且以專(zhuān)業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

效果如下:

使用Android自定義流式布局實(shí)現(xiàn)淘寶搜索記錄

使用Android自定義流式布局實(shí)現(xiàn)淘寶搜索記錄

使用Android自定義流式布局實(shí)現(xiàn)淘寶搜索記錄

廢話不多說(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 &#63; sizeWidth : width + getPaddingLeft() + getPaddingRight(),
  modeHeight == MeasureSpec.EXACTLY &#63; 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 &#63; 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 = &#63;", 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 = &#63;", 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 = &#63;", 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 = &#63;", 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 = &#63;", 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 = &#63; and keyword like '%&#63;%'", 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 = &#63;", 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 = &#63;", 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 = &#63; and keyword = &#63;", 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="&#63;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è)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI