AndroidAPP全局黑白化實現(xiàn)方案-創(chuàng)新互聯(lián)

在清明節(jié)時各大APP都會進行黑白化處理,當時在接到這個需求的時候感覺好麻煩,是不是又要搞一套皮膚?

創(chuàng)新互聯(lián)建站從2013年成立,先為施秉等服務建站,施秉等地企業(yè),進行企業(yè)商務咨詢服務。為施秉企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務解決您的所有建站問題。

然而在一系列搜索之后,找到了兩位大神(鴻洋、U2tzJTNE)的實現(xiàn)方案,其實相當?shù)暮唵危?/p>

讓我們一起站在巨人的肩膀上來分析一下原理,并思考會不會有更簡便的實現(xiàn)?

一、原理

兩位大神的置灰方案是相同的,都能看到一段同樣的代碼:

Paint mPaint = new Paint();
ColorMatrix mColorMatrix = new ColorMatrix();
// 設置飽和度為0
mColorMatrix.setSaturation(0);
mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));

他們都用了Android提供的ColorMatrix(顏色矩陣),將其飽和度設置為0,這樣使用Paint繪制出來的都是沒有飽和度的灰白樣式!

然而兩位在何時使用Paint繪制時選擇了不同方案。

1.1 鴻洋:重寫draw方法

鴻洋分析,如果我們把每個Activity的根布局飽和度設置為0是不是就可以了?

那根布局是誰?

鴻洋分析我們的布局最后setContentView最后都會設置到一個R.id.content的FrameLayout當中。

我們?nèi)プ远x一個GrayFrameLayout,在draw的時候使用這個飽和度為0的畫筆,被這個FrameLayout包裹的布局都會變成黑白。

// 轉(zhuǎn)載自鴻洋
// https://blog.csdn.net/lmj623565791/article/details/105319752
public class GrayFrameLayout extends FrameLayout {
    private Paint mPaint = new Paint();

    public GrayFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        mPaint.setColorFilter(new ColorMatrixColorFilter(cm));
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.dispatchDraw(canvas);
        canvas.restore();
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        canvas.restore();
    }
}

然后我們用GrayFrameLayout去替換這個R.id.content的FrameLayout,是不是就可以做到將頁面黑白化了?

替換FrameLayout的方法可以去【鴻洋】這篇文章下查看。

1.2 U2tzJTNE:監(jiān)聽DecorView的添加

U2tzJTNE大佬 使用了另一種巧妙的方案。

他先創(chuàng)建了一個具有數(shù)據(jù)變化感知能力的ObservableArrayList(當內(nèi)容發(fā)生變化有回調(diào))。

之后使用反射將WindowManagerGlobal內(nèi)的mViews容器(ArrayList,該容器會存放所有的DecorView),替換為ObservableArrayList,這樣就可以監(jiān)聽到每個DecorView的創(chuàng)建,并且拿到View本身。

拿到DecorView,那就可以為所欲為了!

大佬使用了setLayerType(View.LAYER_TYPE_HARDWARE, mPaint),對布局進行了重繪。至于為什么要用LAYER_TYPE_HARDWARE?因為默認的View.LAYER_TYPE_NONE會把Paint強制設置為null。

// 轉(zhuǎn)載自U2tzJTNE
// https://juejin.cn/post/6892277675012915207
public static void enable(boolean enable) {
    try {
        //灰色調(diào)Paint
        final Paint mPaint = new Paint();
        ColorMatrix mColorMatrix = new ColorMatrix();
        mColorMatrix.setSaturation(enable ? 0 : 1);
        mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));

        //反射獲取windowManagerGlobal
        @SuppressLint("PrivateApi")
        ClasswindowManagerGlobal = Class.forName("android.view.WindowManagerGlobal");
        @SuppressLint("DiscouragedPrivateApi")
        java.lang.reflect.Method getInstanceMethod = windowManagerGlobal.getDeclaredMethod("getInstance");
        getInstanceMethod.setAccessible(true);
        Object windowManagerGlobalInstance = getInstanceMethod.invoke(windowManagerGlobal);

        //反射獲取mViews
        Field mViewsField = windowManagerGlobal.getDeclaredField("mViews");
        mViewsField.setAccessible(true);
        Object mViewsObject = mViewsField.get(windowManagerGlobalInstance);

        //創(chuàng)建具有數(shù)據(jù)感知能力的ObservableArrayList
        ObservableArrayListobserverArrayList = new ObservableArrayList<>();
        observerArrayList.addOnListChangedListener(new ObservableArrayList.OnListChangeListener() {
            @Override
            public void onChange(ArrayList list, int index, int count) {
            }

            @Override
            public void onAdd(ArrayList list, int start, int count) {
            	// 拿到DecorView觸發(fā)重繪
                View view = (View) list.get(start);
                if (view != null) {
                    view.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
                }
            }

            @Override
            public void onRemove(ArrayList list, int start, int count) {
            }
        });
        //將原有的數(shù)據(jù)添加到新創(chuàng)建的list
        observerArrayList.addAll((ArrayList) mViewsObject);
        //替換掉原有的mViews
        mViewsField.set(windowManagerGlobalInstance, observerArrayList);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

