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

介绍

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

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

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

再来一张自己的效果

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) {
        //&#x5355;&#x51FB;&#x7684;&#x65F6;&#x5019;&#x9700;&#x8981;&#x5904;&#x7406;&#x5730;&#x533A;&#x70B9;&#x51FB;&#x4E8B;&#x4EF6;&#xFF0C;&#x7EDF;&#x4E00;&#x4EA4;&#x7ED9;Activity&#x5904;&#x7406;
        AreaInfo areaInfo = (AreaInfo) parent.getAdapter().getItem(position);
        if (areaInfo==null) return;
        if (mListener!=null){
            mListener.onFragmentInteraction(areaInfo);
        }
    }

    //&#x7528;&#x6765;&#x548C;Activity&#x4EA4;&#x4E92;&#x7684;&#x56DE;&#x8C03;&#x63A5;&#x53E3;
    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; < code></position&&lastposition!=0){></areainfo>

很常见的一个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);
        //&#x65B0;&#x5EFA;&#x7B2C;&#x4E00;&#x7EA7;&#x5730;&#x533A;&#xFF0C;parentCode&#x53C2;&#x6570;&#x4E3A;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;
    }

     /**
     * &#x5904;&#x7406;&#x4EA4;&#x4E92;&#xFF0C;hide&#x524D;&#x4E00;&#x4E2A;fragment&#xFF0C;&#x5E76;&#x4E14;&#x8C03;&#x7528;addToBackStack&#x8BA9;Fragment&#x53EF;&#x4EE5;&#x70B9;&#x51FB;back&#x7684;&#x65F6;&#x5019;&#x663E;&#x793A;&#x524D;&#x4E00;&#x4E2A;fragment
     * &#x5982;&#x679C;&#x662F;&#x7B2C;&#x4E09;&#x7EA7;&#x5730;&#x533A;&#x5219;&#x76F4;&#x63A5;&#x8FD4;&#x56DE;&#x5730;&#x533A;&#x9009;&#x62E9;&#x6570;&#x636E;&#x7ED9;&#x4E0A;&#x4E2A;Activity
     * @param areaInfo &#x88AB;&#x70B9;&#x51FB;&#x7684;&#x5730;&#x533A;&#x4FE1;&#x606F;
     */
    @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仿淘宝地区选择

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

Original: https://www.cnblogs.com/yangqiangyu/p/5572982.html
Author: So,Cool
Title: Android 轻松实现仿淘宝地区选择

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/607621/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 【一】pig4cloud项目一键build — 容器中安装build环境

    容器基础配置 1、安装centos 容器 docker-compose.yaml 文件内容,指定一个映射目录 version: ‘3’ services: centos-build…

    Java 2023年6月8日
    079
  • 安卓电池健康查看软件AccuBattery 分享

    一、天下苦秦久矣 说实话,我是小米的忠实粉丝(雷总打钱),手里目前是红米k30pro标准版, 室友中有用华为也有苹果的,据我所知苹果系统是可以看到电池健康的,但是安卓却不行, 所以…

    Java 2023年6月5日
    0281
  • 日常踩坑

    有人的地方就有江湖,有代码的地方就有坑 一。集合 List与Set继承自Collection。Collection提供定义了一个移除元素的接口 可以看出,在Collection中该…

    Java 2023年6月9日
    099
  • Spring5新特性—Log4j2

    Spring5新特性—Log4j2 Spring5新特性—Log4j2 创建一个Maven项目,导入依赖 org.apache.logging.log4j log4j-core 2…

    Java 2023年6月5日
    071
  • 每天代码量多少正常?

    关于程序员每天代码量的讨论,众说纷纭。我个人觉得需要根据具体情况而定。 如果你刚实习,或刚步入工作不久,上司一般都会交给你一些简单的功能实现,这时候一般都含有大量简单而重复或者说类…

    Java 2023年6月13日
    085
  • 【主流技术】Spring Boot中的微信支付(小程序)

    前言 微信支付是企业级项目中经常使用到的功能,作为后端开发人员,完整地掌握该技术是十分有必要的。 logo 一、申请流程和步骤 图1-1 注册微信支付账号 获取微信小程序APPID…

    Java 2023年6月6日
    0118
  • Java学习-第一部分-第一阶段-第八节:项目-房屋出租系统

    项目-房屋出租系统 笔记目录:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 房屋出租系统-需求 ●项目需求说明 实现基于…

    Java 2023年6月16日
    085
  • 发送POST请求时发生 java-org.springframework.http.converter.HttpMessageNotReadableException

    本质上就是因为尝试用@RequestBody 接收一个变量,但是这样spring是不允许的,所以需要专门写一个类或者直接用实体类接收,这样就可以了. https://www.jb5…

    Java 2023年5月29日
    094
  • dubbo源码分析3(dubbo中的spi机制)

    上一篇我们看过了jdk中的spi机制,也分析了它的缺点就是会一次性将META-INF/services下的配置文件中,对应接口的全部实现类都给加载; 而dubbo中的spi肯定是提…

    Java 2023年6月6日
    082
  • Vulnhub-OSCP靶机实战-适合新手的靶场

    前言 这个靶机适合新手,因为步骤很短很短 靶机下载地址:https://www.vulnhub.com/entry/infosec-prep-oscp,508/ KALI地址:19…

    Java 2023年6月13日
    079
  • 聊聊redis的主从复制吧

    聊聊基础概念 主从复制与主从替换 主从复制不同于主从替换,主从复制是正常情况下主节点同步数据到从节点;主从替换是主节点挂了之后,把从节点替换为主节点; 从节点存在的意义:备份主节点…

    Java 2023年6月8日
    076
  • Spring Boot 集成Shiro的多realm配置

    我在做毕设的时候采用shiro进行登录认证和权限管理的实现。其中需求涉及使用三个角色分别是:学生、教师、管理员。现在要三者实现分开登录。即需要三个Realm——StudentRea…

    Java 2023年5月30日
    099
  • 堆排序算法剖析

    1.将待排序列以一个完全二叉树存储,设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。 2…

    Java 2023年6月16日
    0105
  • Mac M1 安装 Nacos 操作及问题解决

    先安装mysql ,这里使用的是8+版本,原因在于原本的 5.7 版本中并没有对 m1 的良好支持,如果启动会有报错说查询不到对应版本信息(虽然可以通过自定义 mirror 实现)…

    Java 2023年6月15日
    096
  • 异步线程里的日志不好追踪?小支一招,轻松搞定!

    众所周知,通过唯一的链路id来追踪一次请求的所有日志,对于排查生产问题来说,会是非常给力的。这个比较容易实现。我之前的博客也有多次提及 ▄︻┻┳═一 https://www.cnb…

    Java 2023年6月15日
    067
  • 通过宿主主机访问部署在虚拟机上的网站

    网站部署在笔记本的虚拟机(CentOS 6.8)上,虚拟机通过桥接的方式联网,网站开启成功,在虚拟机上可以打开,但是在宿主的浏览器打不开,后面百度一下发现是虚拟机的防火墙导致的。关…

    Java 2023年5月30日
    062
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球