地区选择功能在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;
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进行对比。
到这里分组的功能就实现了,效果如下:
欢迎继续阅读下一篇