注册

又到公祭日,App快速实现“哀悼主题”方案


今天是南京大屠杀死难者国家公祭日,很多APP已经变成哀悼主题,怎么实现的呢?看看这个Android方案!

原标题:App快速实现“哀悼主题”方案

4月4日,今天是国家,为了纪念和哀悼为新冠疫情做出努力和牺牲的烈士以及在新冠中逝去的同胞“举行全国哀悼”的日子!
今天10时全国停止一切娱乐活动,并默哀3分钟!至此,各大app(爱奇艺、腾讯、虎牙...)响应号召,统一将app主题更换至“哀悼色”...,本篇文章简谈Android端的一种实现方案。

系统api:saveLayer

saveLayer可以为canvas创建一个新的透明图层,在新的图层上绘制,并不会直接绘制到屏幕上,而会在restore之后,绘制到上一个图层或者屏幕上(如果没有上一个图层)。为什么会需要一个新的图层,例如在处理xfermode的时候,原canvas上的图(包括背景)会影响src和dst的合成,这个时候,使用一个新的透明图层是一个很好的选择

public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint, @Saveflags int saveFlags) {
       if (bounds == null) {
           bounds = new RectF(getClipBounds());
      }
       checkValidSaveFlags(saveFlags);
       return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint,
               ALL_SAVE_FLAG);
  }

public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint) {
       return saveLayer(bounds, paint, ALL_SAVE_FLAG);
  }

ColorMatrix中setSaturation设置饱和度,给布局去色(0为灰色,1为原图)

/**
    * Set the matrix to affect the saturation of colors.
    *
    * @param sat A value of 0 maps the color to gray-scale. 1 is identity.
    */
   public void setSaturation(float sat) {
       reset();
       float[] m = mArray;

       final float invSat = 1 - sat;
       final float R = 0.213f * invSat;
       final float G = 0.715f * invSat;
       final float B = 0.072f * invSat;

       m[0] = R + sat; m[1] = G;       m[2] = B;
       m[5] = R;       m[6] = G + sat; m[7] = B;
       m[10] = R;      m[11] = G;      m[12] = B + sat;
  }

1.在view上的实践

自定义MourningImageVIew

public class MourningImageView extends AppCompatImageView {
   private Paint mPaint;
   public MourningImageView(Context context) {
       this(context,null);
  }
   public MourningImageView(Context context, @Nullable AttributeSet attrs) {
       this(context, attrs,0);
  }
   public MourningImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
       createPaint();
  }
   private void createPaint(){
       if(mPaint == null) {
           mPaint = new Paint();
           ColorMatrix cm = new ColorMatrix();
           cm.setSaturation(0);
           mPaint.setColorFilter(new ColorMatrixColorFilter(cm));
      }
  }
   @Override
   public void draw(Canvas canvas) {
       createPaint();
       canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
       super.draw(canvas);
       canvas.restore();
  }
   @Override
   protected void dispatchDraw(Canvas canvas) {
       createPaint();
       canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
       super.dispatchDraw(canvas);
       canvas.restore();
  }
}

和普通的ImageView对比效果:

202112130851724.png

举一反三在其他的view上也是可以生效的,实际线上项目不可能去替换所有view,接下来我们在根布局上做文章。

自定义各种MourningViewGroup实际替换效果:

xml version="1.0" encoding="utf-8"?>
<com.example.sample.MourningLinearlayout 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:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity"
   android:orientation="vertical"
   >

   <ImageView
       android:layout_width="wrap_content"
       android:src="@mipmap/ic_launcher"
       android:layout_margin="20dp"
       android:layout_height="wrap_content"/>

   <com.example.sample.MourningImageView
       android:layout_width="wrap_content"
       android:src="@mipmap/ic_launcher"
       android:layout_margin="20dp"
       android:layout_height="wrap_content"/>
com.example.sample.MourningLinearlayout>

202112130852311.png

也是可以生效的,那接下来的问题就是如何用最少的代码替换所有的页面根布局的问题了。在Activity创建的时候同时会创建一个 Window,其中包含一个 DecoView,在我们Activity中调用setContentView(),其实就是把它添加到decoView中,可以统一拦截decoview并对其做文章,降低入侵同时减少代码量。

202112130852706.png

2.Hook Window DecoView

application中注册ActivityLifecycleCallbacks,创建hook点

public class MineApplication extends Application {

   @Override
   protected void attachBaseContext(Context base) {
       super.attachBaseContext(base);
       registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
           @Override
           public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
              //获取decoview
               ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
               if(decorView!= null && decorView.getChildCount() > 0) {
                   //获取设置的contentview
                   View child = decorView.getChildAt(0);
                   //从decoview中移除contentview
                   decorView.removeView(child);
                   //创建哀悼主题布局
                   MourningFramlayout mourningFramlayout = new MourningFramlayout(activity);
                   //将contentview添加到哀悼布局中
                   mourningFramlayout.addView(child);
                   //将哀悼布局添加到decoview中
                   decorView.addView(mourningFramlayout);
              }
...
}

找个之前做的项目试试效果:

pic_d01b3833.png

效果还行,暂时没发现什么坑,但是可能存在坑....😄😄😄

作者:code_balance
来源:https://www.jianshu.com/p/abdebc2c508e


0 个评论

要回复文章请先登录注册