با استفاده از یک انتقال، چیدمان را متحرک کنید

روش نوشتن را امتحان کنید
Jetpack Compose ابزار رابط کاربری پیشنهادی برای اندروید است. یاد بگیرید که چگونه از انیمیشن‌ها در Compose استفاده کنید.

چارچوب انتقال اندروید با ارائه طرح‌بندی‌های شروع و پایان، به شما امکان می‌دهد انواع حرکت را در رابط کاربری خود متحرک‌سازی کنید. می‌توانید نوع انیمیشن مورد نظر خود را انتخاب کنید - مانند محو شدن نماها به داخل یا خارج، یا تغییر اندازه نماها - و چارچوب انتقال نحوه متحرک‌سازی از طرح‌بندی شروع تا طرح‌بندی پایان را تعیین می‌کند.

چارچوب انتقال شامل ویژگی‌های زیر است:

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

برای نمونه کدی که بین تغییرات طرح‌بندی انیمیشن ایجاد می‌کند، به BasicTransition مراجعه کنید.

فرآیند اساسی برای متحرک‌سازی بین دو طرح‌بندی به شرح زیر است:

  1. یک شیء Scene برای طرح‌بندی‌های شروع و پایان ایجاد کنید. با این حال، صحنه طرح‌بندی شروع اغلب به طور خودکار از طرح‌بندی فعلی تعیین می‌شود.
  2. یک شیء Transition ایجاد کنید تا نوع انیمیشن مورد نظر خود را تعریف کنید.
  3. TransitionManager.go() را فراخوانی کنید، و سیستم انیمیشن را برای تعویض طرح‌بندی‌ها اجرا می‌کند.

نمودار شکل ۱ رابطه بین طرح‌بندی‌ها، صحنه‌ها، انتقال و انیمیشن نهایی شما را نشان می‌دهد.

شکل ۱. تصویر اولیه‌ای از چگونگی ایجاد انیمیشن توسط چارچوب گذار.

یک صحنه ایجاد کنید

صحنه‌ها وضعیت سلسله مراتب نماها، شامل تمام نماها و مقادیر ویژگی‌های آنها را ذخیره می‌کنند. چارچوب انتقال می‌تواند انیمیشن‌ها را بین صحنه شروع و پایان اجرا کند.

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

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

ایجاد صحنه از یک منبع طرح‌بندی

شما می‌توانید یک نمونه Scene را مستقیماً از یک فایل منبع layout ایجاد کنید. از این تکنیک زمانی استفاده کنید که سلسله مراتب view در فایل عمدتاً ایستا باشد. صحنه حاصل، وضعیت سلسله مراتب view را در زمانی که نمونه Scene ایجاد کرده‌اید، نشان می‌دهد. اگر سلسله مراتب view را تغییر دهید، صحنه را دوباره ایجاد کنید. چارچوب، صحنه را از کل سلسله مراتب view در فایل ایجاد می‌کند. شما نمی‌توانید صحنه‌ای را از بخشی از یک فایل layout ایجاد کنید.

برای ایجاد یک نمونه Scene از یک فایل منبع layout، ریشه scene را از layout خود به عنوان یک ViewGroup بازیابی کنید. سپس، تابع Scene.getSceneForLayout() را با ریشه scene و شناسه منبع فایل layout که شامل سلسله مراتب view برای scene است، فراخوانی کنید.

تعریف طرح‌بندی برای صحنه‌ها

قطعه کدهای موجود در ادامه‌ی این بخش، نحوه‌ی ایجاد دو صحنه‌ی متفاوت با عنصر ریشه‌ی یکسان صحنه را نشان می‌دهند. این قطعه کدها همچنین نشان می‌دهند که می‌توانید چندین شیء Scene نامرتبط را بدون اینکه به ارتباط آنها با یکدیگر اشاره شود، بارگذاری کنید.

