最近的项目需要做本地历史记录功能,以前写的有些过时了,就在网上查了查较好的开源库、实现方式等。最终选择了鸿洋大神的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上还有许许多多优秀的开源库,结合自己的具体需求,开源库可以实现各种各样的效果,小伙伴们赶紧去尝试吧。