محو کردن پنجره

در Android 12، میاناهای برنامه‌سازی کاربردی عمومی برای پیاده‌سازی جلوه‌های محو کردن پنجره، مثل محو کردن پس‌زمینه و محو کردن پشت، دردسترس است.

از محو کردن پنجره یا محو کردن بین پنجره‌ها برای محو کردن صفحه پشت پنجره موردنظر استفاده می‌شود. دو نوع محو کردن پنجره وجود دارد که می‌توان از آن‌ها برای دستیابی به جلوه‌های دیداری مختلف استفاده کرد:

  • محو کردن پس‌زمینه به شما امکان می‌دهد پنجره‌هایی با پس‌زمینه‌های محو ایجاد کنید و جلوه شیشه مات ایجاد کنید.

  • محو کردن پشت به شما امکان می‌دهد کل صفحه‌نمایش را در پشت پنجره (گفتگو) محو کنید و جلوه عمق میدان ایجاد کنید.

این دو جلوه را می‌توان به‌صورت جداگانه یا ترکیبی استفاده کرد، همان‌طور که در شکل زیر نشان داده شده است:

فقط محو کردن پس‌زمینه

الف

فقط محو کردن پشت

b

محو کردن پشت و محو کردن پس‌زمینه

c

شکل ۱. فقط محو کردن پس‌زمینه (a)، فقط محو کردن پشت (b)، محو کردن پس‌زمینه و محو کردن پشت (c)

ویژگی محو کردن پنجره در همه پنجره‌ها کار می‌کند، یعنی وقتی برنامه دیگری پشت پنجره شما باشد نیز کار می‌کند. این جلوه با جلوه رندر محو کردن یکسان نیست، که محتوای داخل همان پنجره را محو می‌کند. محو کردن پنجره برای کادرهای گفتگو، برگ‌های زیرین، و دیگر پنجره‌های شناور مفید است.

پیاده‌سازی

توسعه‌دهندگان نرم‌افزار

توسعه‌دهندگان برنامه باید شعاع محوی را برای ایجاد جلوه محو کردن ارائه دهند. شعاع محو کردن میزان تراکم محو کردن را کنترل می‌کند، یعنی هرچه شعاع بیشتر باشد، محو کردن متراکم‌تر است. تاری ۰ پیکسل یعنی بدون تاری. برای محو کردن پشت، شعاع ۲۰ پیکسل جلوه عمق میدان خوبی ایجاد می‌کند، درحالی‌که شعاع محو کردن پس‌زمینه ۸۰ پیکسل جلوه شیشه مات خوبی ایجاد می‌کند. از شعاع محو کردن بیشتر از ۱۵۰ پیکسل استفاده نکنید، زیرا این کار تأثیر قابل‌توجهی بر عملکرد خواهد داشت.

برای دستیابی به جلوه محو موردنظر و افزایش خوانایی، مقدار شعاع محوی را انتخاب کنید که با لایه نیمه‌شفاف رنگ تکمیل شود.

پس‌زمینه محو

از محو کردن پس‌زمینه در پنجره‌های شناور برای ایجاد جلوه پس‌زمینه پنجره استفاده کنید که تصویری محوشده از محتوای زیرین است. برای افزودن پس‌زمینه محو به پنجره، کارهای زیر را انجام دهید:

  1. برای تنظیم شعاع محو کردن پس‌زمینه، Window#setBackgroundBlurRadius(int) را فراخوانی کنید. یا در طرح زمینه پنجره، R.attr.windowBackgroundBlurRadius را تنظیم کنید.

  2. برای شفاف کردن پنجره، R.attr.windowIsTranslucent را روی true تنظیم کنید. محوشدگی در زیر سطح پنجره ترسیم می‌شود، بنابراین پنجره باید نیمه‌شفاف باشد تا محوشدگی قابل‌مشاهده باشد.

  3. درصورت تمایل، Window#setBackgroundDrawableResource(int) را فراخوانی کنید تا یک پس‌زمینه پنجره مستطیلی قابل‌رسم با رنگ نیمه‌شفاف اضافه کنید. یا در زمینه پنجره، R.attr.windowBackground را تنظیم کنید.

  4. برای پنجره‌ای با گوشه‌های گرد، گوشه‌های گرد را برای ناحیه محو با تنظیم کردن ShapeDrawable با گوشه‌های گرد به‌عنوان پس‌زمینه پنجره قابل‌رسم تعیین کنید.

  5. وضعیت‌های فعال و غیرفعال محو کردن را مدیریت کنید. برای اطلاعات بیشتر، به بخش دستورالعمل‌های استفاده از محو کردن پنجره در برنامه‌ها مراجعه کنید.

