問題描述
當我將Support包由V22.2.0升級到V24.0.0的版本之后逼蒙,發(fā)現(xiàn)原來正常顯示的Toolbar顯示異常。前提是我并沒有修改任何代碼腰涧。請看下圖
NavigationIcon和Title的距離正確
NavigationIcon和Title的距離出現(xiàn)了異常
問題的解決方法
解決辦法很簡單臣镣,見代碼
為了方便起見,先定義一個Toolbar的Theme
<style name="NoSpaceActionBarTheme" parent="Base.Widget.AppCompat.Toolbar">
<item name="contentInsetStart">0dp</item>
<item name="contentInsetStartWithNavigation">0dp</item>
</style>
如果在布局文件中添加Toolbar的話可以通過增加style來實現(xiàn)竿报,代碼如下
<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就會出現(xiàn)這樣的問題仰楚,還是需要翻看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();
//首先是獲取系統(tǒng)的偏移量
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();
}
因為我們是從左向右顯示所以會調(diào)用getCurrentContentInsetStart()這個方法欢峰,我們繼續(xù)看這個方法
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屬性的這段內(nèi)容
<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>
我們發(fā)現(xiàn)contentInsetStart這個是16dp涨共,而沒有contentInsetStartWithNavigation這個屬性,這是因為contentInsetStartWithNavigation這個屬性是在V22之后的版本才加上的宠漩,而V22的Toolbar代碼中只會根據(jù)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的相關代碼