فولد برنامه خود را آگاه کنید

نمایشگرهای تاشو بزرگ و حالت‌های تا شده منحصر به فرد، تجربه کاربری جدیدی را در دستگاه‌های تاشو امکان‌پذیر می‌سازد. برای آگاه کردن برنامه خود، از کتابخانه Jetpack WindowManager استفاده کنید، که یک سطح API برای ویژگی‌های پنجره دستگاه تاشو مانند تا و لولا فراهم می‌کند. وقتی برنامه شما از تا کردن آگاه باشد، می‌تواند طرح‌بندی خود را برای جلوگیری از قرار دادن محتوای مهم در ناحیه چین‌ها یا لولاها تنظیم کند و از چین‌ها و لولاها به عنوان جداکننده طبیعی استفاده کند.

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

اطلاعات پنجره

رابط WindowInfoTracker در Jetpack WindowManager اطلاعات طرح بندی پنجره را در معرض دید قرار می دهد. متد windowLayoutInfo() واسط جریانی از داده‌های WindowLayoutInfo را برمی‌گرداند که برنامه شما را در مورد وضعیت فولد دستگاه تاشو مطلع می‌کند. متد WindowInfoTracker#getOrCreate() نمونه ای از WindowInfoTracker ایجاد می کند.

WindowManager برای جمع آوری داده های WindowLayoutInfo با استفاده از جریان های Kotlin و callbacks جاوا پشتیبانی می کند.

کاتلین جریان دارد

برای شروع و توقف جمع‌آوری داده‌های WindowLayoutInfo ، می‌توانید از یک برنامه‌ریزی با قابلیت راه‌اندازی مجدد استفاده کنید که در آن بلوک کد repeatOnLifecycle زمانی اجرا می‌شود که چرخه حیات حداقل STARTED است و با STOPPED چرخه حیات متوقف می‌شود. اجرای بلوک کد به طور خودکار با شروع مجدد چرخه حیات مجدداً STARTED می شود. در مثال زیر، بلوک کد داده های WindowLayoutInfo را جمع آوری و استفاده می کند:

class DisplayFeaturesActivity : AppCompatActivity() {

    private lateinit var binding: ActivityDisplayFeaturesBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
                    .windowLayoutInfo(this@DisplayFeaturesActivity)
                    .collect { newLayoutInfo ->
                        // Use newLayoutInfo to update the layout.
                    }
            }
        }
    }
}

تماس های جاوا

لایه سازگاری پاسخ به تماس موجود در وابستگی androidx.window:window-java به شما امکان می‌دهد به‌روزرسانی‌های WindowLayoutInfo را بدون استفاده از جریان Kotlin جمع‌آوری کنید. این آرتیفکت شامل کلاس WindowInfoTrackerCallbackAdapter است که یک WindowInfoTracker برای پشتیبانی از ثبت (و لغو ثبت) تماس‌ها برای دریافت به‌روزرسانی‌های WindowLayoutInfo تطبیق می‌دهد، به عنوان مثال:

public class SplitLayoutActivity extends AppCompatActivity {

    private WindowInfoTrackerCallbackAdapter windowInfoTracker;
    private ActivitySplitLayoutBinding binding;
    private final LayoutStateChangeCallback layoutStateChangeCallback =
            new LayoutStateChangeCallback();

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

       windowInfoTracker =
                new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
   }

   @Override
   protected void onStart() {
       super.onStart();
       windowInfoTracker.addWindowLayoutInfoListener(
                this, Runnable::run, layoutStateChangeCallback);
   }

   @Override
   protected void onStop() {
       super.onStop();
       windowInfoTracker
           .removeWindowLayoutInfoListener(layoutStateChangeCallback);
   }

   class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
       @Override
       public void accept(WindowLayoutInfo newLayoutInfo) {
           SplitLayoutActivity.this.runOnUiThread( () -> {
               // Use newLayoutInfo to update the layout.
           });
       }
   }
}

پشتیبانی از RxJava

اگر از RxJava (نسخه 2 یا 3 ) استفاده می کنید، می توانید از مصنوعاتی استفاده کنید که به شما امکان می دهد از Observable یا Flowable برای جمع آوری به روز رسانی های WindowLayoutInfo بدون استفاده از جریان Kotlin استفاده کنید.

