轻松实现Android仿淘宝地区选择功能
最近用淘宝客户端的时候,编辑地址的时候有个地区选择的功能。看上面的效果觉得挺酷,滚动的时候,是最后一个从下面飞上来挨着前一个。就自己鼓捣一个出来玩玩。
说了效果可能不太直观,下面上两张图看看效果
淘宝地区选择效果
再来一张自己的效果
gif的效果可能不太好,大家自己用android手机打开淘宝看看
实现分析
展示很简单,listview就可以了。对于动画效果,只需要在getview的时候获取到要展示的view,通过属性动画修改translationy就ok啦。由于地区选择是一个界面,所以这里还用到了fragment的 addtobackstack知识
1、用来展示的fragment
用一个fragment来接受parentcode参数来获取父地区的所有子地区,然后进行显示。这里用fragment来做是因为用activity的话,这样的连续点击都是同一类的界面不太适合。
public class areafragment extends fragment implements adapterview.onitemclicklistener { private static final string arg_param1 = "parentcode"; @bind(r.id.refresh_list_view) listview mrefreshlistview; @bind(r.id.loadingbar) progressbar mloadingbar; private string mparam1;//parentcode参数 okhttpclient mokhttpclient = new okhttpclient(); private onfragmentinteractionlistener mlistener; private areaadapter adapter;//地区adapter public areafragment() { } /** * use this factory method to create a new instance of * this fragment using the provided parameters. * * @param param1 parameter 1. * @return a new instance of fragment areafragment. */ public static areafragment newinstance(string param1) { areafragment fragment = new areafragment(); bundle args = new bundle(); args.putstring(arg_param1, param1); fragment.setarguments(args); return fragment; } @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); if (getarguments() != null) { //获取父地区的code,用来查询子地区 mparam1 = getarguments().getstring(arg_param1); } } @override public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) { // inflate the layout for this fragment view view = inflater.inflate(r.layout.fragment_area, container, false); butterknife.bind(this, view); mrefreshlistview.setonitemclicklistener(this); formencodingbuilder builder = new formencodingbuilder(); builder.add(arg_param1,mparam1); //通过parentcode来请求地区,如果parentcode不存在就是第一级 final request request = new request.builder() .url("http://123.184.16.19:8008/area/list") .post(builder.build()) .build(); mokhttpclient.newcall(request).enqueue(new callback(){ @override public void onfailure(request request, ioexception e) { } @override public void onresponse(response response) throws ioexception { final string res = response.body().string(); if (res!=null){ gson gson = new gson(); jsonresult jsonresult = gson.fromjson(res, jsonresult.class); if (jsonresult.issuccess()){ list list = (list) jsonresult.getresult(); list newlist = new arraylist(); iterator iterator = list.iterator(); while (iterator.hasnext()){ map map = (map) iterator.next(); areainfo areainfo = gson.fromjson(gson.tojson(map),areainfo.class); newlist.add(areainfo); } adapter = new areaadapter(getcontext(),newlist); getactivity().runonuithread(new runnable() { @override public void run() { //拿到数据进行展示 mrefreshlistview.setadapter(adapter); } }); } } } }); return view; } @override public void onattach(context context) { super.onattach(context); if (context instanceof onfragmentinteractionlistener) { mlistener = (onfragmentinteractionlistener) context; } else { throw new runtimeexception(context.tostring() + " must implement onfragmentinteractionlistener"); } } @override public void ondetach() { super.ondetach(); mlistener = null; } @override public void ondestroyview() { super.ondestroyview(); butterknife.unbind(this); } @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { //单击的时候需要处理地区点击事件,统一交给activity处理 areainfo areainfo = (areainfo) parent.getadapter().getitem(position); if (areainfo==null) return; if (mlistener!=null){ mlistener.onfragmentinteraction(areainfo); } } //用来和activity交互的回调接口 public interface onfragmentinteractionlistener { void onfragmentinteraction(areainfo areainfo); }
我们用了一个fragment来接受parentcode,用于请求下一级的地区,获取成功之后进行了展示。并且提供了一个onfragmentinteractionlistener用来在onitemclick时与activity交互。
接下来看adapter,最开始我们提到了要实现淘宝的效果我们只需要拿到即将显示的view,设置动画就可以了。
2、处理显示效果的adapter
class areaadapter extends baseadapter { private list list; private int lastposition; public areaadapter(context context, list<areainfo> list) { this.list = list; } @override public int getcount() { return list.size(); } @override public object getitem(int position) { return list.get(position); } @override public long getitemid(int position) { return 0; } @override public view getview(int position, view convertview, viewgroup parent) { viewholder viewholder = null; if (convertview==null){ convertview = layoutinflater.from(getcontext()).inflate(r.layout.area_list_item,parent,false); viewholder = new viewholder(); viewholder.textview = (textview) convertview.findviewbyid(android.r.id.text1); convertview.settag(viewholder); } viewholder = (viewholder) convertview.gettag(); areainfo item = (areainfo) list.get(position); viewholder.textview.settext(item.getareaname()); if (lastposition<position&&lastposition!=0){ objectanimator.offloat(convertview,"translationy",convertview.getheight()*2,0).setduration(500).start(); } lastposition = position; return convertview; } class viewholder{ textview textview; } }
很常见的一个adapter写法,只是在getview当中获取到了要显示的view,通过
objectanimator.offloat(convertview,”translationy”,convertview.getheight()*2,0).setduration(500).start()为veiw设置了动画,
这里还用了个变量position来区别只有在向上滚动的时候才会有动画。不过我觉得不加position区别的效果也不错,大家可以试试。
其实这样已经实现了效果,接下来顺便提一下activity对framgnet中onitemclick的处理。
3、activity和fragment的交互处理
public class areaselectactivity extends appcompatactivity implements areafragment.onfragmentinteractionlistener{ private fragment onefragment; private fragment twofragment; private map map = new hashmap(); @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_area_select); butterknife.bind(this); //新建第一级地区,parentcode参数为null onefragment = areafragment.newinstance(""); fragmentmanager fragmentmanager = getsupportfragmentmanager(); fragmentmanager.begintransaction().replace(r.id.content,onefragment).commit(); } @override public boolean onoptionsitemselected(menuitem item) { switch (item.getitemid()){ case android.r.id.home: fragmentmanager fragmentmanager = getsupportfragmentmanager(); if (fragmentmanager.getbackstackentrycount()>0){ fragmentmanager.popbackstack(); }else{ finish(); } break; } return true; } /** * 处理交互,hide前一个fragment,并且调用addtobackstack让fragment可以点击back的时候显示前一个fragment * 如果是第三级地区则直接返回地区选择数据给上个activity * @param areainfo 被点击的地区信息 */ @override public void onfragmentinteraction(areainfo areainfo) { if (areainfo==null){ return; } fragmenttransaction transaction = getsupportfragmentmanager().begintransaction(); int level = areainfo.getlevel(); switch (level){ case 1: map.put("provid",areainfo.getid()); map.put("provname",areainfo.getareaname()); if (areainfo.isleaf()){ intent intent = new intent(); intent.putextra("addressinfo", (serializable) map); setresult(result_ok,intent); finish(); }else{ transaction.hide(onefragment); transaction.add(r.id.content,twofragment=areafragment.newinstance(areainfo.getareacode()+"")).addtobackstack(null).commit(); } break; case 2: map.put("cityid",areainfo.getid()); map.put("cityname",areainfo.getareaname()); if (areainfo.isleaf()){ intent intent = new intent(); intent.putextra("addressinfo", (serializable) map); setresult(result_ok,intent); finish(); }else { transaction.hide(twofragment); transaction.add (r.id.content, areafragment.newinstance(areainfo.getareacode()+"")).addtobackstack(null).commit(); } break; case 3: map.put("districtid",areainfo.getid()); map.put("districtname",areainfo.getareaname()); intent intent = new intent(); intent.putextra("addressinfo", (serializable) map); setresult(result_ok,intent); finish(); break; } } }
这样仿淘宝地区选择就实现啦!
结语
大家可以自己写测试接口,也可以直接调用我写好的接口: http://123.184.16.19:8008/area/list
源码提供给大家参考:android仿淘宝地区选择
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。