Similar effect
Simple way to write theme attributes custom_theme_attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 自定义属性 -->
<attr name="text_color" format="color" />
<!-- 日间主题 -->
<style name="DayTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="text_color">@color/black_tx_color</item>
</style>
<!-- 夜间主题 -->
<style name="NightTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="text_color">@color/white_tx_color</item>
</style>
</resources>
Color definition custom_theme_colors.xml
<resources>
<!-- 日间模式 -->
<color name="black_tx_color">#333333</color>
<!-- 夜间模式 -->
<color name="white_tx_color">#f0f0f0</color>
</resources>
layout
<LinearLayout
android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/change_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="给我改主题"
android:textColor="@color/black"
/>
<Button
android:textColor="@color/black"
android:id="@+id/second_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/change_btn"
android:layout_marginTop="20dp"
android:text="Recyclerview"
/>
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="120dp"
android:gravity="center"
android:text="aaaaaaaa"
android:textColor="?attr/text_color"
android:textSize="20dp" />
</LinearLayout>
code segment
//切换主题
public class MainActivity5 extends AppCompatActivity {
Button change_btn;
Button second_btn;
TextView tv_name;
private Colorful mColorful;
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.DayTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
change_btn = (Button) findViewById(R.id.change_btn);
second_btn = (Button) findViewById(R.id.second_btn);
tv_name = (TextView) findViewById(R.id.tv_name);
change_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
animChangeColor(R.style.NightTheme);
}
});
second_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
animChangeColor(R.style.DayTheme);
}
});
//Recyclerview
// ViewGroupSetter rvSetter = new ViewGroupSetter(mNightRv, R.attr.root_view_bg);
// rvSetter.childViewTextColor(R.id.category_desc, R.attr.one_text_bg);
// rvSetter.childViewTextColor(R.id.category_author, R.attr.two_text_bg);
// rvSetter.childViewTextColor(R.id.category_date, R.attr.two_text_bg);
// rvSetter.childViewBgColor(R.id.night_rl, R.attr.cardview_bg);
// 构建Colorful对象
mColorful = new Colorful.Builder(this)
.textColor(R.id.tv_name, R.attr.text_color) // 设置文本颜色
// .setter(rvSetter)
.create();
}
boolean isNight = false ;
// 切换主题
private void changeThemeWithColorful() {
if (!isNight) {
mColorful.setTheme(R.style.DayTheme);
} else {
mColorful.setTheme(R.style.NightTheme);
}
isNight = !isNight;
}
/**
* 给夜间模式增加一个动画,颜色渐变
*
* @param newTheme
*/
private void animChangeColor(final int newTheme) {
final View rootView = getWindow().getDecorView();
rootView.setDrawingCacheEnabled(true);
rootView.buildDrawingCache(true);
final Bitmap localBitmap = Bitmap.createBitmap(rootView.getDrawingCache());
rootView.setDrawingCacheEnabled(false);
if (null != localBitmap && rootView instanceof ViewGroup) {
final View tmpView = new View(this);
tmpView.setBackgroundDrawable(new BitmapDrawable(getResources(), localBitmap));
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup
.LayoutParams.MATCH_PARENT);
((ViewGroup) rootView).addView(tmpView, params);
tmpView.animate().alpha(0).setDuration(400).setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mColorful.setTheme(newTheme);
System.gc();
}
@Override
public void onAnimationEnd(Animator animation) {
((ViewGroup) rootView).removeView(tmpView);
localBitmap.recycle();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
}).start();
}
}
private Disposable countdownDisposable;
@Override
protected void onDestroy() {
super.onDestroy();
countdownDisposable.dispose();
}
}
Tools used Colorful
public class Colorful
{
/**
* Colorful Builder
*/
Builder mBuilder;
/**
* private constructor
*
* @param builder
*/
private Colorful(Builder builder)
{
mBuilder = builder;
}
/**
* 设置新的主题
*
* @param newTheme
*/
public void setTheme(int newTheme)
{
mBuilder.setTheme(newTheme);
}
/**
* 构建Colorful的Builder对象
*
* @author mrsimple
*/
public static class Builder
{
/**
* 存储了视图和属性资源id的关系表
*/
List<ViewSetter> mElements = new ArrayList<>();
/**
* 目标Activity
*/
Activity mActivity;
/**
* @param activity
*/
public Builder(Activity activity)
{
mActivity = activity;
}
/**
* @param fragment
*/
public Builder(Fragment fragment)
{
mActivity = fragment.getActivity();
}
private View findViewById(int viewId)
{
return mActivity.findViewById(viewId);
}
/**
* 将View id与存储该view背景色的属性进行绑定
*
* @param viewId 控件id
* @param colorId 颜色属性id
* @return
*/
public Builder backgroundColor(int viewId, int colorId)
{
mElements.add(new ViewBackgroundColorSetter(findViewById(viewId), colorId));
return this;
}
/**
* 将View id与存储该view背景Drawable的属性进行绑定
*
* @param viewId 控件id
* @param drawableId Drawable属性id
* @return
*/
public Builder backgroundDrawable(int viewId, int drawableId)
{
mElements.add(new ViewBackgroundDrawableSetter(
findViewById(viewId), drawableId));
return this;
}
/**
* 将TextView id与存储该TextView文本颜色的属性进行绑定
*
* @param viewId TextView或者TextView子类控件的id
* @param colorId 颜色属性id
* @return
*/
public Builder textColor(int viewId, int colorId)
{
TextView textView = (TextView) findViewById(viewId);
mElements.add(new TextColorSetter(textView, colorId));
return this;
}
/**
* 用户手动构造并且添加Setter
*
* @param setter 用户自定义的Setter
* @return
*/
public Builder setter(ViewSetter setter)
{
mElements.add(setter);
return this;
}
/**
* 设置新的主题
*
* @param newTheme
*/
protected void setTheme(int newTheme)
{
mActivity.setTheme(newTheme);
makeChange(newTheme);
}
/**
* 修改各个视图绑定的属性
*/
private void makeChange(int themeId)
{
Theme curTheme = mActivity.getTheme();
for (ViewSetter setter : mElements)
{
setter.setValue(curTheme, themeId);
}
}
/**
* 创建Colorful对象
*
* @return
*/
public Colorful create()
{
return new Colorful(this);
}
}
}
RecyclerViewSetter
public class RecyclerViewSetter extends ViewGroupSetter
{
public RecyclerViewSetter(ViewGroup targetView, int resId)
{
super(targetView, resId);
}
public RecyclerViewSetter(ViewGroup targetView)
{
super(targetView);
}
@Override
protected void clearRecyclerViewRecyclerBin(View rootView)
{
super.clearRecyclerViewRecyclerBin(rootView);
((RecyclerView) rootView).getRecycledViewPool().clear();
}
}
TextColorSetter
public class TextColorSetter extends ViewSetter
{
public TextColorSetter(TextView textView, int resId)
{
super(textView, resId);
}
public TextColorSetter(int viewId, int resId)
{
super(viewId, resId);
}
@Override
public void setValue(Theme newTheme, int themeId)
{
if (mView == null)
{
return;
}
Log.e("color123", "setValue(TextColorSetter.java:29)");
((TextView) mView).setTextColor(getColor(newTheme));
}
}
ViewBackgroundColorSetter
public class ViewBackgroundColorSetter extends ViewSetter
{
public ViewBackgroundColorSetter(View target, int resId)
{
super(target, resId);
}
public ViewBackgroundColorSetter(int viewId, int resId)
{
super(viewId, resId);
}
@Override
public void setValue(Theme newTheme, int themeId)
{
if (mView != null)
{
Log.e("color123","setValue(ViewBackgroundColorSetter.java:29)");
mView.setBackgroundColor(getColor(newTheme));
}
}
}
ViewBackgroundDrawableSetter
public class ViewBackgroundDrawableSetter extends ViewSetter
{
public ViewBackgroundDrawableSetter(View targetView, int resId)
{
super(targetView, resId);
}
public ViewBackgroundDrawableSetter(int viewId, int resId)
{
super(viewId, resId);
}
@SuppressWarnings("deprecation")
@Override
public void setValue(Theme newTheme, int themeId)
{
if (mView == null)
{
return;
}
TypedArray a = newTheme.obtainStyledAttributes(themeId,
new int[]{mAttrResId});
int attributeResourceId = a.getResourceId(0, 0);
Drawable drawable = mView.getResources().getDrawable(
attributeResourceId);
a.recycle();
mView.setBackgroundDrawable(drawable);
}
}
ViewGroupSetter
public class ViewGroupSetter extends ViewSetter
{
/**
* ListView的子试图的Setter
*/
protected Set<ViewSetter> mItemViewSetters = new HashSet<ViewSetter>();
/**
* @param targetView
* @param resId
*/
public ViewGroupSetter(ViewGroup targetView, int resId)
{
super(targetView, resId);
}
public ViewGroupSetter(ViewGroup targetView)
{
super(targetView, 0);
}
/**
* 设置View的背景色
*
* @param viewId
* @param colorId
* @return
*/
public ViewGroupSetter childViewBgColor(int viewId, int colorId)
{
mItemViewSetters.add(new ViewBackgroundColorSetter(viewId, colorId));
return this;
}
/**
* 设置View的drawable背景
*
* @param viewId
* @param drawableId
* @return
*/
public ViewGroupSetter childViewBgDrawable(int viewId, int drawableId)
{
mItemViewSetters.add(new ViewBackgroundDrawableSetter(viewId,
drawableId));
return this;
}
/**
* 设置文本颜色,因此View的类型必须为TextView或者其子类
*
* @param viewId
* @param colorId
* @return
*/
public ViewGroupSetter childViewTextColor(int viewId, int colorId)
{
mItemViewSetters.add(new TextColorSetter(viewId, colorId));
return this;
}
@Override
public void setValue(Theme newTheme, int themeId)
{
mView.setBackgroundColor(getColor(newTheme));
// 清空AbsListView的元素
clearListViewRecyclerBin(mView);
// 清空RecyclerView
clearRecyclerViewRecyclerBin(mView);
// 修改所有子元素的相关属性
changeChildenAttrs((ViewGroup) mView, newTheme, themeId);
}
/**
* @param viewId
* @return
*/
private View findViewById(View rootView, int viewId)
{
View targetView = rootView.findViewById(viewId);
return targetView;
}
/**
* 修改子视图的对应属性
*
* @param viewGroup
* @param newTheme
* @param themeId
*/
private void changeChildenAttrs(ViewGroup viewGroup, Theme newTheme, int themeId)
{
int childCount = viewGroup.getChildCount();
//递归用法
for (int i = 0; i < childCount; i++)
{
View childView = viewGroup.getChildAt(i);
// 深度遍历
if (childView instanceof ViewGroup)
{
changeChildenAttrs((ViewGroup) childView, newTheme, themeId);
}
// 遍历子元素与要修改的属性,如果相同那么则修改子View的属性
for (ViewSetter setter : mItemViewSetters)
{
// 每次都要从ViewGroup中查找数据
setter.mView = findViewById(viewGroup, setter.mViewId);
//因为viewgroup中的所有的id都添加进来了,而且每一个id都是独一无二的,
// 所以会有一个判断,当childView的id和setter中的id相等时,才会修改属性
if (childView.getId() == setter.getViewId())
{
setter.setValue(newTheme, themeId);
}
}
}
}
private void clearListViewRecyclerBin(View rootView)
{
if (rootView instanceof AbsListView)
{
try
{
@SuppressLint("SoonBlockedPrivateApi")
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]);
Log.e("", "### 清空AbsListView的RecycerBin ");
} 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();
}
}
}
protected void clearRecyclerViewRecyclerBin(View rootView)
{
if (rootView instanceof RecyclerView)
{
try
{
Field localField = RecyclerView.class
.getDeclaredField("mRecycler");
localField.setAccessible(true);
Method localMethod = Class.forName(
"android.support.v7.widget.RecyclerView$Recycler")
.getDeclaredMethod("clear", new Class[0]);
localMethod.setAccessible(true);
localMethod.invoke(localField.get(rootView), new Object[0]);
Log.e("", "### 清空RecyclerView的Recycer ");
rootView.invalidate();
((RecyclerView) rootView).getRecycledViewPool().clear();
} 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();
}
}
}
}
ViewSetter
public abstract class ViewSetter
{
/**
* 目标View
*/
protected View mView;
/**
* 目标view id,有时在初始化时还未构建该视图,比如ListView的Item View中的某个控件
*/
protected int mViewId;
/**
* 目标View要的特定属性id
*/
protected int mAttrResId;
public ViewSetter(View targetView, int resId)
{
mView = targetView;
mAttrResId = resId;
}
public ViewSetter(int viewId, int resId)
{
mViewId = viewId;
mAttrResId = resId;
}
/**
* @param newTheme
* @param themeId
*/
public abstract void setValue(Theme newTheme, int themeId);
/**
* 获取视图的Id
*
* @return
*/
protected int getViewId()
{
return mView != null ? mView.getId() : -1;
}
protected boolean isViewNotFound()
{
return mView == null;
}
/**
* @param newTheme
* @return
*/
protected int getColor(Theme newTheme)
{
//返回重新指定后的资源id
TypedValue typedValue = new TypedValue();
newTheme.resolveAttribute(mAttrResId, typedValue, true);
return typedValue.data;
}
}