修改效果
#解析結構
- 導讀 想要擴充套件首先我需要執行下面幾個步驟
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<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 的文字顏色的設定。 效果:就是最上面擴充套件的效果圖 插曲: 應趙蘿貝要求加了箭頭在文字的方向屬性
應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);
複製程式碼
新增tabMenu點選回撥介面
總結
樣式性擴充套件:我們儘量從 xml 中載入 view ,根據指定 id 獲取控制元件,到達最大程度的樣式解耦 功能性擴充套件:將 tabViews 陣列順序的位置不依賴於父 View 的 child ,而是依賴於一個動態的陣列。我們對父 View 的 child 的新增並不會影響到原來的功能。這樣可以做到 tabView 的功能性擴充套件
該篇文章程式碼在 Github上