热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

详解Android多级联动控件实现思路讨论

这篇文章主要介绍了详解Android多级联动控件实现思路讨论,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

最近有一个需求是选择多级联动数据,数据级别不固定,可能是五级,可能是两级,具体看用户等级。

所以就需要一个多级联动选择控件 ,在网上一番搜索或找到了这个控件, Android-PickerView

这个控件在三级以内的的联动都没有问题,但是最多只能到三级。

我在原有的基础上做了一些扩展,主要是添加了两个 picker

MultiWheelPickerView 可以根据数据动态生成多个滚轮,不再局限于两个三个选项 DynamicWheelPickerView 也是动态生成,但可以一级一级的加载数据并追加滚轮。

在使用时,根据自身情况让你的 JavaBean 实现 IWheelItem 或者 IDynamicWheelItem 就好。

这里记录并分享一下我的思路和实现,也希望能和大家一起讨论更好的实现方案。

起初,只是想根据获取到的数据动态的生成滚轮,有多少级就生成多少个,自动排列出来就好。

在看了源码后发现原来的 OptionsPickerView 里写死了三个 WheelView ,所以最多只能是三个。

如果想动态生成 WheelView 就不能写死,只能根据数据生成,所以我选择使用代码创建 WheelView,不使用 layout 布局固定数量了。

除了 WheelView 部分外,其他部分还都是使用原来的布局。

因为要动态显示数据,就不能使用原来的 IPickerViewData 了,使用了一个新的 IWheelItem

public interface IWheelItem {

  /**
   *
   * @return 显示在滚轮的文本
   */
  String getShowText();

  /**
   *
   * @return 下一级的数据
   */
   List getNextItems();

}

只有两个方法,返回显示数据用来显示在滚轮上;在选择了一级后自动获取下一级内容显示。

这种多级联动的数据,明显有着上下级关系,我就默认为这种结构了,一级套着一级。

并在 WheelView 里做了调整

/**
   * 获取所显示的数据源
   *
   * @param item data resource
   * @return 对应显示的字符串
   */
  private String getContentText(Object item) {
    if (item == null) {
      return "";
    } else if (item instanceof IPickerViewData) {
      return ((IPickerViewData) item).getPickerViewText();
    } else if (item instanceof Integer) {
      //如果为整形则最少保留两位数.
      return getFixNum((int) item);
    }else if (item instanceof IWheelItem){
      return ((IWheelItem)item).getShowText();
    }
    return item.toString();
  }

First of all, 确定数据的层级,根据层级决定生成 WheelView 的数量。

/**
   * 获取当前 list 的层级,最深有多少层
   * 需要根据层级确定多少个滚轮
   * @param list 数据
   * @return 最深层级
   */
  private int getLevel(List list) {
    int level = 0;
    if (list != null && list.size() > 0) {
      level = 1;
      int childLevel = 0;
      for (T code : list) {
        List children =code.getNextItems();
        int temp = getLevel(children);
        if (temp > childLevel) {
          childLevel = temp;
        }
      }
      level += childLevel;
    }
    return level;
  }

我使用的是一个 LinearLayout 横向排列,用来承载动态生成的 WheelView 。

<&#63;xml version="1.0" encoding="utf-8"&#63;>


  

  
  

注意:这里有一个问题就是,如果生成的滚轮很多,会显得比较拥挤。

知道了要生成多少个滚轮后,代码创建直接添加到 LinearLayout 里了。