لایه سازگاری ارائه شده توسط وابستگی‌های androidx.window:window-rxjava2 و androidx.window:window-rxjava3 شامل متدهای WindowInfoTracker#windowLayoutInfoFlowable() و WindowInfoTracker#windowLayoutInfoObservable() می‌شود که به روز رسانی، برنامه را برای WindowLayoutInfo می‌تواند دریافت کند.

class RxActivity: AppCompatActivity {

    private lateinit var binding: ActivityRxBinding

    private var disposable: Disposable? = null
    private lateinit var observable: Observable<WindowLayoutInfo>

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

        // Create a new observable.
        observable = WindowInfoTracker.getOrCreate(this@RxActivity)
            .windowLayoutInfoObservable(this@RxActivity)
   }

   @Override
   protected void onStart() {
       super.onStart();

        // Subscribe to receive WindowLayoutInfo updates.
        disposable?.dispose()
        disposable = observable
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { newLayoutInfo ->
            // Use newLayoutInfo to update the layout.
        }
   }

   @Override
   protected void onStop() {
       super.onStop();

        // Dispose of the WindowLayoutInfo observable.
        disposable?.dispose()
   }
}

ویژگی های نمایشگرهای تاشو

کلاس WindowLayoutInfo از Jetpack WindowManager ویژگی های یک پنجره نمایش را به عنوان لیستی از عناصر DisplayFeature در دسترس قرار می دهد.

FoldingFeature نوعی از DisplayFeature است که اطلاعاتی در مورد نمایشگرهای تاشو ارائه می دهد، از جمله موارد زیر:

  • state : حالت تا شده دستگاه، FLAT یا HALF_OPENED

  • orientation : جهت چین یا لولا، HORIZONTAL یا VERTICAL

  • occlusionType : چه تاشو یا لولا قسمتی از نمایشگر را پنهان کند، NONE یا FULL

  • isSeparating : این که آیا تاشو یا لولا دو ناحیه نمایش منطقی ایجاد می کند، درست یا نادرست

یک دستگاه تاشو که HALF_OPENED است همیشه isSeparating به عنوان درست گزارش می دهد زیرا صفحه نمایش به دو ناحیه نمایشگر جدا شده است. همچنین، زمانی که برنامه در هر دو صفحه باشد، isSeparating همیشه در دستگاه‌های دو صفحه‌نمایش صادق است.

ویژگی FoldingFeature bounds (که از DisplayFeature به ارث رسیده است) مستطیل محدود کننده یک ویژگی تاشو مانند یک تا یا لولا را نشان می دهد. از کران ها می توان برای قرار دادن عناصر روی صفحه نسبت به ویژگی استفاده کرد:

کاتلین

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch(Dispatchers.Main) {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Safely collects from WindowInfoTracker when the lifecycle is
            // STARTED and stops collection when the lifecycle is STOPPED.
            WindowInfoTracker.getOrCreate(this@MainActivity)
                .windowLayoutInfo(this@MainActivity)
                .collect { layoutInfo ->
                    // New posture information.
                    val foldingFeature = layoutInfo.displayFeatures
                        .filterIsInstance<FoldingFeature>()
                        .firstOrNull()
                    // Use information from the foldingFeature object.
                }

        }
    }
}

جاوا

private WindowInfoTrackerCallbackAdapter windowInfoTracker;
private final LayoutStateChangeCallback layoutStateChangeCallback =
                new LayoutStateChangeCallback();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    windowInfoTracker =
            new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}

@Override
protected void onStart() {
    super.onStart();
    windowInfoTracker.addWindowLayoutInfoListener(
            this, Runnable::run, layoutStateChangeCallback);
}

@Override
protected void onStop() {
    super.onStop();
    windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}

class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
    @Override
    public void accept(WindowLayoutInfo newLayoutInfo) {
        // Use newLayoutInfo to update the Layout.
        List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures();
        for (DisplayFeature feature : displayFeatures) {
            if (feature instanceof FoldingFeature) {
                // Use information from the feature object.
            }
        }
    }
}

