Toolbar的Title與NavigationIcon距離異常
NavigationIcon和Title的距離正確
距離顯示正確.jpg
NavigationIcon和Title的距離出現了異常
距離顯示異常.jpg
問題的解決方法
解決辦法很簡單,見程式碼
為了方便起見,先定義一個Toolbar的Theme
<style name="NoSpaceActionBarTheme" parent="Base.Widget.AppCompat.Toolbar"> <item name="contentInsetStart">0dp</item> <item name="contentInsetStartWithNavigation">0dp</item> </style>
如果在佈局檔案中新增Toolbar的話可以通過增加style來實現,程式碼如下
<android.support.v7.widget.Toolbar style="@style/NoSpaceActionBarTheme" android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:navigationIcon="?attr/homeAsUpIndicator" app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
如果定義過Activity的Theme是ActionBar的話,可以在Theme的定義中加上一句程式碼,如下
<style name="ActionBarTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="toolbarStyle">@style/NoSpaceActionBarTheme</item> </style>
問題原因
為了搞明白為什麼Support包從V22.2.0版本升級到V24.0.0就會出現這樣的問題,還是需要翻看Toolbar的原始碼,OK,我們直接看Toolbar的程式碼(V24.0.0包中),順便說一下Toolbar是在appcompact-v7包下面
//用到的主要屬性名稱為contentInsetStart
private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper();
//對應的屬性名稱為contentInsetStartWithNavigation
private int mContentInsetStartWithNavigation;
mContentInsets 這個成員變數和mContentInsetStartWithNavigation用來控制NavigationIcon和Title之間的距離的,我們接著看這兩個變數是如何影響這個距離的
最主要的程式碼在Toolbar中的onLayout方法中,下面我摘取主要程式碼來說明
final int paddingLeft = getPaddingLeft();
//首先是獲取系統的偏移量
int left = paddingLeft;
//這段程式碼用來計算Navigation的Layout
if (shouldLayout(mNavButtonView)) {
if (isRtl) {
right = layoutChildRight(mNavButtonView, right, collapsingMargins,
alignmentHeight);
} else {
//計算完之後left的距離為paddingLeft+mNavButtonView的寬度+mNavButtonView自身的偏移量
left = layoutChildLeft(mNavButtonView, left, collapsingMargins,
alignmentHeight);
}
}
//核心的方法,返回就是那個讓距離錯誤的值
final int contentInsetLeft = getCurrentContentInsetLeft();
//left會從之前的left值也就是計算過Navigation的距離之後 和contentInsetLeft比較,取最大值
left = Math.max(left, contentInsetLeft);
...接下來計算Title的佈局的時候左邊距就是用的這個left
我們來看看getCurrentContentInsetLeft()這個方法
public int getCurrentContentInsetLeft() {
return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL
? getCurrentContentInsetEnd()
: getCurrentContentInsetStart();
}
因為我們是從左向右顯示所以會呼叫getCurrentContentInsetStart()這個方法,我們繼續看這個方法
public int getCurrentContentInsetStart() {
return getNavigationIcon() != null
? Math.max(getContentInsetStart(), Math.max(mContentInsetStartWithNavigation, 0))
: getContentInsetStart();
}
首先我們是有NavigationIcon的所以會走這個分支
Math.max(getContentInsetStart(), Math.max(mContentInsetStartWithNavigation, 0))
其中Math.max(mContentInsetStartWithNavigation, 0)返回的就是mContentInsetStartWithNavigation這個值
mContentInsetStartWithNavigation這個值就是從contentInsetStartWithNavigation這個屬性中取得的
getContentInsetStart()這個返回的值就是contentInsetStart這個屬性對應的值
所以最後就是比較contentInsetStart和contentInsetStartWithNavigation這兩個屬性的值
OK,接下來我們來看這兩個屬性的值在V22.2.0和V24.0.0的版本中到底是多少
具體的檔案為首先找到對應版本的appcompact-v7包的aar檔案
然後解壓找到/res/values/values.xml這個檔案
首先說明預設Toolbar的Style是Widget.AppCompat.Toolbar
Widget.AppCompat.Toolbar的Parent是Base.Widget.AppCompat.Toolbar
所以只要找到Base.Widget.AppCompat.Toolbar對應的Style就OK了
首先我們來看V22.2.0版本中,我找到了描述Toolbar屬性的這段內容
<style name="Base.Widget.AppCompat.Toolbar" parent="android:Widget">
<item name="titleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Title</item>
<item name="subtitleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle</item>
<item name="android:minHeight">?attr/actionBarSize</item>
<item name="titleMargins">4dp</item>
<item name="maxButtonHeight">56dp</item>
<item name="collapseIcon">?attr/homeAsUpIndicator</item>
<item name="collapseContentDescription">@string/abc_toolbar_collapse_description</item>
<item name="contentInsetStart">16dp</item>
</style>
我們發現contentInsetStart這個是16dp,而沒有contentInsetStartWithNavigation這個屬性,這是因為contentInsetStartWithNavigation這個屬性是在V22之後的版本才加上的,而V22的Toolbar程式碼中只會根據contentInsetStart來計算Title的左邊距
接下來我們來看V24.0.0版本中的程式碼
<style name="Base.Widget.AppCompat.Toolbar" parent="android:Widget">
<item name="titleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Title</item>
<item name="subtitleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle</item>
<item name="android:minHeight">?attr/actionBarSize</item>
<item name="titleMargin">4dp</item>
<item name="maxButtonHeight">@dimen/abc_action_bar_default_height_material</item>
<item name="buttonGravity">top</item>
<item name="collapseIcon">?attr/homeAsUpIndicator</item>
<item name="collapseContentDescription">@string/abc_toolbar_collapse_description</item>
<item name="contentInsetStart">16dp</item>
<item name="contentInsetStartWithNavigation">@dimen/abc_action_bar_content_inset_with_nav</item>
<item name="android:paddingLeft">@dimen/abc_action_bar_default_padding_start_material</item>
<item name="android:paddingRight">@dimen/abc_action_bar_default_padding_end_material</item>
</style>
OK,contentInsetStart這個也是16dp,contentInsetStartWithNavigation這個定義在dimen中,我們來看看這個值
<dimen name="abc_action_bar_content_inset_with_nav">72dp</dimen>
OK,我們回過來看這段程式碼
//核心的方法,返回就是那個讓距離錯誤的值
final int contentInsetLeft = getCurrentContentInsetLeft();
//left會從之前的left值也就是計算過Navigation的距離之後 和contentInsetLeft比較,取最大值
left = Math.max(left, contentInsetLeft);
left的值一開始是NavigationIcon的寬度,一般為56dp,而contentInsetLeft這個值是72dp,所以left的值就變成了72dp,就最後導致了距離顯示異常
至此,我們終於瞭解了這個錯誤的來龍去脈,不僅瞭解了怎麼改,也瞭解了為什麼這麼改,同時瞭解了Toolbar的相關程式碼