android app换肤(更换主题)

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有以下说明:
/**
 * Attach a custom Factory interface for creating views while using
 * this LayoutInflater. This must not be null, and can only be set once;
 * after setting, you can not change the factory.
 *
 * @see LayoutInflater#setFactory(android.view.LayoutInflater.Factory)
 */
大致意思是说这个方法在系统绘制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,遇到这些控件都报异常,暂时只能以其他常用控件代替之


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值