前言:
很多Android应用都提供不同的主题供用户选择。切换主题后,应用的背景,字体等会相应发生改变。这样的功能是怎么做的的呢?
在同一个界面,字体大小,颜色,背景等也可以有所不同,怎么设置和应用这些不同样式?
接下来,就让我们一起大概了解一下Android的主题。
一、Android主题(Theme)和样式(Style)简介
在Android应用资源目录下,res/values/目录有style.xml和theme.xml文件,在这两个xml中我们可以自定义需要的主题和样式,如果工程中没有这两个文件的话我们可以自己创建。
比如在styles.xml中自定义一个样式。文件完整代码如下:
<resources>
<style name="MyTheme" parent="@android:style/Theme.NoTitleBar">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textColor">#ffff00</item>
<item name="android:typeface">monospace</item>
<item name="stringValue">"hello"</item>
<item name="colorBackground">@android:color/holo_blue_light</item>
</style>
</resources>
这样一个简单的样式就定义好了。themes.xml中自定义样式的规则和styles.xml是一样的。
下面解释下这段代码:
1.style name=”MyTheme”` 指明了这个样式的名字为”MyTheme”。
2.parent="@android:style/Theme.NoTitleBar"
指明我们自定义的这个样式是继承自”Theme.NoTitleBar”这个系统预置样式。
注:我们自定义的样式必须继承自某个系统预置样式,即使不指明继承的是哪个样式,也会默认继承系统标准根样式。标准根样式视API的不同而有差别,比如API 14的默认根样式为Theme.DeviceDefault。
系统预置的样式定义位置为Android源码的frameworks/base/core/res/res/values目录下以下几个文件:
themes.xml,themes_device_defaults.xml,styles.xml,styles_device_defaults.xml
这几个文件中定义了各种系统Theme,Style。
3.fill_parent` 定义了在这个样式里,属性layout_width的值为fill_parent。也就是说,谁用了这个样式,谁的layout_width的属性值就是fill_parent, 其余的属性以类似方式定义。
其中有两个我自定义的属性“stringValue”和”colorBackground”,。
如何自定义属性呢?在res/values/目录有attrs.xml文件,用于自定义属性。
比如一个attrs.xml文件实例如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="colorValue" format="color" />
<attr name="floatValue" format="float" />
<attr name="integerValue" format="integer" />
<attr name="booleanValue" format="boolean" />
<attr name="dimensionValue" format="dimension" />
<attr name="stringValue" format="string" />
<attr name="colorBackground" format="reference" />
</resources>
这里面自定义了多种格式的属性,我上面用到的是后两个,一个是String类型,一个引是用类型的属性。
二、style.xml和theme.xml有什么区别?
1、使用的地方不同
1).theme.xml:应用于整个application或Activity,不能用于单独的View。
2) style.xml:用于单独的View。
2、在R.attr定义中以window开头的一些属性只对theme有效。
3、如果一个应用使用了theme,同时应用下的view也使用了style,那么当theme与样式style发生冲突时,style的优先级高于theme。
三、theme和style的用法:
1、AndroidManifest中的用法
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Black">
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
2、Activity的用法
<activity
android:theme="@style/styleTheme"
android:label="@string/app_name"
android:name=".HelloTietuActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
3、Android控件的用法
<EditText android:layout_height="wrap_content"
android:text="EditText"
style="@style/Title"
android:layout_width="fill_parent"
android:id="@+id/editText1"/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="?attr/stringValue"/>
4、代码中动态设置:
如果使用setTheme()来动态设置主题,那么setTheme()应该在初始化任何View之前调用。比如在setContentView(View) 和inflate(int, ViewGroup)方法前调用。
如果要使用动态切换主题,那么就必须调用actvity.finish()。然后再重新加载setTheme()。
例子如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
setTheme(android.R.style.Theme_Light);
setContentView(R.layout.linear_layout_3);
}
四、apk主题
很多手机提供了主题商城,还有很多第三方主题可以下载安装。这些下载的主题一般也都是apk包,安装后可以在data/app/主题包名/目录下找到。
我们自己如何制作并应用这样一个apk主题包呢?下面用一个例子简单说明。
首先要建立两个工程,一个要切换主题的主应用MainAPP,另一个是主题包工程MyTheme。
1、新建android工程MyTheme,package为:com.test.mytheme,在MyTheme工程的drawable文件夹下放入back.png文件,用作背景图。然后打包成apk安装。
2、MainAPP工程的drawable文件夹下也放入一个图片back.png,与MyTheme工程的同名但不是同一个图。
布局文件layout_main.xml内容如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/layout"
android:background="@drawable/back"
tools:context="com.example.androidtest.MainActivity" >
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
可以看到我们给布局设置了背景图为back.png。
MainActivity的主要代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final LinearLayout layout = (LinearLayout)this.findViewById(R.id.layout);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
try {
Context appTheme = createPackageContext("com.test.mytheme", Context.CONTEXT_IGNORE_SECURITY);
Resources res = appTheme.getResources();
layout.setBackground(res.getDrawable(R.drawable.back,appTheme));
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
});
}
这样就实现了点击Button切换背景图的功能。
注意,上面我们用layout.setBackground(res.getDrawable(R.drawable.back,appTheme))设置新的背景,这个方法的参数在不同API中是不同的,如果这样不成功的话,可以改为layout.setBackground(res.getDrawable(R.drawable.back))。
在实际应用中,内置主题和apk主题一般是结合起来进行应用。在资源文件中预置几个主题,同时在代码中可以动态扩展更多主题。这样灵活性大,也易于维护。