app換膚主要是更換一下幾點:
1,狀态欄背景更換
2,标題欄背景更換
3,标題欄字型顔色更換
4,界面背景更換
5,界面字型顔色更換
這裡狀态欄更換隻支援android版本号大于20的
方法有兩種:
方法1:
setThem(int resid)設定,缺點是每次設定完都要重新開機該activity,才能應用主題
android項目建立時都會在styles.xml中生成類似以下的代碼
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>//标題欄顔色
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>//狀态欄顔色
<item name="colorAccent">@color/colorPrimary</item>//特殊醒目的顔色
<item name="android:windowBackground">@color/windowBg</item>//背景顔色
</style>
android系統有提供Theme設定的方法setTheme(@StyleRes final int resid),對應的調用方法setThem(R.style.AppTheme)
在上邊的基礎上我們就可建多個Theme,然後每個Activity都可以設定成不同的Theme
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme">
我們主要是目的是在一個Activity裡更換多種主題,那麼我們就不在manifest中設定Theme了改為在代碼中設定
public void myTheme(){
SharedPreferences sharedPreferences = context.getSharedPreferences(
APPTHEME, context.MODE_PRIVATE);
int currentTheme = sharedPreferences.getInt("theme_type", 3);
int theme ;
switch (currrentTheme) {
case 1:
theme = R.style.Theme1
break;
case 2:
theme = R.style.Theme2
break;
case 3:
theme = R.style.Theme3
break;
}
setThem(theme )
}
//選擇Theme時
public void changeTheme(int theme){
SharedPreferences sharedPreferences = context.getSharedPreferences(APPTHEME, context.MODE_PRIVATE);
sharedPreferences.edit().putInt("theme_type",theme).commit();
//重新開機activity
}
方法2:
步驟1:layout檔案把需要改變字型或背景顔色的控件做上标記
步驟2:在Activity繪制ui的時候将有标記的控件全部存儲到記憶體中
步驟3:根據标記的類型改變控件的背景,字型等以達到更換主題的效果
當然也有異曲同工的笨方法:在Activity中把需要更改字型或背景顔色的控件執行個體化,存儲起來,換膚時,逐個改變控件的設定
此笨方法需要在每個需要換膚的Activity中都做重複的工作,而且沒有技術含量,乃程式員之大忌,是以我們繼續我們這種優雅的換膚方法。
關于收集有标記的View,可以通過android 提供的setFactory(LayoutInflater inflater, LayoutInflaterFactory factory),關于該方法,android有以下說明:
大緻意思是說這個方法在系統繪制view時用到的,僅隻能使用一次
既然是在繪制view時用到,那麼view的各種屬性都可在這裡拿到,比如grivity,layout_height等。當然我們自己做的标記也能拿到
public class MyInflaterFactory implements LayoutInflaterFactory {
public static final String TAG = "MyInflaterFactory";
public static final String NAMESPACE = "http://schemas.android.com/android/skin";
public static final String ATTR_SKIN_TYPE = "theme_type";
public static final String BackGround = "BackGround";
public static final String TextColor = "TextColor";
public static final String BackGround_Primary = "BackGround|Primary";
public static final String ColorAccent = "ColorAccent";
private Context context;
private List<View> views = new ArrayList<>();
int[] colors = {0x03a9f4,0x038dcc,0xffffff,0x84ffff};//colorPrimary,colorPrimaryDark,TextColor,windowBg
public MyInflaterFactory(){
updateStatusBar();
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
this.context = context;
// 檢測目前View是否有更換皮膚的需求
// boolean isSkinEnable = attrs.getAttributeBooleanValue(NAMESPACE, ATTR_SKIN_ENABLE, false);
String changeType = attrs.getAttributeValue(NAMESPACE, ATTR_SKIN_TYPE);
if (TextUtils.isEmpty(changeType)) {
return null;//傳回空就使用預設的InflaterFactory
}
View view = createView(context,name, attrs);
if (view == null) {//沒有找到這個View
return null;
}
view.setTag(changeType);
views.add(view);//将有标記的控件存入數組,以便換膚
updateItemView(view,colors);//更新預設皮膚
return view;
}
public void setNewTheme(int[] colors){//外部換膚調用
this.colors = colors;
for(View view:views){
updateItemView(view,this.colors);
}
}
public int[] getThemeColors() {
return colors;
}
public int[] updateStatusBar(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
((Activity)context).getWindow().setStatusBarColor(colors[1]);//android版本号>=21改變狀态欄
}
((Activity)context).getWindow().setBackgroundDrawable(new ColorDrawable(colors[3]));//改變Activity背景顔色
return colors;
}
private View createView(Context context, String name, AttributeSet attrs) {
Log.i(TAG, "createView:" + name);
View view = null;
try {
if (-1 == name.indexOf('.')) {
if ("View".equals(name)) {
view = LayoutInflater.from(context).createView(name, "android.view.", attrs);
}
if (view == null) {
view = LayoutInflater.from(context).createView(name, "android.widget.", attrs);
}
if (view == null) {
view = LayoutInflater.from(context).createView(name, "android.webkit.", attrs);
}
} else {
view = LayoutInflater.from(context).createView(name, null, attrs);
}
} catch (Exception e) {
Log.e(TAG, "error while create 【" + name + "】 : " + e.getMessage());
view = null;
}
return view;
}
@SuppressLint("NewApi")
private void updateItemView(View v,int[]colors){
String type_color = (String) v.getTag();
if(type_color.equals(BackGround)){
if (v instanceof CardView) {//這裡對CardView特殊處理下
CardView cardView = (CardView) v;
cardView.setCardBackgroundColor(colors[1]);
} else {
v.setBackgroundColor(colors[1]);
}
}
else if(type_color.equals(TextColor)){
if(v instanceof TextView){
((TextView) v).setTextColor(colors[2]);
}else if (v instanceof EditText){
((EditText) v).setTextColor(colors[2]);
}
}
else if(type_color.equals(BackGround_Primary)){
v.setBackgroundColor(colors[0]);
}
if(type_color.equals(ColorAccent)){
v.setBackgroundColor(colors[2]);
}
}
}
Activity onCreate(...)方法中在調用setContentView()之前:
MyInflaterFactory factory = new MyInflaterFactory();
LayoutInflaterCompat.setFactory(getLayoutInflater(), factory);
布局檔案中做标記:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:skin="http://schemas.android.com/android/skin"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="zhao.edifier.com.mynotepaper.MainActivity">
<RelativeLayout
android:id="@+id/al_title"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
skin:theme_type="BackGround|Primary"
android:elevation="10dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="17sp"
android:text="@string/app_name"
android:padding="8dp"
skin:theme_type="TextColor"
/>
<ImageView
android:id="@+id/iv_more"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:src="@drawable/ic_more_withe"
android:padding="16dp"
android:scaleType="centerInside"
android:layout_centerVertical="true"
android:background="@drawable/recycler_bg"
/>
</RelativeLayout>
</RelativeLayout>
根目錄中的xmlns:skin="http://schemas.android.com/android/skin"
和其他skin:theme_type="XX"都是我們自己的标記
XX是 MyInflaterFactory中的
public static final String BackGround = "BackGround";
public static final String TextColor = "TextColor";
public static final String BackGround_Primary = "BackGround|Primary";
public static final String ColorAccent = "ColorAccent";
在點選按鈕換膚時調用factory.setNewTheme(int[] colors);
這種發法可以不用重新開機activity,的缺點是收集不到最新控件的view,比如:FloatingActionButton,和toolbar,遇到這些控件都報異常,暫時隻能以其他常用控件代替之