Android城市选择列表(一)——RecyclerView数据分组

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a1533588867/article/details/52933145

地区选择功能在APP中是非常常见的,Demo主要简单实现了快速索引选择地区的功能,本帖围绕此demo,介绍如何在RecyclerView中分组展示数据以及快速索引。

先上效果图:

这里写图片描述

1.虚拟数据

创建一个类保存一串json格式的地区数据

/**
 * Created by kun on 2016/10/26.
 * 模拟数据
 */
public class Data {

    public static final String citiesJson = "{\"datas\":[{\"alifName\":\"C\",\"addressList\":[{\"id\":37,\"name\":\"潮州\"}]},{\"alifName\":\"D\",\"addressList\":[{\"id\":20,\"name\":\"东莞\"}]},{\"alifName\":\"F\",\"addressList\":[{\"id\":21,\"name\":\"佛山\"}]},{\"alifName\":\"G\",\"addressList\":[{\"id\":5,\"name\":\"广州\"}]},{\"alifName\":\"H\",\"addressList\":[{\"id\":29,\"name\":\"惠州\"},{\"id\":32,\"name\":\"河源\"},{\"id\":33,\"name\":\"河源\"}]},{\"alifName\":\"J\",\"addressList\":[{\"id\":25,\"name\":\"江门\"},{\"id\":38,\"name\":\"揭阳\"}]},{\"alifName\":\"M\",\"addressList\":[{\"id\":27,\"name\":\"茂名\"},{\"id\":30,\"name\":\"梅州\"}]},{\"alifName\":\"Q\",\"addressList\":[{\"id\":7,\"name\":\"泉州\"},{\"id\":35,\"name\":\"清远\"}]},{\"alifName\":\"S\",\"addressList\":[{\"id\":6,\"name\":\"深圳\"},{\"id\":22,\"name\":\"韶关\"},{\"id\":24,\"name\":\"汕头\"},{\"id\":31,\"name\":\"汕尾\"}]},{\"alifName\":\"Y\",\"addressList\":[{\"id\":34,\"name\":\"阳江\"},{\"id\":39,\"name\":\"云浮\"}]},{\"alifName\":\"Z\",\"addressList\":[{\"id\":23,\"name\":\"珠海\"},{\"id\":26,\"name\":\"湛江\"},{\"id\":28,\"name\":\"肇庆\"},{\"id\":36,\"name\":\"中山\"}]}]}";
}

格式如下

{
    "datas":[
        {
            "alifName":"C",
            "addressList":[
                {
                    "id":37,
                    "name":"潮州"
                }
            ]
        }
      ]
}

2.MainActivity

首先看一下布局文件,很简单,就一个RecyclerView。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f1f6f9">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

接着在MainActivity中初始化数据

 private void initView() {
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        Gson gson = new Gson();
        CitiesBean citiesBean = gson.fromJson(Data.citiesJson, CitiesBean.class);
        adapter = new CitiesAdapter(this,citiesBean.getDatas());
        recyclerView.setAdapter(adapter);
    }

这里主要给RecyclerView设置了垂直的线性布局管理者,接着封装本地的地区数据,然后创建CitiesAdapter,最后设置给RecyclerView。 数据的分组主要是交由Adapter来处理的,因此重点还是在CitiesAdapter中。

3.CitiesAdapter

a.首先CitiesAdapter需要继承RecyclerView.Adapter

public class CitiesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
}

b.接着我们要确定Adapter的ItemCount。

这里写图片描述

结合上图,在这里我把搜索、当前位置、热门城市做为一个Item,这里的数据是写死的。因此ItemCount至少是1。而每个字母以及地区都要单独占据应该Item。所以在获取ItemCount的代码如下:

 //获取数据的数量
    @Override
    public int getItemCount() {
        if (datas == null) return 1;//用于显示头部搜索、定位地区、热门地区
        int childCount = datas.size();//字母的数量
        for (int i = 0; i < datas.size(); i++) {
            childCount += datas.get(i).getAddressList().size();//地区的数量
        }
        return childCount + 1;
    }

c.对Item的视图进行分类——实现getItemViewType()

获取到Item的数量后,我们要实现getItemViewType()方法。这是方法是实现分组的关键,先看官方API的介绍:

Return the view type of the item at <code>position</code> for the purposes
of view recycling.
<p>The default implementation of this method returns 0, making the assumption of
a single view type for the adapter. Unlike ListView adapters, types need not
be contiguous. Consider using id resources to uniquely identify item view types.

