1. 程式人生 > >載入自定義屬性實現app換膚功能

載入自定義屬性實現app換膚功能

在各大app中的換膚換主題的功能實現。博主的理解就是一種當用戶點選更換主題按鈕,從伺服器下載主題。這種就是外掛化載入。另一種就是自定義多套的屬性,當用戶點選的時候,就通過反射機制,在達到更換主題面板的效果。

下面,就通過一個小例子來實現換膚的功能,初次嘗試,如有紕漏的地方望大神指出,多多交流大笑大笑

1:在values資料夾下新建attr.xml檔案(這裡就拿顏色變化來說,當然也可以定義圖片資源。)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="main_bg" format="reference|color"/>
    <attr name="main_textcolor" format="reference|color"/>
    <attr name="other_bg" format="reference|color"/>
    <attr name="other_textcolor" format="reference|color"/>

</resources>
2:在styles.xml中定義2套主題(裡面的color值我用漢語拼音寫的,通俗易懂)
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--  主題A  -->
    <style name="theme_a" >
        <item name="main_bg">@color/bai</item>
        <item name="main_textcolor">@color/hei</item>
        <item name="other_bg">@color/bai</item>
        <item name="other_textcolor">@color/hei</item>
    </style>

    <!--  主題B -->
    <style name="theme_b" >
        <item name="main_bg">@color/hei</item>
        <item name="main_textcolor">@color/bai</item>
        <item name="other_bg">@color/hei</item>
        <item name="other_textcolor">@color/bai</item>
    </style>
</resources>

3:maniactivity的佈局檔案layout:
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?attr/main_bg"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">


    <Button
        android:id="@+id/btn_a"
        android:text="跳轉"
        android:layout_centerInParent="true"
        android:textColor="?attr/main_textcolor"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn_b"
        android:layout_centerHorizontal="true"
        android:text="換膚"
        android:layout_below="@id/btn_a"
        android:layout_marginTop="30dip"
        android:textColor="?attr/main_textcolor"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

注意其中自定義屬性的引用:
android:textColor="?attr/main_textcolor"
效果圖:



兩個按鈕,跳轉是跳到另一個activity中。來模擬點選換膚的時候app其他的頁面是否也做了變換。

另一個activity的佈局很簡單,這裡直接給出效果圖


這裡注意仔細看,預設的顏色第一個activity背景是白色,按鈕上面的字型顏色是黑色,第二個activity背景是白色,字型顏色是黑色

3:在來看activity類的程式碼

/**
 * Created by W61 on 2016/11/25.
 */

public class BaseActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SpUtils.init(this, "henry_theme");
        if(SpUtils.getInt("theme", 0) == 1) {
            setTheme(R.style.theme_b);
        } else {
            setTheme(R.style.theme_a);
        }
    }
}

public class MainActivity extends BaseActivity {

    private Button btn_a,btn_b;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        btn_a = (Button) findViewById(R.id.btn_a);
        btn_a.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this,OtherActivity.class));
            }
        });
        btn_b = (Button) findViewById(R.id.btn_b);
        btn_b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(SpUtils.getInt("theme", 0) == 1) {
                    SpUtils.setInt("theme", 0);
                    setTheme(R.style.theme_a);
                } else {
                    SpUtils.setInt("theme", 1);
                    setTheme(R.style.theme_b);
                }
                final View rootView = getWindow().getDecorView();
                ColorUiUtil.changeTheme(rootView, getTheme());
                startActivity(new Intent(MainActivity.this,MainActivity.class));
                finish();
            }
        });
    }

建了一個baseactivity便於統一管理,sputils是儲存當前到底是那套面板的標識。大家可以自由發揮

注意:

startActivity(new Intent(MainActivity.this,MainActivity.class));
finish();
博主這個寫這句程式碼是想點選換膚後當前頁面也隨之重新整理。這裡如果大家有更好的方法可以留言指出。

下面是一個幫助類(這個類拷貝網上大神寫的方法)

/**
 * Created by chengli on 15/6/10.
 */
public class ColorUiUtil {
    /**
     * 切換應用主題
     *
     * @param rootView
     */
    public static void changeTheme(View rootView, Resources.Theme theme) {
        if (rootView instanceof ColorUiInterface) {
            ((ColorUiInterface) rootView).setTheme(theme);
            if (rootView instanceof ViewGroup) {
                int count = ((ViewGroup) rootView).getChildCount();
                for (int i = 0; i < count; i++) {
                    changeTheme(((ViewGroup) rootView).getChildAt(i), theme);
                }
            }
            if (rootView instanceof AbsListView) {
                try {
                    Field localField = AbsListView.class.getDeclaredField("mRecycler");
                    localField.setAccessible(true);
                    Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear", new Class[0]);
                    localMethod.setAccessible(true);
                    localMethod.invoke(localField.get(rootView), new Object[0]);
                } catch (NoSuchFieldException e1) {
                    e1.printStackTrace();
                } catch (ClassNotFoundException e2) {
                    e2.printStackTrace();
                } catch (NoSuchMethodException e3) {
                    e3.printStackTrace();
                } catch (IllegalAccessException e4) {
                    e4.printStackTrace();
                } catch (InvocationTargetException e5) {
                    e5.printStackTrace();
                }
            }
        } else {
            if (rootView instanceof ViewGroup) {
                int count = ((ViewGroup) rootView).getChildCount();
                for (int i = 0; i < count; i++) {
                    changeTheme(((ViewGroup) rootView).getChildAt(i), theme);
                }
            }
            if (rootView instanceof AbsListView) {
                try {
                    Field localField = AbsListView.class.getDeclaredField("mRecycler");
                    localField.setAccessible(true);
                    Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear", new Class[0]);
                    localMethod.setAccessible(true);
                    localMethod.invoke(localField.get(rootView), new Object[0]);
                } catch (NoSuchFieldException e1) {
                    e1.printStackTrace();
                } catch (ClassNotFoundException e2) {
                    e2.printStackTrace();
                } catch (NoSuchMethodException e3) {
                    e3.printStackTrace();
                } catch (IllegalAccessException e4) {
                    e4.printStackTrace();
                } catch (InvocationTargetException e5) {
                    e5.printStackTrace();
                }
            }
        }
    }

下面來看最終執行圖:


大家可以仔細觀察點選換膚前和換膚後的背景顏色和字型顏色。

這裡建議大家在點選換膚時彈出一個載入框。來緩衝一下當前介面的重新整理。