Android 本地歷史記錄、及產品標籤(支援單選、多選)實現(附原始碼)(使用鴻洋大神的FlowLayout開源庫)
最近的專案需要做本地歷史記錄功能,以前寫的有些過時了,就在網上查了查較好的開源庫、實現方式等。最終選擇了鴻洋大神的FlowLayout流式佈局,再搭配SharedPreferencesUtil工具類來實現本地歷史記錄功能。同時FlowLayout開源庫還非常適合實現產品標籤(支援單選、多選)功能,所以在Demo中也有具體的使用,研究研究總是沒錯的。
開源庫的下載、匯入等操作就不再廢話了,直接開始專案演示。
注:文章末尾附專案原始碼下載地址及FlowLayout開源庫地址
效果展示
主要功能:FlowLayout的使用、從SP中讀取歷史記錄、將歷史記錄寫入到SP中、歷史記錄最大數量限制、歷史記錄不可重複、最新查詢的在最前邊、清楚歷史記錄;FlowLayout預先設定選中、設定最大選中數、設定標籤點選和選中監聽、獲取選中的標籤、通過selecter完成標籤選擇的切換等。方便演示加入了測試資料。
本地歷史記錄的實現
1.頁面佈局:主要是在佈局檔案中使用TagFlowLayout實現流失佈局,並通過自定義屬性(zhy:max_select="0")規定標籤的最大可選數。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:zhy="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#eeeeee" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@mipmap/titlebar_bg"> <RelativeLayout android:id="@+id/rl" android:layout_width="match_parent" android:layout_height="48dp" android:background="@null"> <TextView android:id="@+id/tv_titlebar_center" android:layout_width="200dp" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:ellipsize="end" android:gravity="center" android:maxLength="18" android:singleLine="true" android:text="商品查詢" android:textColor="@color/themeTextColor" android:textSize="@dimen/themeTextSize" /> <ImageView android:id="@+id/iv_titlebar_left" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:background="@null" android:padding="14dp" android:src="@mipmap/titlebar_back" /> </RelativeLayout> <RelativeLayout android:id="@+id/rl_search" android:layout_width="match_parent" android:layout_height="40dp" android:layout_below="@id/rl" android:layout_marginLeft="14dp" android:layout_marginRight="14dp" android:layout_marginTop="24dp" android:background="@drawable/shape_circle_corner_white"> <ImageView android:id="@+id/iv_search" android:layout_width="30dp" android:layout_height="30dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="5dp" android:background="@drawable/shape_circle_corner_blue5" android:padding="7dp" android:src="@mipmap/titlebar_search" /> <EditText android:id="@+id/et_search" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toLeftOf="@id/iv_search" android:background="@null" android:hint="請輸入要查詢的條碼" android:paddingLeft="15dp" android:paddingRight="15dp" android:singleLine="true" android:textColorHint="#D7DAE6" android:textCursorDrawable="@null" android:textSize="14sp" /> </RelativeLayout> <LinearLayout android:id="@+id/ll_history" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/rl_search" android:orientation="vertical" android:visibility="gone"> <com.zhy.view.flowlayout.TagFlowLayout android:id="@+id/flowlayout_history" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="11dp" zhy:max_select="0"></com.zhy.view.flowlayout.TagFlowLayout> <TextView android:id="@+id/tv_clear" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="14dp" android:paddingLeft="14dp" android:paddingRight="14dp" android:paddingTop="5dp" android:text="清除歷史記錄" android:textColor="#60ffffff" android:textSize="12sp" /> </LinearLayout> </RelativeLayout> </LinearLayout>
2.定義從SP中讀取歷史記錄方法:從SP中獲取儲存的字串,並通過wy標識將字串分割為字串陣列,然後新增到List中,最後將list返回。
/** * 從SP中讀取歷史記錄 */ private List<String> readHistory() { List<String> readHistoryList = new ArrayList<>(); String search_history = SharedPreferencesUtil.getString(context, "search_history", null); //將String轉為List if (!TextUtils.isEmpty(search_history)) { String[] strs = search_history.split("wy"); for (int i = 0; i < strs.length; i++) { readHistoryList.add(i, strs[i]); } } return readHistoryList; }
3.定義將歷史記錄寫入到SP中方法:先判空,不為空則獲取已有的歷史記錄,再將新的歷史記錄新增list中,最後把list轉換成字串(新增wy標識)存入到SP中。需要注意的是:1.歷史記錄最大數量限制,2.歷史記錄不可重複,3.最新查詢的在最前邊。
/**
* 將歷史記錄寫入到SP中
*/
private void writeHistory(String write) {
if (TextUtils.isEmpty(write)) {
return;
}
String writeHistory = "";
//獲取歷史記錄
List<String> readHistoryList = readHistory();
//如果不重複,則新增為第一個歷史記錄;
//如果重複,則刪除已有,再新增為第一個歷史記錄;
for (int i = 0; i < readHistoryList.size(); i++) {
boolean hasWrite = readHistoryList.get(i).equals(write);
if (hasWrite) {
readHistoryList.remove(i);
break;
}
}
readHistoryList.add(0, write);
//歷史記錄最多為10個
if (readHistoryList.size() > 10) {
readHistoryList = readHistoryList.subList(0, 10);
}
//將ArrayList轉為String
for (int i = 0; i < readHistoryList.size(); i++) {
writeHistory += readHistoryList.get(i) + "wy";
}
SharedPreferencesUtil.putString(context, "search_history", writeHistory);
}
4.初始化歷史記錄:先通過本地歷史記錄判斷頁面的顯示與隱藏,在為FlowLayout填充資料、設定點選事件監聽。
/**
* 初始化歷史記錄
*/
private void initHistory() {
final List<String> readHistory = readHistory();
if (readHistory != null && readHistory.size() > 0) {
llHistory.setVisibility(View.VISIBLE);
} else {
llHistory.setVisibility(View.GONE);
}
//為FlowLayout填充資料
flowlayoutHistory.setAdapter(new TagAdapter(readHistory) {
@Override
public View getView(FlowLayout parent, int position, Object o) {
TextView view = (TextView) View.inflate(context, R.layout.flowlayout_textview, null);
view.setText(readHistory.get(position));
return view;
}
});
//為FlowLayout的標籤設定監聽事件
flowlayoutHistory.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() {
@Override
public boolean onTagClick(View view, int position, FlowLayout parent) {
ToastUtil.makeText(context, readHistory.get(position));
etSearch.setText(readHistory.get(position));
etSearch.setSelection(readHistory.get(position).length());
return true;
}
});
}
5.點選搜尋按鈕:首先獲取輸入框中的內容,然後新增到本地歷史記錄中,最後再呼叫initHistory()重新整理頁面資料。具體的搜尋邏輯就不寫了,不是重點。
/**
* 初始化搜尋
*/
private void initSearch() {
String inputSearch = etSearch.getText().toString().trim();
if (TextUtils.isEmpty(inputSearch)) {
ToastUtil.makeText(context, "請輸入要查詢的條碼");
return;
}
writeHistory(inputSearch);
initHistory();
}
6.清除歷史記錄:將本地歷史記錄置為空字串,再呼叫initHistory()重新整理頁面資料即可,大功告成。
SharedPreferencesUtil.putString(context, "search_history", "");
initHistory();
產品標籤(支援單選、多選)的實現
##特色(開源庫中copy的)
-
以setAdapter形式注入資料
-
直接設定selector為background即可完成標籤選則的切換,類似CheckBox
-
支援控制選擇的Tag數量,比如:單選、多選
-
支援setOnTagClickListener,當點選某個Tag回撥
-
支援setOnSelectListener,當選擇某個Tag後回撥
-
支援adapter.notifyDataChanged
-
Activity重建(或者旋轉)後,選擇的狀態自動儲存
1.頁面佈局:主要是兩個TextVIew和兩個FlowLayout,並不複雜就不展示了。
2.初始化所有標籤的FlowLayout:包括預先設定選中、設定最大選中數、點選和選中事件監聽。當標籤選中時,初始化選中標籤的FlowLayout;當選中的標籤數量大於等於最大選中數時,給使用者友好的提示。
/**
* 初始化所有標籤的FlowLayout
*/
private void initFlowLayout() {
TagAdapter tagAdapter = new TagAdapter(mVals) {
@Override
public View getView(FlowLayout parent, int position, Object o) {
TextView view = (TextView) View.inflate(context, R.layout.flowlayout_textview_selected, null);
view.setText(mVals[position]);
return view;
}
};
//預先設定選中
tagAdapter.setSelectedList(0, 1);
flowlayout.setAdapter(tagAdapter);
//設定最大選中數
flowlayout.setMaxSelectCount(maxSelected);
//為FlowLayout的標籤設定監聽事件
flowlayout.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() {
@Override
public boolean onTagClick(View view, int position, FlowLayout parent) {
if (selectedList.size() >= maxSelected) {
ToastUtil.makeText(context, "已達最大選中數" + maxSelected);
} else {
ToastUtil.makeText(context, mVals[position]);
}
return true;
}
});
//為FlowLayout的標籤設定選中監聽事件
flowlayout.setOnSelectListener(new TagFlowLayout.OnSelectListener() {
@Override
public void onSelected(Set<Integer> selectPosSet) {
initFlowLayoutSelected();
}
});
}
3.初始化選中標籤的FlowLayout:獲取所有選中的position的Set集合,通過Iterator遍歷Set集合,並將資料新增到新的String[]中,最後為FlowLayout填充資料。因為在佈局檔案中將此FlowLayout的最大選中數設定為0,所以不能選中了。
/**
* 初始化選中標籤的FlowLayout
*/
private void initFlowLayoutSelected() {
int i = 0;
//獲得所有選中的position集合,例如[1,2,3,4]
selectedList = flowlayout.getSelectedList();
mValsSelected = new String[selectedList.size()];
Iterator<Integer> iterator = selectedList.iterator();
while (iterator.hasNext()) {
mValsSelected[i] = mVals[iterator.next()];
i++;
}
tvSelected.setText("最大選中數為:" + maxSelected + "(已選中" + selectedList.size() + ")" + "(position:" + selectedList.toString() + ")");
flowlayoutSelected.setAdapter(new TagAdapter(mValsSelected) {
@Override
public View getView(FlowLayout parent, int position, Object o) {
TextView view = (TextView) View.inflate(context, R.layout.flowlayout_textview_no_selected, null);
view.setText(mValsSelected[position]);
return view;
}
});
}
小結
以上就是兩個功能:本地歷史記錄和產品標籤的實現了,雖然功能不算複雜,但完成之後還是很有成就感的,拿著玩一玩吧。GitHub上還有許許多多優秀的開源庫,結合自己的具體需求,開源庫可以實現各種各樣的效果,小夥伴們趕緊去嘗試吧。