可以大致了解到该方法默认是返回0,而返回的参数主要是对应postion的Item身份资源唯一标识,用于View进行创建或回收的时候标识Item的视图类型。简单地说我们可以在这个方法中对每个position对应的Item进行视图类型标识,在onCreateViewHolder中就能根据图类型标识区分显示的是头部搜索内容、字母或者地区名。

这里我们有三种视图类型,分别为头部搜索(包含了当前和热门)、组名(字母)以及地区名。因此我们定义三个常量来代表对应的类型。

 private final int HEAD = 0;
    private final int WORD = 1;
    private final int CITY = 2;

接着是getItemViewType的具体实现


    @Override
    public int getItemViewType(int position) {
        int count = 0;
        if(position==count) return HEAD;//下标为0的固定显示头部布局。

        for(int i = 0; i < cities.size(); i++){
            count++;
            if(position==count){
                return WORD;
            }
            List<CitiesBean.DatasBean.AddressListBean> addressList = cities.get(i).getAddressList();
            for(int j =0;j<addressList.size();j++){
                count++;
                if(position==count){
                    return CITY;
                }
            }
        }
        return super.getItemViewType(position);
    }

从代码中我们可以看到,如果下标为0,则返回HEAD。接着是显示一个分组。从cities中取出第一个分组,此时下标为1时,需要先显示第一个分组的字母,所以返回WORD,接着遍历第一个分组中的地区,返回CITY。以此类推。这里我们就能标识好position对应的Item显示的视图。

d.定义ViewHodler

定义三种视图类型对应的ViewHodler


    public static class HeadViewHolder extends RecyclerView.ViewHolder {
        public HeadViewHolder(View view) {
            super(view);
        }
    }
    public static class WordViewHolder extends RecyclerView.ViewHolder {
        TextView textWord;
        public WordViewHolder(View view) {
            super(view);
            textWord = (TextView) view.findViewById(R.id.textWord);
        }
    }
    public static class CityViewHolder extends RecyclerView.ViewHolder {

        TextView textCity;
        public CityViewHolder(View view) {
            super(view);
            textCity = (TextView) view.findViewById(R.id.textCity);
        }
    }

e.创建视图——实现onCreateViewHolder方法

@Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        if (viewType == TOP) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_index_select_city_top, viewGroup, false);
            return new TopViewHolder(view);
        } else if (viewType == TITLE) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_select_gradute_institution_word, viewGroup, false);
            return new WordViewHolder(view);
        } else {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_commen_textview, viewGroup, false);
            return new CityItemViewHolder(view);
        }
    }

在这里可以看到方法中返回了一个viewType参数,这个参数其实就是在getItemViewType中返回的。因此我们可以根据viewType返回对应ViewHolder。

d.进行数据绑定——实现onBindViewHolder()

 @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {

        if (position == 0) {
            topViewHolder = (TopViewHolder) viewHolder;
            initTopViewHolder(topViewHolder);
        } else {
            int count = 0;
            for (int i = 0; i < datas.size(); i++) {
                count += 1;
                if (position == count) {
                    WordViewHolder wordViewHolder = (WordViewHolder) viewHolder;
                    wordViewHolder.textWord.setText(datas.get(i).getAlifName());
                } else {
                    List<AddressListResult.CollectionBean.DatasBean.AddressListBean> addressList = datas.get(i).getAddressList();
                    for (int j = 0; j < addressList.size(); j++) {
                        count += 1;
                        if (position == count) {
                            final AddressListResult.CollectionBean.DatasBean.AddressListBean addressListBean = addressList.get(j);
                            CityItemViewHolder schoolViewHolder = (CityItemViewHolder) viewHolder;
                            schoolViewHolder.textSchoolName.setText(addressListBean.getName());
                            schoolViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
                                @Override
                                public void onClick(View v) {
                                    EventMessage message = new EventMessage();
                                    message.setType(24);
                                    message.setData(addressListBean.getName());
                                    EventBus.getDefault().post(message);
                                    ((Activity) context).finish();
                                }
                            });
                        }
                    }
                }
            }
        }
    }

在这里,主要还是通过position与count进行对比,区分当前Item对应的视图类型。其实也可以通过viewHolder.getItemViewType()的方法获取到当前的视图类型。但是基于当前的需求场景,通过第一种方法会比较方便,因此这里采用了position与count进行对比。

到这里分组的功能就实现了,效果如下:

这里写图片描述

欢迎继续阅读下一篇

Android地区选择列表(二)——快速索引

猜你喜欢

转载自blog.csdn.net/a1533588867/article/details/52933145