وضعیت روی میز

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

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

شکل 1. یک برنامه پخش کننده ویدیو در وضعیت قرارگیری روی میز.

FoldingFeature.State و FoldingFeature.Orientation برای تعیین اینکه آیا دستگاه در وضعیت رومیزی قرار دارد یا خیر استفاده کنید:

کاتلین

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

جاوا

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}

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

در Android 15 (سطح API 35) و بالاتر، می‌توانید یک API همزمان را فراخوانی کنید تا تشخیص دهید که آیا دستگاه بدون توجه به وضعیت فعلی دستگاه، از حالت رومیزی پشتیبانی می‌کند یا خیر.

API لیستی از وضعیت های مورد حمایت دستگاه را ارائه می دهد. اگر فهرست حاوی وضعیت قرارگیری روی میز باشد، می‌توانید طرح‌بندی برنامه‌تان را برای پشتیبانی از وضعیت بدن تقسیم کنید و آزمایش‌های A/B را در رابط کاربری برنامه‌تان برای چیدمان‌های روی میز و تمام صفحه اجرا کنید.

کاتلین

if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) {
    val postures = WindowInfoTracker.getOrCreate(context).supportedPostures
    if (postures.contains(TABLE_TOP)) {
        // Device supports tabletop posture.
   }
}

جاوا

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures();
    if (postures.contains(SupportedPosture.TABLETOP)) {
        // Device supports tabletop posture.
    }
}

نمونه ها

وضعیت کتاب

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

اگر می‌خواهید هنگام عکس‌برداری بدون هندز، نسبت ابعاد متفاوتی را ثبت کنید، می‌تواند برای عکاسی نیز استفاده شود.

وضعیت قرارگیری کتاب را با همان تکنیک هایی که برای وضعیت قرارگیری روی میز استفاده می شود، اجرا کنید. تنها تفاوت این است که کد باید بررسی کند که جهت ویژگی تاشو به جای افقی عمودی باشد:

کاتلین

fun isBookPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
}

جاوا

boolean isBookPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL);
}

اندازه پنجره تغییر می کند

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

کلاس Jetpack WindowManager WindowMetricsCalculator شما را قادر می سازد تا معیارهای فعلی و حداکثر پنجره را بازیابی کنید. مانند پلتفرم WindowMetrics که در سطح API 30 معرفی شده است، WindowManager WindowMetrics محدودیت های پنجره را ارائه می دهد، اما API تا سطح API 14 سازگار است.

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

منابع اضافی

نمونه ها

  • Jetpack WindowManager : مثالی از نحوه استفاده از کتابخانه Jetpack WindowManager
  • Jetcaster : اجرای وضعیت قرارگیری روی میز با Compose

Codelabs

،

نمایشگرهای تاشو بزرگ و حالت‌های تا شده منحصر به فرد، تجربه کاربری جدیدی را در دستگاه‌های تاشو امکان‌پذیر می‌سازد. برای آگاه کردن برنامه خود، از کتابخانه Jetpack WindowManager استفاده کنید، که یک سطح API برای ویژگی‌های پنجره دستگاه تاشو مانند تا و لولا فراهم می‌کند. وقتی برنامه شما از تا کردن آگاه باشد، می‌تواند طرح‌بندی خود را برای جلوگیری از قرار دادن محتوای مهم در ناحیه چین‌ها یا لولاها تنظیم کند و از چین‌ها و لولاها به عنوان جداکننده طبیعی استفاده کند.

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

اطلاعات پنجره

رابط WindowInfoTracker در Jetpack WindowManager اطلاعات طرح بندی پنجره را در معرض دید قرار می دهد. متد windowLayoutInfo() واسط جریانی از داده‌های WindowLayoutInfo را برمی‌گرداند که برنامه شما را در مورد وضعیت فولد دستگاه تاشو مطلع می‌کند. متد WindowInfoTracker#getOrCreate() نمونه ای از WindowInfoTracker ایجاد می کند.

WindowManager برای جمع آوری داده های WindowLayoutInfo با استفاده از جریان های Kotlin و callbacks جاوا پشتیبانی می کند.

