DropdownMenu的介绍和使用

背景

我在项目中不止一次的要求写下拉菜单以选择参数,之前写的很乱,难以复用,写过单个菜单的,下次项目又要求两个menu,而且都要带translate动画效果。所以这次整理出一个自定义View(其实是个自定义RelativeLayout)。

效果

功能并不复杂,支持一或多个ListView与本控件绑定,支持自定义title,供有需之人取之。不多说,贴图:
DropdownMenu demo

代码分析

  • 思路
    本控件继承自RelativeLayout,考虑到展开时的黑色背景要铺开,所以width和height均默认MATCH_PARENT。并没有做太复杂的layout和draw,只是放置对应N个TextView&箭头drawable于顶部,以LinearLayout为container,组织成默认1:1的样式。这就要求控件在使用前提供几个必须的参数:listView的个数,相应的listView,以及相应的title。因此,在xml方式使用时,应提供足够的自定义参数。具体使用方式见使用指南。
    核心是如何实现translate动画。如果使用ViewAnimation,画面上看起来移动但点击区域并没有位移。其实只要将listView实际位置设在title下方,动画开始就从-Y移动到0,点击时就没有问题。只不过我觉得既然3.0后属性动画已经引入,不如用性能更好的。所以该控件使用借助Scroller计算位移、通过对ListView设置translationY的方式完成位移效果。这样由于Scroller默认使用Viscous-Interpolator,效果也比匀速动画更官方范和美观。
    还需要注意的是,该控件大致分为三个部分:Title,ListView以及黑色背景。这三者在addView的时候需要注意先后顺序,保证从顶至底为:Title-ListView-黑色背景。

  • 简易流程
    如下,由postDelay来实现循环判断:

流程图示意

其中startScroll负责初始化参数:

if (mDirection == MOVE_DOWN) {
    mScroller.startScroll(0, -mHeight, 0, mHeight, mDuration);
} else if (mDirection == MOVE_UP) {
    mScroller.startScroll(0, 0, 0, -mHeight, mDuration);
}

附Github地址:源码

使用指南

  • From xml

    <com.opticalix.dropdown_lib.DropdownView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/dropdown_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:drop_down_arrow_up="@mipmap/ic_arrow_up"
        app:drop_down_arrow_down="@mipmap/ic_arrow_down"
        app:drop_down_duration="200"
        app:drop_down_enable_dim="true"
        app:drop_down_list_count="2"
        app:drop_down_list_height="150dp"
        app:drop_down_title_background="@android:color/white"
        app:drop_down_title_drawable_padding="10dp"
        app:drop_down_title_padding_bottom="7dp"
        app:drop_down_title_padding_top="7dp"
        app:drop_down_title_text="@array/drop_down_titles"
        app:drop_down_title_text_color="#444444"
        app:drop_down_title_text_size="10sp"/>
    

然后关联对应的ListView,通常还需要设置item的点击回调事件。

mDropdownView = (DropdownView) findViewById(R.id.dropdown_view);
mDropdownView.setup(simpleListView1, simpleListView2);
mDropdownView.setOnDropdownItemClickListener(new DropdownView.OnDropdownItemClickListener() {
    @Override
    public void onItemClick(View v, int whichList, int position) {
        mDropdownView.setTitleText(whichList, whichList + "-" + position);
    }
});
  • From code
    新建该控件,调用setup初始化必要参数。
DropdownView dropdownView = new DropdownView(this);
        dropdownView.setup(3, titles_arr, simpleListView1, simpleListView2, simpleListView3);

//config dropdownView...

//remember to addView
root.addView(dropdownView);
  • 注意
    在5.0以上手机有可能出现其他View覆盖本控件的现象,虽然明明是DropdownView最后添加,按理说应该处于最顶层…可能是引入MD风格后,Z轴方向加强管理而导致的覆盖机制变化。可通过setZ解决:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    mDropdownView.setZ(100);//larger than others
}