实现京东商城地址选择效果(效果还挺一致的)

实现京东商城地址选择效果(效果还挺一致的)前言部分最近新项目要中设计一个地址选择的效果(效果和某东的商城一样一样的),虽然网上已经有现成的方案了,但是最近刚好时间还算充裕所以想了想还是准备自己来做一个,顺便加深一下对自定义dialog的认识

前言部分

最近新项目要中设计一个地址选择的效果(效果和某东的商城一样一样的),虽然网上已经有现成的方案了,但是最近刚好时间还算充裕所以想了想还是准备自己来做一个,顺便加深一下对自定义dialog的认识。

Demo中主要实现了京东的地址选择的控件效果,开始本来计划用dialog来实现的,但是网上似乎说推荐使用DialogFragment来封装,刚好我还没使用过DialogFragment来做一个,说是封装其实只是定义了一个AddressSelectDialog对外提供了一些基础的方法,如果需要使用可以自行修改。

放个效果图镇楼

掉帧啊

内容部分

写一个功能的基本流程:构思5分,代码3分,完善2分。

因为以前我写功能都是上来就开写,一口气写个差不多完事,可是完成后你会发现写的很混乱,因为写之前没有很好的构思项目,这样看似效率很高,但是后来你回来改的时间会很多,这也就是为什么软件开发一直都是强调架构的原因吧。

进入正题

其实这个地址选择权很以前的三级联动一样不过是样式上不同,个人觉得还是比较舒服的。

功能分析拆解一下,做起来更有条不紊。

  1. 我们首先需要头部三个tab,这三个tab也不是一个固定的可能发生增减。

  2. 下方上一个地址的列表,省–市–区。

  3. 省市区的列表是有关联关系的,但是实际上同一时间只会有一个列表出现。

