最近在做的一个项目里,需要在用户个人资料设置页有选择地区功能,看了一些开源的实现方法,大多还停留在底部弹出Dialog的形式,感觉并不友好,微信的地区选择界面是本人比较喜欢的展示形式,地区选择的功能应该算是一个基础功能吧,社交类、外卖类等等APP貌似都需要用户输入地址,选择地区等等,所以就想着自己做一个类似的,不足的是没有加入定位功能,以后还有很多要补充,现在先把这一块功能独立出来做个demo,和大家交流学习一下(文末有实用小 ~~公 举 ~~ 工具推荐哦)。
先上图看一下效果:讲一下思路:结构很简单,点击第一个activity的设置地区,开启第二个activity,同时把已经选择的地区传值,格式为“省份 城市 地区”(没有引号),然后地区设置完成后setResult就可以了。地区activity选择用RecycleView实现。
AActivity <----------------onActivityResult()
| |
startActivityForResult() |
| |
BActivity---------->setResult()---------->
布局
和使用ListView差不多,不同的是RecycleView并没有item点击事件,因此这个需要自己实现,这个后面会讲到。ok,第一步实现item布局和ViewHolder,分析一下item需要展示的内容:首先是地名,还有就是后面有没有已选择,不要忽略item的点击事件。
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在线
顺便再推荐一个Material Design配色工具:MaterialPalette在线
是不是非常的炫酷实用呢?有了这个还要什么设计狮( ⊙ o ⊙ )!
最后再看一下项目结构:
demo下载地址
github项目地址