App快速實現“哀悼主題”方案

4月4日,今天是國家,為了紀念和哀悼為新冠疫情做出努力和犧牲的烈士以及在新冠中逝去的同胞“舉行全國哀悼”的日子!
今天10時全國停止一切娛樂活動,并默哀3分鐘!至此,各大app(愛奇藝、騰訊、虎牙...)響應號召,統一將app主題更換至“哀悼色”...,本篇文章簡談Android端的一種實現方案。

系統api:saveLayer

saveLayer可以為canvas創(chuàng)建一個新的透明圖層,在新的圖層上繪制,并不會直接繪制到屏幕上,而會在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對比效果:


MourningImageView.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>

image.png

也是可以生效的,那接下來的問題就是如何用最少的代碼替換所有的頁面根布局的問題了。在Activity創(chuàng)建的時候同時會創(chuàng)建一個Window,其中包含一個DecoView,在我們Activity中調用setContentView(),其實就是把它添加到decoView中,可以統一攔截decoview并對其做文章,降低入侵同時減少代碼量。
MourningLinearlayout.png

2.Hook Window DecoView

application中注冊ActivityLifecycleCallbacks,創(chuàng)建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);
                    //創(chuàng)建哀悼主題布局
                    MourningFramlayout mourningFramlayout = new MourningFramlayout(activity);
                    //將contentview添加到哀悼布局中
                    mourningFramlayout.addView(child);
                    //將哀悼布局添加到decoview中
                    decorView.addView(mourningFramlayout);
                }
  ...
}

找個之前做的項目試試效果:


HistoryProject.png

效果還行,暫時沒發(fā)現什么坑,但是可能存在坑....??????

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容