1. 概述
话说Android里面的主题真是琳琅满目,虽然平时开发时我们都是固定,约定成熟的使用某个主题,例如官方推荐的AppComat系列,但是Android有多少主题,它们之间有什么联系和区别很少人去认真探究。我本人开发时都是直接使用Android Studio生成的主题,或者是继承至它。Holo主题基本没有使用过,似乎是一个比较老的主题,比我早接触Android的前辈们应该比较熟悉。Material主题就属于比较新的主题。还有呢?
2. 一些主要的根主题以及它的继承结构
这里我使用了“根主题”的说法,因为大多数主题都是继承至某个主题,追根究底,会找到几个没有parent的根主题。我们来看看这几个根主题:
-
Theme:API 10及其以下的默认主题。
-
Theme.Holo:API 11 - 13的默认主题,
-
Theme.Material:API 21及其以上。
2.1 如何追溯到这些Theme?
你可以随便新建一个Theme,parent继承至Theme.AppCompat
,按住Ctrl
(在Mac上是Command
)点击parent主题,这样一直追溯到Platform.AppComat
会有几个选择:
到这里,Theme的继承就区别开来了,/values.xml
最终会继承至Theme
,/values-v11.xml
和/values-v14.xml
最终会继承至Theme.Holo
,而/values-v21.xml
会继承至Theme.Material
。所以说,一般我们选择主题选择继承至AppCompat
系列的主题就可以了(至少现在是如此),AppCompat
已经做好适配工作了。
2.2 它们有什么区别呢?
据我所对比观察,它们的参数基本上相同:
双方有些细微的增缺,只有值大不一样,但这并不影响什么,我们必要的时候直接去所关心的Theme那里了解一下目标参数。一个主题规范严格的app,风格的定义应该在App Theme上就得到了统一,这些根主题的参数给了我们修改,参考的依据,了解这些参数是很有必要的。
2.3 只有这些根主题吗?
当然,Android系统如此庞杂,远远不止这些根主题,上述3个根主题所在的xml文件里定义了很多根主题,适当了解一下会有好处。
2.4 如此多的子主题,我们该如何抉择?看看它的继承结构。
我先贴上一张自己总结的图:
我以AppCompat
系列的主题为例,图中并没有将所有主题都收拢进去,毕竟太多了。图中枝节点的依据是这样子:例如Base.TextAppearance.AppCompat.Widget.Actionbar.Subtitle.Inverse
这个style,已.
为分割线,Base
、TextAppearance
...为节点作成的结构图。从图中可以很直观的得出结论:主题分为TextAppearance
、Theme
、ThemeOverlay
和Widget
4种。
-
TextAppeareance: 定义字体样式。
-
Theme: 定义Dialog和Application的样式。
-
ThemeOverlay: 定义悬浮层的样式,例如点击ActionBar上的更多或者下拉菜单弹出的悬浮界面的样式。
-
Widget:顾名思义,定义
Button
之类的组件的样式。
如果你想使用某个字体主题或者自定义,你可以使用或者继承TextAppearance系列:
同样的Theme系列:
ThemeOverlay系列:
Widget系列:
Android的Style命名已经极大的便利我们使用,我们唯一要做的事就是根据业务需求了解某个主题的参数和值,并且在必要的时候复写它。
3 自定义样式
现在用实际行动来验证我们的猜想。我新建了一个Activity,它是这样子:
Activity
代码:
public class ActionBarActivity extends AppCompatActivity {
@Bind(R.id.toolbar) Toolbar mToolbar;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_action_bar);
ButterKnife.bind(this);
mToolbar.setTitle("Thanatosx");
mToolbar.setSubtitle("Widgets");
setSupportActionBar(mToolbar);
ActionBar mActionBar = getSupportActionBar();
if (mActionBar != null){
mActionBar.setDisplayHomeAsUpEnabled(true);
mActionBar.setHomeButtonEnabled(false);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menus, menu);
return super.onCreateOptionsMenu(menu);
}
}
Layout布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#80123456"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
以及Theme主题:
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
</style>
现在我们想要通过修改Theme来改变Toolbar的样式达到以下效果:
-
改变返回图标
-
改变Title和Subtitle的字体大小和颜色
-
改变more icon的颜色
首先,第一个问题是:去哪里修改?前面说到,Theme主题基本可以定义一个Application的大部分地方的样式,我们需要去搜索一下父主题是否有这样的定义,从Theme.AppCompat.Light
这个主题开始往上追溯,在Base.Theme.AppCompat.Light
上会有个版本选择:
这个无所谓,选择低版本的就可以了,低版本都支持的话就不用担心高版本,首先会在Base.v7.Theme.AppCompat.Light
上发现Toolbar Style的定义:
那么,我们就可以复写这个属性,自定义我们的Toolbar Style了,同样的,有个actionOverflowButtonStyle
属性,它就是定义more icon button的属性了。
修改后的Theme:
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="actionOverflowButtonStyle">@style/AppWidget.ActionButton.Overflow</item>
<item name="toolbarStyle">@style/AppTheme.Toolbar</item>
</style>
<style name="AppTheme.Toolbar" parent="Widget.AppCompat.Toolbar">
<item name="android:minHeight">50dp</item>
<item name="titleTextAppearance">@style/AppTextAppearance.Widget.Actionbar.Title</item>
<item name="subtitleTextAppearance">@style/AppTextAppearance.Widget.Actionbar.Subtitle</item>
<item name="navigationIcon">@mipmap/ic_back</item>
<item name="android:background">?attr/colorPrimary</item>
</style>
<style name="AppTextAppearance.Widget.Actionbar.Title" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">14sp</item>
<item name="android:textColor">#FFF</item>
</style>
<style name="AppTextAppearance.Widget.Actionbar.Subtitle" parent="TextAppearance.Widget.AppCompat.Toolbar.Subtitle">
<item name="android:textSize">12sp</item>
<item name="android:textColor">#FFF</item>
</style>
<style name="AppWidget.ActionButton.Overflow" parent="Widget.AppCompat.ActionButton.Overflow">
<item name="android:src">@mipmap/ic_more</item>
</style>
parent继承至原使用的Style Theme,以此基础上重写自己需要修改的属性,最终效果图:
4 坑
在修改Activity的返回按钮的时候我在AppTheme
里找到这个属性:homeAsUpIndicator
,老司机们肯定明白,这就是设置返回按钮的图标,但是无论我如何设置,修改,这个属性没有任何效果!!无奈之下,我去overflowstack上寻找答案,众说纷纭,倒是看到这个属性navigationIcon
,我在Theme上没看到,在Toolbar的主题上也没有看到,但是当我进入Toolbar的源码,我发现是有这个属性的:
我直接跑去R.java文件去看,一目了然:
源码之下,没有秘密
还有一点,设置navigationIcon
之后,点击返回按钮没有任何响应了,需要设置Navigation Click Listener
// after set support action bar
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});