BottomNavigationView實現底部導航欄的實現(一)
支援庫的依賴(Android Design Support Library)
開始之前打一波推廣,推廣的就是我自己,最近喜歡hexo的部落格主題,自己上手搭建一個,從開始使用到Hexo的個性化配置,自己邊學習邊記錄,形成了Hexo搭建個人網站的一個體系,同步在個人的部落格上:
本文是對底部導航學習和最後筆記記錄,使用的是BottomNavigationView 控制元件.
選取常見的圖示:
注意: 下面的連線請在微信上開啟
支援庫的依賴
首先是上程式碼:
// 支援庫 compile 'com.android.support:design:25.3.1'
不然是不會出現對應的提示;
開始新建佈局書寫
開始之前學習一下子 ColorStateList 型別的資源:
ColorStateList物件可以在XML中定義,像color一樣使用,它能根據它應用到的View物件的狀態實時改變顏色。例如,Button可以存在多種狀態(pressed、focused或other),如果使用ColorStateList,你就能為它的每個狀態提供不同的顏色。
新建專案開始決定來書寫:
添加布局檔案時候注意的事項:
- 屬性一: iteamBackground 指定的是底部導航欄的背景顏色,預設是主題的顏色;
- 屬性二: iteamIconTint 指的是導航欄中圖片的顏色
- 屬性三: iteamTextColor 指的是導航欄文字的顏色
- 屬性四: menu
BottomNavigationView在使用時,除了普通空間的屬性外,還需要注意如下幾個特有屬性:
app:itemBackground:指定底部導航欄的背景顏色,預設是當前主題的背景色,白色or黑色;
app:itemIconTint:指定底部導航欄元素圖示的著色方式,預設元素選中時icon顏色為@color/colorPrimary;
app:itemTextColor:指定底部導航欄元素文字的著色方式;
app:menu:使用Menu的形式為底部導航欄指定元素;
首先是程式碼的展示:
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_alignParentBottom="true"
app:itemBackground="@color/colorAccent"
app:itemIconTint="@color/status_text"
app:itemTextColor="@color/yellow"
app:menu="@menu/bottom_navigation_main"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
之後是使用men給底部導航指定對應的元素:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_favorites"
android:enabled="true"
android:icon="@mipmap/canvas"
android:title="@string/text_favorites"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_schedules"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:title="@string/text_schedules"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_music"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:title="@string/text_music"
app:showAsAction="ifRoom" />
</menu>
展示的效果圖:
實現選中和未選中的顏色變化
我們看到的底部導航欄的實現中,選中和未選中的顏色是不一致的,我們看到 itemIconTint屬性和itemTextColor都是前面提到過的ColorStatusList物件,也就是我們可以設定對應的選擇和未選中時候的顏色list.
新建bottom_item_icon_tint_list.xml檔案,設定的是圖示選擇和未選中的顏色的著色的方式.
### 記錄新建一個drawable的xml小操作 ###
時間長了,這個都忘記了,首先是一個具有狀態對應的像color使用一樣的drawable.
在drawable選中,右擊→new →Drawable Resources file
展示如下:
輸入檔案的名稱後(後面不需要.xml,只需要的是檔案的名稱),建立成功展示如下:
程式碼展示和說明:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="@color/background_blue" ></item>
<item android:state_checked="false" android:color="@color/yellow"></item>
</selector>
一個狀態對應於一個iteam,我們這裡設定的圖片的著色是選擇blue,未選中黃色,我們依次設定文字的顏色一致.
感慨一下子,書寫程式碼要注意力很集中,不集中出現的錯誤需要我們花費更大的精力去執行檢查錯誤.
選中和未選中圖片的變化的設定
上面的設定是在選中和未選中的時候著色的設定,如果是圖片的話怎麼做.
選中使用的是一個顏色,那我們也可以在選中的時候使用一張圖片,沒有選中的時候使用一張圖片. 那麼就是一個選擇器,我們直接建立.
bottom_png_list.xml;
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@drawable/music"></item>
<item android:drawable="@drawable/music_gray"></item>
</selector>
圖片展示如下:
我們使用的meu選單設定元素到底部導航欄裡面的,引用圖片的位置換一下就行了!
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_favorites"
android:enabled="true"
android:icon="@drawable/bottom_png_list"
android:title="@string/text_favorites"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_schedules"
android:enabled="true"
android:icon="@drawable/bottom_png_list"
android:title="@string/text_schedules"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_music"
android:enabled="true"
android:icon="@drawable/bottom_png_list"
android:title="@string/text_music"
app:showAsAction="ifRoom" />
</menu>
此時不需要設定 itemIconTint和itemTextColor 顏色
也可以在程式碼中設定 iconTint為null:
mBottomNavigationView2.setItemIconTintList(null);
執行展示:
下面的就是對選單menu的監聽和頁面的切換.
四個導航欄的問題
測試的模擬器的解析度是 720*480 p
Java 中Math.min(a,b)是比較a和b 的大小,返回的是較小的數值.
設定和前面的一樣,我們不在展示,展示的是出現的問題.
我們直接進BottomNavigationView的原始碼檢視:
BottomNavigationView原始碼
說明最大值item有一個值:(這個值是5)
我們設定五個iteam看看,沒有出錯,展示的圖片如下:
最大支援的是5個iteam,我們加到六個iteam,看看結果是怎麼樣的:
結果是直接出錯了,我們直接看看錯誤的資訊是展示:
這裡面明確的說明BottomNavigationView支援的最大的number of iteam 是5
2018/2/28 13:57:02
我們繼續下面的這個問題,為什麼沒有平分佈局,展示原始碼如下:
出現了一個新類,在後面設定了setBottomNavigationMenuView
進入到BottomNavigationMeunView
我們直接看 onMeasure()方法.
// 原始碼 - BottomNavigationMeunView.class
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int count = getChildCount();
final int heightSpec = MeasureSpec.makeMeasureSpec(mItemHeight, MeasureSpec.EXACTLY);
if (mShiftingMode) {
final int inactiveCount = count - 1;
final int activeMaxAvailable = width - inactiveCount * mInactiveItemMinWidth;
final int activeWidth = Math.min(activeMaxAvailable, mActiveItemMaxWidth);
final int inactiveMaxAvailable = (width - activeWidth) / inactiveCount;
final int inactiveWidth = Math.min(inactiveMaxAvailable, mInactiveItemMaxWidth);
int extra = width - activeWidth - inactiveWidth * inactiveCount;
for (int i = 0; i < count; i++) {
mTempChildWidths[i] = (i == mSelectedItemPosition) ? activeWidth : inactiveWidth;
if (extra > 0) {
mTempChildWidths[i]++;
extra--;
}
}
} else {
final int maxAvailable = width / (count == 0 ? 1 : count);
final int childWidth = Math.min(maxAvailable, mActiveItemMaxWidth);
int extra = width - childWidth * count;
for (int i = 0; i < count; i++) {
mTempChildWidths[i] = childWidth;
if (extra > 0) {
mTempChildWidths[i]++;
extra--;
}
}
}
int totalWidth = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(MeasureSpec.makeMeasureSpec(mTempChildWidths[i], MeasureSpec.EXACTLY),
heightSpec);
ViewGroup.LayoutParams params = child.getLayoutParams();
params.width = child.getMeasuredWidth();
totalWidth += child.getMeasuredWidth();
}
setMeasuredDimension(
ViewCompat.resolveSizeAndState(totalWidth,
MeasureSpec.makeMeasureSpec(totalWidth, MeasureSpec.EXACTLY), 0),
ViewCompat.resolveSizeAndState(mItemHeight, heightSpec, 0));
}
快速執行: shift + F10 連線上的會直接回車
執行停止的命令: ctrl+ F2 debug執行的命令: shift + F9 連線的話是直接回車就行.
我debug一些資訊:
學習一個英文單詞: inactive 閒置的;
閒置的iteam最大寬度是:144 閒置的iteam最小的寬度是: 84 啟用的iteam的最大的寬度是252 iteam的高度是: 84
我們看到一個判斷 mShitingMode,我們看看這個變數
後面有沒有對這個變數的再次賦值:
我們這裡看到menu.size有一個判斷, 判斷值是3 大於三是true,不大於是false
當mShitingMode=false的時候
此時表示的iteam的數量在三或者之下:
dubug的展示圖片:
我們看到此時所有的childWidth的寬度是240 這個寬度= 獲取的width/count (此時debug的count是3)
當mShifingMode = true的時候
對onMeasure進行debug測試:
此時的模擬器 720*480 count 數量是5
看出選中的寬度是 252 限制的寬度是117
問題的處理
全域性搜尋: double shift;
當我們iteam是大於5的時候,我們獲取到 mShifingMode,設定為false,就會平分了!
還有注意的一點,忘記說了:
複製網上的程式碼:
/**
* Created by Administrator on 2018/2/28.
*/
public class BottomNavigationViewHelper {
@SuppressLint("RestrictedApi")
public static void disableShiftMode(BottomNavigationView view) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); i++) {
BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
//noinspection RestrictedApi
item.setShiftingMode(false);
// set once again checked value, so view will be updated
//noinspection RestrictedApi
item.setChecked(item.getItemData().isChecked());
}
} catch (NoSuchFieldException e) {
Log.e("BNVHelper", "Unable to get shift mode field", e);
} catch (IllegalAccessException e) {
Log.e("BNVHelper", "Unable to change value of shift mode", e);
}
}
}
在Activity中的使用:
執行看看:
其他的設定對底部導航
設定如下:
第一設定圖片和文字之間的距離
直接在dimens中設定:
<dimen tools:override="true" name="design_bottom_navigation_margin">5dp</dimen>
展示效果如下:
增加底部導航欄的高度
預設的底部導航欄的高度是 56dp
<dimen tools:override="true" name="design_bottom_navigation_height">84dp</dimen>
展示如下:
設定選中後字型不變
我們現在設定的選中的時候字型會變大,我們設定為不變:
<dimen tools:override="true" name="design_bottom_navigation_text_size">12sp</dimen>
<dimen tools:override="true" name="design_bottom_navigation_active_text_size">12sp</dimen>
設定選中和未選中的字型的大小一致解決:
不展示了!