转自:
介绍
最近用淘宝客户端的时候,编辑地址的时候有个地区选择的功能。看上面的效果觉得挺酷,滚动的时候,是最后一个从下面飞上来挨着前一个。就自己鼓捣一个出来玩玩。
说了效果可能不太直观,下面上两张图看看效果
淘宝地区选择效果
再来一张自己的效果
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仿淘宝地区选择
如果对你有帮助的话,动动手点个赞给个评论支持一下。
期待你的关注