Android theme change

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

Guess you like

Origin blog.csdn.net/qq_15059163/article/details/124500768