کاتلین جریان دارد

برای شروع و توقف جمع‌آوری داده‌های WindowLayoutInfo ، می‌توانید از یک برنامه‌ریزی با قابلیت راه‌اندازی مجدد استفاده کنید که در آن بلوک کد repeatOnLifecycle زمانی اجرا می‌شود که چرخه حیات حداقل STARTED است و با STOPPED چرخه حیات متوقف می‌شود. اجرای بلوک کد به طور خودکار با شروع مجدد چرخه حیات مجدداً STARTED می شود. در مثال زیر، بلوک کد داده های WindowLayoutInfo را جمع آوری و استفاده می کند:

class DisplayFeaturesActivity : AppCompatActivity() {

    private lateinit var binding: ActivityDisplayFeaturesBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
                    .windowLayoutInfo(this@DisplayFeaturesActivity)
                    .collect { newLayoutInfo ->
                        // Use newLayoutInfo to update the layout.
                    }
            }
        }
    }
}

تماس های جاوا

لایه سازگاری پاسخ به تماس موجود در وابستگی androidx.window:window-java به شما امکان می‌دهد به‌روزرسانی‌های WindowLayoutInfo را بدون استفاده از جریان Kotlin جمع‌آوری کنید. این آرتیفکت شامل کلاس WindowInfoTrackerCallbackAdapter است که یک WindowInfoTracker برای پشتیبانی از ثبت (و لغو ثبت) تماس‌ها برای دریافت به‌روزرسانی‌های WindowLayoutInfo تطبیق می‌دهد، به عنوان مثال:

public class SplitLayoutActivity extends AppCompatActivity {

    private WindowInfoTrackerCallbackAdapter windowInfoTracker;
    private ActivitySplitLayoutBinding binding;
    private final LayoutStateChangeCallback layoutStateChangeCallback =
            new LayoutStateChangeCallback();

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

       windowInfoTracker =
                new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
   }

   @Override
   protected void onStart() {
       super.onStart();
       windowInfoTracker.addWindowLayoutInfoListener(
                this, Runnable::run, layoutStateChangeCallback);
   }

   @Override
   protected void onStop() {
       super.onStop();
       windowInfoTracker
           .removeWindowLayoutInfoListener(layoutStateChangeCallback);
   }

   class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
       @Override
       public void accept(WindowLayoutInfo newLayoutInfo) {
           SplitLayoutActivity.this.runOnUiThread( () -> {
               // Use newLayoutInfo to update the layout.
           });
       }
   }
}

پشتیبانی از RxJava

اگر از RxJava (نسخه 2 یا 3 ) استفاده می کنید، می توانید از مصنوعاتی استفاده کنید که به شما امکان می دهد از Observable یا Flowable برای جمع آوری به روز رسانی های WindowLayoutInfo بدون استفاده از جریان Kotlin استفاده کنید.

لایه سازگاری ارائه شده توسط وابستگی‌های androidx.window:window-rxjava2 و androidx.window:window-rxjava3 شامل متدهای WindowInfoTracker#windowLayoutInfoFlowable() و WindowInfoTracker#windowLayoutInfoObservable() می‌شود که به روز رسانی، برنامه را برای WindowLayoutInfo می‌تواند دریافت کند.

class RxActivity: AppCompatActivity {

    private lateinit var binding: ActivityRxBinding

    private var disposable: Disposable? = null
    private lateinit var observable: Observable<WindowLayoutInfo>

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

        // Create a new observable.
        observable = WindowInfoTracker.getOrCreate(this@RxActivity)
            .windowLayoutInfoObservable(this@RxActivity)
   }

   @Override
   protected void onStart() {
       super.onStart();

        // Subscribe to receive WindowLayoutInfo updates.
        disposable?.dispose()
        disposable = observable
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { newLayoutInfo ->
            // Use newLayoutInfo to update the layout.
        }
   }

   @Override
   protected void onStop() {
       super.onStop();

        // Dispose of the WindowLayoutInfo observable.
        disposable?.dispose()
   }
}

ویژگی های نمایشگرهای تاشو