این مثال شامل تعاریف طرح‌بندی زیر است:

  • طرح‌بندی اصلی یک اکتیویتی با یک برچسب متنی و یک FrameLayout فرزند.
  • یک ConstraintLayout برای صحنه اول با دو فیلد متنی.
  • یک ConstraintLayout برای صحنه دوم با همان دو فیلد متنی اما با ترتیب متفاوت.

این مثال طوری طراحی شده است که تمام انیمیشن‌ها در طرح‌بندی فرزند طرح‌بندی اصلی برای اکتیویتی رخ دهند. برچسب متن در طرح‌بندی اصلی ثابت باقی می‌ماند.

طرح اصلی برای فعالیت به شرح زیر تعریف شده است:

res/layout/activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/master_layout">
    <TextView
        android:id="@+id/title"
        ...
        android:text="Title"/>
    <FrameLayout
        android:id="@+id/scene_root">
        <include layout="@layout/a_scene" />
    </FrameLayout>
</LinearLayout>

این تعریف layout شامل یک فیلد متنی و یک FrameLayout فرزند برای ریشه scene است. layout مربوط به scene اول در فایل layout اصلی گنجانده شده است. این به برنامه اجازه می‌دهد تا آن را به عنوان بخشی از رابط کاربری اولیه نمایش دهد و همچنین آن را در یک scene بارگذاری کند، زیرا فریم ورک فقط می‌تواند یک فایل layout کامل را در یک scene بارگذاری کند.

طرح صحنه اول به شرح زیر تعریف شده است:

res/layout/a_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <TextView
        android:id="@+id/text_view1"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="Text Line 1"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>
    <TextView
        android:id="@+id/text_view2"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="Text Line 2"
        app:layout_constraintTop_toBottomOf="@id/text_view1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

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

res/layout/another_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <TextView
        android:id="@+id/text_view2"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="Text Line 2"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    <TextView
        android:id="@+id/text_view1"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="Text Line 1"
        app:layout_constraintTop_toBottomOf="@id/text_view2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

تولید صحنه‌ها از طرح‌بندی‌ها

پس از ایجاد تعاریف برای دو طرح‌بندی محدودیت، می‌توانید برای هر یک از آنها یک صحنه (scene) ایجاد کنید. این به شما امکان می‌دهد بین دو پیکربندی رابط کاربری جابجا شوید. برای به دست آوردن یک صحنه، به یک ارجاع به ریشه صحنه و شناسه منبع طرح‌بندی نیاز دارید.

قطعه کد زیر نحوه دریافت ارجاع به ریشه صحنه و ایجاد دو شیء Scene از فایل‌های طرح‌بندی را نشان می‌دهد:

کاتلین

val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
val aScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this)
val anotherScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this)

جاوا

Scene aScene;
Scene anotherScene;

// Create the scene root for the scenes in this app.
sceneRoot = (ViewGroup) findViewById(R.id.scene_root);

// Create the scenes.
aScene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this);
anotherScene =
    Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this);

در برنامه، اکنون دو شیء Scene بر اساس سلسله مراتب نما وجود دارد. هر دو صحنه از ریشه صحنه که توسط عنصر FrameLayout در res/layout/activity_main.xml تعریف شده است، استفاده می‌کنند.

یک صحنه در کد خود ایجاد کنید

همچنین می‌توانید یک نمونه Scene را در کد خود از یک شیء ViewGroup ایجاد کنید. از این تکنیک هنگام تغییر سلسله مراتب نماها به طور مستقیم در کد خود یا هنگام ایجاد آنها به صورت پویا استفاده کنید.

برای ایجاد یک صحنه از سلسله مراتب نما در کد خود، از سازنده‌ی Scene(sceneRoot, viewHierarchy) استفاده کنید. فراخوانی این سازنده معادل فراخوانی تابع Scene.getSceneForLayout() است، زمانی که قبلاً یک فایل طرح‌بندی را inflate کرده‌اید.