只需要在Application里面調(diào)用該方法即可。

1.3 方案分析

兩位大佬的方案都非常的棒,咱們理性的來對比一下。

  • 鴻洋: 使用自定義FrameLayout的方案需要一個BaseActivity統(tǒng)一設置,稍顯麻煩,代碼侵入性較強。

  • U2tzJTNE: 方案更加簡單、動態(tài),一行代碼設置甚至可以做到在當前頁從彩色變黑白,但是使用了反射,有一點點性能消耗。

二、簡易方案(直接復制)

既然研究明白了大佬的方案,那有沒有又不需要反射,設置又簡單的方法呢?

能不能使用原生方式獲取DecorView的實例呢?

突然靈光一閃,Application里面不是有registerActivityLifecycleCallbacks這個注冊監(jiān)聽方法嗎?監(jiān)聽里面的onActivityCreated不是可以獲取到當前的Activity嗎?那DecorView不就拿到了!

搞起!上代碼!

public class StudyApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        Paint mPaint = new Paint();
        ColorMatrix mColorMatrix = new ColorMatrix();
        mColorMatrix.setSaturation(0);
        mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));

        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {

            @Override
            public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
            	// 當Activity創(chuàng)建,我們拿到DecorView,使用Paint進行重繪
                View decorView = activity.getWindow().getDecorView();
                decorView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
            }

			....
        });
    }
}

這樣看起來是不是更簡單了!使用了APP原生的方法實現(xiàn)了黑白化!當然也有缺點,因為在Activity級別設置,無法做到在當前頁面即時變?yōu)楹诎住?/p>三、注意事項

這三種方案因為都使用了顏色矩陣,所以坑都是一樣的,請注意。

3.1 啟動圖windowBackground無法變色

在我們可以設置渲染的時候windowBackground已經(jīng)展示完畢了。

解決方案:只能在當前的包里修改,或者不去理會。

3.2 SurfaceView無法變色

因為我們使用了setLayerType進行重繪,而SurfaceView是有獨立的Window,脫離布局內(nèi)的Window,運行在其他線程,不影響主線程的繪制,所以當前方案無法使SurfaceView變色。

解決方案:
1、使用TextureView。
2、看下這個SurfaceView是否可以設置濾鏡,正常都是一些三方或者自制的播放器。

3.3 多進程變色

我們可能會在APP內(nèi)置小程序,小程序基本是運行在單獨的進程中,但是如果我們的黑白配置在運行過程中發(fā)生變化,其他進程是無法感知的。

解決方案:使用 MMKV 存儲黑白配置,并設置多進程共享,在開啟小程序之前都判斷一下黑白展示。

總結(jié)

最后咱們再總結(jié)一下黑白化方案。

使用了ColorMatrix設置飽和度為0,設置到Paint中,讓根布局拿著這個Paint去進行重繪。

這樣APP全局黑白化的介紹就結(jié)束了,希望大家讀完這篇文章,會對APP黑白化有一個更深入的了解。如果我的文章能給大家?guī)硪稽c點的福利,那在下就足夠開心了。

更多Android 知識點歸整

Android 性能調(diào)優(yōu)系列:https://0a.fit/dNHYY

Android 車載學習指南:https://0a.fit/jdVoy

Android Framework核心知識點筆記:https://0a.fit/acnLL

Android 音視頻學習筆記:https://0a.fit/BzPVh

Jetpack全家桶(含Compose):https://0a.fit/GQJSl

Kotlin 入門到精進:https://0a.fit/kdfWR

Flutter 基礎到進階實戰(zhàn):https://0a.fit/xvcHV

Android 八大知識體系:https://0a.fit/mieWJ

Android 中高級面試題錦:https://0a.fit/YXwVq

后續(xù)如有新知識點,將會持續(xù)更新,盡請期待……

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

分享標題:AndroidAPP全局黑白化實現(xiàn)方案-創(chuàng)新互聯(lián)
文章URL:http://muchs.cn/article18/cdicgp.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供建站公司網(wǎng)站制作、全網(wǎng)營銷推廣App設計、網(wǎng)站內(nèi)鏈網(wǎng)站策劃

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設