محو کردن پشت

محو کردن در پشت، کل صفحه پشت پنجره را محو می‌کند. این جلوه برای هدایت توجه کاربر به محتوای پنجره با محو کردن هر چیزی در صفحه پشت پنجره استفاده می‌شود.

برای محو کردن محتوای پشت پنجره، این مراحل را دنبال کنید:

  1. برای فعال کردن محو کردن در پس‌زمینه، FLAG_BLUR_BEHIND را به پرچم‌های پنجره اضافه کنید. یا در زمینه پنجره، R.attr.windowBlurBehindEnabled را تنظیم کنید.

  2. برای تنظیم شعاع محو کردن پشت، WindowManager.LayoutParams#setBlurBehindRadiusتماس بگیرید. یا در زمینه پنجره، R.attr.windowBlurBehindRadius را تنظیم کنید.

  3. درصورت تمایل، مقدار کم‌نور شدن مکمل را انتخاب کنید.

  4. وضعیت‌های فعال و غیرفعال محو کردن را مدیریت کنید. برای اطلاعات بیشتر، به بخش دستورالعمل‌های استفاده از محو کردن پنجره در برنامه‌ها مراجعه کنید.

دستورالعمل‌های استفاده از محو کردن پنجره در برنامه‌ها

پشتیبانی از محو کردن پنجره‌ها به موارد زیر بستگی دارد:

  • نسخه Android: میاناهای برنامه‌سازی کاربردی محو کردن پنجره‌ها فقط در Android نسخه ۱۲ و بالاتر دردسترس است. «کیت توسعه نرم‌افزار» دستگاه را برای نسخه Android بررسی کنید.

  • عملکرد گرافیکی: دستگاه‌هایی که GPU با عملکرد ضعیف‌تری دارند ممکن است از محو کردن پنجره پشتیبانی نکنند.

  • وضعیت سیستم: سرور سیستم ممکن است به‌طور موقت محو کردن پنجره را در زمان اجرا غیرفعال کند، برای مثال، درطول حالت صرفه‌جویی در باتری، هنگام پخش انواع خاصی از محتوای ویدیویی، یا به‌دلیل لغو توسعه‌دهنده.

برای اینکه برنامه‌تان در نسخه‌های Android، دستگاه‌ها، و وضعیت‌های سیستم سازگار باشد، این دستورالعمل‌ها را دنبال کنید:

  • ازطریق WindowManager#addCrossWindowBlurEnabledListener شنونده‌ای اضافه کنید تا هنگام فعال یا غیرفعال شدن محو کردن پنجره‌ها به شما اطلاع دهد. علاوه‌براین، از WindowManager#isCrossWindowBlurEnabled برای پرسمان اینکه آیا محو کردن پنجره درحال‌حاضر فعال است یا نه استفاده کنید.

  • دو نسخه برای پس‌زمینه پنجره پیاده‌سازی کنید تا وضعیت فعال یا غیرفعال بودن محو کردن پنجره را دربر بگیرد.

    وقتی محو کردن فعال باشد، پس‌زمینه پنجره باید نیمه‌شفاف باشد تا محو کردن قابل‌مشاهده باشد. در این حالت، وقتی محو کردن غیرفعال می‌شود، محتوای پنجره مستقیماً با محتوای پنجره زیرین هم‌پوشانی می‌کند و پنجره هم‌پوشانی را کمتر خوانا می‌کند. برای جلوگیری از چنین اثری، وقتی محو کردن پنجره غیرفعال است، واسط کاربر برنامه را به‌صورت زیر تطبیق دهید:

    • برای محو کردن پس‌زمینه، آلفای پس‌زمینه پنجره کشیدنی را افزایش دهید تا مات‌تر شود.

    • برای محو کردن پشت، لایه کم‌نوری با میزان کم‌نوری بیشتر اضافه کنید.

