本人所有博客均是记录自己的学习和爬坑的过程,顺便分享一些好用的东西给同学,没有任何商用行为,若有侵权,请及时联系本人1521976977@qq.com。如果对你有帮助的话不妨点赞呗~谢谢
老样子还是先上效果图
简单介绍下实现过程
首先:
将需要切换的资源文件配置一下
styles.xml文件
<!--白天主题-->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<!-- 避免控件会顶到状态栏上 -->
<!--<item name="android:fitsSystemWindows">true</item>-->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="mainBackground">@color/line</item>
<item name="mainBackground1">@color/white</item>
<item name="text_color">@color/text_color</item>
<item name="text_color1">@color/text_color</item>
<item name="mine_icon2">@mipmap/mine_icon_2</item>
<item name="mine_icon3">@mipmap/mine_icon_3</item>
<item name="mine_icon5">@mipmap/mine_icon_5</item>
<item name="mine_icon7">@mipmap/scan</item>
<item name="bg_main_bottom">@drawable/bg_main_bottom1</item>
<item name="line">@color/line</item>
<item name="background1">@color/colorPrimary1</item>
<item name="background2">@color/colorPrimary1</item>
<item name="background3">@color/white</item>
</style>
<!-- 夜间模式-->
<style name="NightAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<!-- 避免控件会顶到状态栏上 -->
<!--<item name="android:fitsSystemWindows">true</item>-->
<item name="colorPrimary">@color/nightColorPrimary</item>
<item name="colorPrimaryDark">@color/nightColorPrimaryDark</item>
<item name="colorAccent">@color/nightColorAccent</item>
<item name="mainBackground">#141E2B</item>
<item name="mainBackground1">#151E2C</item>
<item name="text_color">@color/night_text_color</item>
<item name="text_color1">@color/gray_text_color</item>
<item name="bg_main_bottom">@drawable/bg_main_bottom2</item>
<item name="mine_icon2">@mipmap/mine_icon_2</item>
<item name="mine_icon3">@mipmap/mine_icon_3</item>
<item name="mine_icon5">@mipmap/mine_icon_5</item>
<item name="mine_icon7">@mipmap/scan</item>
<item name="line">#18222F</item>
<item name="background1">#18222F</item>
<item name="background2">#18222F</item>
<item name="background3">#18222F</item>
</style>
sttrs文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="mainBackground" format="color|reference" />
<attr name="mainBackground1" format="color|reference" />
<attr name="bg_main_bottom" format="reference"/>
<attr name="background1" format="color|reference"/>
<attr name="background2" format="color|reference"/>
<attr name="background3" format="color|reference"/>
<attr name="text_color" format="color|reference" />
<attr name="text_color1" format="color|reference" />
<attr name="mine_icon1" format="reference" />
<attr name="mine_icon2" format="reference" />
<attr name="mine_icon3" format="reference" />
<attr name="mine_icon4" format="reference" />
<attr name="mine_icon5" format="reference" />
<attr name="mine_icon6" format="reference" />
<attr name="mine_icon7" format="reference" />
<attr name="line" format="color|reference" />
<!--设置bar的样式-->
<declare-styleable name="CircleImageView">
<attr name="border_width" format="dimension"/>
<attr name="border_color" format="color"/>
</declare-styleable>
</resources>
然后就是在布局文件里面怎么引入:
布局中通过 ?attr/mainBackground(这里是atrs配置的资源文件name)就可以自动毗匹配当前主题配置的资源
这是抽屉效果页面的布局:
使用布局DrawerLayout 可以实现抽屉效果,滑动的抽屉设置android:layout_gravity="left"则可以将抽屉至于左侧布局外边,这样创建的页面就已经可以通过左滑滑出抽屉,如果需要绑定点击事件打开抽屉效果,直接修改这个布局的位置 drawerLayout1.openDrawer(GravityCompat.START);就ok
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout 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"
android:id="@+id/id_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/mainBackground">
<!--tools:context=".ui.activity.NewMainActivity">-->
<include layout="@layout/activity_main_tab" />
<RelativeLayout
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="left"
>
<!--android:layout_gravity="left"-->
<LinearLayout
android:id="@+id/linear"
android:layout_width="262dp"
android:layout_height="match_parent"
android:background="?attr/mainBackground1"
android:choiceMode="singleChoice"
android:orientation="vertical">
<include layout="@layout/draw_heard_layout" />
<RelativeLayout
android:id="@+id/mine_safe"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginLeft="15dp">
<ImageView
android:id="@+id/mine_icon_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="?attr/mine_icon2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="14dp"
android:layout_toEndOf="@+id/mine_icon_2"
android:layout_toRightOf="@+id/mine_icon_2"
android:text="修改密码"
android:textColor="?attr/text_color"
android:textSize="15dp" />
<ImageView
android:layout_width="13dp"
android:layout_height="13dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="16dp"
android:src="@mipmap/point_right" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/mine_night"
android:layout_width="wrap_content"
android:layout_height="56dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="17dp">
<ImageView
android:id="@+id/mine_icon_7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="?attr/mine_icon3" />
<TextView
android:id="@+id/t_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="14dp"
android:layout_toEndOf="@+id/mine_icon_7"
android:layout_toRightOf="@+id/mine_icon_7"
android:text="夜间模式"
android:textColor="?attr/text_color"
android:textSize="15dp" />
<CheckBox
android:id="@+id/mine_light"
android:layout_width="30dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:background="@drawable/checkbox_line"
android:button="@null"
android:checked="false" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/mine_about"
android:layout_width="wrap_content"
android:layout_height="56dp"
android:layout_marginLeft="15dp">
<ImageView
android:id="@+id/mine_icon_8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="?attr/mine_icon5" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="14dp"
android:layout_toEndOf="@+id/mine_icon_8"
android:layout_toRightOf="@+id/mine_icon_8"
android:text="关于APP"
android:textColor="?attr/text_color"
android:textSize="15dp" />
<ImageView
android:layout_width="13dp"
android:layout_height="13dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="16dp"
android:src="@mipmap/point_right" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
</android.support.v4.widget.DrawerLayout>
Activity代码:
创建视图之前
// 设置主题色,,,一定要在setView之前
//第一步 当前要立即变色的页面
ChangeModeController.getInstance().init(this,R.attr.class).setTheme(this, R.style.AppTheme, R.style.NightAppTheme);
//其他页面
//ChangeModeController.setTheme(this, R.style.DayTheme, R.style.NightTheme);
切换主题:
mineLight.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String themes;
switch (buttonView.getId()) {
case R.id.mine_light:
if (isChecked) {
ChangeModeController.changeNight(MainActivity.this, R.style.NightAppTheme);
ChangeModeHelper.setStatus(MainActivity.this,true);
} else {
ChangeModeController.changeDay(MainActivity.this, R.style.AppTheme);
ChangeModeHelper.setStatus(MainActivity.this,false);
}
break;
}
}
});
然后就是两个切换主题的辅助类:
ChangeModeController
package com.wzq.light_or_night.skinTheme;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.support.v4.view.LayoutInflaterCompat;
import android.support.v4.view.LayoutInflaterFactory;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.wzq.light_or_night.R;
import com.wzq.light_or_night.ui.MainActivity;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* 夜间模式控制器
*
*/
public class ChangeModeController {
/**
* 属性背景
*/
private static final String ATTR_BACKGROUND = "backgroundAttr";
/**
* 属性背景图片
*/
private static final String ATTR_BACKGROUND_DRAWABLE = "backgroundDrawableAttr";
/**
* 属性字体颜色
*/
private static final String ATTR_TEXTCOLOR = "textColorAttr";
private static List<AttrEntity<View>> mBackGroundViews;
private static List<AttrEntity<View>> mBackGroundDrawableViews;
private static List<AttrEntity<TextView>> mTextColorViews;
private static ChangeModeController mChangeModeController;
private ChangeModeController(){}
public static ChangeModeController getInstance(){
if(mChangeModeController == null){
mChangeModeController = new ChangeModeController();
}
return mChangeModeController;
}
/**
* 初始化保存集合
*/
private void init(){
mBackGroundViews = new ArrayList<>();
mTextColorViews = new ArrayList<>();
mBackGroundDrawableViews = new ArrayList<>();
}
/**
* 初始化夜间控制器
* @param activity 上下文
* @return
*/
public ChangeModeController init(final Activity activity,final Class mClass){
init();
LayoutInflaterCompat.setFactory(LayoutInflater.from(activity), new LayoutInflaterFactory() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
View view = null;
try {
if(name.indexOf('.') == -1){
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{
if (view == null){
view = LayoutInflater.from(context).createView(name, null, attrs);
}
}
if(view != null){
// Log.e("TAG", "name = " + name);
for (int i = 0; i < attrs.getAttributeCount(); i++) {
// Log.e("TAG", attrs.getAttributeName(i) + " , " + attrs.getAttributeValue(i));
if (attrs.getAttributeName(i).equals(ATTR_BACKGROUND)) {
mBackGroundViews.add(new AttrEntity<View>(view,getAttr(mClass,attrs.getAttributeValue(i))));
}
if (attrs.getAttributeName(i).equals(ATTR_TEXTCOLOR)) {
mTextColorViews.add(new AttrEntity<TextView>((TextView)view,getAttr(mClass,attrs.getAttributeValue(i))));
}
if (attrs.getAttributeName(i).equals(ATTR_BACKGROUND_DRAWABLE)) {
mBackGroundDrawableViews.add(new AttrEntity<View>(view,getAttr(mClass,attrs.getAttributeValue(i))));
}
}
}
}catch (Exception e){
e.printStackTrace();
}
return view;
}
});
return this;
}
/**
* 反射获取文件id
* @param attrName 属性名称
* @return 属性id
*/
public static int getAttr(Class draw,String attrName) {
if (attrName == null || attrName.trim().equals("") || draw == null) {
return R.attr.colorPrimary;
}
try {
Field field = draw.getDeclaredField(attrName);
//field.setAccessible(true);
return field.getInt(attrName);
} catch (Exception e) {
return R.attr.colorPrimary;
}
}
/**
* 设置当前主题
* @param ctx 上下文
* @param Style_Day 白天
* @param Style_Night 夜间
*/
public static void setTheme(Context ctx,int Style_Day,int Style_Night){
if(ChangeModeHelper.getChangeMode(ctx) == ChangeModeHelper.MODE_DAY){
ctx.setTheme(Style_Day);
}else if(ChangeModeHelper.getChangeMode(ctx) == ChangeModeHelper.MODE_NIGHT){
ctx.setTheme(Style_Night);
}
}
/**
*
* @param ctx 上下文
* @param style 切换style
*/
public static void changeNight(Activity ctx,int style) {
if(mBackGroundDrawableViews == null || mTextColorViews == null || mBackGroundViews == null){
throw new RuntimeException("请先调用init()初始化方法!");
}
ChangeModeHelper.setChangeMode(ctx, ChangeModeHelper.MODE_NIGHT);
showAnimation(ctx);
ctx.setTheme(style);
// refreshUI(ctx);
Intent intent = new Intent(ctx, MainActivity.class);
// Intent intent = context.getIntent();
// FLAG_ACTIVITY_NO_ANIMATION
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION );//跳转无闪烁、
intent.putExtra("theme",1);
ctx.startActivity(intent);
ctx.finish();
}
/**
*
* @param ctx 上下文
* @param style 切换style
*/
public static void changeDay(Activity ctx,int style) {
if(mBackGroundDrawableViews == null || mTextColorViews == null || mBackGroundViews == null){
throw new RuntimeException("请先调用init()初始化方法!");
}
ChangeModeHelper.setChangeMode(ctx, ChangeModeHelper.MODE_DAY);
showAnimation(ctx);
ctx.setTheme(style);
// refreshUI(ctx);
Intent intent = new Intent(ctx, MainActivity.class);
// Intent intent = context.getIntent();
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION );
intent.putExtra("theme",1);
ctx.startActivity(intent);
ctx.finish();
}
/**
* 刷新UI界面
* @param ctx 上下文
*/
private static void refreshUI(Activity ctx) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = ctx.getTheme();
theme.resolveAttribute(R.attr.colorPrimary, typedValue, true);
View view = ctx.findViewById(R.id.action_bar);
if(view!=null){
view.setBackgroundResource(typedValue.resourceId);
}
for(AttrEntity<View> entity:mBackGroundViews){
theme.resolveAttribute(entity.colorId, typedValue, true);
entity.v.setBackgroundResource(typedValue.resourceId);
}
for(AttrEntity<View> entity:mBackGroundDrawableViews){
theme.resolveAttribute(entity.colorId, typedValue, true);
entity.v.setBackgroundResource(typedValue.resourceId);
}
for (AttrEntity<TextView> entity:mTextColorViews){
theme.resolveAttribute(entity.colorId, typedValue, true);
entity.v.setTextColor(ctx.getResources().getColor(typedValue.resourceId));
}
refreshStatusBar(ctx);
}
/**
* 获取某个属性的TypedValue
* @param ctx 上下文
* @param attr 属性id
* @return
*/
public static TypedValue getAttrTypedValue(Activity ctx,int attr){
TypedValue typedValue = new TypedValue();
Resources.Theme theme = ctx.getTheme();
theme.resolveAttribute(attr, typedValue, true);
return typedValue;
}
/**
* 刷新 StatusBar
* @param ctx 上下文
*/
private static void refreshStatusBar(Activity ctx) {
if (Build.VERSION.SDK_INT >= 21) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = ctx.getTheme();
theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true);
ctx.getWindow().setStatusBarColor(ctx.getResources().getColor(typedValue.resourceId));
}
}
/**
* 展示切换动画
*/
private static void showAnimation(Activity ctx) {
final View decorView = ctx.getWindow().getDecorView();
Bitmap cacheBitmap = getCacheBitmapFromView(decorView);
if (decorView instanceof ViewGroup && cacheBitmap != null) {
final View view = new View(ctx);
view.setBackgroundDrawable(new BitmapDrawable(ctx.getResources(), cacheBitmap));
ViewGroup.LayoutParams layoutParam = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
((ViewGroup) decorView).addView(view, layoutParam);
ValueAnimator objectAnimator = ValueAnimator.ofFloat(1f, 0f);//view, "alpha",
objectAnimator.setDuration(500);
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
((ViewGroup) decorView).removeView(view);
}
});
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float alpha = (Float) animation.getAnimatedValue();
view.setAlpha(alpha);
}
});
objectAnimator.start();
}
}
/**
* 获取一个 View 的缓存视图
*
* @param view
* @return
*/
private static Bitmap getCacheBitmapFromView(View view) {
final boolean drawingCacheEnabled = true;
view.setDrawingCacheEnabled(drawingCacheEnabled);
view.buildDrawingCache(drawingCacheEnabled);
final Bitmap drawingCache = view.getDrawingCache();
Bitmap bitmap;
if (drawingCache != null) {
bitmap = Bitmap.createBitmap(drawingCache);
view.setDrawingCacheEnabled(false);
} else {
bitmap = null;
}
return bitmap;
}
/**
* 视图销毁时调用
*/
public static void onDestory(){
mBackGroundViews.clear();
mTextColorViews.clear();
mBackGroundDrawableViews.clear();
mBackGroundViews = null;
mTextColorViews = null;
mBackGroundDrawableViews = null;
mChangeModeController = null;
}
/**
* 添加背景颜色属性
* @param view
* @param colorId
* @return
*/
public ChangeModeController addBackgroundColor(View view, int colorId) {
mBackGroundViews.add(new AttrEntity(view,colorId));
return this;
}
/**
*添加背景图片属性
* @param view
* @param drawableId 属性id
* @return
*/
public ChangeModeController addBackgroundDrawable(View view, int drawableId) {
mBackGroundDrawableViews.add(new AttrEntity(view,drawableId));
return this;
}
/**
* 添加字体颜色属性
* @param view
* @param colorId 属性id
* @return
*/
public ChangeModeController addTextColor(View view, int colorId) {
mTextColorViews.add(new AttrEntity<TextView>((TextView) view,colorId));
return this;
}
class AttrEntity<T>{
T v;//控件
int colorId;//属性id
public AttrEntity(T v, int colorId) {
this.v = v;
this.colorId = colorId;
}
}
}
ChangeModeHelper :
package com.wzq.light_or_night.skinTheme;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
/**
* 夜间模式辅助类
*/
public class ChangeModeHelper {
public static final int MODE_DAY = 1;
public static final int MODE_NIGHT = 2;
public static final boolean isCheck = false;
private static String Mode = "mode";
public static void setChangeMode(Context ctx, int mode) {
SharedPreferences sp = ctx.getSharedPreferences("config_mode", Context.MODE_PRIVATE);
sp.edit().putInt(Mode, mode).commit();
}
public static int getChangeMode(Context ctx) {
SharedPreferences sp = ctx.getSharedPreferences("config_mode", Context.MODE_PRIVATE);
return sp.getInt(Mode, MODE_DAY);
}
public static void setStatus(Context context, boolean status) {
//第一个写的是保存的文件名
SharedPreferences sharedPreferences = context.getSharedPreferences("themeapp", Context.MODE_PRIVATE); //私有数据
SharedPreferences.Editor editor = sharedPreferences.edit();//获取编辑器
editor.putBoolean("isNight", status);
editor.commit();//提交修改
}
public static boolean getStatus(Context context) {
SharedPreferences share = context.getSharedPreferences("themeapp", context.MODE_PRIVATE);
Boolean i = share.getBoolean("isNight", false);
return i;
}
}
简单说下实现夜间模式切换的方法吧 使用安卓自带的setTheme这个方法就能实现style里面配置的主题切换,为了当前页面UI立即切换主题,使用了重新创建的方法——就是利用Intent的跳转,跳转当前页面,让修改的主题生效。很大的弊端就是这个方案数据保存以及状态保存很麻烦,还有就是不适合在主页面意外的其他页面使用,这样会造成生命周期混乱等。
再补充一点跳转页面不闪烁:intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION );//跳转无闪烁、
以及finish页面不闪烁的配置:
public void finish() {
super.finish();
//关闭当前活动无闪烁
overridePendingTransition(0,0);
}
下面就是贴上我最近整理的小demo,这些东西都有,如果对你有帮助的话就给个赞吧 当然要是给个star那最好啦 哈哈哈
https://github.com/KuoLuoC/AndroidDemo1