قطعه کد زیر نحوه ایجاد یک نمونه Scene از عنصر ریشه Scene و سلسله مراتب نمای Scene در کد شما را نشان می‌دهد:

کاتلین

val sceneRoot = someLayoutElement as ViewGroup
val viewHierarchy = someOtherLayoutElement as ViewGroup
val scene: Scene = Scene(sceneRoot, viewHierarchy)

جاوا

Scene mScene;

// Obtain the scene root element.
sceneRoot = (ViewGroup) someLayoutElement;

// Obtain the view hierarchy to add as a child of
// the scene root when this scene is entered.
viewHierarchy = (ViewGroup) someOtherLayoutElement;

// Create a scene.
mScene = new Scene(sceneRoot, mViewHierarchy);

ایجاد اکشن‌های صحنه

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

اقدامات صحنه برای رسیدگی به این موارد مفید هستند:

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

برای ارائه اکشن‌های صحنه سفارشی، اکشن‌های خود را به عنوان اشیاء Runnable تعریف کنید و آنها را به توابع Scene.setExitAction() یا Scene.setEnterAction() ارسال کنید. این فریم‌ورک قبل از اجرای انیمیشن انتقال، تابع setExitAction() را در صحنه شروع و پس از اجرای انیمیشن انتقال، تابع setEnterAction() را در صحنه پایان فراخوانی می‌کند.

اعمال یک گذار

چارچوب گذار، سبک انیمیشن بین صحنه‌ها را با یک شیء Transition نشان می‌دهد. می‌توانید با استفاده از زیرکلاس‌های داخلی، مانند AutoTransition و Fade ، یک Transition نمونه‌سازی کنید، یا گذار خودتان را تعریف کنید . سپس، می‌توانید انیمیشن را بین صحنه‌ها با ارسال Scene انتهایی و Transition به TransitionManager.go() اجرا کنید.

چرخه حیات گذار (transition lifecycle) مشابه چرخه حیات فعالیت (activity lifecycle) است و نشان‌دهنده حالت‌های گذار (transition states) است که چارچوب (framework) بین شروع و تکمیل یک انیمیشن نظارت می‌کند. در حالت‌های مهم چرخه حیات، چارچوب توابع فراخوانی (callback functions) را فراخوانی می‌کند که می‌توانید برای تنظیم رابط کاربری خود در مراحل مختلف گذار پیاده‌سازی کنید.

ایجاد یک گذار

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

جدول 1. انواع انتقال داخلی.

کلاس برچسب اثر
AutoTransition <autoTransition/> انتقال پیش‌فرض. به ترتیب محو می‌شود، حرکت می‌کند و تغییر اندازه می‌دهد و در نماها محو می‌شود.
ChangeBounds <changeBounds/> نماها را جابجا و تغییر اندازه می‌دهد.
ChangeClipBounds <changeClipBounds/> تابع View.getClipBounds() قبل و بعد از تغییر صحنه را ثبت می‌کند و آن تغییرات را در طول انتقال متحرک‌سازی می‌کند.
ChangeImageTransform <changeImageTransform/> ماتریس یک ImageView را قبل و بعد از تغییر صحنه ضبط می‌کند و آن را در طول انتقال متحرک‌سازی می‌کند.
ChangeScroll <changeScroll/> ویژگی‌های اسکرول اهداف را قبل و بعد از تغییر صحنه ضبط می‌کند و هرگونه تغییری را متحرک‌سازی می‌کند.
ChangeTransform <changeTransform/> مقیاس و چرخش نماها را قبل و بعد از تغییر صحنه ضبط می‌کند و آن تغییرات را در طول انتقال متحرک‌سازی می‌کند.
Explode <explode/> تغییرات در میزان دید نماهای هدف در صحنه‌های شروع و پایان را ردیابی می‌کند و نماها را از لبه‌های صحنه به داخل یا خارج حرکت می‌دهد.
Fade <fade/> fade_in باعث محو شدن نماها می‌شود.
fade_out نماها را محو می‌کند.
fade_in_out (پیش‌فرض) یک fade_out و به دنبال آن یک fade_in انجام می‌دهد.
Slide <slide/> تغییرات در میزان دید نماهای هدف در صحنه‌های شروع و پایان را ردیابی می‌کند و نماها را از یکی از لبه‌های صحنه به داخل یا خارج حرکت می‌دهد.

