RecycleView實現的地區選擇模塊
最近在做的一個項目里,需要在用戶個人資料設置頁有選擇地區功能,看了一些開源的實現方法,大多還停留在底部彈出Dialog的形式,感覺并不友好,微信的地區選擇界面是本人比較喜歡的展示形式,地區選擇的功能應該算是一個基礎功能吧,社交類、外賣類等等APP貌似都需要用戶輸入地址,選擇地區等等,所以就想著自己做一個類似的,不足的是沒有加入定位功能,以后還有很多要補充,現在先把這一塊功能獨立出來做個demo,和大家交流學習一下。
先上圖看一下效果:

RegionSelector.gif
講一下思路:結構很簡單,點擊第一個activity的設置地區,開啟第二個activity,同時把已經選擇的地區傳值,格式為 “省份 城市 地區” (沒有引號),然后地區設置完成后setResult就可以了。地區activity選擇用RecycleView實現。
AActivity <----------------onActivityResult()
| |
startActivityForResult() |
| |
BActivity---------->setResult()---------->
布局
和使用ListView差不多,不同的是RecycleView并沒有item點擊事件,因此這個需要自己實現,這個后面會講到。ok,第一步實現item布局和ViewHolder,分析一下item需要展示的內容:首先是 地名 ,還有就是后面有沒有 已選擇 ,不要忽略item的點擊事件。

item_region_layout.png
xml布局如下:
<RelativeLayout
android:id="@+id/item_btn" ... >
<TextView
android:id="@+id/item_tv" ... />
<TextView
android:id="@+id/checked"... />
</RelativeLayout>
ViewHolder如下:
public class RegionViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public TextView checked;
public ViewGroup itemBtn;
public RegionViewHolder(View itemView) {
super(itemView);
initView();
}
private void initView() {
textView = (TextView) itemView.findViewById(R.id.item_tv);
checked = (TextView) itemView.findViewById(R.id.checked);
itemBtn = (ViewGroup) itemView.findViewById(R.id.item_btn);
}
}
Adapter適配
我們的adapter要繼承RecyclerView.Adapter,并且實現該抽象類的幾個重要方法:
public class RegionAdapter extends RecyclerView.Adapter<RegionViewHolder> {
private List<String> itemList;//用于存放要展示的數據列表
private String checkedStr;//當前選中的地區
private Context context;
private LayoutInflater layoutInflater;
private OnItemClickListener listener;//回調點擊事件
public RegionAdapter(Context context) {
this.context = context;
layoutInflater = LayoutInflater.from(context);
}
/* 更新數據并展示 */
public void setData(List<String> itemList,String checkedStr) {
this.itemList = itemList;
this.checkedStr = checkedStr;
notifyDataSetChanged();
}
@Override
public RegionViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new RegionViewHolder(layoutInflater.inflate(R.layout.item_region_layout,parent,false));
}
@Override
public void onBindViewHolder(final RegionViewHolder holder, final int position) {
holder.textView.setText(itemList.get(position));
holder.checked.setVisibility(checkedStr.equals(itemList.get(position))? View.VISIBLE:View.GONE);
holder.itemBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null)
listener.onItemClick(holder,position);
}
});
}
@Override
public int getItemCount() {
return isEmpty(itemList) ? 0 : itemList.size();
}
private <D> boolean isEmpty(List<D> list) {
return (list == null || list.isEmpty());
}
public void setListener(OnItemClickListener listener) {
this.listener = listener;
}
public interface OnItemClickListener {
void onItemClick(RegionViewHolder holder, int position);
}
}
RegionActivity實現
在最開始的時,本來是想省份做一個activity,城市一個activity,然后地區一個,這樣的話代碼利用率太低,最好的辦法還是在一個activity里實現,其實就是定義了一個curPage變量,記錄用戶當前選擇到第幾步了:
三個操作階段
switch (curPage) {
case PROVINCE://用戶選擇了某個省份
province = itemList.get(position);
for (Result result : result_List) {
if (province.equals(result.getProvince()))
city_List = result.getCity();
}
provinces.clear();
provinces.addAll(itemList);
itemList.clear();
for (City city : city_List) {
if (this.city.equals(city.getCity()))
itemList.add(0,city.getCity());
else
itemList.add(city.getCity());
}
adapter.setData(itemList,city);
curPage++;
break;
case CITY://用戶選擇了某個城市
city = itemList.get(position);
for (City city : city_List) {
if (this.city.equals(city.getCity()))
district_List = city.getDistrict();
}
cities.clear();
cities.addAll(itemList);
itemList.clear();
for (District district : district_List) {
if (this.district.equals(district.getDistrict()))
itemList.add(0,district.getDistrict());
else
itemList.add(district.getDistrict());
}
adapter.setData(itemList,district);
curPage++;
break;
case DISTRICT://用戶選擇了某個地區
district = itemList.get(position);
setResult(RESULT_OK,new Intent().putExtra("result"
,province + " " + city + " " + district));
finish();
break;
}
}
其實代碼一眼就能看清楚,就是三個階段,到最后一個階段時就setResult把結果回傳過去,不過這里要考慮到如果用戶選擇到最后一步時,如果想退出去重新選擇城市的話,一按返回就退出activity了[捂臉/(ㄒoㄒ)/~~],所以我們還需要重寫onBackPressed:
三個返回階段
@Override
public void onBackPressed() {
switch (curPage) {
case PROVINCE:
finish();
break;
case CITY:
itemList.clear();
itemList.addAll(provinces);
adapter.setData(itemList,province);
curPage--;
break;
case DISTRICT:
itemList.clear();
itemList.addAll(cities);
adapter.setData(itemList,city);
curPage--;
break;
}
}
補充一下數據的裝載,首先把json數據從raw文件夾里面讀取出來,然后格式化:
try {
if (result_List == null) {
Gson gson = new Gson();
Root root = gson.fromJson(StreamUtils.get(this,R.raw.city), Root.class);
result_List = root.getResult();
}
for (Result result : result_List) {
if (province.equals(result.getProvince()))
itemList.add(0,result.getProvince());
else
itemList.add(result.getProvince());
}
adapter.setData(itemList,province);
} catch (JsonSyntaxException e) {
e.printStackTrace();
}
這里推薦一個實用的工具,就是json數據格式化工具: pojo在線

pojo_demo.gif
順便再推薦一個Material Design配色工具:

materialpalette.gif
是不是非常的炫酷實用呢?有了這個還要什么設計獅( ⊙ o ⊙ )!
最后再看一下項目結構:

project_structure.png
來自:http://www.jianshu.com/p/ead90d9542b2