کلاس WindowLayoutInfo از Jetpack WindowManager ویژگی های یک پنجره نمایش را به عنوان لیستی از عناصر DisplayFeature در دسترس قرار می دهد.

FoldingFeature نوعی از DisplayFeature است که اطلاعاتی در مورد نمایشگرهای تاشو ارائه می دهد، از جمله موارد زیر:

  • state : حالت تا شده دستگاه، FLAT یا HALF_OPENED

  • orientation : جهت چین یا لولا، HORIZONTAL یا VERTICAL

  • occlusionType : چه تاشو یا لولا قسمتی از نمایشگر را پنهان کند، NONE یا FULL

  • isSeparating : این که آیا تاشو یا لولا دو ناحیه نمایش منطقی ایجاد می کند، درست یا نادرست

یک دستگاه تاشو که HALF_OPENED است همیشه isSeparating به عنوان درست گزارش می دهد زیرا صفحه نمایش به دو ناحیه نمایشگر جدا شده است. همچنین، زمانی که برنامه در هر دو صفحه باشد، isSeparating همیشه در دستگاه‌های دو صفحه‌نمایش صادق است.

ویژگی FoldingFeature bounds (که از DisplayFeature به ارث رسیده است) مستطیل محدود کننده یک ویژگی تاشو مانند یک تا یا لولا را نشان می دهد. از کران ها می توان برای قرار دادن عناصر روی صفحه نسبت به ویژگی استفاده کرد:

کاتلین

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch(Dispatchers.Main) {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Safely collects from WindowInfoTracker when the lifecycle is
            // STARTED and stops collection when the lifecycle is STOPPED.
            WindowInfoTracker.getOrCreate(this@MainActivity)
                .windowLayoutInfo(this@MainActivity)
                .collect { layoutInfo ->
                    // New posture information.
                    val foldingFeature = layoutInfo.displayFeatures
                        .filterIsInstance<FoldingFeature>()
                        .firstOrNull()
                    // Use information from the foldingFeature object.
                }

        }
    }
}

جاوا

private WindowInfoTrackerCallbackAdapter windowInfoTracker;
private final LayoutStateChangeCallback layoutStateChangeCallback =
                new LayoutStateChangeCallback();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    windowInfoTracker =
            new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}

@Override
protected void onStart() {
    super.onStart();
    windowInfoTracker.addWindowLayoutInfoListener(
            this, Runnable::run, layoutStateChangeCallback);
}

@Override
protected void onStop() {
    super.onStop();
    windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}

class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
    @Override
    public void accept(WindowLayoutInfo newLayoutInfo) {
        // Use newLayoutInfo to update the Layout.
        List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures();
        for (DisplayFeature feature : displayFeatures) {
            if (feature instanceof FoldingFeature) {
                // Use information from the feature object.
            }
        }
    }
}

وضعیت روی میز

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

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

شکل 1. یک برنامه پخش کننده ویدیو در وضعیت قرارگیری روی میز.

FoldingFeature.State و FoldingFeature.Orientation برای تعیین اینکه آیا دستگاه در وضعیت رومیزی قرار دارد یا خیر استفاده کنید:

کاتلین

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

جاوا

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}

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

در Android 15 (سطح API 35) و بالاتر، می‌توانید یک API همزمان را فراخوانی کنید تا تشخیص دهید که آیا دستگاه بدون توجه به وضعیت فعلی دستگاه، از حالت رومیزی پشتیبانی می‌کند یا خیر.

API لیستی از وضعیت های مورد حمایت دستگاه را ارائه می دهد. اگر فهرست حاوی وضعیت قرارگیری روی میز باشد، می‌توانید طرح‌بندی برنامه‌تان را برای پشتیبانی از وضعیت بدن تقسیم کنید و آزمایش‌های A/B را در رابط کاربری برنامه‌تان برای چیدمان‌های روی میز و تمام صفحه اجرا کنید.

کاتلین

if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) {
    val postures = WindowInfoTracker.getOrCreate(context).supportedPostures
    if (postures.contains(TABLE_TOP)) {
        // Device supports tabletop posture.
   }
}