مثالی از محو کردن پشت و محو کردن پس‌زمینه

این بخش نمونه‌ای کاربردی از فعالیتی را ارائه می‌دهد که هم از محو کردن پشت و هم از محو کردن پس‌زمینه استفاده می‌کند.

مثال زیر از MainActivity.java کادر گفتگویی با محوی پشت شعاع ۲۰ پیکسل و محوی پس‌زمینه با شعاع ۸۰ پیکسل است. این پنجره گوشه‌های گرد دارد که در xml در پس‌زمینه پنجره قابل‌رسم تعریف شده است. این API نسخه‌های مختلف Android، دستگاه‌های مختلف (که احتمالاً از محو کردن پنجره پشتیبانی نمی‌کنند)، و تغییرات فعال یا غیرفعال بودن محو کردن زمان اجرا را به‌درستی مدیریت می‌کند. این ویژگی با تنظیم آلفای قابل‌رسم پس‌زمینه پنجره و میزان تار شدن پنجره، تضمین می‌کند که محتوای کادر گفتگو در هریک از این شرایط خوانا باشد.

public class MainActivity extends Activity {

    private final int mBackgroundBlurRadius = 80;
    private final int mBlurBehindRadius = 20;

    // We set a different dim amount depending on whether window blur is enabled or disabled
    private final float mDimAmountWithBlur = 0.1f;
    private final float mDimAmountNoBlur = 0.4f;

    // We set a different alpha depending on whether window blur is enabled or disabled
    private final int mWindowBackgroundAlphaWithBlur = 170;
    private final int mWindowBackgroundAlphaNoBlur = 255;

    // Use a rectangular shape drawable for the window background. The outline of this drawable
    // dictates the shape and rounded corners for the window background blur area.
    private Drawable mWindowBackgroundDrawable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWindowBackgroundDrawable = getDrawable(R.drawable.window_background);
        getWindow().setBackgroundDrawable(mWindowBackgroundDrawable);

        if (buildIsAtLeastS()) {
            // Enable blur behind. This can also be done in xml with R.attr#windowBlurBehindEnabled
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);

