您的位置:首页 > 产品设计 > UI/UE

UI进阶——Style的详细介绍

2016-11-13 12:51 127 查看

楔子

在android UI的开发中,合理的利用style资源是必不可少的一件事。但是个人翻阅很多文章,很少有详细的描述style的原理及其具体的使用。所以决定写一篇详细描述style的文章。

style的简单描述

style的作用

当创建App之后,我想大家肯定都有这样的疑惑,为什么刚创建的Activity的背景是白色的,使用TextView显示出的字的颜色都是黑色的?这种一开始就默认的属性是由什么控制的呢?这个就是style资源的作用了

style指定了你的界面上的属性默认情况下通过什么样的方法进行装饰。

属性指的是:颜色、高度、填充和字体大小等。

比如说:界面的背景默认采用哪种颜色,界面的文字默认采用哪种颜色等等。

style的作用范围

好了现在我们知道了style是用来设置界面的默认属性的。那么style可以设置在哪些对象上呢?

Style可以设置在三个对象上,分别是Application、Activity、View(布局和控件的总称)

根据应用的对象不同,style的称呼作用也是不一样的:

被称作”theme”的style是Application和Activity中使用(是针对窗体级别的,可以改变窗体样式)

被称作”style”的style是在View(指Layout与View控件)上使用的(是针对窗体元素级别的,改变指定控件或者Layout的样式)

疑问一:为什么Google大费周章的让style可应用与三个对象呢?

相信大部分兄弟已经猜到了,这是因为style应用在不同的对象上,其可作用范围也就不同。

可作用范围图(包含关系):



为什么叫做可作用范围呢?意思是当style作用于Application时,Application内部的元素可以选择使用该style中设定的默认值,也可以选择不使用该style中设定的默认值。

比如说,Application的style背景色设置为灰色。那么所有Activity下的Layout选择使用该style的背景色,所以Layout的背景是灰色的。但是其中Button不使用该style设定的默认背景。

效果图



所以证明了,style资源其实只是一种声明,真正是否使用是在代码中确定的。为什么我们可以直接使用style,而不需要在代码中声明?这是因为Goole自定义了一堆style资源的声明,并在原生控件的代码中确定使用了style资源的声明而已。

如何确定哪些控件使用了style的默认值,哪些控件没使用呢?

这个就得靠经验的积累了(最典型的比如,Button的背景色不根据style来设置的,而TextView的背景色根据style来设置的)

那么代码是如何获取style资源的呢,并且如何在代码中确定是否使用该声明呢?

由于该涉及到自定义View的内容,就不描述了,详细内容可以参考鸿洋:深入理解Android中的自定义属性

我们回到作用范围上,那么如果将style运用在FirstActivity上,意思就是该Activity内部的元素可以选择使用该style中设定的默认值。用style设置默认背景色为天蓝色。

效果图



从图中我们可以看到,只有FirstActivity的背景色变成天蓝色了,其他Activity的背景色还是灰色的。(这个就是作用范围,说明Activity的style的作用范围,只在当前Activity内部)并且在FirstActivity中TextView的背景色也变成天蓝色了,但是Button的背景色还是灰色的。(这个就是可选择,说明不是所有控件的背景色都是由style控制的)

这个就是Style的可选择范围的意思

同样的作用范围越大,责任就越重~~。被称为theme的style,必须得考虑到应用中所有可能运用到的默认值,并设置相关的默认属性值。所以都会采用Android自带的theme。自动为我们设定了必要的默认值。

疑问二:那么style作用不同,又是什么意思呢?

style的作用难道不是设置默认属性吗,还有什么其他作用吗?这里作用指的是style默认值设置的数量,就跟刚才说的作用范围越大,责任就越重,必须得考虑到应用中所有可能运用到的默认值。举个不恰当的例子。我们将Application当做一个房子,Activity当做一个房间,那么View就是房间的部件。那么我们开始用style来设计房子。首先我们先粉刷View,View是一把椅子,我想要一把棕色的靠背椅,那么就将style的背景变成棕色,将形状设置成靠背椅。这个style就完成了。好了我现在将这个style应用在房子上,发现整个房子只有靠背椅被设计出来了,房子内其他的东西都没有被设计出来(比如墙啊,天花板啊,房间的颜色,大小,位置等)。由于房子和房间的设计太多了太复杂了,就被称为theme。像对View这种简单,少的设计称为style。为了减少开发者的复杂度,Android就自己设计了几款房子的样式供我们使用。

style的组成及工作原理

基本组成

