• Android 实现切换主题皮肤功能(类似于众多app中的 夜间模式,主题包等)


    首先来个最简单的一键切换主题功能,就做个白天和晚上的主题好了。

    先看我们的styles文件:

     
     1 <resources>
     2 
     3     <!-- Base application theme. -->
     4     <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
     5         <!-- Customize your theme here. -->
     6         <item name="colorPrimary">@color/colorPrimary</item>
     7         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
     8         <item name="colorAccent">@color/colorAccent</item>
     9     </style>
    10     <style name="AppTheme.NoActionBar">
    11         <item name="windowActionBar">false</item>
    12         <item name="windowNoTitle">true</item>
    13     </style>
    14     <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
    15     <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
    16 
    17     <!--白天主题-->
    18     <style name="dayTheme" parent="AppTheme">
    19           <item name="android:textColor">#525252</item>
    20         <item name="android:background">#f7f7f7</item>
    21     </style>
    22 
    23     <!--夜间主题-->
    24     <style name="nightTheme" parent="AppTheme">
    25         <item name="android:textColor">#868a96</item>
    26         <item name="android:background">#1e1e2a </item>
    27     </style>
    28 
    29 
    30 </resources>
     

    好,然后我们来看看主要activity

     
    package com.example.administrator.mainchangethemeapp;
    
    import android.content.SharedPreferences;
    import android.os.Bundle;
    import android.support.design.widget.FloatingActionButton;
    import android.support.design.widget.Snackbar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.util.Log;
    import android.view.View;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity {
    
        private TextView tv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            //sputils是對SharedPreferences的封裝,代碼就不上了,大家理解意思就行了
            if(SPUtils.get(this,"theme","dayTheme").equals("dayTheme"))
            {
                //默認是白天主題
                setTheme(R.style.dayTheme);
            }else
            {
                //否则是晚上主題
                setTheme(R.style.nightTheme);
            }
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
    
            tv=(TextView)this.findViewById(R.id.tv);
            tv.setOnClickListener(new View.OnClickListener(){
    
                @Override
                public void onClick(View v) {
                    if(SPUtils.get(MainActivity.this,"theme","dayTheme").equals("dayTheme"))
                    {
                        SPUtils.put(MainActivity.this,"theme","nightTheme");
                    }else
                    {
                        SPUtils.put(MainActivity.this, "theme", "dayTheme");
                    }
                    recreate();
                }
            });
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.menu_main, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            int id = item.getItemId();
    
            //noinspection SimplifiableIfStatement
            if (id == R.id.action_settings) {
                return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    }
     

    然后来看下效果:

    当然了,上面这个demo实际上是有缺陷的。有人会说了 你看人家新浪微博,那么多主题,你要是用这种方法,你这个app得有多大,

    能做成微博那样要用什么就去下载什么么?答案是可以的。其实方案也很简单。

    这种方案的思路就是,把主题包 额外做成一个apk,注意这个apk 是不会在桌面显示的。你只能在设置里的app列表里找到他。

    然后在你的主activity里 取这个apk里的资源 即可。这里我也把这种方案的代码写一遍。注意下载apk 安装apk的代码我就不写了。

    我们就假设 我们要切换的主题就是系统自带的主题就行了,我们的主activity 使用的是白天主题。然后我们的子app里面

    为了简化代码,我们就不放自定义主题了,就使用android studio 原始的代码,一步步来,

    先放上子app里的styles文件,其实这个里面一行代码都没更改,全是ide自己生成的:

     
     1 <resources>
     2 
     3     <!-- Base application theme. -->
     4     <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
     5         <!-- Customize your theme here. -->
     6         <item name="colorPrimary">@color/colorPrimary</item>
     7         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
     8         <item name="colorAccent">@color/colorAccent</item>
     9     </style>
    10     <style name="AppTheme.NoActionBar">
    11         <item name="windowActionBar">false</item>
    12         <item name="windowNoTitle">true</item>
    13     </style>
    14     <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
    15     <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
    16 
    17 
    18 </resources>
     

    然后就是子activity里的 配置文件manifest:

     
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.administrator.sonproject" >
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" >
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:theme="@style/AppTheme.NoActionBar" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
     

    注意这里 

    <category android:name="android.intent.category.LAUNCHER" />

    的这行代码 我是没写的。这样就是保证在这个apk 安装好以后不会在桌面出现。
    然后我们来看看主activity代码
     
     1 package com.example.administrator.mainchangethemeapp;
     2 
     3 import android.content.Context;
     4 import android.content.SharedPreferences;
     5 import android.content.pm.PackageManager;
     6 import android.content.res.TypedArray;
     7 import android.os.Bundle;
     8 import android.support.design.widget.FloatingActionButton;
     9 import android.support.design.widget.Snackbar;
    10 import android.support.v4.app.Fragment;
    11 import android.support.v7.app.AppCompatActivity;
    12 import android.support.v7.widget.Toolbar;
    13 import android.util.Log;
    14 import android.util.TypedValue;
    15 import android.view.View;
    16 import android.view.Menu;
    17 import android.view.MenuItem;
    18 import android.widget.TextView;
    19 
    20 public class MainActivity extends AppCompatActivity {
    21 
    22     private TextView tv;
    23 
    24     @Override
    25     protected void onCreate(Bundle savedInstanceState) {
    26         //sputils是對SharedPreferences的封裝,代碼就不上了,大家理解意思就行了
    27         if (SPUtils.get(this, "theme", "dayTheme").equals("dayTheme")) {
    28             //默認是白天主題
    29             setTheme(R.style.dayTheme);
    30         } else {
    31             //否则是晚上主題,這裡晚上主題我們就去加載我們晚上主題apk里的資源
    32             int resourceId = getResourceId(getPackageContext(this, "com.example.administrator.sonproject"), "style", "AppTheme");
    33             setTheme(resourceId);
    34         }
    35         super.onCreate(savedInstanceState);
    36         setContentView(R.layout.activity_main);
    37         tv = (TextView) this.findViewById(R.id.tv);
    38         tv.setOnClickListener(new View.OnClickListener() {
    39 
    40             @Override
    41             public void onClick(View v) {
    42                 if (SPUtils.get(MainActivity.this, "theme", "dayTheme").equals("dayTheme")) {
    43                     SPUtils.put(MainActivity.this, "theme", "nightTheme");
    44                 } else {
    45                     SPUtils.put(MainActivity.this, "theme", "dayTheme");
    46                 }
    47                 recreate();
    48             }
    49         });
    50     }
    51 
    52     @Override
    53     public boolean onCreateOptionsMenu(Menu menu) {
    54         // Inflate the menu; this adds items to the action bar if it is present.
    55         getMenuInflater().inflate(R.menu.menu_main, menu);
    56         return true;
    57     }
    58 
    59     @Override
    60     public boolean onOptionsItemSelected(MenuItem item) {
    61         // Handle action bar item clicks here. The action bar will
    62         // automatically handle clicks on the Home/Up button, so long
    63         // as you specify a parent activity in AndroidManifest.xml.
    64         int id = item.getItemId();
    65 
    66         //noinspection SimplifiableIfStatement
    67         if (id == R.id.action_settings) {
    68             return true;
    69         }
    70 
    71         return super.onOptionsItemSelected(item);
    72     }
    73 
    74 
    75     /**
    76      * 获取其他apk的context
    77      * @param context
    78      * @param packageName
    79      * @return
    80      */
    81     public static Context getPackageContext(Context context, String packageName) {
    82         try {
    83             return context.createPackageContext(packageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
    84         } catch (PackageManager.NameNotFoundException e) {
    85             e.printStackTrace();
    86         }
    87         return null;
    88     }
    89 
    90     //获取指定context的 type里面的 name属性
    91     public static int getResourceId(Context context, String type, String name) {
    92         return context.getResources().getIdentifier(name, type, context.getPackageName());
    93     }
    94 }
     
    
    

    你看,到这里 我们就实现了,动态加载主题的方式了。当然了,到这里依旧是不完美的,因为settheme方法大家都知道一定要重启activity才有效啊。这样就不好了。

    体验并非最佳。总是给人很突兀的感觉。而且我们都知道重启activity的成本很大。要考虑很多生命周期之类的东西。那我们继续往下看,看看有什么比较好的解决方案能解决这个问题。

    首先 我们可以考虑一下这个问题,所谓的切换主题 之类的,无非就是把你那些控件的 背景色啊 字体颜色之类的 改变了一下。对于一个app来说,用户一般只有一个界面 会有切换主题的这个入口,

    换句话说,我们的app里面 只有这一个activity 需要实现 不重启activity就切换 控件style的功能,其他activity我们是不需要实现这个功能的,因为再切换过去的时候基本上都会走oncreate。所以

    我们只需要考虑这个 有切换主题按钮的 这个activity能实现 不重启activity就换皮肤的功能就可以了。其他activity不需要考虑。so 这样一想 这个功能就简单清晰了很多。

    首先我们可以自定义2个属性,我们把他放在attrs 这个xml里面

     
    1 <?xml version="1.0" encoding="utf-8"?>
    2 <resources>
    3     <!-- 首先自定义属性,这里我们为了简便 只自定义了这2种属性,如果你要自己做的话 看需求可以增加属性-->
    4     <!-- 另外注意我们这里并没有使用declare-styleable  来包裹这里的2个属性 好处就是在xml文件里 不用另外定义前缀了-->
    5     <attr name="custom_background" format="reference|color"/>
    6     <attr name="custom_textcolor" format="reference|color"/>
    7 </resources>
     

    然后我们去定义一下我们的主题:

     
     1 <resources>
     2 
     3     <!-- 这里就设置2个最简单的 主题就行了,-->
     4     <!--白天主题-->
     5     <style name="dayTheme" >
     6         <item name="custom_background">#f7f7f7</item>
     7         <item name="custom_textcolor">#525252</item>
     8     </style>
     9 
    10     <!--晚上主题-->
    11     <style name="nightTheme">
    12         <item name="custom_background">#1e1e2a</item>
    13         <item name="custom_textcolor">#868a96</item>
    14     </style>
    15 </resources>
     

    然后我们来写一下mainactivity的xml布局文件,我们假设这个布局是非常简单的:

     
     1 <?xml version="1.0" encoding="utf-8"?>
     2 <com.example.administrator.mainchangethemeapp.ThemeRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:background="?attr/custom_background"
     6     android:id="@+id/mainview"
     7     >
     8 
     9     <com.example.administrator.mainchangethemeapp.ThemeTextView
    10         android:id="@+id/tv"
    11         android:layout_width="100dp"
    12         android:layout_height="100dp"
    13         android:layout_centerInParent="true"
    14         android:text="一鍵切換主題"
    15         android:background="?attr/custom_background"
    16         android:textColor="?attr/custom_textcolor"
    17         />
    18 
    19 </com.example.administrator.mainchangethemeapp.ThemeRelativeLayout>
     

    你看这个布局里面 就一个relativelayout和一个textview。 和以前xml唯一的区别就是这里backaground和textcolor使用的值 是我们前面定义好的属性了(但是要注意 这属性是没有值的 赋值的操作放在java代码里实现)。同时这2个控件 也并非是系统控件 而是自定义控件。然后我们看看这个自定义控件是怎么写的。注意我这里就做了2个自定义控件,如果你们的那个切换主题的入口页面里面有其他控件的话,就要学着下面的方法 自己拓展一下了,其实也很简单的。

    首先呢,我们来定义一个接口,谁实现了这个接口 就说明这个控件可以不启动activity直接换肤!

     
     1 package com.example.administrator.mainchangethemeapp;
     2 
     3 import android.content.res.Resources;
     4 import android.view.View;
     5 
     6 /**
     7  * Created by Administrator on 2015/11/14.
     8  */
     9 public interface ThemeUIInterface {
    10 
    11     public View getView();
    12     public  void setTheme(Resources.Theme themeId);
    13 }
     

    然后我们来看看2个自定义控件怎么写:

     
     1 package com.example.administrator.mainchangethemeapp;
     2 
     3 import android.content.Context;
     4 import android.content.res.Resources;
     5 import android.util.AttributeSet;
     6 import android.util.Log;
     7 import android.view.View;
     8 import android.widget.RelativeLayout;
     9 
    10 /**
    11  * Created by Administrator on 2015/11/14.
    12  */
    13 public class ThemeRelativeLayout extends RelativeLayout implements ThemeUIInterface{
    14 
    15     private int attr_background = -1;
    16 
    17     public ThemeRelativeLayout(Context context) {
    18         super(context);
    19     }
    20 
    21     public ThemeRelativeLayout(Context context, AttributeSet attrs) {
    22         super(context, attrs);
    23         this.attr_background =ViewAttributeUtil.getBackgroundAttibute(attrs);
    24     }
    25 
    26     public ThemeRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    27         super(context, attrs, defStyleAttr);
    28         this.attr_background =ViewAttributeUtil.getBackgroundAttibute(attrs);
    29 
    30     }
    31 
    32 
    33     @Override
    34     public View getView() {
    35         return this;
    36     }
    37 
    38     @Override
    39     public void setTheme(Resources.Theme themeId) {
    40         if(attr_background!=-1) {
    41             ViewAttributeUtil.applyBackgroundDrawable(this, themeId, attr_background);
    42         }
    43     }
    44 }
     
     
     1 package com.example.administrator.mainchangethemeapp;
     2 
     3 import android.content.Context;
     4 import android.content.res.Resources;
     5 import android.util.AttributeSet;
     6 import android.view.View;
     7 import android.widget.TextView;
     8 
     9 /**
    10  * Created by Administrator on 2015/11/16.
    11  */
    12 public class ThemeTextView extends TextView implements ThemeUIInterface{
    13 
    14     private int attr_drawable=-1;
    15     private int attr_textColor=-1;
    16 
    17     public ThemeTextView(Context context) {
    18         super(context);
    19     }
    20 
    21     public ThemeTextView(Context context, AttributeSet attrs) {
    22         super(context, attrs);
    23         this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs);
    24         this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs);
    25     }
    26 
    27     public ThemeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    28         super(context, attrs, defStyleAttr);
    29         this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs);
    30         this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs);
    31     }
    32 
    33     @Override
    34     public View getView() {
    35         return this;
    36     }
    37 
    38     @Override
    39     public void setTheme(Resources.Theme themeId) {
    40         if (attr_drawable != -1) {
    41             ViewAttributeUtil.applyBackgroundDrawable(this, themeId, attr_drawable);
    42         }
    43         if (attr_textColor != -1) {
    44             ViewAttributeUtil.applyTextColor(this, themeId, attr_textColor);
    45         }
    46     }
    47 }
     

    看上去 其实也蛮简单的对吧,无非就相比传统控件,他对外暴露了 setTheme这个方法罢了,而这个setTheme方法 其实就做了一件事,调用系统自己的方法重新set那些属性罢了。

     
     1 package com.example.administrator.mainchangethemeapp;
     2 
     3 
     4 import android.content.res.Resources;
     5 import android.content.res.TypedArray;
     6 import android.graphics.drawable.Drawable;
     7 import android.util.AttributeSet;
     8 import android.util.Log;
     9 import android.widget.ImageView;
    10 import android.widget.TextView;
    11 
    12 public class ViewAttributeUtil {
    13 
    14     public static int getAttributeValue(AttributeSet attr, int paramInt) {
    15         int value = -1;
    16         int count = attr.getAttributeCount();
    17         for(int i = 0; i <count;i++) {
    18             if(attr.getAttributeNameResource(i) == paramInt) {
    19                 String str = attr.getAttributeValue(i);
    20                 if(null != str && str.startsWith("?")) {
    21                     value = Integer.valueOf(str.substring(1, str.length())).intValue();
    22                     return value;
    23                 }
    24             }
    25         }
    26         return value;
    27     }
    28 
    29     public static int getBackgroundAttibute(AttributeSet attr) {
    30         return getAttributeValue(attr , android.R.attr.background);
    31     }
    32 
    33 
    34     public static int getTextColorAttribute(AttributeSet attr) {
    35         return getAttributeValue(attr, android.R.attr.textColor);
    36     }
    37 
    38     public static void applyBackgroundDrawable(ThemeUIInterface ci, Resources.Theme theme, int paramInt) {
    39         TypedArray ta = theme.obtainStyledAttributes(new int[]{paramInt});
    40         Drawable drawable = ta.getDrawable(0);
    41         if(null != ci) {
    42             (ci.getView()).setBackgroundDrawable(drawable);
    43         }
    44         ta.recycle();
    45     }
    46     public static void applyTextColor(ThemeUIInterface ci, Resources.Theme theme, int paramInt) {
    47         TypedArray ta = theme.obtainStyledAttributes(new int[]{paramInt});
    48         int resourceId = ta.getColor(0,0);
    49         if(null != ci && ci instanceof TextView) {
    50             ((TextView)ci.getView()).setTextColor(resourceId);
    51         }
    52         ta.recycle();
    53     }
    54 
    55 }
     

    好,到这里 脉络就逐渐清晰了,我们就看看主activity里怎么写了:

     
     1 package com.example.administrator.mainchangethemeapp;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 import android.view.Menu;
     6 import android.view.MenuItem;
     7 import android.view.View;
     8 import android.widget.TextView;
     9 
    10 public class MainActivity extends Activity {
    11 
    12     private TextView tv;
    13 
    14     private View view;
    15 
    16     @Override
    17     protected void onCreate(Bundle savedInstanceState) {
    18         //这行代码千万不能漏掉,否则会报错的。因为你在xml里 的那些值 根本就没有实际的值,如果不在这里强制性的setTheme就直接报错了
    19         //在实际使用中 我们当然可以把这个方法 放在baseactivity里面。我们这里默认主题就白天吧
    20         setTheme(R.style.dayTheme);
    21         super.onCreate(savedInstanceState);
    22         setContentView(R.layout.activity_main);
    23         //这个就是跟布局
    24         view = this.findViewById(R.id.mainview);
    25         tv = (TextView) this.findViewById(R.id.tv);
    26         tv.setOnClickListener(new View.OnClickListener() {
    27 
    28             @Override
    29             public void onClick(View v) {
    30 
    31                 //这边逻辑没啥好说的 其实可以不需要看啊,就是白天就切换到黑夜 黑夜就切换到白天呗
    32                 if (SPUtils.get(MainActivity.this, "theme", "dayTheme").equals("dayTheme")) {
    33                     SPUtils.put(MainActivity.this, "theme", "nightTheme");
    34                     setTheme(R.style.nightTheme);
    35                 } else {
    36                     SPUtils.put(MainActivity.this, "theme", "dayTheme");
    37                     setTheme(R.style.dayTheme);
    38                 }
    39                 //这个方法就是实现不重启页面切换主题的
    40                 ThemeUiUtil.changeTheme(view, getTheme());
    41             }
    42         });
    43     }
    44 
    45     @Override
    46     public boolean onCreateOptionsMenu(Menu menu) {
    47         // Inflate the menu; this adds items to the action bar if it is present.
    48         getMenuInflater().inflate(R.menu.menu_main, menu);
    49         return true;
    50     }
    51 
    52     @Override
    53     public boolean onOptionsItemSelected(MenuItem item) {
    54         // Handle action bar item clicks here. The action bar will
    55         // automatically handle clicks on the Home/Up button, so long
    56         // as you specify a parent activity in AndroidManifest.xml.
    57         int id = item.getItemId();
    58 
    59         //noinspection SimplifiableIfStatement
    60         if (id == R.id.action_settings) {
    61             return true;
    62         }
    63 
    64         return super.onOptionsItemSelected(item);
    65     }
    66 
    67 }
     

    然后看一下切换主题的方法是怎么做的:

     
     1 package com.example.administrator.mainchangethemeapp;
     2 
     3 import android.content.res.Resources;
     4 import android.util.Log;
     5 import android.view.View;
     6 import android.view.ViewGroup;
     7 import android.widget.AbsListView;
     8 
     9 import java.lang.reflect.Field;
    10 import java.lang.reflect.InvocationTargetException;
    11 import java.lang.reflect.Method;
    12 
    13 /**
    14  * Created by Administrator on 2015/11/14.
    15  */
    16 public class ThemeUiUtil {
    17     /**
    18      * 切换应用主题
    19      *
    20      * @param rootView
    21      */
    22     public static void changeTheme(View rootView, Resources.Theme theme) {
    23         //這裡邏輯很簡單 就是递归调用changeTheme-----递归调用setTheme了。
    24         //注意 你们如果是listview也包含在里面的话 listview自定义实现接口的时候要稍微复杂一些,看你们需要不需要也刷新listview里的item了
    25         //这里为了简单 我就不写那么复杂了,就这一个逻辑:先set自己的theme 然后遍历自己的子控件 逐一set
    26         if (rootView instanceof ThemeUIInterface) {
    27             ((ThemeUIInterface) rootView).setTheme(theme);
    28             if (rootView instanceof ViewGroup) {
    29                 int count = ((ViewGroup) rootView).getChildCount();
    30                 for (int i = 0; i < count; i++) {
    31                     changeTheme(((ViewGroup) rootView).getChildAt(i), theme);
    32                 }
    33             }
    34         }
    35     }
    36 
    37 
    38 }
     

    你看,到这里 我们不重启activity实现换肤的功能就基本实现了,当然要做的完美的话 还需要各位自己扩充一下 其他控件。但是思路都是一样的。

    看到这里 有人仍然会说,你这个虽然没有重启activity,但是还是不好看呀,还是显的突兀了,能否做到知乎 android app那样 切换白天黑夜主题的时候 显的很柔顺呢。

    答案是可以的,而且解决方案也比较简单。就是给个动画就完事了,在切换前 先保留一下 之前界面的bitmap 然后切换皮肤的的时候 把这个bitmap 显示出来以后 然后改变他的

    alpha值 就可以了,当全部动画显示结束以后 把那些不需要的资源全部释放 就ok了!

    来看下代码,这里就放出点击事件的代码了,其他地方与前面的代码一致:

     
     1 {
     2 
     3                 //我们先取这个根布局的 bitmap缓存 这个实际上跟截屏是差不多的一个东西。
     4                 view.setDrawingCacheEnabled(true);
     5                 view.buildDrawingCache(true);
     6                 final Bitmap localBitmap = Bitmap.createBitmap(view.getDrawingCache());
     7                 view.setDrawingCacheEnabled(false);
     8 
     9                 //这边逻辑没啥好说的 其实可以不需要看啊,就是白天就切换到黑夜 黑夜就切换到白天呗
    10                 if (SPUtils.get(MainActivity.this, "theme", "dayTheme").equals("dayTheme")) {
    11                     SPUtils.put(MainActivity.this, "theme", "nightTheme");
    12                     setTheme(R.style.nightTheme);
    13                 } else {
    14                     SPUtils.put(MainActivity.this, "theme", "dayTheme");
    15                     setTheme(R.style.dayTheme);
    16                 }
    17                 //我们new出来的这个蒙版view --mengbanview 就把他放到跟布局view里面 并且让他充满 同时这个view的background就是截屏前我们的那个截图bitmap
    18                 final View mengbanView = new View(getApplicationContext());
    19                 mengbanView.setBackgroundDrawable(new BitmapDrawable(getResources(), localBitmap));
    20                 ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    21                 ((ViewGroup) view).addView(mengbanView, params);
    22                 mengbanView.animate().alpha(0).setDuration(400).setListener(new Animator.AnimatorListener() {
    23                     @Override
    24                     public void onAnimationStart(Animator animation) {
    25                         //这个方法就是实现不重启页面切换主题的
    26                         ThemeUiUtil.changeTheme(view, getTheme());
    27                     }
    28 
    29                     @Override
    30                     public void onAnimationEnd(Animator animation) {
    31                         //动画结束的时候移出这个蒙版view 并释放bitmap
    32                         ((ViewGroup) view).removeView(mengbanView);
    33                         localBitmap.recycle();
    34                     }
    35 
    36                     @Override
    37                     public void onAnimationCancel(Animator animation) {
    38 
    39                     }
    40 
    41                     @Override
    42                     public void onAnimationRepeat(Animator animation) {
    43 
    44                     }
    45                 }).start();
    46 
    47 
    48             }
     

    最后来看下效果是否和知乎一样:

    大功告成,看上去自然多了!

  • 相关阅读:
    Java异常处理设计(三)
    TS 3.1
    TS 3.1
    Other
    样式
    Other
    Other
    TS 3.1
    TS 3.1
    TS 3.1
  • 原文地址:https://www.cnblogs.com/android-blogs/p/4968941.html
Copyright © 2020-2023  润新知