ایجاد یک نمونه گذار از یک فایل منبع

این تکنیک به شما امکان می‌دهد تعریف گذار خود را بدون تغییر کد اکتیویتی خود تغییر دهید. این تکنیک همچنین برای جدا کردن تعاریف پیچیده گذار از کد برنامه شما مفید است، همانطور که در بخش مربوط به تعیین چندین گذار نشان داده شده است.

برای مشخص کردن یک گذار داخلی در یک فایل منبع، مراحل زیر را دنبال کنید:

  • پوشه res/transition/ را به پروژه خود اضافه کنید.
  • یک فایل منبع XML جدید درون این دایرکتوری ایجاد کنید.
  • یک گره XML برای یکی از انتقال‌های داخلی اضافه کنید.

برای مثال، فایل منبع زیر، گذار Fade را مشخص می‌کند:

res/transition/fade_transition.xml

<fade xmlns:android="http://schemas.android.com/apk/res/android" />

قطعه کد زیر نحوه‌ی inflate کردن یک نمونه‌ی Transition درون activity شما از یک فایل منبع را نشان می‌دهد:

کاتلین

var fadeTransition: Transition =
    TransitionInflater.from(this)
                      .inflateTransition(R.transition.fade_transition)

جاوا

Transition fadeTransition =
        TransitionInflater.from(this).
        inflateTransition(R.transition.fade_transition);

یک نمونه گذار در کد خود ایجاد کنید

این تکنیک برای ایجاد اشیاء گذار به صورت پویا در صورت تغییر رابط کاربری در کد و ایجاد نمونه‌های گذار ساده داخلی با پارامترهای کم یا بدون پارامتر مفید است.

برای ایجاد یک نمونه از یک گذار داخلی، یکی از سازنده‌های عمومی را در زیرکلاس‌های کلاس Transition فراخوانی کنید. برای مثال، قطعه کد زیر یک نمونه از گذار Fade ایجاد می‌کند:

کاتلین

var fadeTransition: Transition = Fade()

جاوا

Transition fadeTransition = new Fade();

اعمال یک گذار

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

برای ایجاد تغییر در صحنه هنگام اعمال یک گذار در پاسخ به یک رویداد در اکتیویتی خود، تابع کلاس TransitionManager.go() را به همراه صحنه پایانی و نمونه گذار مورد استفاده برای انیمیشن فراخوانی کنید، همانطور که در قطعه کد زیر نشان داده شده است:

کاتلین

TransitionManager.go(endingScene, fadeTransition)

جاوا

TransitionManager.go(endingScene, fadeTransition);

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

اگر نمونه‌ای از گذار (transition) مشخص نکنید، مدیر گذار می‌تواند یک گذار خودکار اعمال کند که در بیشتر مواقع عملکرد معقولی دارد. برای اطلاعات بیشتر، به مرجع API برای کلاس TransitionManager مراجعه کنید.

نماهای هدف خاص را انتخاب کنید

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

هر نمایی که این گذار آن را متحرک می‌کند، یک هدف (target) نامیده می‌شود. شما فقط می‌توانید اهدافی را انتخاب کنید که بخشی از سلسله مراتب نمای مرتبط با یک صحنه باشند.

برای حذف یک یا چند نما از لیست اهداف، قبل از شروع انتقال، متد removeTarget() را فراخوانی کنید. برای اضافه کردن فقط نماهایی که مشخص می‌کنید به لیست اهداف، تابع addTarget() را فراخوانی کنید. برای اطلاعات بیشتر، به مرجع API برای کلاس Transition مراجعه کنید.