当我们创建一个项目的时候,会在res/values/路径下,默认生成style.xml这样的文件。并且提供了这样的默认样式。

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>


这些都是什么意思呢,作用是什么呢?

第一:< style > < / style>

创建style,就必须写在style.xml这个文件中。

并通过< style > < / style >标签来表示,创建了一个style

第二:< style >标签的属性

name:表示当前style的名字,调用方通过名字选择具体的style。

parent:表示该style继承了哪一个< style >。(说明style采用的是继承制,继承是继承父类的子标签,也就是< item >。)只有继承Android自带的theme的style,才能被称为theme。

第三:< style >的子标签< item >

表示默认值的名字,及其值。一个样式中,会存在各种各样的默认值设置。(比如说,宽度,高度,字体大小,字体颜色等)这些每个的默认值,都由一个item表示。

name:默认值的名字,当然这些name可不是随便设置的

第一种方式:android已经为我们自定义了一堆item了

比如平时经常在layout中针对View使用到的

android:layout_width="xx"
android:layout_height="xx"
android:background="xx"
...


其实是item的一部分,也可以在< style >中使用,这些属性主要是针对View的设置。其他一部分,比如说针对Activity的ActionBar的样式修改

就只能在< style >中使用了。

第二种方式:自定义item (这个先放一放,在创建Style技巧的时候进行说明)

工作原理

首先,android本身自带了一堆style,和定义的item。这些style被称作为theme。也就是说都是被Application和Activity使用的。

(关于style的种类,及其< item >属性太多了,只能自己翻阅相关文档了 = =)

其次,当然android自带的theme可能有些item设置的默认属性不符合我们的要求,我们想要去修改它,那么如何修改这些item呢?

从组成中我们知道,style采用的是继承制(原理跟java的继承是一样的就不多说了),通过< style >的parent的属性继承其他style。与java同样我们只需要对想修改的部分进行重写就可以了。在java中是子类与父类的方法名相同,在style中是子style中< item >的 name属性与父style中< item > 的 name属性相同。

例:

<!--继承父类   parent="Theme.AppCompat.Light.DarkActionBar"-->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<!--重写background-->
<item name="android:background">#343434</item>
</style>


这样一个符合自己风格的默认样式就诞生了。

最后,就是控件获取该样式,并进行判断是否使用该样式中的默认值。

style的使用及优点

说了这么多理论,大家现在肯定是云里雾里的,现在就快快上手使用style吧。

style设置在Application上

当我们创建一个项目的时候。会在res/values/路径下,默认生成style.xml这样的文件。并且提供了这样的默认样式。

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>


最后在AndroidManifest.xml下,通过 android:theme=”@style/AppTheme”设置为Application的< style >

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--以下省略-->
...
</application>


从中我们可以了解到:

创建App的时候Android自动生成默认的style并设置在Applicaion上。

该< style >必须是一个theme,也就是必须继承Android自带的< style >

在res/values/styles.xml中创建< style >

在AndroidManifest.xml中通过设置< application >标签的 android:theme=”@style/AppTheme”属性设置Application的style。

style设置于Activity上

首先制作一个style

<style name="MyBackground" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:background">@color/black</item>
</style>


解析:

创建一个style,取名为MyBackground,并继承名为DarkActionBar的style,并重写父style中名为android:background的< item >,默认值为黑色。

将style添加到Activity上

与Application用法差不多,在AndroidManifest.xml中对< activity >标签设置属性 android:theme=”@style/MyBackground”

<activity
android:name=".FirstActivity"
android:theme="@style/MyBackground">
</activity>


这样我们就创建出于其他Activity样式不同的Activity了。

作用一:创建个性样式,并且Activity内部的元素可选择使用该属性)

style设置于Layout(ViewGroup)和View上

由于ViewGroup与View设置style方法是一样的,所以就以更典型的View为例子。

我们在制作过程中可能会遇到这样的版面



原始代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/umeng_white"
android:gravity="center">
<Button
android:layout_width="fill_parent"
android:layout_height="38dp"
android:id="@+id/sinainfo"
android:textColor="@color/umeng_white"
android:background="@color/umeng_blue"
android:layout_marginTop="10dp"
android:text="@string/umeng_socialize_text_sina_key"/>
<Button
android:layout_width="fill_parent"
android:layout_height="38dp"
android:id="@+id/qqinfo"
android:textColor="@color/umeng_white"
android:background="@color/umeng_blue"
android:layout_marginTop="10dp"
android:text="@string/umeng_socialize_text_qq_key"/>
<!--下面的代码就不贴了,跟上面写法是一样的-->
</LinearLayout>