int level =getLevel(wheelItems);
if (level > 0) {
  //生成 滚轮
  for (int i = 0; i 

生成 WheelView 之后,就是给控件赋值了,我这里默认取第一个当做选中的值。

只要前边一级选中了,那就获取它的下一级数据给下一个控件赋值,如此递归到最后一个。

protected void initWheel(List list, int wheelIndex) {
    WheelView wheelView = (WheelView) mLlContainer.getChildAt(wheelIndex);
    if (null == wheelView) {
      Log.d(MultiWheelPickerView.class.getSimpleName(), "initWheel: 超出了范围 " + wheelIndex + " > " + mLlContainer.getChildCount());
      return;
    }
    if (null != list && list.size() > 0) {
      wheelView.setAdapter(new MultiWheelAdapter(list));
      wheelView.setCurrentItem(0);
      wheelView.setOnItemSelectedListener(new MultiWheelItemSelector(list, wheelIndex));
      //默认选中第一项,添加到结果里。
      T wheelItem = list.get(0);
      addToResult(wheelItem, wheelIndex);
      List children = list.get(0).getNextItems();
      //有子集,继续添加
      wheelIndex++;
      initWheel(children, wheelIndex);
    }else{
      for (int i=wheelIndex;i

关于选中的数据和事件,和原来一样,只是换了一种形式,使用 List 容器。

按照顺序,把选中的数据都列在里面了,逻辑如下

protected void addToResult(T value, int index) {
    // 检测是否发生了变化,需要对外释放信号
    int size = resultList.size();
    Log.d(MultiWheelPickerView.class.getSimpleName(), "addToResult: " + index + "-->" + value + "; size->" + size);
    //上级换了人,下级全部移除掉
    while (index 

就这样稍微改一改,一个动态多级关联控件就有了,在使用时,让你的 JavaBean 实现 IWheelItem 就好。

简单使用方式如下

MultiWheelPickerView fixedPickerView;

  private void fixedPicker() {
    if (null == fixedPickerView) {
      MultiWheelPickerBuilder builder = new MultiWheelPickerBuilder<>(this,
          new MultiWheelSelectListener() {
            @Override
            public void onChange(List result) {
              //在滚轮选择发生变化时会被调用
              showChange(result);
            }

            @Override
            public void onSelect(List result) {
              //在按下确定按钮时会被调用
              StringBuffer buffer = new StringBuffer();
              int size = result.size();
              for (int i = 0; i ");
                }
                buffer.append(result.get(i).getShowText());
              }
              mTvResult.setText(buffer.toString());
            }

            @Override
            public boolean isAddToResult(CodeTable selectValue) {
              //此方法返回值会确定这个值是否可以被选中
              return !selectValue.getCode().equalsIgnoreCase("all");
            }
          });
      fixedPickerView = builder.build();
      fixedPickerView.setTitleText("行政区划");
      fixedPickerView.setWheelItems(getPickerData());
    }
    fixedPickerView.show();
  }

虽然实现了多级联动,但是在实际使用时又发现了不可忽视的问题: 如果数据过多,就会加载很长时间,从省级到村级,会有数万条记录,一次获取过来体验太差了,而且有崩溃的风险。

更好的办法是一级一级的去获取数据,选中省级再去获取下属的市级并追加滚轮显示,选中市级再去获取县级,如此类推。

So, 接续改,因为数据也是多次获取了,就无法确定层级了,故需要每有新的层级时添加新的 WheelView 追加到显示容器里(突然增加一个View会出现横跳的情况,最好是加入一个动画平滑一点)。

在选中一个数据时,也要判断是否需要去加载下一级,在我的需求里,有的是需要到村级,有的则需要到县级。

所以具体是否要加载下一级的配置要放出来,我这里放在了数据接口上,由数据自身判断。

在 IWheelItem 的基础上扩展了一个 IDynamicWheelItem

public interface IDynamicWheelItem extends IWheelItem {
  /**
   * @return 是否需要加载下一级
   */
  boolean isLoadNext() ;
}

然后是在生成 WheelView 这里做了一些修改,根据传入的数据生成。

也是默认选择了第一项,如果能被选中,则继续生成或者去加载子级数据。

protected void generateWheel(List data) {
    if (data != null && data.size() > 0) {
      //需要生成 wheel
      WheelView wheelView = generateWheel();
      wheelView.setAdapter(new ArrayWheelAdapter(data));
      mLlContainer.addView(wheelView);
      int level = mLlContainer.getChildCount() - 1;
      wheelView.setOnItemSelectedListener(new DynamicWheelItemSelector(data, level));
      T iWheelItem = data.get(0);
      addToResult(iWheelItem, level);
      if (canSelect(iWheelItem)) {
        List nextItems = iWheelItem.getNextItems();
        if (null != nextItems && nextItems.size() > 0) {
          generateWheel(nextItems);
        } else {
          if (iWheelItem.isLoadNext()) {
            loadNext(iWheelItem, ++level);
          }
        }
      }

    }
  }

在选中一个数据后的滚轮赋值也做了修改,如果是判断是否需要去加载下一级数据或者是否现有数据

在后续没有数据的情况下,也没有移除掉 WheelView 。一旦没有数据就移除,会出现左右横跳的情况(这里也可以做一个动画,会显得没有那么突兀)。

/**
   * 设置下级Wheel 的数据
   *
   * @param current 数据
   * @param nextLevel  下一层
   */
  private void setupChildWheel(T current, int nextLevel) {
    if (mLlContainer.getChildCount() == nextLevel) {
      if (current.isLoadNext()) { //最后一级了,但是下一级仍然需要显示
        loadNext(current, nextLevel);
      }
      return;
    }
    List nextItems = current.getNextItems();
    //对于下级wheel的设置上对应的数据,即使没有那么多级的,也不能移除view,只能将数据设置为null
    WheelView wheelView = (WheelView) mLlContainer.getChildAt(nextLevel);
    if (null != nextItems && nextItems.size() > 0) {
      //有子集
      //在 level ==count 时可能为空
      if (wheelView == null) {
        wheelView = generateWheel();
      }
      wheelView.setAdapter(new ArrayWheelAdapter(nextItems));
      wheelView.setCurrentItem(0);
      wheelView.setOnItemSelectedListener(new DynamicWheelItemSelector(nextItems, nextLevel));
      T wheelItem = nextItems.get(0);
      addToResult(wheelItem, nextLevel);
      nextLevel++;
      if (canSelect(wheelItem)) {
        setupChildWheel(wheelItem, nextLevel);
      }else{ //当前已经不能选择了,之后的滚轮数据也必须置空
        for (int i = nextLevel; i 

在加载数据成功后,要将数据追加到对应的滚轮上

public void appendWheel(List list, int level) {
    WheelView wheelView = null;
    if (level  0)
        mLlContainer.addView(wheelView);
    }
    if (null != list && list.size() > 0) {
      wheelView.setAdapter(new MultiWheelAdapter(list));
      wheelView.setCurrentItem(0);
      T codeTable = list.get(0);
      addToResult(codeTable,level);
      wheelView.setOnItemSelectedListener(new DynamicWheelItemSelector(list, level));
      if (canSelect(codeTable)) { //合法数据,能被选择。
        //需要加载下一级
        level++;
        setupChildWheel(codeTable,level);
      }

    }
  }

至此,改完了,比之前那个多放出来两个方法。

在侦听器里扩展了一个加载下级的方法。

public interface DynamicWheelSelectListenerextends MultiWheelSelectListener {
  /**
   * 加载下一级的数据
   * @param item 当前数据
   * @param nextLevel 下一级的层级
   */
  void loadNextItems(T item, int nextLevel);
}

使用办法和上面的 MultiWheelPickerView 大同小异

DynamicWheelPickerView dynamicPickerView;
  private void dynamicPicker() {
    if (null == dynamicPickerView) {
      dynamicPickerView =new DynamicWheelPickerBuilder(this,new DynamicWheelSelectListener() {
        @Override
        public void loadNextItems(CodeTable item, int nextLevel) {
          //这里模拟的数据,在加载后将 isLoadNext 设置为 false。
          List child = getChild(random());
          item.setChildren(child);
          item.setLoadNext(false);
          //将数据赋值到对应的控件上,nextLevel就是控件的位置。
          dynamicPickerView.appendWheel(child, nextLevel);
        }

        @Override
        public void onChange(List result) {
          showChange(result);
        }

        @Override
        public void onSelect(List result) {
          StringBuffer buffer = new StringBuffer();
          int size = result.size();
          for (int i = 0; i ");
            }
            buffer.append(result.get(i).getShowText());
          }
          mTvResult.setText(buffer.toString());
        }

        @Override
        public boolean isAddToResult(CodeTable selectValue) {
          //是 0 的不能被选择
          return !selectValue.getCode().equalsIgnoreCase("0");
        }
      })
          .build();
      dynamicPickerView.setTitleText("行政区划");
      dynamicPickerView.setWheelItems(getChild(random()));

    }
    dynamicPickerView.show();
  }

具体用法可以看代码,在这里 TestMultiWheelActivity

其他想法:

  • 目前使用 LinearLayout 包裹的,是否可以换成 RecyclerView 呢,是否能更好的控制在一行超出多少个后换行,避免拥挤。
  • 目前在动态追加滚轮时是很生硬的追加上去的,可以优化为使用动画平滑的过渡可能体验更好些。

目前把代码放在了这里 Android-PickerView

我的实现方式就是这样,希望能和大家讨论更好的方式。

到此这篇关于详解Android 多级联动控件实现思路讨论的文章就介绍到这了,更多相关Android 多级联动内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
author-avatar
宝宝抱抱你啊
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有