Android style定義中,咱們能夠給一個新的style樣式指定一個父style。衆所周知,能夠經過兩種方式實現:
1,新的style name中經過.
將父style name與新的style name鏈接,如常見的:java
<style name="Theme.AppCompat.Light.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="Theme.AppCompat.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
複製代碼
2,新的style name中經過parent
指定特定的父style name,如Android源碼中的:android
<style name="aerr_list_item" parent="Widget.Material.Light.Button.Borderless">
<item name="minHeight">?attr/listPreferredItemHeightSmall</item>
<item name="textAppearance">?attr/textAppearanceListItemSmall</item>
<item name="textColor">?attr/textColorAlertDialogListItem</item>
<item name="gravity">center_vertical</item>
<item name="paddingStart">?attr/dialogPreferredPadding</item>
<item name="paddingEnd">?attr/dialogPreferredPadding</item>
<item name="background">?attr/selectableItemBackground</item>
<item name="drawablePadding">32dp</item>
<item name="drawableTint">?android:attr/colorAccent</item>
<item name="drawableTintMode">src_atop</item>
</style>
複製代碼
以上兩種狀況,子style最終都會從父styel中繼承屬性,同時,若是子style中存在同名屬性,則覆蓋之。markdown
可是,現實中咱們每每看到另一種寫法,咋一看上去是雜糅了上述兩種方式,如appcompat中常見的:app
<style name="RtlOverlay.Widget.AppCompat.Search.DropDown" parent="android:Widget">
<item name="android:paddingLeft">@dimen/abc_dropdownitem_text_padding_left</item>
<item name="android:paddingRight">4dp</item>
</style>
<style name="RtlOverlay.Widget.AppCompat.Search.DropDown.Icon1" parent="android:Widget">
<item name="android:layout_alignParentLeft">true</item>
</style>
<style name="RtlOverlay.Widget.AppCompat.Search.DropDown.Icon2" parent="android:Widget">
<item name="android:layout_toLeftOf">@id/edit_query</item>
</style>
<style name="RtlOverlay.Widget.AppCompat.Search.DropDown.Query" parent="android:Widget">
<item name="android:layout_alignParentRight">true</item>
</style>
複製代碼
咱們發現,這種寫法仍是很廣泛的,包括咱們項目本身在定義style時,也有很多是此類寫法。less
那麼,問題來了,這種寫法下,style最終是怎麼樣的一個規則呢? 以.
寫法做爲父style,仍是以parent
做爲父style,抑或二者都做爲父style,而後按照其餘規則處理屬性優先級?ide
最近正好遇到一個相關的Bug。主工程接上UI組件庫後,測試效果,寫了個簡單的TextView
,其中textColor
使用?attr/brand_color_primary
形式指向自定義屬性。天然的,須要將當前項目Activity theme(style名是以.
鏈接),指定UI組件庫中的一個基礎style做爲parent,具體經過parent
指定,爲描述方便,簡單將其簡化爲:oop
<style name="A.B.C" parent="D">
....
</style>
複製代碼
但運行時出現閃退。錯誤信息以下:佈局
Process: com.corn.debug, PID: 16205
android.view.InflateException: Binary XML file line #144 in com.corn.debug:layout/fragment_home_me: Failed to resolve attribute at index 5: TypedValue{t=0x2/d=0x7f040271 a=-1}
Caused by: java.lang.UnsupportedOperationException: Failed to resolve attribute at index 5: TypedValue{t=0x2/d=0x7f040271 a=-1}
at android.content.res.TypedArray.getDimensionPixelSize(TypedArray.java:783)
at android.view.ViewGroup$MarginLayoutParams.<init>(ViewGroup.java:8226)
at androidx.constraintlayout.widget.ConstraintLayout$LayoutParams.<init>(ConstraintLayout.java:2691)
at androidx.constraintlayout.widget.ConstraintLayout.generateLayoutParams(ConstraintLayout.java:1916)
at androidx.constraintlayout.widget.ConstraintLayout.generateLayoutParams(ConstraintLayout.java:482)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1129)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1130)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1130)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1130)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)
at android.view.LayoutInflater.inflate(LayoutInflater.java:686)
at android.view.LayoutInflater.inflate(LayoutInflater.java:538)
at com.corn.base.BaseFragment.onCreateView(BaseFragment.java:29)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:310)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1185)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1354)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1432)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1495)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447)
at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2167)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1945)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1847)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7520)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
複製代碼
一開始一直覺得是自定義屬性寫法有問題,最終運行時沒有找到其對應的屬性值。但最終發現,錯誤信息指示位置還沒有來到新增的測試佈局位置,在以前的其餘頁面就已經發生了閃退。測試
從報錯信息上看,應該是自定義屬性沒有取到值,經過查看閃退佈局,發現有以下寫法:ui
<ImageView
android:id="@id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="?attr/insetRight"
android:layout_marginRight="?attr/insetRight"
android:src="@drawable/icon_arrow_right"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
複製代碼
很顯然,應該是?attr/insetRight
處出了問題。
Activity的主題是在Manifest中進行的配置,追查發現,insetRight
是一個自定義屬性。
<attr name="insetRight" format="dimension" />
複製代碼
其賦值具體是在<style name="A.B.C" parent="D">....</style>
中的B
中。也就是說,新指定parent="D"
後,最終style沒有繼承到來自B
的特有屬性。
從新爲insetLeft
賦值後,一切運行正常。
<style name="A.B.C" parent="D">
<item name="insetRight">14dp</item>
....
</style>
複製代碼
從結果上看,咱們得出的結論以下:
style爲單繼承,parent方式優先級高於.
繼承,同時存在時,前綴的做用只做爲名字。
也能夠本身寫自定義幾個style再次測試下:
<style name="MyStyle1">
<item name="android:textColor">#00f</item>
</style>
<style name="MyStyle2">
<item name="android:textSize">18sp</item>
</style>
<style name="MyStyle2.SubStyle" parent="MyStyle1">
<item name="android:textStyle">bold</item>
</style>
複製代碼
從提示上看,MyStyle2
是沒有生效的。同時,運行後,也確實只有SubStyle
與MyStyle1
的效果。此時,咱們直接將MyStyle2.SubStyle
理解成新的style name
便可。
Google官方文檔上,其實也有相似描述,平時沒太注意,本覺得只是同時存在.
命名與parent
時,是二者屬性的都會繼承,而後再按照parent
的爲準,最後纔是以本身的屬性爲最終標準的。如今看確實理解錯了。
附上官網文檔。
Styles and Themes
其中關鍵的描述爲:
踩坑留念。
end~