遇到这种,每个Button样式都是重复的,大家肯定不愿意一个一个去写吧。

这个简单啊,我只要写完第一个Button,之后的Button复制粘贴出来,不同的部分简单改一改就可以了,没什么麻烦的呀 0 0。

的确可以这样子,但是如果今天老板说我觉得蓝色的背景不好看,你把它换成红色的 - -,那么没办法,只能把background复制粘贴七次。有没有更具有可复用性,减少代码修改次数的方法呢?

第一种方法:将Button设置为一个layout,然后通过include添加到activity的layout中,最后通过代码动态初始化或者修改(由于本文讲的是style,就不过多的描述了)

第二种方法:将重复的部分设置为一个style

首先判断重复的部分,然后在values/style.xml中创建style

<!--因为针对的是View,所以不用设置为theme,parent属性也就不需要了-->
<style name="MyButton">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">38dp</item>
<item name="android:textColor">@color/umeng_white</item>
<item name="android:background">@color/umeng_blue</item>
<item name="android:layout_marginTop">10dp</item>
</style>


然后将该style设置在Button上,通过使用style=”@style/xxx”

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/umeng_white"
android:gravity="center">

<Button
style="@style/MyButton"
android:id="@+id/sinainfo"
android:text="@string/umeng_socialize_text_sina_key"/>
<Button
style="@style/MyButton"
android:id="@+id/qqinfo"
android:text="@string/umeng_socialize_text_qq_key"/>
<!--下面的代码就不贴了,跟上面样式是一样的-->
</LinearLayout>


作用二:简化样式的可复用性和减少控件的修改次数。

创建Style的技巧

style的命名

在App默认生成的< style >,不知道大家有没有对其中Theme的命名方式感到奇怪parent=”Theme.AppCompat.Light.DarkActionBar”

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!--省略-->
</style>


我们一般命名都是用XXX来表示,而这个Theme的命名则是XXX.XXX.XXX,中间含有多个”.”号。这是为什么呢?其实这是style一种具有风格的命名方式。’

例如

<style name="AppTheme.Blue" parent="AppTheme">
<item android:background>@color/blue</item>
</style>


name表达的意思就是,这个style是AppTheme的子类,且功能是Blue。

这样就很好的表达出了,该style的关系和作用。并且在寻找该style的时候,可以根据这个继承链来查找,相对于只是用Blue或者AppThemeBlue来表示会好很多。

比如说,刚才都是按钮的图,太难看了,背景都是蓝色的。我想改成蓝黄相间的。像这样

效果图:



<!--将刚才制作的MyButton中的android:background="xx"删除-->
<style name="MyButton">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">38dp</item>
<item name="android:textColor">@color/umeng_white</item>
<item name="android:layout_marginTop">10dp</item>
</style>


基于MyButton制作两份颜色不同的style

<style name="MyButton.Blue" parent="MyButton">
<item name="android:background">@color/blue</item>
</style>

<style name="MyButton.Red" parent="MyButton">
<item name="android:background">@color/red</item>
</style>


在layout中使用

<Button
style="@style/MyButton.Blue"
android:id="@+id/sinainfo"
android:text="@string/umeng_socialize_text_sina_key"/>
<Button
style="@style/MyButton.Red"
android:id="@+id/qqinfo"
android:text="@string/umeng_socialize_text_qq_key"/>


总结: style有两种命名方式

直接只用XxxXxx来命名。(一般在创建没有父类的style的时候使用)

使用Xxx.Xxx.Xxx来命名。(一般在创建具有父类的style的时候使用)

自定义item

Android自定的item只是一些基础的item,我们在开发中经常会有特殊的需求,基础的item完全不够我们使用怎么办,这个时候我们就需要用到自定义的item了

创建自定义的item

在values文件夹下,创建attr_xxx.xml的文件,xxx的名字可以自己写,但一定要带上attr,表名身份。

<resources>
<attr name="test" format="reference"></attr>
</resources>


使用标签< attr > 表示声明一个item。

参数声明:

name:顾名思义,表示该item的名字

format:表示该item 接收的类型。关于有哪些类型,拿编译器的字符提示,一看就明白了。

就说下reference,reference可以表示持有任意类型,一般在需要用到例如(@style/xxx,@string/xxx)这样还需要引用其他文件参数的书写格式的时候使用该参数。

例如:

<style name="test">
<item name="test">@string/test</item>
<style>


如果使用format=”string”,上面的style是会报错的,因为string不接受这样的引用字段。

