RecycleView实现的地区选择模块
凉拌菜
8年前
<p>最近在做的一个项目里,需要在用户个人资料设置页有选择地区功能,看了一些开源的实现方法,大多还停留在底部弹出Dialog的形式,感觉并不友好,微信的地区选择界面是本人比较喜欢的展示形式,地区选择的功能应该算是一个基础功能吧,社交类、外卖类等等APP貌似都需要用户输入地址,选择地区等等,所以就想着自己做一个类似的,不足的是没有加入定位功能,以后还有很多要补充,现在先把这一块功能独立出来做个demo,和大家交流学习一下。</p> <p>先上图看一下效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/8df25e46672c3846034048c6b36bbf17.gif"></p> <p style="text-align:center">RegionSelector.gif</p> <p>讲一下思路:结构很简单,点击第一个activity的设置地区,开启第二个activity,同时把已经选择的地区传值,格式为 <strong>“省份 城市 地区”</strong> (没有引号),然后地区设置完成后setResult就可以了。地区activity选择用RecycleView实现。</p> <pre> <code class="language-java">AActivity <----------------onActivityResult() | | startActivityForResult() | | | BActivity---------->setResult()----------></code></pre> <h2><strong>布局</strong></h2> <p>和使用ListView差不多,不同的是RecycleView并没有item点击事件,因此这个需要自己实现,这个后面会讲到。ok,第一步实现item布局和ViewHolder,分析一下item需要展示的内容:首先是 <strong>地名</strong> ,还有就是后面有没有 <strong>已选择</strong> ,不要忽略item的点击事件。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/8e48e0463d9e72198f0668ce4035af20.png"></p> <p style="text-align:center">item_region_layout.png</p> <p>xml布局如下:</p> <pre> <code class="language-java"><RelativeLayout android:id="@+id/item_btn" ... > <TextView android:id="@+id/item_tv" ... /> <TextView android:id="@+id/checked"... /> </RelativeLayout></code></pre> <p>ViewHolder如下:</p> <pre> <code class="language-java">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); } }</code></pre> <h2><strong>Adapter适配</strong></h2> <p>我们的adapter要继承RecyclerView.Adapter,并且实现该抽象类的几个重要方法:</p> <pre> <code class="language-java">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); } }</code></pre> <h2><strong>RegionActivity实现</strong></h2> <p>在最开始的时,本来是想省份做一个activity,城市一个activity,然后地区一个,这样的话代码利用率太低,最好的办法还是在一个activity里实现,其实就是定义了一个curPage变量,记录用户当前选择到第几步了:</p> <h3><strong>三个操作阶段</strong></h3> <pre> <code class="language-java">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; } }</code></pre> <p>其实代码一眼就能看清楚,就是三个阶段,到最后一个阶段时就setResult把结果回传过去,不过这里要考虑到如果用户选择到最后一步时,如果想退出去重新选择城市的话,一按返回就退出activity了[捂脸/(ㄒoㄒ)/~~],所以我们还需要重写onBackPressed:</p> <h3><strong>三个返回阶段</strong></h3> <pre> <code class="language-java">@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; } }</code></pre> <p>补充一下数据的装载,首先把json数据从raw文件夹里面读取出来,然后格式化:</p> <pre> <code class="language-java">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(); }</code></pre> <p>这里推荐一个实用的工具,就是json数据格式化工具: <a href="/misc/goto?guid=4959716695512743223" rel="nofollow,noindex">pojo在线</a></p> <p style="text-align:center"><img src="https://simg.open-open.com/show/4355d145309d7fa6fcba026c798f8add.gif"></p> <p style="text-align:center">pojo_demo.gif</p> <p>顺便再推荐一个Material Design配色工具: </p> <p style="text-align:center"><img src="https://simg.open-open.com/show/f8a86bc4647d212d4c795c308c8f599a.gif"></p> <p style="text-align:center">materialpalette.gif</p> <p>是不是非常的炫酷实用呢?有了这个还要什么设计狮( ⊙ o ⊙ )!</p> <p>最后再看一下项目结构:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ed1dca516b53b985be57ab4db7a81a06.png"></p> <p style="text-align:center">project_structure.png</p> <p> </p> <p>来自:http://www.jianshu.com/p/ead90d9542b2</p> <p> </p>