چندین انتقال را مشخص کنید

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

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

برای تعریف یک مجموعه انتقال از مجموعه‌ای از انتقال‌ها در XML، یک فایل منبع در دایرکتوری res/transitions/ ایجاد کنید و انتقال‌ها را در زیر عنصر TransitionSet فهرست کنید. برای مثال، قطعه کد زیر نحوه تعیین یک مجموعه انتقال را نشان می‌دهد که رفتاری مشابه کلاس AutoTransition دارد:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="sequential">
    <fade android:fadingMode="fade_out" />
    <changeBounds />
    <fade android:fadingMode="fade_in" />
</transitionSet>

برای اینکه مجموعه انتقال را در کد خود به یک شیء TransitionSet تبدیل کنید، تابع TransitionInflater.from() را در activity خود فراخوانی کنید. کلاس TransitionSet از کلاس Transition ارث‌بری می‌کند، بنابراین می‌توانید مانند هر نمونه Transition دیگر، از آن با یک مدیر انتقال استفاده کنید.

اعمال یک گذار بدون صحنه

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

برای مثال، می‌توانید یک تعامل جستجو را با یک طرح‌بندی واحد پیاده‌سازی کنید. با طرحی شروع کنید که یک فیلد ورودی جستجو و یک آیکون جستجو را نشان می‌دهد. برای تغییر رابط کاربری برای نمایش نتایج، وقتی کاربر دکمه جستجو را لمس می‌کند، با فراخوانی تابع ViewGroup.removeView() آن را حذف کنید و با فراخوانی تابع ViewGroup.addView() نتایج جستجو را اضافه کنید.

اگر گزینه‌ی جایگزین، داشتن دو سلسله مراتب تقریباً یکسان باشد، می‌توانید از این رویکرد استفاده کنید. به جای ایجاد و نگهداری دو فایل طرح‌بندی جداگانه برای یک تفاوت جزئی در رابط کاربری، می‌توانید یک فایل طرح‌بندی داشته باشید که شامل یک سلسله مراتب نما باشد که می‌توانید آن را در کد تغییر دهید.

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

برای ایجاد یک گذار تأخیری در یک سلسله مراتب نمای واحد، این مراحل را دنبال کنید:

  1. وقتی رویدادی که باعث انتقال می‌شود رخ می‌دهد، تابع TransitionManager.beginDelayedTransition() را فراخوانی کنید و نمای والد تمام نماهایی که می‌خواهید تغییر دهید و انتقال مورد استفاده را ارائه دهید. این چارچوب وضعیت فعلی نماهای فرزند و مقادیر ویژگی‌های آنها را ذخیره می‌کند.
  2. طبق نیاز مورد استفاده خود، تغییراتی را در نماهای فرزند ایجاد کنید. چارچوب، تغییراتی را که در نماهای فرزند و ویژگی‌های آنها ایجاد می‌کنید، ثبت می‌کند.
  3. وقتی سیستم رابط کاربری را بر اساس تغییرات شما دوباره ترسیم می‌کند، فریم‌ورک تغییرات بین حالت اولیه و حالت جدید را متحرک‌سازی می‌کند.

مثال زیر نحوه‌ی متحرک‌سازی افزودن یک نمای متنی به سلسله مراتب نماها را با استفاده از یک انتقال تأخیری نشان می‌دهد. قطعه کد اول، فایل تعریف طرح‌بندی را نشان می‌دهد:

res/layout/activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <EditText
        android:id="@+id/inputText"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    ...
</androidx.constraintlayout.widget.ConstraintLayout>

قطعه کد بعدی، کدی را نشان می‌دهد که افزودن نمای متن را متحرک‌سازی می‌کند:

فعالیت اصلی

کاتلین

