修改效果
解析结构
- 导读
想要扩展首先我需要执行下面几个步骤
1.fork DropDownMenu到自己的github账号
2.使用 as 的 vcs checkout 出来
3.提交到github
4.发起 pull request
- 源码实现原理
作者对该控件的分析 导读
这是我对该 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 ,且不影响下拉功能。
代码实现细则
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 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 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 ) ,样式的可扩展性大大增强
过滤获取 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 的文本颜色的设置。
效果:就是最上面扩展的效果图
插曲:
应赵萝贝要求加了箭头在文本的方向属性
应 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 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);
新增tabMenu点击回调接口
总结
样式性扩展:我们尽量从 xml 中加载 view ,根据指定 id 获取控件,到达最大程度的样式解耦
功能性扩展:将 tabViews 数组顺序的位置不依赖于父 View 的 child ,而是依赖于一个动态的数组。我们对父 View 的 child 的添加并不会影响到原来的功能。这样可以做到 tabView 的功能性扩展
该篇文章代码在 Github上