جاوا

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures();
    if (postures.contains(SupportedPosture.TABLETOP)) {
        // Device supports tabletop posture.
    }
}

نمونه ها

وضعیت کتاب

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

اگر می‌خواهید هنگام عکس‌برداری بدون هندز، نسبت ابعاد متفاوتی را ثبت کنید، می‌تواند برای عکاسی نیز استفاده شود.

وضعیت قرارگیری کتاب را با همان تکنیک هایی که برای وضعیت قرارگیری روی میز استفاده می شود، اجرا کنید. تنها تفاوت این است که کد باید بررسی کند که جهت ویژگی تاشو به جای افقی عمودی باشد:

کاتلین

fun isBookPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
}

جاوا

boolean isBookPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL);
}

اندازه پنجره تغییر می کند

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

کلاس Jetpack WindowManager WindowMetricsCalculator شما را قادر می سازد تا معیارهای فعلی و حداکثر پنجره را بازیابی کنید. مانند پلتفرم WindowMetrics که در سطح API 30 معرفی شده است، WindowManager WindowMetrics محدودیت های پنجره را ارائه می دهد، اما API تا سطح API 14 سازگار است.

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

منابع اضافی

نمونه ها

  • Jetpack WindowManager : مثالی از نحوه استفاده از کتابخانه Jetpack WindowManager
  • Jetcaster : اجرای وضعیت قرارگیری روی میز با Compose

Codelabs

،

نمایشگرهای تاشو بزرگ و حالت‌های تا شده منحصر به فرد، تجربه کاربری جدیدی را در دستگاه‌های تاشو امکان‌پذیر می‌سازد. برای آگاه کردن برنامه خود، از کتابخانه Jetpack WindowManager استفاده کنید، که یک سطح API برای ویژگی‌های پنجره دستگاه تاشو مانند تا و لولا فراهم می‌کند. وقتی برنامه شما از تا کردن آگاه باشد، می‌تواند طرح‌بندی خود را برای جلوگیری از قرار دادن محتوای مهم در ناحیه چین‌ها یا لولاها تنظیم کند و از چین‌ها و لولاها به عنوان جداکننده طبیعی استفاده کند.

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

اطلاعات پنجره

رابط WindowInfoTracker در Jetpack WindowManager اطلاعات طرح بندی پنجره را در معرض دید قرار می دهد. متد windowLayoutInfo() واسط جریانی از داده‌های WindowLayoutInfo را برمی‌گرداند که برنامه شما را در مورد وضعیت فولد دستگاه تاشو مطلع می‌کند. متد WindowInfoTracker#getOrCreate() نمونه ای از WindowInfoTracker ایجاد می کند.

WindowManager برای جمع آوری داده های WindowLayoutInfo با استفاده از جریان های Kotlin و callbacks جاوا پشتیبانی می کند.

کاتلین جریان دارد

برای شروع و توقف جمع‌آوری داده‌های WindowLayoutInfo ، می‌توانید از یک برنامه‌ریزی با قابلیت راه‌اندازی مجدد استفاده کنید که در آن بلوک کد repeatOnLifecycle زمانی اجرا می‌شود که چرخه حیات حداقل STARTED است و با STOPPED چرخه حیات متوقف می‌شود. اجرای بلوک کد به طور خودکار با شروع مجدد چرخه حیات مجدداً STARTED می شود. در مثال زیر، بلوک کد داده های WindowLayoutInfo را جمع آوری و استفاده می کند:

class DisplayFeaturesActivity : AppCompatActivity() {

    private lateinit var binding: ActivityDisplayFeaturesBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
                    .windowLayoutInfo(this@DisplayFeaturesActivity)
                    .collect { newLayoutInfo ->
                        // Use newLayoutInfo to update the layout.
                    }
            }
        }
    }
}

تماس های جاوا

لایه سازگاری پاسخ به تماس موجود در وابستگی androidx.window:window-java به شما امکان می‌دهد به‌روزرسانی‌های WindowLayoutInfo را بدون استفاده از جریان Kotlin جمع‌آوری کنید. این آرتیفکت شامل کلاس WindowInfoTrackerCallbackAdapter است که یک WindowInfoTracker برای پشتیبانی از ثبت (و لغو ثبت) تماس‌ها برای دریافت به‌روزرسانی‌های WindowLayoutInfo تطبیق می‌دهد، به عنوان مثال:

public class SplitLayoutActivity extends AppCompatActivity {

    private WindowInfoTrackerCallbackAdapter windowInfoTracker;
    private ActivitySplitLayoutBinding binding;
    private final LayoutStateChangeCallback layoutStateChangeCallback =
            new LayoutStateChangeCallback();

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

       windowInfoTracker =
                new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
   }

   @Override
   protected void onStart() {
       super.onStart();
       windowInfoTracker.addWindowLayoutInfoListener(
                this, Runnable::run, layoutStateChangeCallback);
   }

   @Override
   protected void onStop() {
       super.onStop();
       windowInfoTracker
           .removeWindowLayoutInfoListener(layoutStateChangeCallback);
   }

   class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
       @Override
       public void accept(WindowLayoutInfo newLayoutInfo) {
           SplitLayoutActivity.this.runOnUiThread( () -> {
               // Use newLayoutInfo to update the layout.
           });
       }
   }
}

پشتیبانی از RxJava

اگر از RxJava (نسخه 2 یا 3 ) استفاده می کنید، می توانید از مصنوعاتی استفاده کنید که به شما امکان می دهد از Observable یا Flowable برای جمع آوری به روز رسانی های WindowLayoutInfo بدون استفاده از جریان Kotlin استفاده کنید.

لایه سازگاری ارائه شده توسط وابستگی‌های androidx.window:window-rxjava2 و androidx.window:window-rxjava3 شامل متدهای WindowInfoTracker#windowLayoutInfoFlowable() و WindowInfoTracker#windowLayoutInfoObservable() می‌شود که به روز رسانی، برنامه را برای WindowLayoutInfo می‌تواند دریافت کند.

class RxActivity: AppCompatActivity {

    private lateinit var binding: ActivityRxBinding

    private var disposable: Disposable? = null
    private lateinit var observable: Observable<WindowLayoutInfo>

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

        // Create a new observable.
        observable = WindowInfoTracker.getOrCreate(this@RxActivity)
            .windowLayoutInfoObservable(this@RxActivity)
   }

   @Override
   protected void onStart() {
       super.onStart();

        // Subscribe to receive WindowLayoutInfo updates.
        disposable?.dispose()
        disposable = observable
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { newLayoutInfo ->
            // Use newLayoutInfo to update the layout.
        }
   }

   @Override
   protected void onStop() {
       super.onStop();

        // Dispose of the WindowLayoutInfo observable.
        disposable?.dispose()
   }
}

ویژگی های نمایشگرهای تاشو

کلاس WindowLayoutInfo از Jetpack WindowManager ویژگی های یک پنجره نمایش را به عنوان لیستی از عناصر DisplayFeature در دسترس قرار می دهد.

FoldingFeature نوعی از DisplayFeature است که اطلاعاتی در مورد نمایشگرهای تاشو ارائه می دهد، از جمله موارد زیر:

  • state : حالت تا شده دستگاه، FLAT یا HALF_OPENED

  • orientation : جهت چین یا لولا، HORIZONTAL یا VERTICAL

  • occlusionType : چه تاشو یا لولا قسمتی از نمایشگر را پنهان کند، NONE یا FULL

  • isSeparating : این که آیا تاشو یا لولا دو ناحیه نمایش منطقی ایجاد می کند، درست یا نادرست

یک دستگاه تاشو که HALF_OPENED است همیشه isSeparating به عنوان درست گزارش می دهد زیرا صفحه نمایش به دو ناحیه نمایشگر جدا شده است. همچنین، زمانی که برنامه در هر دو صفحه باشد، isSeparating همیشه در دستگاه‌های دو صفحه‌نمایش صادق است.

ویژگی FoldingFeature bounds (که از DisplayFeature به ارث رسیده است) مستطیل محدود کننده یک ویژگی تاشو مانند یک تا یا لولا را نشان می دهد. از کران ها می توان برای قرار دادن عناصر روی صفحه نسبت به ویژگی استفاده کرد:

کاتلین

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch(Dispatchers.Main) {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Safely collects from WindowInfoTracker when the lifecycle is
            // STARTED and stops collection when the lifecycle is STOPPED.
            WindowInfoTracker.getOrCreate(this@MainActivity)
                .windowLayoutInfo(this@MainActivity)
                .collect { layoutInfo ->
                    // New posture information.
                    val foldingFeature = layoutInfo.displayFeatures
                        .filterIsInstance<FoldingFeature>()
                        .firstOrNull()
                    // Use information from the foldingFeature object.
                }

        }
    }
}

جاوا

private WindowInfoTrackerCallbackAdapter windowInfoTracker;
private final LayoutStateChangeCallback layoutStateChangeCallback =
                new LayoutStateChangeCallback();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    windowInfoTracker =
            new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}

@Override
protected void onStart() {
    super.onStart();
    windowInfoTracker.addWindowLayoutInfoListener(
            this, Runnable::run, layoutStateChangeCallback);
}

@Override
protected void onStop() {
    super.onStop();
    windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}

class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
    @Override
    public void accept(WindowLayoutInfo newLayoutInfo) {
        // Use newLayoutInfo to update the Layout.
        List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures();
        for (DisplayFeature feature : displayFeatures) {
            if (feature instanceof FoldingFeature) {
                // Use information from the feature object.
            }
        }
    }
}

وضعیت روی میز

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

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

شکل 1. یک برنامه پخش کننده ویدیو در وضعیت قرارگیری روی میز.

FoldingFeature.State و FoldingFeature.Orientation برای تعیین اینکه آیا دستگاه در وضعیت رومیزی قرار دارد یا خیر استفاده کنید:

کاتلین

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

جاوا

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}

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

در Android 15 (سطح API 35) و بالاتر، می‌توانید یک API همزمان را فراخوانی کنید تا تشخیص دهید که آیا دستگاه بدون توجه به وضعیت فعلی دستگاه، از حالت رومیزی پشتیبانی می‌کند یا خیر.

API لیستی از وضعیت های مورد حمایت دستگاه را ارائه می دهد. اگر فهرست حاوی وضعیت قرارگیری روی میز باشد، می‌توانید طرح‌بندی برنامه‌تان را برای پشتیبانی از وضعیت بدن تقسیم کنید و آزمایش‌های A/B را در رابط کاربری برنامه‌تان برای چیدمان‌های روی میز و تمام صفحه اجرا کنید.

کاتلین

if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) {
    val postures = WindowInfoTracker.getOrCreate(context).supportedPostures
    if (postures.contains(TABLE_TOP)) {
        // Device supports tabletop posture.
   }
}

جاوا

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures();
    if (postures.contains(SupportedPosture.TABLETOP)) {
        // Device supports tabletop posture.
    }
}

نمونه ها

وضعیت کتاب

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

اگر می‌خواهید هنگام عکس‌برداری بدون هندز، نسبت ابعاد متفاوتی را ثبت کنید، می‌تواند برای عکاسی نیز استفاده شود.

وضعیت قرارگیری کتاب را با همان تکنیک هایی که برای وضعیت قرارگیری روی میز استفاده می شود، اجرا کنید. تنها تفاوت این است که کد باید بررسی کند که جهت ویژگی تاشو به جای افقی عمودی باشد:

کاتلین

fun isBookPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
}

جاوا

boolean isBookPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL);
}

اندازه پنجره تغییر می کند

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

کلاس Jetpack WindowManager WindowMetricsCalculator شما را قادر می سازد تا معیارهای فعلی و حداکثر پنجره را بازیابی کنید. مانند پلتفرم WindowMetrics که در سطح API 30 معرفی شده است، WindowManager WindowMetrics محدودیت های پنجره را ارائه می دهد، اما API تا سطح API 14 سازگار است.

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

منابع اضافی

نمونه ها

  • Jetpack WindowManager : مثالی از نحوه استفاده از کتابخانه Jetpack WindowManager
  • Jetcaster : اجرای وضعیت قرارگیری روی میز با Compose

Codelabs