扩展第三方DropDownMenu v1.5

作者: keyboard3 | 来源:发表于2016-10-15 00:25 被阅读5185次

    修改效果

    dropdown_demo.gif

    解析结构

    • 导读
      想要扩展首先我需要执行下面几个步骤

    1.fork DropDownMenu到自己的github账号
    2.使用 as 的 vcs checkout 出来
    3.提交到github
    4.发起 pull request

    • 源码实现原理
      作者对该控件的分析 导读
    DropDwonMenu 的结构.png

    这是我对该 DropDownMenu 的组成结构进行的图解

    DropDownMenu :下拉菜单控件 继承自 LinearLayout
    tabMenuView :顶部菜单布局 继承自 LinearLayout
    containerView :底部容器,包含 popupMenuViews , maskView 继承自 FrameLayout
    popupMenuViews :弹出菜单父布局 继承自 FrameLayout
    maskView: 遮罩半透明 View ,点击可关闭 DropDownMenu 继承自 View
    contentView :一个页面除了顶部菜单栏以外的所有内容
    tabView : ListView → 1 : 1

    调用方法基本解析

    DropDownMenu :对 tabMenuView 、 containerView 进行初始化
    setDropDownMenu :传参为 tabTexts (字符串数组), popupViews ( ListView 数组), contentView (内容 View )。调用 addtab 方法向 tabMenuView 添加 tabView 并设置对应 tabView 点击切换显示 ListView
    addTab :循环 tabTexts 文本, TextView 赋值添加到 tabMenuView
    switchMenu :切换 tab 调用对应的 popupMenuViews 里面的 ListView 显示,其他的 ListView 隐藏

    一般情况下在我们的 UI 图不是对 tab 特别要求的话,那么这种已经符合要求了。但是奈不住它就是不长这样啊。

    • tabView 样式扩展
      有时候 UI 图就是这么可恶,^这个箭头不是靠右,空的那么开。当然我这里只是举一个例子。
    箭头居中而不是居最右
    • tabView 功能扩展
      这个需求更加丧心病狂了, tab 不都是下拉框。实现扩展之后可以在 tabMenu 中任意顺序插入自定义的 tabView ,且不影响下拉功能。
    可以在 tab 位置中插入自己需要的 tabView

    代码实现细则

    tabView 样式扩展源码实现

    这里我们说这个 dropDownMenu 的 tab 为 TextView 肯定无法达到我们想要的效果了。
    那么最差将 tab 换成 LinearLayout ,那么自定义效果就随你自己了。但是我们就这样实现的话肯定性能跟原来有些差距。那么这个库 tab 都默认是 viewGroup 多渲染了一层,我们能不能在用的时候,自己定义的 tab_item.xml。 xml 中我们想要 viewGroup 就写 ViewGroup 包裹,想只要 TextView 就只有 TextView 。
    其实我们只需要定义 id 约束, xml 中 TextView 必须指定为(例如)R.id.tv_tab。 DropDownMenu 底层在设置 tab 的内容的时候多一步操作,加载指定的 tab_laytou.xml,然后如果是 ViewGroup 就 findViewById 找到 TextView ,否则就直接转成 TextView 。
    

    1.addTab()方法从代码中直接 new TextView 改成从 layout 中加载
    2.将原来 tabView ( textView )相关的设置代码全部先用获取 textView 的过滤方法筛选一下 textView

    这里只截取关键代码
    原 addTab()

        private void addTab(@NonNull List<String> tabTexts, int i) {
            final TextView tab = new TextView(getContext());
            ...//tab的样式设置
            tab.setText(tabTexts.get(i));
            tab.setPadding(dpTpPx(5), dpTpPx(12), dpTpPx(5), dpTpPx(12));
            //添加点击事件
            ...
            tabMenuView.addView(tab);
            //添加分割线
            ...
        }
    

    改 addTab()

        private void addTab(@NonNull List<String> tabTexts, int i) {
            View tab = inflate(getContext(), R.layout.tab_item, null);
            tab.setLayoutParams(new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f));
            ...//样式设置
            tabMenuView.addView(tab);
            //添加分割线
            ...
        }
    

    增加 tab_item.xml( viewGroup 包含 textView / 只是 textView ) ,样式的可扩展性大大增强

    <!-----------ViewGroup------------------>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center">
    
        <TextView
            android:id="@+id/tv_tab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawablePadding="7dp"
            android:gravity="center"
            android:paddingBottom="12dp"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:paddingTop="12dp"
            android:text="adfad"
            android:textColor="#26a8e0" />
    </LinearLayout>
    <!-------或者只有TextView,也没有问题---------------------->
    <?xml version="1.0" encoding="utf-8"?>
    <TextView android:id="@+id/tv_tab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawablePadding="7dp"
        android:gravity="center"
        android:paddingBottom="12dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:paddingTop="12dp"
        android:textColor="#26a8e0"
        xmlns:android="http://schemas.android.com/apk/res/android" />
    

    过滤获取 TextView 方法

        /**
         * 获取tabView中id为tv_tab的textView
         *
         * @param tabView
         * @return
         */
        private TextView getTabTextView(View tabView) {
            TextView tabtext = (TextView) tabView.findViewById(R.id.tv_tab);
            return tabtext;
        }
    

    hint: 然后代码中凡是涉及到设置 tab textView 相关设置的地方都需要先用我这个方法过滤,替换一下。主要有这么几个地方,初始化设置 tab ,选中 List 单项确定设置 tab ,打开和关闭菜单对 tab 的文本颜色的设置。
    效果:就是最上面扩展的效果图
    插曲:
    赵萝贝要求加了箭头在文本的方向属性

    icon方向属性 效果图
    jeff_sun要求添加了可以控制分隔线的高度的属性 分隔线高度属性 效果图
    还是这位 jeff_sun (ps: 你们公司 UI 要求真特殊),应他的要求为 popupWindows 集合的 view 增加了对 LayoutParams 的支持。
    代码图
    效果图

    tabView 功能扩展源码实现

    从上面可以知道,现在 tabMenuView 的 tab 和 popupMenuViews 的 ListView 的数量是相同的。而现在我们要实现的是 tabMenuView 中 tab 的数量> popupMenuViews 的 ListView 的数量。多的那部分 tabView 就只是展示的功能,不会触发点击下拉展示。
      另外因为 tabtexts 文本是作为数组顺序添加的。所以我们需要用 dropTabViews 类记录 tabtexts 添加的顺序。当点击了一个 tabView 看是否存在于 dropTabViews 数组中,不存在就不处理,存在就 indexOf 获取当前 tabView 在 dropTabViews 中的顺序然后去对应找 ListView 。这样处理之后, tabMenuView 设置 tabtexts 之后就可以随便在哪个位置上插入需要的 tabView 样式了,且不影响功能。

    1. 创建记录 tabtexts 顺序的而创建的 tabView 数组
    2. 在 switchMenu 切换 popupMenuViews 的 ListView 的获取方式要过滤一下
    

    旧 switchMenu 方法

       private void switchMenu(View target) {
            System.out.println(current_tab_position);
            for (int i = 0; i < tabMenuView.getChildCount(); i = i + 2) {
                if (target == tabMenuView.getChildAt(i)) {//找到点击到的tabView
                    if (current_tab_position == i) {//点击的view是原来显示的tabView则关闭菜单
                        closeMenu();
                    } else {//不是,就显示菜单
                        if (current_tab_position == -1) {
                            ...
                            popupMenuViews.getChildAt(i / 2).setVisibility(View.VISIBLE);
                        } else {
                            popupMenuViews.getChildAt(i / 2).setVisibility(View.VISIBLE);
                        }
                        ...
                    }
                } else {//没找到就颜色等属性设置成普通
                    TextView textView = getTabTextView(tabMenuView.getChildAt(i));
                    textView.setTextColor(textUnselectedColor);
                    textView.setCompoundDrawablesWithIntrinsicBounds(null, null,
                            getResources().getDrawable(menuUnselectedIcon), null);
                    popupMenuViews.getChildAt(i / 2).setVisibility(View.GONE);
                }
            }
        }
    

    修改的 switchMenu 方法

     private void switchMenu(View target) {
            for (int i = 0; i < tabMenuView.getChildCount(); i = i + 2) {
                if (target == tabMenuView.getChildAt(i)) {//找到点击到的tabView
                    if (current_tab_position == i) {//点击的view是原来显示的tabView则关闭菜单
                        closeMenu();
                    } else {//不是,就显示菜单
                        ...
                        View listView = getListView(tabMenuView.getChildAt(i));
                        if (listView != null) {
                            listView.setVisibility(View.VISIBLE);
                        }
                        ...
                    }
                } else {//没找到就颜色等属性设置成普通
                    TextView textView = getTabTextView(tabMenuView.getChildAt(i));
                    View listView = getListView(tabMenuView.getChildAt(i));
                    if (listView != null) {
                        if(textView!=null){
                            textView.setCompoundDrawablesWithIntrinsicBounds(null, null,
                                    getResources().getDrawable(menuUnselectedIcon), null);
                        }
                        listView.setVisibility(View.GONE);
                    }
                }
            }
        }
    

    addTab 进一步修改

        private void addTab(@NonNull List<String> tabTexts, int i) {
           ...
            dropTabViews.add(tab);//记录创建的添加顺序
        }
    

    新增 getListView 方法

        /**
         * 获取dropTabViews中对应popupMenuViews数组中的ListView
         *
         * @param view
         * @return
         */
        private View getListView(View view) {
            if (dropTabViews.contains(view)) {
                int index = dropTabViews.indexOf(view);
                return popupMenuViews.getChildAt(index);
            } else {
                return null;
            }
        }
    

    调用演示 MainActivity.java

          mDropDownMenu.setDropDownMenu(Arrays.asList(headers), popupViews, contentView);
            //测试tabView扩展功能
            TextView textView= (TextView) getLayoutInflater().inflate(R.layout.tab_text,null);
            textView.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f));
            textView.setText("2位置");
            mDropDownMenu.addTab(textView,2);
    
    使用方式
    Paste_Image.png

    新增tabMenu点击回调接口

    ItemMenu.png

    总结

    样式性扩展:我们尽量从 xml 中加载 view ,根据指定 id 获取控件,到达最大程度的样式解耦
    功能性扩展:将 tabViews 数组顺序的位置不依赖于父 View 的 child ,而是依赖于一个动态的数组。我们对父 View 的 child 的添加并不会影响到原来的功能。这样可以做到 tabView 的功能性扩展

    该篇文章代码在 Github上

    相关文章

      网友评论

      • 卑微的张:如果只有两个下拉选项的话 它的箭头跟文字离得太远了,怎么改啊
      • Freyr1K:请问是否有快速重置各选项默认值的方法?
      • 土豆啊你个马铃薯:怎么才能加上刷新和加载更多呢????
        keyboard3:改成你想要的:smile:
      • 土豆啊你个马铃薯:最外层套了一个scrollview 下拉菜单不能滑动了? 如何解决啊?
        keyboard3:@土豆啊你个马铃薯 :smile: 看好你哦
        土豆啊你个马铃薯:@keyboard3 加了事件拦截处理还是没ok 好尴尬
        keyboard3:这个目前不支持的,如果需要你自己改造这个容器,我提供一点思路吧!可以提前计算出内容区域的高度加到这个DropDownMenu上,那么SrcollVIew就可以滑动了,可能还需要处理一下触摸事件拦截
      • 土豆啊你个马铃薯:内容展示区域可以放一个listview 么?
        keyboard3:可以啊,内容展示区就是用来放你展示内容的地方
      • 唯一_f286:7.0的系统,显示不正常,没找到原因啊
        keyboard3:我github更新了 下个新的apk试试
      • 13af09417604:在android7.0以上版本测试发现,点击下拉后不出窗口,布局是乱的。有遇到过这样的问题吗?android4.4是正常
        keyboard3:@bigtreee 我也是genymotion唉 咋没出现!私聊我QQ吧
        13af09417604:@keyboard3 我下载你的那个demo-APP也是一样的情况,用Genymotion搭Android7.0测试同样下拉不出,界面是乱的,android5.1完全的正常
        keyboard3:我试过了 没发现问题啊
      • Allen__Qin:那个下拉框的阴影是在当前的view中,假如在fragment中这个阴影就不会将activity中的布局遮盖了
        keyboard3:对的。这个东东 要放到页面的根上。 放到fragment 就会小于Activity页面至少一个嵌套
      • 5b57468d053f:代码down下来以后,你的弹窗都不弹了。。。。是不是改出问题了?
        keyboard3:@Ken_de8f 我重新down下来了,跑起来没问题啊。是不是你下了一个假的的啊:smile: 。如果真的有问题,私信你了,加我QQ聊
        keyboard3:@Ken_de8f 啊,怎么会!我去看看
        5b57468d053f:这里不能贴图,你的最上面一层menu 也就是城市、年龄、星座....那一排撑满了全屏 ,是不是高度设置的有问题?
      • 3242b1ef9bf6:博主你好,为什么我的切换箭头图标的属性没有效果?app:ddmenuSelectedIcon="@mipmap/drop_down_selected_icon"这个属性没有效果
        3242b1ef9bf6:@keyboard3 :+1: 搞定了,谢谢博主
        keyboard3:对不起,我的锅,已修复。看看我github修改记录:smile:
      • 73ece15c815f:能不能曝露一个联动的回调接口啊!
        73ece15c815f:@keyboard3 :哈哈,好的。我的问题已经解决了,我把源码下载下来添加了一个方法而已。谢谢咯
        keyboard3:不知道你说的联动是啥,大兄弟自己研究研究加上好了,也是一种锻炼嘛!过了半年再回头去看别人的代码,好费劲哦:cold_sweat:
      • af821b4d2271:发现一个小问题,menuUnselectedIcon设置了位置之后,点击后图标不会变,看你源码,好像并没有对menuSelectedIcon做处理是不?
        keyboard3:@keyboard3 今天有人又提了,看了一下真的是我的锅,对不起:cold_sweat:
        keyboard3:抱歉 没看到消息,源码中并没有设置,你可以扩展这一功能
      • Waverr:您好,拜读了你对DropDownMenu的定制,很收益,谢谢你的无私分享。
        我在我的项目demo中使用你的DropDownMenu时,碰见了两个问题:
        1、在我的布局文件中是一个垂直的线性布局,<com.yyydjk.library.DropDownMenu/>下面就是一个<android.support.v7.widget.RecyclerView/>,当我点击DropDownMenu中的tab时,其下面的RecyclerView列表要么侵占DropDownMenu下拉GridView的一部分空间,要么就是将RecyclerView列表彻底挤出屏幕空间。
        2、在我使用您添加的功能DropDownMenu.addTab()时,设置了该TextView的颜色为selector,但是当我点击这个以TextView的内容的tab时,他的颜色不会随着点击事件而改变。
        不知您能否劳烦您告知我这两个现象的解决方案,无比感谢!
        keyboard3:@LittleWaver 恩恩 对的
        Waverr: @LittleWaver 谢谢您的指点,受益匪浅!关于第一个问题,DropDownMenu这个控件应该是可以在它之上放线性布局的吧,比如标题栏。
        keyboard3:第一个问题应该是你没正确使用这个控件。这个DropDownMenu是作为布局中的根节点使用,而内容RecyclerView是作为内容view填入初始化方法中
        第二个问题应该是你设置的颜色被二次覆盖了。第二个很简单,找到原因的话,你只要在点击的时候打个断点,一步步执行下去,看哪一步会覆盖你设置的颜色。(ps:楼上的那位网友也出现这个类似颜色被覆盖的问题,debug找出原因自己改掉了)
      • jeff_sun:你好,用了你的DropDownMenu,但是点击后.菜单没有显示,不知道是什么原因,我的每个菜单都是inflate的布局
        keyboard3:@jeff_sun 已添加可以控制分隔线高度属性,已经添加到github上,本文章也有描述。
        jeff_sun:@keyboard3 现在有个新问题,就是tab之间的分割线不是跟tab一样高,需要怎么解决?
        keyboard3:@jeff_sun 不好意思 现在才看见,耽误你了。不过您的描述信息比较少,很难帮到你。出现这个情况,你可以打断点单步调试 查看一下到底代码执行到哪一步卡出住了,没有把菜单显示出来。
      • 2015哈哈哈:赞一个,不错 :+1:
        keyboard3:@伟伟and莉莉 谢谢 观看哈 :smile:

      本文标题:扩展第三方DropDownMenu v1.5

      本文链接:https://www.haomeiwen.com/subject/mlqeyttx.html