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

 更新时间:2020年10月27日 13:10:47   作者:迷路国王  
这篇文章主要为大家详细介绍了Android自定义流式布局实现淘宝搜索记录,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了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();
 }
}

欢迎大家点赞,评论!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 保存ListView上次的滚动条的位置实例(必看)

    保存ListView上次的滚动条的位置实例(必看)

    下面小编就为大家带来一篇保存ListView上次的滚动条的位置实例(必看)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Android自定义封装banner组件

    Android自定义封装banner组件

    这篇文章主要为大家详细介绍了Android自定义封装banner组件的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • Android实现一个简易的带边输入框

    Android实现一个简易的带边输入框

    如今市面上APP的输入框可以说是千奇百怪,不搞点花样出来貌似代表格局没打开,还在使用系统自带的输入框的兄弟可以停下脚步,哥带你实现一个简易的带边框输入框,感兴趣的同学可以自己动手试一下
    2023-08-08
  • Android XML数据解析要点介绍

    Android XML数据解析要点介绍

    这篇文章主要为大家介绍了Android XML数据解析要点介绍,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Android几种多渠道打包的步骤详解

    Android几种多渠道打包的步骤详解

    在不同的应用市场可能有不同的统计需求,需要为每个应用市场发布一个安装包,这里就引出了Android的多渠道打包。这篇文章主要介绍了Android几种多渠道打包,需要的朋友可以参考下
    2019-09-09
  • pp列表之分组ListView详解

    pp列表之分组ListView详解

    本篇文章是对Android中的ListView进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • Android音视频开发之VideoView使用指南

    Android音视频开发之VideoView使用指南

    VideoView组件内部同样是使用MediaPlayer+SurfaceView的形式控制MediaPlayer对视频文件进行播放,本文就来详细讲讲它的使用方法,需要的可以参考一下
    2022-04-04
  • android实现上下滚动的TextView

    android实现上下滚动的TextView

    android实现上下滚动的TextView,需要的朋友可以参考一下
    2013-05-05
  • Android布局控件之常用linearlayout布局

    Android布局控件之常用linearlayout布局

    LinearLayout是线性布局控件,它包含的子控件将以横向或竖向的方式排列,按照相对位置来排列所有的widgets或者其他的containers,超过边界时,某些控件将缺失或消失
    2016-01-01
  • Android实现志愿者系统详细步骤与代码

    Android实现志愿者系统详细步骤与代码

    这篇文章主要介绍了Android实现志愿者系统,本系统采用MVC架构设计,SQLite数据表有用户表、成员表和活动表,有十多个Activity页面。打开应用,进入欢迎界面,3s后跳转登录界面,用户先注册账号,登录成功后进入主界面
    2023-02-02

最新评论