            // Register a listener to adjust window UI whenever window blurs are enabled/disabled
            setupWindowBlurListener();
        } else {
            // Window blurs are not available prior to Android S
            updateWindowForBlurs(false /* blursEnabled */);
        }

        // Enable dim. This can also be done in xml, see R.attr#backgroundDimEnabled
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    }

    /**
     * Set up a window blur listener.
     *
     * Window blurs might be disabled at runtime in response to user preferences or system states
     * (e.g. battery saving mode). WindowManager#addCrossWindowBlurEnabledListener allows to
     * listen for when that happens. In that callback we adjust the UI to account for the
     * added/missing window blurs.
     *
     * For the window background blur we adjust the window background drawable alpha:
     *     - lower when window blurs are enabled to make the blur visible through the window
     *       background drawable
     *     - higher when window blurs are disabled to ensure that the window contents are readable
     *
     * For window blur behind we adjust the dim amount:
     *     - higher when window blurs are disabled - the dim creates a depth of field effect,
     *       bringing the user's attention to the dialog window
     *     - lower when window blurs are enabled - no need for a high alpha, the blur behind is
     *       enough to create a depth of field effect
     */
    @RequiresApi(api = Build.VERSION_CODES.S)
    private void setupWindowBlurListener() {
        Consumer<Boolean> windowBlurEnabledListener = this::updateWindowForBlurs;
        getWindow().getDecorView().addOnAttachStateChangeListener(
                new View.OnAttachStateChangeListener() {
                    @Override
                    public void onViewAttachedToWindow(View v) {
                        getWindowManager().addCrossWindowBlurEnabledListener(
                                windowBlurEnabledListener);
                    }

                    @Override
                    public void onViewDetachedFromWindow(View v) {
                        getWindowManager().removeCrossWindowBlurEnabledListener(
                                windowBlurEnabledListener);
                    }
                });
    }

    private void updateWindowForBlurs(boolean blursEnabled) {
        mWindowBackgroundDrawable.setAlpha(blursEnabled && mBackgroundBlurRadius > 0 ?
                mWindowBackgroundAlphaWithBlur : mWindowBackgroundAlphaNoBlur);
        getWindow().setDimAmount(blursEnabled && mBlurBehindRadius > 0 ?
                mDimAmountWithBlur : mDimAmountNoBlur);

        if (buildIsAtLeastS()) {
            // Set the window background blur and blur behind radii
            getWindow().setBackgroundBlurRadius(mBackgroundBlurRadius);
            getWindow().getAttributes().setBlurBehindRadius(mBlurBehindRadius);
            getWindow().setAttributes(getWindow().getAttributes());
        }
    }

    private static boolean buildIsAtLeastS() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
    }
}

برای ایجاد گوشه‌های گرد برای پنجره، پس‌زمینه پنجره را در res/drawable/window_background.xml به‌عنوان ShapeDrawable با گوشه‌های گرد با شعاع ۲۰ dp به‌صورت زیر تعریف می‌کنیم:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
    <corners android:radius="20dp"/>
    <solid android:color="#AAAAAA"/>
</shape>

محو کردن پنجره محتوای پنجره زیر فعالیت را محو می‌کند. تصویر محو زیر پنجره این فعالیت رسم می‌شود، بنابراین پنجره فعالیت باید نیمه‌شفاف باشد تا محو شدن قابل‌مشاهده باشد. برای نیمه‌شفاف کردن پنجره، R.attr.windowIsTranslucent را در زمینه فعالیت به‌صورت زیر تنظیم می‌کنیم:

<style name="Theme.BlurryDialog" parent="Theme.MaterialComponents.Dialog">
    <item name="android:windowIsTranslucent">true</item>
</style>

سازندگان اصلی محصول و شرکا

برای داشتن محو کردن پنجره در دستگاه، سازنده اصلی محصول باید اعلام کند که دستگاه از محو کردن پنجره پشتیبانی می‌کند.

برای بررسی اینکه دستگاهتان از محو کردن پنجره پشتیبانی می‌کند یا نه، مراحل زیر را انجام دهید:

  • مطمئن شوید که دستگاه می‌تواند بار اضافی «واحد پردازش گرافیکی» را تحمل کند. دستگاه‌های رده پایین‌تر ممکن است نتوانند بار اضافی را تحمل کنند، که می‌تواند باعث افت فریم شود. فقط در دستگاه‌های آزمایش‌شده با قدرت کافی واحد پردازش گرافیکی، محو کردن پنجره فعال می‌شود.

  • اگر موتور رندر سفارشی دارید، مطمئن شوید که موتور رندر شما منطق محو کردن را پیاده‌سازی می‌کند. موتور پردازش پیش‌فرض Android 12 منطق محو کردن را در BlurFilter.cpp پیاده‌سازی می‌کند.

پس‌از اینکه مطمئن شدید دستگاهتان از محو کردن پنجره پشتیبانی می‌کند، موارد زیر را در surface flinger sysprop تنظیم کنید:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

