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地区选择列表(二)——快速索引
相关文章推荐
- Android使用RecyclerView实现列表数据选择操作
- 【Android 仿微信通讯录 导航分组列表-下】自定义View为RecyclerView打造右侧索引导航栏IndexBar
- Android 自定义View实现城市选择列表
- RecyclerView+CheckBox实现列表数据选择操作
- 【Android 仿微信通讯录 导航分组列表-上】使用ItemDecoration为RecyclerView打造带悬停头部的分组列表
- android 实现城市选择、联系人等功能的易拓展 RecyclerView 库,包含自动索引,粘性等功能
- Android RecyclerView实现数据列表展示效果
- Android 仿微信通讯录 导航分组列表-上】使用ItemDecoration为RecyclerView打造带悬停头部的分组列表
- Android 仿美团选择城市、微信通讯录、饿了么点餐列表的导航悬停分组索引列表
- Android创建自定义视图列表对话框(数据库中的数据作为数据源,使用RecyclerView作为列表)
- Android客户端之“微服私访”App的系统学习(六)RecyclerView 展现复杂数据列表以及水波纹效果+CardView显示头像
- Android开发技巧——使用RecyclerView实现分组列表
- 解决列表 (ListView GrifView RecyclerView )结合CheckBox实现列表选择的的问题
- Android使用RecyclerView实现仿微信联系人列表
- Android使用RecyclerView实现自定义列表、点击事件以及下拉刷新
- Android RecyclerView 加载更多数据 及 不同类型itemView的使用
- RecyclerView学习(四)----城市导航列表的实现(上)
- [Android]自定义EmptyView列表数据为空显示
- Android 利用RecyclerView.Adapter刷新列表中的单个view问题
- Android 城市列表ListView 之 按首字母分组