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上还有许许多多优秀的开源库,结合自己的具体需求,开源库可以实现各种各样的效果,小伙伴们赶紧去尝试吧。

      附:

        项目源码下载地址

        GitHub项目地址

        FlowLayout开源库地址

猜你喜欢

转载自blog.csdn.net/qq941263013/article/details/81223574
今日推荐