اعتبارسنجی

برای تأیید اینکه پنجره برنامه‌تان هنگام جابه‌جایی بین حالت‌های فعال بودن محو کردن و غیرفعال بودن محو کردن به‌درستی مدیریت می‌شود، این مراحل را دنبال کنید:

  1. واسط کاربر دارای محو را باز کنید.

  2. با روشن و خاموش کردن محو کردن پنجره، محو کردن پنجره را فعال یا غیرفعال کنید.

  3. تأیید کنید که رابط کاربری پنجره طبق انتظار به حالت محو تغییر می‌کند و از آن خارج می‌شود.

روشن و خاموش کردن محو کردن پنجره

برای آزمایش نحوه پرداز واسط کاربر پنجره با جلوه محو کردن پنجره، محو کردن را بااستفاده از یکی از روش‌های زیر فعال یا غیرفعال کنید:

  • از «گزینه‌های توسعه‌دهندگان»:

    تنظیمات -> سیستم -> گزینه‌های توسعه‌دهنده -> پردازش با شتاب سخت‌افزاری -> اجازه دادن به محو کردن سطح پنجره

  • از پایانه در دستگاه با دسترسی ریشه‌ای:

    adb shell wm disable-blur 1 # 1 disables window blurs, 0 allows them

برای بررسی اینکه آیا دستگاه Android 12+ شما از محو کردن پنجره پشتیبانی می‌کند یا نه و اینکه آیا محو کردن پنجره درحال‌حاضر فعال است یا نه، adb shell wm disable-blur را در دستگاه ریشه‌یابی‌شده اجرا کنید.

عیب‌یابی

از موارد زیر به‌عنوان راهنمای عیب‌یابی درطول اعتبارسنجی استفاده کنید.

تاری کشیده نشد

  • بررسی کنید که محو کردن درحال‌حاضر فعال باشد و سخت‌افزارتان از آن پشتیبانی کند. به روشن و خاموش کردن محو کردن پنجره مراجعه کنید.

  • مطمئن شوید که رنگ پس‌زمینه پنجره را نیمه‌شفاف تنظیم کرده‌اید. رنگ پس‌زمینه پنجره مات ناحیه محو را پنهان می‌کند.

دستگاه آزمایشی از محو کردن پنجره پشتیبانی نمی‌کند

  • برنامه‌تان را در شبیه‌ساز Android 12 آزمایش کنید. برای راه‌اندازی شبیه‌ساز Android، به راه‌اندازی شبیه‌ساز Android مراجعه کنید. هر دستگاه مجازی Android که با شبیه‌ساز ایجاد می‌کنید از محو کردن پنجره پشتیبانی می‌کند.

بدون گوشه‌های گرد

به‌روزرسانی گزینه توسعه‌دهنده، محو کردن را فعال نمی‌کند

  • بررسی کنید که آیا دستگاه در حالت صرفه‌جویی در باتری است یا از تونل‌سازی چندرسانه‌ای استفاده می‌کند. در برخی‌از دستگاه‌های تلویزیون، ممکن است محو کردن پنجره‌ها درحین بازپخش ویدیو نیز غیرفعال شود.

محو کردن پس‌زمینه به‌صورت تمام‌صفحه، نه در محدوده پنجره

  • برای اطمینان از اینکه پنجره‌تان به‌عنوان شناور علامت‌گذاری شده است، android:windowIsFloating را بررسی کنید

  • مطمئن شوید که window background drawable تنظیم شده باشد. این تنظیم محدوده ناحیه محو را تعیین می‌کند.

به‌روزرسانی‌های شنونده روی صفحه‌نمایش اعمال نمی‌شود

  • به‌روزرسانی‌های شنونده ممکن است روی نمونه پنجره قدیمی اعمال شود. بررسی کنید که آیا پنجره با به‌روزرسانی شنونده صحیح ازبین می‌رود و دوباره ایجاد می‌شود یا نه.