当然如果不在attr_xxx.xml文件中,声明一个< item >为test的话,就表示该< style > 中的 < item >是无效的,也会报错。

使用自定义的item

我们在前面讲述过,声明与使用是两回事。我们现在声明好了item,那么怎么使用item到控件中去呢?我们知道android自带的item能够使用,是因为google编写原生控件的时候,选择接收了其自带的item,并使用在控件上。

第一种方式

就是通过自定义控件,获取自定义item的值,并设置在控件上。由于本文主讲style不涉及自定义View,详细内容同样可以参考鸿洋:深入理解Android中的自定义属性

第二种方式:

在layout中通过使用?attr/xxx来利用item。

同样是以Button的背景为例。为了方便起见,就自定义背景和字体的颜色。

<resources>
<attr name="myButtonBackground" format="reference"></attr>
<attr name="myButtonTextColor" format="reference"></attr>
</resources>


将自定义的item,用于style中

<style name="AppTheme"  parent="Theme.AppCompat.Light.NoActionBar">

<!--以上省略自带的item-->

<!--使用自定义的item-->
<item name="myButtonBackground">@color/umeng_blue</item>
<item name="muButtonTextColor">@color/umeng_white</item>
</style>

<!--删除android:background和android:textColor-->
<style name="MyButton">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">38dp</item>
<item name="android:layout_marginTop">10dp</item>
</style>


这里大家肯定有一个疑惑,为什么自定义的item必须在AppTheme中使用,不是应该在MyButton中使用吗,不是用来设置Button的颜色的吗?

这是因为自定义的Item必须设置在Application或者Activity使用的style中才有效,因为App默认是使用AppTheme作为Application的Item的,所以自定义Item应该放在AppTheme下。

在layout中使用自定义Item

<Button
style="@style/MyButton"
android:id="@+id/sinainfo"
android:background="?attr/myButtonBackground"
android:textColor="?attr/myButtonTextColor"
android:text="@string/umeng_socialize_text_sina_key"/>

<Button
style="@style/MyButton"
android:id="@+id/qqinfo"
android:background="?attr/myButtonBackground"
android:textColor="?attr/myButtonTextColor"
android:text="@string/umeng_socialize_text_qq_key"/>


例子中android:background通过?attr/myButtonBackground获取item的值。 (- - 这不就相当于< item > myButtonBackground将自己的值转交给了< item > android:background吗,这样做不是多此一举吗)的确是这样的,这是因为这个方法并不是为了方便使用,自定义Item的作用体现在动态切换Application或Activity的style时

比如:现在老板又说,我觉得这个界面加上夜间模式会对大众更友好一点。

那么我们就要创建两个Theme,一个是日间模式,一个是夜间模式。

<style name="AppTheme"  parent="Theme.AppCompat.Light.NoActionBar">
<!--省略自带的item参数-->
</style>

<!--日间模式-->
<style name="AppTheme.Light" parent="AppTheme">
<item name="myButtonBackground">@color/umeng_blue</item>
<item name="myButtonTextColor">@color/umeng_white</item>
</style>

<!--夜间模式-->
<style name="AppTheme.Night" parent="AppTheme">
<item name="myButtonBackground">@color/gray</item>
<item name="myButtonTextColor">@color/umeng_white</item>
</style>


最后,通过Button,动态切换Activity的Theme

效果图:



相关代码:

public class UserinfoActivity extends Activity implements OnClickListener{
private static final String BUNDLE_ISNIGHT = "bundle_isNight";

private Button mBtnNightMode;

//判断当前Theme类型
private boolean isNight = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*重启获取数据*/
if (savedInstanceState != null){
isNight = savedInstanceState.getBoolean(BUNDLE_ISNIGHT);
}
//设置相应的Theme,必须设置在setContentView()之前,否则无效
if (!isNight){
setTheme(R.style.AppTheme_Light);
}
else {
setTheme(R.style.AppTheme_Night);
}

setContentView(R.layout.app_user);

mBtnNightMode = (Button)findViewById(R.id.nightMode);

mBtnNightMode.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View view){
//切换当前模式
if (!isNight){
isNight = true;
mBtnNightMode.setText("夜间模式");
}
else {
isNight = false;
mBtnNightMode.setText("日间模式");
}
//重启Activity
UserinfoActivity.this.recreate();
}
});
}

@Override
protected void onSaveInstanceState(Bundle outState) {
//保存当前theme的类型
outState.putBoolean(BUNDLE_ISNIGHT,isNight);
}
}


改变Acitivty的Theme的同时,改变Activity内部元素的风格,这才是自定义Item的第二种用处。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android