Android开发

关于避免切换主题时闪屏的几种方式


在activity中调用setTheme来切换夜间模式的方法可能大家有看过相关的文章了,但是调用setTheme设置的主题后界面并没有变化,这时需要调用activity的recreate方法另设置的主题生效,但是试过的朋友们都知道,activity调用recreate方法以后会有一瞬间的闪屏

今天写这篇文章的主题主要是关于如何避免setTheme切换主题后调用recreate的闪屏
关于如何通过改变theme更换主题的文章如果您还没有看过的话可以看一下这篇文章或者自行搜索一下

recreate

效果实现

1.属性动画 实现

使用属性动画配合ArgbEvaluator这个类来对所有需要变换颜色的View设置一个渐变动画

属性动画

这个方法的缺点有一下几个:

  • 只要是需要有颜色变化的View要设置id并通过findViewById获取其对象,增加代码量,大家都知道设置id写findViewById有多蛋疼吧(笔者最讨厌设置控件的id了)
  • 每一个设置的attr的color就需要写一个属性动画,代码量又增多了
  • RecyclerView或ListView还有某些特殊控件如何进行颜色改变(如MD风格的Button RadioButton Switch 等不能通过设置background改变颜色的控件)

接下来来看一下代码中如何实现吧

通过以下方法获取主题中设置的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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {int color = (int) animation.getAnimatedValue();//设置背景色的控件(Toolbar,背景layout等)mView.setBackgroundColor(color);//TextView字体颜色mTextView.setTextColor(color);//ImageView设置TintmImageView.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));

通过属性动画实现的缺点比较明显,布局越复杂编写的难度以及代码量就越多,最终实现的效果也不是非常完美,难点在于特殊的控件如何改变颜色,这里有兴趣的读者可以自行研究一下,下面介绍一种更简单的方式


2.startActivity 实现

此方法原理和调用recreate是相似的,通过创建一个相同的activity并加上动画可以避免闪屏

startActivity

这个方法的难点在于:

  • 如何还原上一个activity的状态让用户感觉不到控件的变化

下面上代码

创建一个新的相同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() {@Overridepublic void run() {mRecyclerView.scrollBy(0,getIntent().getIntExtra("scrollY", 0));}});

使用startActivity这种方式实现的效果是不是比较简单,代码量相对也较小
难点就在于网络请求的list数据如何进行传递、保存,intent里面传递过多数据容易引起崩溃
因为是创建的新activity,最终实现的效果也比较完美


总结

以上就是我总结的如何避免切换主题时闪屏的方法,这是我第一次写技术总结,肯定有不少问题,希望需要的朋友们能够从种学习到新的知识
最后附上Demo链接https://github.com/Misutesu/NightModeDemo
(此Demo选择切换模式的RadioButton的选中状态好像有点问题,我暂时没有找到原因,如果有发现了的朋友欢迎留言告诉我)

感谢您的阅读

Android开发 关于避免切换主题时免闪屏的几种方式相关推荐

  1. android webview加载闪屏,Android Webview:加载url时出现闪屏

    我是Android应用程序中的新手,这是我的第一个应用程序. 我已经创建了启动画面和工程..但其后走了一个长长的白色空白屏幕约2-5秒,然后URL开始加载..Android Webview:加载url ...

  2. android中英文切换功能,Android开发之中英文切换

    这篇文章详细介绍了Android开发之中英文切换,文中穿插有实例代码和示例程序介绍的很详细,遇到同样问题的朋友可以参考一下,如果有更好的解决方法,请留言分享帮助更多的程序员. 首先配置文件的appli ...

  3. 直播短视频系统开发,动态切换主题色

    直播短视频系统开发,动态切换主题色相关的代码: 点击按钮变量存储切换状态: override fun onClick(v: View?) {when (v?.id) {R.id.btn_theme1 ...

  4. Android应用启动时白色闪屏原因及解决办法

    白色闪屏现象: 如果OnCreate().onStart().onResume()中操作太多,耗时较久.那么就会在主界面显示出来之前出现白色闪屏.为了更清晰的看出白色闪屏,这里在代码中执行50000次 ...

  5. Android 应用开发 之通过AsyncTask与ThreadPool(线程池)两种方式异步加载大量数据的分析与对比

    Android 应用开发 之通过AsyncTask与ThreadPool(线程池)两种方式异步加载大量数据的分析与对比 标签: AndroidAsyncTaskThreadPool异步加载view 2 ...

  6. 从一个视图控制器切换到另一个视图控制器的几种方式

    从一个视图控制器切换到另一个视图控制器的几种方式 1,模态(modal)画面的显示方法: 例如iphone通讯录管理程序中,追加新的通讯纪录时,就是使用这种模态画面 例:点击一个按钮,进入另一个界面 ...

  7. JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式

    JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式,以及如何使用Eclips Memory Analyzer(MAT)插件进行堆内存分析. 方法一: jmap -du ...

  8. java oom dump_JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式

    JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式,以及如何使用Eclips Memory Analyzer(MAT)插件进行堆内存分析. 方法一: jmap -du ...

  9. SSH远程免密登录的两种方式

    SSH远程免密登录的两种方式 一.ssh远程登录操作 1.先ping测试下看看网络是否通畅 2.ssh 192.168.150.148 二.ssh免密登录方式一 1.生成公钥.私钥 2.拷贝公钥到目标 ...

最新文章

  1. 机器学习帮助人类找到最硬的过渡金属氮化物
  2. Google cpp style guide 之 include
  3. 好好爱惜自己的牙齿(电动牙刷)
  4. Ubuntu循环登录
  5. linux c之通过popen和pclose函数创建管道执行shell 运行命令使用总结
  6. 有一个会泰勒级数的八岁表妹是怎样一种体验?
  7. CentOS搭建安装SVN
  8. 关于Content-Type: multipart/form-data的支持
  9. 冒充“老干妈”公司工作人员行骗三人被提起公诉
  10. 那些年让我们头疼的CSS3动画
  11. Reachability
  12. mesh gradient的求法
  13. Android短信页面
  14. 为项目编写Readme.MD文件
  15. Echarts 实现 设备运行状态图 工业大数据展示
  16. Java就业方向有哪些?
  17. 程序员怎么做项目管理?
  18. oh god job
  19. SpringCloud open feign too many bytes written问题处理
  20. gstreamer学习笔记---pad定义、连接、流动

热门文章

  1. mysql mfc120_浪漫主义
  2. 读书笔记-《拆掉思维里的墙》
  3. linux安装rvm,在CentOS上安装rvm
  4. 【图像去噪】基于双立方插值和稀疏表示实现图像去噪matlab源码
  5. 零基础入门学习Java,这几本经典的Java学习书籍,可以推荐你看下,绝对受益匪浅!
  6. 1215. bernoulli
  7. 【大数据AI人工智能】企业级大数据产品体系技术架构白皮书
  8. DeFi之道丨DeFi季节再次降临,协议TVL已回血至1480亿美元,逼近历史新高
  9. 高通平台上的AMSS(Modem端) windows环境搭建
  10. 针对音乐播放器项目进行的测试