赞
踩
在activity中调用setTheme来切换夜间模式的方法可能大家有看过相关的文章了,但是调用setTheme设置的主题后界面并没有变化,这时需要调用activity的recreate方法另设置的主题生效,但是试过的朋友们都知道,activity调用recreate方法以后会有一瞬间的闪屏
今天写这篇文章的主题主要是关于如何避免setTheme切换主题后调用recreate的闪屏
关于如何通过改变theme更换主题的文章如果您还没有看过的话可以看一下这篇文章或者自行搜索一下
recreate |
---|
使用属性动画配合ArgbEvaluator这个类来对所有需要变换颜色的View设置一个渐变动画
属性动画 |
---|
这个方法的缺点有一下几个:
通过以下方法获取主题中设置的attrs对应颜色
- /** * @param theme 需要获取attrs颜色的theme * @param id 需要获取的attrs颜色id * @return color */
- public static int getColorFromTheme(Resources.Theme theme, @AttrRes int id)
- {
- TypedValue typedValue = new TypedValue();
- theme.resolveAttribute(id, typedValue, true);
- return typedValue.data;
- }
我们要做的是获取到当前颜色和更换后的主题颜色
activity中使用getTheme获取到Theme对象
- int startColorPrimary = ThemeUtil.getColorFromTheme(getTheme()R.attr.colorPrimary);
- setTheme(R.style.NightTheme);
- int endColorPrimary = ThemeUtil.getColorFromTheme(getTheme(),R.attr.colorPrimary);
接下来就是设置一个属性动画配合ArgbEvaluator实现颜色的渐变
- ValueAnimator animator = ValueAnimator
- .ofObject(new ArgbEvaluator(),
- startColorPrimary, endColorPrimary)
- .setDuration(300);
-
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int color = (int) animation.getAnimatedValue();
- //设置背景色的控件(Toolbar,背景layout等)
- mView.setBackgroundColor(color);
- //TextView字体颜色
- mTextView.setTextColor(color);
- //ImageView设置Tint
- mImageView.setColorFilter(color);
- //设置状态栏或导航栏颜色(API>=21)
- getWindow().setStatusBarColor(color);
- getWindow().setNavigationBarColor(color);
- }
- });
-
- animator.start();
对所有需要变换颜色的控件进行操作就实现了切换效果
下面是关于RecyclerView和MD风格的Button RadioButton Switch更变颜色的方法
(其中RadioButton Switch的实现并不完美 如果有知道的欢迎补充)
通过获取RecyclerView当前在屏幕上显示的Item改变其颜色
- int childCount = mRecyclerView.getChildCount();
- for (int childIndex = 0; childIndex < childCount; childIndex++) {
- ViewGroup childView = (ViewGroup) mRecyclerView.getChildAt(childIndex);
- //这里的childView是RecyclerView每一个item的最外层view
- //可以通过id获取每一个item里的控件
- View mView = childView.findViewById(R.id.item_view);
- //这里设置属性动画改变view的颜色
- ............
- }
让 RecyclerView 缓存在 Pool 中的 Item 失效
这里的思路是通过反射拿到 AbsListView 类中的 RecycleBin 对象,然后同样再用反射去调用 clear 方法
此方法选取自知乎和简书的夜间模式实现套路
- Class<RecyclerView> recyclerViewClass = RecyclerView.class;
- try {
- Field declaredField = recyclerViewClass.getDeclaredField("mRecycler");
- declaredField.setAccessible(true);
- Method declaredMethod = Class.forName(RecyclerView.Recycler.class.getName()).getDeclaredMethod("clear", (Class<?>[]) new Class[0]);
- declaredMethod.setAccessible(true);
- declaredMethod.invoke(declaredField.get(mRecyclerView), new Object[0]);
- RecyclerView.RecycledViewPool recycledViewPool = mRecyclerView.getRecycledViewPool();
- recycledViewPool.clear();
- } catch (Exception e) {
- e.printStackTrace();
- }
Button RadioButton Switch Progressbar通过设置Tint改变其颜色
- //Switch(不完美,会改变未选择时thumb的颜色 默认为灰白色)
- mSwitch.setThumbTintList(ColorStateList.valueOf(color));
- //RadioButton(不完美,会改变未选中时圆圈的颜色 默认为灰色)
- CompoundButtonCompat.setButtonTintList(mRadioButton, ColorStateList.valueOf(color));
- //Button Progressbar
- ViewCompat.setBackgroundTintList(mBotton, ColorStateList.valueOf(color));
通过属性动画实现的缺点比较明显,布局越复杂编写的难度以及代码量就越多,最终实现的效果也不是非常完美,难点在于特殊的控件如何改变颜色,这里有兴趣的读者可以自行研究一下,下面介绍一种更简单的方式
此方法原理和调用recreate是相似的,通过创建一个相同的activity并加上动画可以避免闪屏
startActivity |
---|
这个方法的难点在于:
创建一个新的相同activity并设置渐入渐出动画然后结束当前activity
- startActivity(new Intent(this, MainActivity.class));
- overridePendingTransition(R.anim.start_anim, R.anim.out_anim);
- finish();
这里通过启动activity创建的intent来传递以前旧界面的数据
比如EditText的输入内容RecyclerView的数据以及滑动距离
这里列出保存RecyclerView滑动距离 具体需要保存的数据需要根据界面的内容来编写
- //获取RecyclerView的滑动距离
- //调用getScrollY获取到的数据为0,也可以通过监听滑动事件保存滑动距离
- private int getScrollYDistance() {
- LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
- int position = layoutManager.findFirstVisibleItemPosition();
- View firstVisibleChildView = layoutManager.findViewByPosition(position);
- int itemHeight = firstVisibleChildView.getHeight();
- return (position) * itemHeight - firstVisibleChildView.getTop();
- }
-
- //通过intent传递数据到新activity
- Intent intent = new Intent(this, MainActivity.class);
- intent.putExtra("scrollY",getcrollYDistance());
-
- //在activity的onCreate方法中还原数据
- //RecyclerView的setScrollBy方法只有在view测量完毕后调用才能生效
- mRecyclerView.post(new Runnable() {
- @Override
- public void run() {
- mRecyclerView.scrollBy(0,getIntent().getIntExtra("scrollY", 0));
- }
- });
使用startActivity这种方式实现的效果是不是比较简单,代码量相对也较小
难点就在于网络请求的list数据如何进行传递、保存,intent里面传递过多数据容易引起崩溃
因为是创建的新activity,最终实现的效果也比较完美
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。