具体的实现过程

  1. 第一部分是头部这里我使用了design包中的TabLayout来实现,用起来还是比较舒服,下面看一下布局和相关代码:

    <android.support.design.widget.TabLayout
            android:id="@+id/tb_head_indicator"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:tabIndicatorColor="@color/red"
            app:tabSelectedTextColor="@color/red" />
    

    上面的布局汇总很简单,这里说明一下注意地方:

    • 其实你观察源码会发现TabLayout是继承了HorizontalScrollView类,这也是可以滚动的原因所在。然后tabIndicatorColor是设置下面的指示标的颜色,tabSelectedTextColor设置选中文字的颜色。这里没有什么特殊的样式,其实TabLayout的功能还是很强大的,基本上你所需要的实现样式都提供了。
  2. 下面就是一个列表了啊,这个地方我们发现同时出现的只有一个列表,虽然是说省市区三个类表。这里的方案我想到三种:

    • 一个是分别弄三个RecyclerView(或者ListView)来实现,然后重叠在这个布局中,通过标记来设置显示哪一个RecyclerView,这个方案的好处就是只需要初始化一次就可以了,并且选中的状态也可以直接保留在列表中。

    • 一个是使用一个RecyclerView并给初始化三个不同的Adapter来实现,这个方案使得RecyclerView一只在界面中存在,通过给RecyclerView绑定不同的RecyclerView来实现切换。本Demo就是通过实现这个方案来做的。

    • 这种就是RecyclerView和RecyclerView用同一个通过切换tab来更换里面的数据来做,其实这个和上面的很像,上面的方案也很容易改成这样。

      以上我觉得都还好吧,写起来难以程度略不同,第2和第3 差不多,主要的逻辑都在通过标识位不断的更新数据上,唯一的区别就是一个更新数据源,一个直接把适配器换掉了;第一个写起来应该算是比较简单,因为独立出来三个不同的列表,只第一次把数据初始化进去就好了,后期只通过标识位来切换显示就可以。

    下面贴出部分代码片段:

    //初始化了三个adapter,先把第一个省的adapter绑定到RecyclerView上了。
    private void initRecyclerView() { 
         
            rvTabExample.setLayoutManager(new LinearLayoutManager(getContext()));
            rvTabExample.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
            //省地址
            recyclerViewTabAdapter = new RecyclerViewTabAdapter(getContext());
            //市地址
            recyclerViewCityAdapter = new RecyclerViewCityAdapter(getContext());
            //区地址
            recyclerViewAreaAdapter = new RecyclerViewAreaAdapter(getContext());
            recyclerViewTabAdapter.setAddressList(cityBeanList);
    
            recyclerViewTabAdapter.setBaseItemClickListener(this);
            recyclerViewCityAdapter.setBaseItemClickListener(this);
            recyclerViewAreaAdapter.setBaseItemClickListener(this);
    
            rvTabExample.setAdapter(recyclerViewTabAdapter);
        }
    

    处理adapter更新的代码部分,主要通过标记tabCurrentPosition当前显示的tab来区分显示,不同tab下adapter传入的类型不同,这里如果使用同一个的话需要在adapter的内部来进行判断了,或者在外面把数据做统一处理,传入adapter的都统一一下。

    private void upDataArray() { 
         
            if (tabCurrentPosition == oldTabCurrentPosition) { 
         
                return;
            }
            //设置颜色
            LogUtil.i("upDataArray--更新列表啊:::" + tabCurrentPosition);
            switch (tabCurrentPosition) { 
         
                case 0:
                    recyclerViewTabAdapter.setCurrentSelect(currentSelectMap.get(tabCurrentPosition));
                    recyclerViewTabAdapter.setAddressList(cityBeanList);
                    rvTabExample.setAdapter(recyclerViewTabAdapter);
                    break;
                case 1:
                    cityIndex = currentSelectMap.get(0) == -1 ? initPosition : currentSelectMap.get(0);
                    recyclerViewCityAdapter.setAddressList(cityBeanList.get(cityIndex).getCity());
                    recyclerViewCityAdapter.setCurrentSelect(currentSelectMap.get(tabCurrentPosition));
                    rvTabExample.setAdapter(recyclerViewCityAdapter);
                    break;
                case 2:
                    cityIndex = currentSelectMap.get(0) == -1 ? initPosition : currentSelectMap.get(0);
                    int cityInnerIndex = currentSelectMap.get(1) == -1 ? initPosition : currentSelectMap.get(1);
                    recyclerViewAreaAdapter.setAddressList(cityBeanList.get(cityIndex).getCity().get(cityInnerIndex).getArea());
                    recyclerViewAreaAdapter.setCurrentSelect(currentSelectMap.get(tabCurrentPosition));
                    rvTabExample.setAdapter(recyclerViewAreaAdapter);
                    break;
                default:
                    break;
            }
            oldTabCurrentPosition = tabCurrentPosition;
        }
    

    因为联动的关系,所以每次点击tab或者点击item的时候都会出发更新的操作。

  3. 这里是比较关键的部分,主要是处理了点击事件,当点击事件发生的时候要伴随着tab的切换操作。

    这里我们有一个标记的当前tab的标识tabCurrentPosition,并且我会把每一个列表里选中的position存储到map中,并且使用tabCurrentPosition当作key,这样在切换tab回显的时候就方便一些,这里以前我使用过List但是效果不好,因为不断切换Tab的话还要去重制List。

    @Override
    public void onItemClick(View view, int position) { 
         
    //设置一下tab上的文字
        switch (tabCurrentPosition) { 
         
                case 0:
                    mTabLayout.getTabAt(tabCurrentPosition).setText(recyclerViewTabAdapter.getAddressList().get(position).getName());
                    break;
                case 1:
                    mTabLayout.getTabAt(tabCurrentPosition).setText(recyclerViewCityAdapter.getAddressList().get(position).getName());
                    break;
                case 2:
                    mTabLayout.getTabAt(tabCurrentPosition).setText(recyclerViewAreaAdapter.getAddressList().get(position));
                    break;
                default:
                    break;
            }
            //相同tab下进行的position变化处理,这里要提醒一下,当满足这个条件的时候,就是说明你下一个tab对应的数据需要重新初始化了。
            if (currentSelectMap.get(tabCurrentPosition) != position) { 
         
                int removeTab = mTabLayout.getTabCount() - 1;
                while (removeTab > tabCurrentPosition) { 
         
                    if (mTabLayout.getTabAt(removeTab) != null) { 
         
                        mTabLayout.removeTabAt(removeTab);
                        currentSelectMap.put(removeTab, -1);
                    }
                    removeTab--;
                }
                  //防止越界啊,目前只写了三级。其实是可以写成很多。这里没有扩展性
                if (tabCurrentPosition >= 2) { 
         
                    tabCurrentPosition = 2;
                } else { 
         
                    mTabLayout.addTab(mTabLayout.newTab().setText("请选择"), false);
                    currentSelectMap.put(tabCurrentPosition + 1, -1);
                }
            }
            //设置当前tab下的选中状态
            currentSelectMap.put(tabCurrentPosition, position);
            //滚动到下一个tab
            tabCurrentPosition++;
            //越界的话处理为最后一个tab
            if (tabCurrentPosition >= mTabLayout.getTabCount()) { 
         
                tabCurrentPosition = mTabLayout.getTabCount() - 1;
    
                if (selectAddressResultListener != null) { 
         
                    int[] result = { 
         currentSelectMap.get(0), currentSelectMap.get(1), currentSelectMap.get(2)};
                    selectAddressResultListener.selectAddressResult(result);
                    dismiss();
    
                }
            } else { 
         
                mTabLayout.setScrollPosition(tabCurrentPosition, 0f, true);
            }
            LogUtil.i("tabCurrentPosition--" + tabCurrentPosition + "tabCount" + mTabLayout.getTabCount());
        //如果在最后一个列表中切换的话这里单独处理一下,因为upDataArray限制里更新的频率。
            if (tabCurrentPosition == oldTabCurrentPosition) { 
         
                switch (tabCurrentPosition) { 
         
                    case 0:
                        recyclerViewTabAdapter.setCurrentSelect(currentSelectMap.get(tabCurrentPosition));
                        recyclerViewTabAdapter.notifyDataSetChanged();
                        break;
                    case 1:
                        recyclerViewCityAdapter.setCurrentSelect(currentSelectMap.get(tabCurrentPosition));
                        recyclerViewCityAdapter.notifyDataSetChanged();
                        break;
                    case 2:
                        recyclerViewAreaAdapter.setCurrentSelect(currentSelectMap.get(tabCurrentPosition));
                        recyclerViewAreaAdapter.notifyDataSetChanged();
                        break;
                    default:
                        break;
                }
            } else { 
         
                upDataArray();
            }
       }
    

    其实上面基本就算主要步骤了,包含初始化—数据填充—点击处理,完成的话基本上就实现了效果,下面我稍微完善一下这个控件。

  4. 数据回显,这是个比较常规的需求,所以也加了上来。如下使用了两个方法,因为我发现如果show页面后直接更新数据的话会报错空指针,如果延迟一会就不出现问题,感觉是初始化界面的未完成就使用控件导致。所以稍微延迟一点来绕过这个问题。

    
    public void setSelectAddress(final int[] selectAddress) { 
         
           
        new Handler().postDelayed(new Runnable() { 
         
                @Override
                public void run() { 
         
                    setSelectAddress(selectAddress, true);
                }
            }, 60);
        }
    
        private void setSelectAddress(int[] selectAddress, boolean isUpdata) { 
         
            if (selectAddress != null && selectAddress.length != 0 && cityBeanList != null) { 
         
                for (int i = 0; i < 3; i++) { 
         
                    //保存选择
                    currentSelectMap.put(i, selectAddress[i]);
                }
    
                CityBean cityBean = cityBeanList.get(currentSelectMap.get(0));
                CityBean.CityBeanInner cityBeanInner = cityBean.getCity().get(currentSelectMap.get(1));
                String name = cityBeanInner.getArea().get(currentSelectMap.get(2));
    
                mTabLayout.removeAllTabs();
                //初始化tab
                mTabLayout.addTab(mTabLayout.newTab().setText(cityBean.getName()), false);
                mTabLayout.addTab(mTabLayout.newTab().setText(cityBeanInner.getName()), false);
                mTabLayout.addTab(mTabLayout.newTab().setText(name), true);
    
                tabCurrentPosition = currentSelectMap.size() - 1;
                //选择最后
                mTabLayout.setScrollPosition(tabCurrentPosition, 0f, true);
                //更新集合
                upDataArray();
            }
        }
    

    数据回显的实现是通过传入标识来完成,因为我在dialog内部维护了这个标志,外部只需要传如标识并且更新一下界面就可以,界面的话需要更新三个tab和最后一个列表就可以了。由于初期就设计了三个列表所以这个标识我只是用了前三个长度(偷懒一下)。

以上步骤就基本完成了这个功能了

剩下一些零碎东西,如:数据源的设置,你需要参考一下我的CityBean实体;监听设置,当点击最后列表的条目时候就把完整的数据返回去取了。

结束部分

现在android开源的东西太多,我们似乎正在沦为代码搬运工,但是我认为有些些东西还是要亲自去做,能看懂和能写出来中间差了很长一段路。再次共勉,共同进步。

传送到GitHub

有问题欢迎纠正,谢谢啦

如果对你有帮助就点个赞把,你的鼓励是我写作的动力。

今天的文章实现京东商城地址选择效果(效果还挺一致的)分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/67356.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注