Android自定義流式布局實現(xiàn)淘寶搜索記錄
更新時間:2020年10月27日 13:10:47 作者:迷路國王
這篇文章主要為大家詳細介紹了Android自定義流式布局實現(xiàn)淘寶搜索記錄,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
本文實例為大家分享了Android實現(xiàn)淘寶搜索記錄的具體代碼,供大家參考,具體內容如下
效果如下:



廢話不多說
實現(xiàn)代碼:
attrs.xml
<declare-styleable name="TagFlowLayout"> <!--最大選擇數(shù)量--> <attr name="max_select" format="integer"/> <!--最大可顯示行數(shù)--> <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為不限制數(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);
//消費事件,不讓事件繼續(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; //默認顯示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;
//在每一次換行之后記錄,是否超過了行數(shù)
int lineCount = 0;//記錄當前的行數(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ī)定的行數(shù)則不進行繪制
int lineCount = 0;//記錄當前的行數(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,需要補償一個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ù)據(jù)變化監(jiān)聽
*/
public void setNotifyDataChanged(NotifyDataChanged notifyDataChanged) {
mNotifyDataChanged = notifyDataChanged;
}
/**
* 移除數(shù)據(jù)變化監(jiān)聽
*/
public void removeNotifyDataChanged() {
if (mNotifyDataChanged != null) {
mNotifyDataChanged = null;
}
}
private synchronized SQLiteDatabase getWritableDatabase() {
return recordHelper.getWritableDatabase();
}
private synchronized SQLiteDatabase getReadableDatabase() {
return recordHelper.getReadableDatabase();
}
/**
* 如果考慮操作頻繁可以到最后不用數(shù)據(jù)庫時關閉
* <p>
* 關閉數(shù)據(jù)庫
*/
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");
//更新搜索歷史數(shù)據(jù)時間
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;
}
/**
* 獲取指定數(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) {
//關閉游標
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 {
}
}
/**
* 清空數(shù)據(jù)庫所有的歷史記錄
*/
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;
//默然展示詞條個數(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);
//默認賬號
String username = "007";
//初始化數(shù)據(jù)庫
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)建歷史標簽適配器
//為標簽設置對應的內容
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之前的數(shù)據(jù)
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);
//清除所有數(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本地實現(xiàn)搜索歷史記錄
- Android實現(xiàn)搜索保存歷史記錄功能
- Android流式布局實現(xiàn)歷史搜索記錄功能
- Android項目類似淘寶 電商 搜索功能,監(jiān)聽軟鍵盤搜索事件,延遲自動搜索,以及時間排序的搜索歷史記錄的實現(xiàn)
- Android實現(xiàn)搜索功能并本地保存搜索歷史記錄
- Android實現(xiàn)簡易計步器功能隔天步數(shù)清零查看歷史運動紀錄
- android中AutoCompleteTextView的簡單用法(實現(xiàn)搜索歷史)
- Android中使用 AutoCompleteTextView 實現(xiàn)手機號格式化附帶清空歷史的操作
- Android實現(xiàn)搜索歷史功能
- Android實現(xiàn)歷史搜索記錄
相關文章
Android音視頻開發(fā)之VideoView使用指南
VideoView組件內部同樣是使用MediaPlayer+SurfaceView的形式控制MediaPlayer對視頻文件進行播放,本文就來詳細講講它的使用方法,需要的可以參考一下2022-04-04
Android實現(xiàn)志愿者系統(tǒng)詳細步驟與代碼
這篇文章主要介紹了Android實現(xiàn)志愿者系統(tǒng),本系統(tǒng)采用MVC架構設計,SQLite數(shù)據(jù)表有用戶表、成員表和活動表,有十多個Activity頁面。打開應用,進入歡迎界面,3s后跳轉登錄界面,用戶先注冊賬號,登錄成功后進入主界面2023-02-02