setContentView(R.layout.activity_main)
val labelText = TextView(this).apply {
    text = "Label"
    id = R.id.text
}
val rootView: ViewGroup = findViewById(R.id.mainLayout)
val mFade: Fade = Fade(Fade.IN)
TransitionManager.beginDelayedTransition(rootView, mFade)
rootView.addView(labelText)

جاوا

private TextView labelText;
private Fade mFade;
private ViewGroup rootView;
...
// Load the layout.
setContentView(R.layout.activity_main);
...
// Create a new TextView and set some View properties.
labelText = new TextView(this);
labelText.setText("Label");
labelText.setId(R.id.text);

// Get the root view and create a transition.
rootView = (ViewGroup) findViewById(R.id.mainLayout);
mFade = new Fade(Fade.IN);

// Start recording changes to the view hierarchy.
TransitionManager.beginDelayedTransition(rootView, mFade);

// Add the new TextView to the view hierarchy.
rootView.addView(labelText);

// When the system redraws the screen to show this update,
// the framework animates the addition as a fade in.

تعریف فراخوانی‌های چرخه حیات گذار

چرخه حیات گذار (transition lifecycle) مشابه چرخه حیات فعالیت (activity lifecycle) است. این چرخه، حالت‌های گذار (transition) را نشان می‌دهد که چارچوب در طول دوره بین فراخوانی تابع TransitionManager.go() و تکمیل انیمیشن، آنها را نظارت می‌کند. در حالت‌های مهم چرخه حیات، چارچوب، فراخوانی‌های برگشتی (callbacks) تعریف شده توسط رابط TransitionListener فراخوانی می‌کند.

فراخوانی‌های چرخه عمر انتقال مفید هستند، برای مثال، برای کپی کردن مقدار یک ویژگی نما از سلسله مراتب نمای شروع به سلسله مراتب نمای پایان در طول تغییر صحنه. شما نمی‌توانید به سادگی مقدار را از نمای شروع به نمای در سلسله مراتب نمای پایان کپی کنید، زیرا سلسله مراتب نمای پایان تا زمانی که انتقال کامل نشده باشد، افزایش نمی‌یابد. در عوض، باید مقدار را در یک متغیر ذخیره کنید و سپس وقتی چارچوب انتقال را تمام کرد، آن را در سلسله مراتب نمای پایان کپی کنید. برای اینکه از اتمام انتقال مطلع شوید، تابع TransitionListener.onTransitionEnd() را در فعالیت خود پیاده‌سازی کنید.

برای اطلاعات بیشتر، به مرجع API برای کلاس TransitionListener مراجعه کنید.

محدودیت‌ها

این بخش برخی از محدودیت‌های شناخته‌شده‌ی چارچوب انتقال را فهرست می‌کند:

  • انیمیشن‌های اعمال شده روی SurfaceView ممکن است به درستی نمایش داده نشوند. نمونه‌های SurfaceView از یک thread غیر UI به‌روزرسانی می‌شوند، بنابراین به‌روزرسانی‌ها ممکن است با انیمیشن‌های سایر نماها هماهنگ نباشند.
  • برخی از انواع خاص گذار ممکن است هنگام اعمال به TextureView جلوه انیمیشن مورد نظر را ایجاد نکنند.
  • کلاس‌هایی که AdapterView ارث‌بری می‌کنند، مانند ListView ، نماهای فرزند خود را به روش‌هایی مدیریت می‌کنند که با چارچوب transitions سازگار نیست. اگر سعی کنید یک نما را بر اساس AdapterView متحرک‌سازی کنید، ممکن است صفحه نمایش دستگاه از کار بیفتد.
  • اگر سعی کنید اندازه یک TextView را با انیمیشن تغییر دهید، متن قبل از اینکه شیء به طور کامل تغییر اندازه دهد، به مکان جدیدی منتقل می‌شود. برای جلوگیری از این مشکل، تغییر اندازه نماهایی که حاوی متن هستند را انیمیشن نکنید.