Android 轻松实现仿淘宝地区选择

转自:


介绍

最近用淘宝客户端的时候,编辑地址的时候有个地区选择的功能。看上面的效果觉得挺酷,滚动的时候,是最后一个从下面飞上来挨着前一个。就自己鼓捣一个出来玩玩。

说了效果可能不太直观,下面上两张图看看效果
淘宝地区选择效果
淘宝

再来一张自己的效果
这里写图片描述

gif的效果可能不太好,大家自己用Android手机打开淘宝看看

实现分析

展示很简单,ListView就可以了。对于动画效果,只需要在getView的时候获取到要展示的View,通过属性动画修改translationY就ok啦。由于地区选择是一个界面,所以这里还用到了Fragment的 addToBackStack知识

1、用来展示的Fragment

用一个Fragment来接受parentCode参数来获取父地区的所有子地区,然后进行显示。这里用Fragment来做是因为用Activity的话,这样的连续点击都是同一类的界面不太适合。

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AreaFragment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Fragment</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">AdapterView</span>.<span class="hljs-title">OnItemClickListener</span> {</span>

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String ARG_PARAM1 = <span class="hljs-string">"parentCode"</span>;
    <span class="hljs-annotation">@Bind</span>(R.id.refresh_list_view)
    ListView mRefreshListView;
    <span class="hljs-annotation">@Bind</span>(R.id.loadingBar)
    ProgressBar mLoadingBar;

    <span class="hljs-keyword">private</span> String mParam1;<span class="hljs-comment">//parentCode参数</span>

    OkHttpClient mOkHttpClient = <span class="hljs-keyword">new</span> OkHttpClient();

    <span class="hljs-keyword">private</span> OnFragmentInteractionListener mListener;

    <span class="hljs-keyword">private</span> AreaAdapter adapter;<span class="hljs-comment">//地区adapter</span>

    <span class="hljs-keyword">public</span> <span class="hljs-title">AreaFragment</span>() {
    }

    <span class="hljs-javadoc">/**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     *<span class="hljs-javadoctag"> @param</span> param1 Parameter 1.
     *<span class="hljs-javadoctag"> @return</span> A new instance of fragment AreaFragment.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> AreaFragment <span class="hljs-title">newInstance</span>(String param1) {
        AreaFragment fragment = <span class="hljs-keyword">new</span> AreaFragment();
        Bundle args = <span class="hljs-keyword">new</span> Bundle();
        args.putString(ARG_PARAM1, param1);
        fragment.setArguments(args);
        <span class="hljs-keyword">return</span> fragment;
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) {
        <span class="hljs-keyword">super</span>.onCreate(savedInstanceState);
        <span class="hljs-keyword">if</span> (getArguments() != <span class="hljs-keyword">null</span>) {
            <span class="hljs-comment">//获取父地区的code,用来查询子地区</span>
            mParam1 = getArguments().getString(ARG_PARAM1);

        }
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> View <span class="hljs-title">onCreateView</span>(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        <span class="hljs-comment">// Inflate the layout for this fragment</span>
        View view = inflater.inflate(R.layout.fragment_area, container, <span class="hljs-keyword">false</span>);
        ButterKnife.bind(<span class="hljs-keyword">this</span>, view);
        mRefreshListView.setOnItemClickListener(<span class="hljs-keyword">this</span>);

        FormEncodingBuilder builder = <span class="hljs-keyword">new</span> FormEncodingBuilder();
        builder.add(ARG_PARAM1,mParam1);

        <span class="hljs-comment">//通过parentCode来请求地区,如果parentCode不存在就是第一级</span>
        <span class="hljs-keyword">final</span> Request request = <span class="hljs-keyword">new</span> Request.Builder()
                .url(<span class="hljs-string">"http://123.184.16.19:8008/area/list"</span>)
                .post(builder.build())
                .build();
        mOkHttpClient.newCall(request).enqueue(<span class="hljs-keyword">new</span> Callback(){
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onFailure</span>(Request request, IOException e) {

            }

            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onResponse</span>(Response response) <span class="hljs-keyword">throws</span> IOException {
                <span class="hljs-keyword">final</span> String res = response.body().string();
                <span class="hljs-keyword">if</span> (res!=<span class="hljs-keyword">null</span>){
                    Gson gson = <span class="hljs-keyword">new</span> Gson();
                    JsonResult jsonResult =  gson.fromJson(res, JsonResult.class);
                    <span class="hljs-keyword">if</span> (jsonResult.isSuccess()){
                        List list = (List) jsonResult.getResult();

                        List newList = <span class="hljs-keyword">new</span> ArrayList();
                        Iterator iterator = list.iterator();
                        <span class="hljs-keyword">while</span> (iterator.hasNext()){
                            Map map = (Map) iterator.next();
                            AreaInfo areaInfo = gson.fromJson(gson.toJson(map),AreaInfo.class);
                            newList.add(areaInfo);
                        }
                        adapter = <span class="hljs-keyword">new</span> AreaAdapter(getContext(),newList);
                        getActivity().runOnUiThread(<span class="hljs-keyword">new</span> Runnable() {
                            <span class="hljs-annotation">@Override</span>
                            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() {
                      <span class="hljs-comment">//拿到数据进行展示         </span>
                       mRefreshListView.setAdapter(adapter);
                            }
                        });
                    }
                }
            }
        });

        <span class="hljs-keyword">return</span> view;
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onAttach</span>(Context context) {
        <span class="hljs-keyword">super</span>.onAttach(context);
        <span class="hljs-keyword">if</span> (context <span class="hljs-keyword">instanceof</span> OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(context.toString()
                    + <span class="hljs-string">" must implement OnFragmentInteractionListener"</span>);
        }
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDetach</span>() {
        <span class="hljs-keyword">super</span>.onDetach();
        mListener = <span class="hljs-keyword">null</span>;
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDestroyView</span>() {
        <span class="hljs-keyword">super</span>.onDestroyView();
        ButterKnife.unbind(<span class="hljs-keyword">this</span>);
    }



    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onItemClick</span>(AdapterView<?> parent, View view, <span class="hljs-keyword">int</span> position, <span class="hljs-keyword">long</span> id) {
        <span class="hljs-comment">//单击的时候需要处理地区点击事件,统一交给Activity处理</span>
        AreaInfo areaInfo = (AreaInfo) parent.getAdapter().getItem(position);
        <span class="hljs-keyword">if</span> (areaInfo==<span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span>;
        <span class="hljs-keyword">if</span> (mListener!=<span class="hljs-keyword">null</span>){
            mListener.onFragmentInteraction(areaInfo);
        }
    }


    <span class="hljs-comment">//用来和Activity交互的回调接口</span>
    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">OnFragmentInteractionListener</span> {</span>
        <span class="hljs-keyword">void</span> onFragmentInteraction(AreaInfo areaInfo);
    }
</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li><li>114</li><li>115</li><li>116</li><li>117</li><li>118</li><li>119</li><li>120</li><li>121</li><li>122</li><li>123</li><li>124</li><li>125</li><li>126</li><li>127</li><li>128</li><li>129</li><li>130</li><li>131</li><li>132</li><li>133</li><li>134</li><li>135</li><li>136</li><li>137</li><li>138</li><li>139</li></ul>

我们用了一个Fragment来接受parentCode,用于请求下一级的地区,获取成功之后进行了展示。并且提供了一个OnFragmentInteractionListener用来在onItemClick时与Activity交互。

接下来看adapter,最开始我们提到了要实现淘宝的效果我们只需要拿到即将显示的View,设置动画就可以了。

2、处理显示效果的adapter

<code class="hljs php has-numbering"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AreaAdapter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">BaseAdapter</span> {</span>

        <span class="hljs-keyword">private</span> <span class="hljs-keyword">List</span> <span class="hljs-keyword">list</span>;

        <span class="hljs-keyword">private</span> int lastPosition;

        <span class="hljs-keyword">public</span> AreaAdapter(Context context, <span class="hljs-keyword">List</span><AreaInfo> <span class="hljs-keyword">list</span>) {
            this.<span class="hljs-keyword">list</span> = <span class="hljs-keyword">list</span>;
        }


        @Override
        <span class="hljs-keyword">public</span> int getCount() {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">list</span>.size();
        }

        @Override
        <span class="hljs-keyword">public</span> Object getItem(int position) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">list</span>.get(position);
        }

        @Override
        <span class="hljs-keyword">public</span> long getItemId(int position) {
            <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
        }

        @Override
        <span class="hljs-keyword">public</span> View getView(int position, View convertView, ViewGroup <span class="hljs-keyword">parent</span>) {
            ViewHolder viewHolder = <span class="hljs-keyword">null</span>;
            <span class="hljs-keyword">if</span> (convertView==<span class="hljs-keyword">null</span>){
               convertView =  LayoutInflater.from(getContext()).inflate(R.layout.area_list_item,<span class="hljs-keyword">parent</span>,<span class="hljs-keyword">false</span>);
                viewHolder = <span class="hljs-keyword">new</span> ViewHolder();
                viewHolder.textView = (TextView) convertView.findViewById(android.R.id.text1);
                convertView.setTag(viewHolder);
            }
            viewHolder = (ViewHolder) convertView.getTag();
            AreaInfo item = (AreaInfo) <span class="hljs-keyword">list</span>.get(position);
            viewHolder.textView.setText(item.getAreaName());
            <span class="hljs-keyword">if</span> (lastPosition<position&&lastPosition!=<span class="hljs-number">0</span>){
                ObjectAnimator.ofFloat(convertView,<span class="hljs-string">"translationY"</span>,convertView.getHeight()*<span class="hljs-number">2</span>,<span class="hljs-number">0</span>).setDuration(<span class="hljs-number">500</span>).start();

            }
            lastPosition = position;
            <span class="hljs-keyword">return</span> convertView;
        }

        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ViewHolder</span>{</span>
            TextView textView;
        }
    }
</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li></ul>

很常见的一个Adapter写法,只是在getView当中获取到了要显示的view,通过
ObjectAnimator.ofFloat(convertView,”translationY”,convertView.getHeight()*2,0).setDuration(500).start()为veiw设置了动画,

这里还用了个变量position来区别只有在向上滚动的时候才会有动画。不过我觉得不加position区别的效果也不错,大家可以试试。

其实这样已经实现了效果,接下来顺便提一下Activity对Framgnet中onItemClick的处理。

3、Activity和fragment的交互处理

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AreaSelectActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AppCompatActivity</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">AreaFragment</span>.<span class="hljs-title">OnFragmentInteractionListener</span>{</span>

    <span class="hljs-keyword">private</span> Fragment oneFragment;
    <span class="hljs-keyword">private</span> Fragment twoFragment;


    <span class="hljs-keyword">private</span> Map map = <span class="hljs-keyword">new</span> HashMap();
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) {
        <span class="hljs-keyword">super</span>.onCreate(savedInstanceState);
        setContentView(R.layout.activity_area_select);
        ButterKnife.bind(<span class="hljs-keyword">this</span>);
        <span class="hljs-comment">//新建第一级地区,parentCode参数为null</span>
        oneFragment = AreaFragment.newInstance(<span class="hljs-string">""</span>);
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.content,oneFragment).commit();
    }


    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onOptionsItemSelected</span>(MenuItem item) {
        <span class="hljs-keyword">switch</span> (item.getItemId()){
            <span class="hljs-keyword">case</span> android.R.id.home:
                FragmentManager fragmentManager = getSupportFragmentManager();
                <span class="hljs-keyword">if</span> (fragmentManager.getBackStackEntryCount()><span class="hljs-number">0</span>){
                    fragmentManager.popBackStack();
                }<span class="hljs-keyword">else</span>{
                    finish();
                }
                <span class="hljs-keyword">break</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }



     <span class="hljs-javadoc">/**
     * 处理交互,hide前一个fragment,并且调用addToBackStack让Fragment可以点击back的时候显示前一个fragment
     * 如果是第三级地区则直接返回地区选择数据给上个Activity
     *<span class="hljs-javadoctag"> @param</span> areaInfo 被点击的地区信息
     */</span>
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onFragmentInteraction</span>(AreaInfo areaInfo) {
        <span class="hljs-keyword">if</span> (areaInfo==<span class="hljs-keyword">null</span>){
            <span class="hljs-keyword">return</span>;
        }
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        <span class="hljs-keyword">int</span> level = areaInfo.getLevel();
        <span class="hljs-keyword">switch</span> (level){
            <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:
                map.put(<span class="hljs-string">"provId"</span>,areaInfo.getId());
                map.put(<span class="hljs-string">"provName"</span>,areaInfo.getAreaName());
                <span class="hljs-keyword">if</span> (areaInfo.isLeaf()){
                    Intent intent = <span class="hljs-keyword">new</span> Intent();
                    intent.putExtra(<span class="hljs-string">"addressInfo"</span>, (Serializable) map);
                    setResult(RESULT_OK,intent);
                    finish();
                }<span class="hljs-keyword">else</span>{
                    transaction.hide(oneFragment);
                    transaction.add(R.id.content,twoFragment=AreaFragment.newInstance(areaInfo.getAreaCode()+<span class="hljs-string">""</span>)).addToBackStack(<span class="hljs-keyword">null</span>).commit();
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:
                map.put(<span class="hljs-string">"cityId"</span>,areaInfo.getId());
                map.put(<span class="hljs-string">"cityName"</span>,areaInfo.getAreaName());
                <span class="hljs-keyword">if</span> (areaInfo.isLeaf()){
                    Intent intent = <span class="hljs-keyword">new</span> Intent();
                    intent.putExtra(<span class="hljs-string">"addressInfo"</span>, (Serializable) map);
                    setResult(RESULT_OK,intent);
                    finish();
                }<span class="hljs-keyword">else</span> {
                    transaction.hide(twoFragment);
                    transaction.add (R.id.content, AreaFragment.newInstance(areaInfo.getAreaCode()+<span class="hljs-string">""</span>)).addToBackStack(<span class="hljs-keyword">null</span>).commit();
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">3</span>:
                map.put(<span class="hljs-string">"districtId"</span>,areaInfo.getId());
                map.put(<span class="hljs-string">"districtName"</span>,areaInfo.getAreaName());
                Intent intent = <span class="hljs-keyword">new</span> Intent();
                intent.putExtra(<span class="hljs-string">"addressInfo"</span>, (Serializable) map);
                setResult(RESULT_OK,intent);
                finish();
                <span class="hljs-keyword">break</span>;
        }

    }
}
</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li></ul>

这样仿淘宝地区选择就实现啦!

结语

大家可以自己写测试接口,也可以直接调用我写好的接口:
http://123.184.16.19:8008/area/list

源码提供给大家参考:
Android仿淘宝地区选择

如果对你有帮助的话,动动手点个赞给个评论支持一下。
期待你的关注


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值