• 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             }
     

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

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

  • 相关阅读:
    679 怎样杀死害虫?(对付一个系统最好的方式是“围城必阙”)
    678 "流浪地球"为什么是个好地方?(系统越复杂拥有好运气的机会也就越大)
    677 人类为什么会养猫?(做一件事理性的原因的背后往往还隐藏着自己都不曾发现的感性原因)
    职场人必知的三原则
    677 怎样当一个少数派?(越在意,越出众)
    675 为什么会有“黑天鹅”?(行为和对行为后果的负责与否决定了很多黑天鹅出现概率)
    不做特殊论者(没有所谓的理所当然,你所谓的成功很有可能只是因为运气)
    事实和观点(就事论事,事实有真假,观点无对错)
    一个程序员的价值观总结
    669 创新也是搞政治?(如何创新)
  • 原文地址:https://www.cnblogs.com/android-blogs/p/4968941.html
Copyright © 2020-2023  润新知