您的位置:首页 > 移动开发 > Android开发

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

2016-10-26 12:36 701 查看

地区选择功能在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地区选择列表(二)——快速索引

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