وقتی از برنامهتان استفاده میشود، اطلاعات جدید روی صفحه ظاهر میشود و اطلاعات قدیمی برداشته میشود. تغییر فوری آنچه روی صفحه نشان داده میشود میتواند ناخوشایند باشد و کاربران ممکن است محتوای جدیدی را که ناگهان ظاهر میشود ازدست بدهند. پویانماییها سرعت تغییرات را کاهش میدهند و با حرکت، چشم کاربر را به خود جلب میکنند تا بهروزرسانیها واضحتر شوند.
سه پویانمایی رایج وجود دارد که میتوانید برای نمایش یا پنهان کردن یک نما استفاده کنید: پویانماییهای آشکارسازی، پویانماییهای محو تدریجی متقاطع، و پویانماییهای چرخش کارت.
ایجاد پویانمایی محو تدریجی متقاطع
پویانمایی محو تدریجی متقاطع—که به آن محو تدریجی نیز میگویند—بهتدریج یک View یا ViewGroup را محو میکند و همزمان دیگری را ظاهر میکند. این پویانمایی برای موقعیتهایی مفید است که میخواهید
محتوا یا نماها را در برنامهتان تغییر دهید. پویانمایی محو تدریجی متقاطع که در اینجا نشان داده شده است از
ViewPropertyAnimator استفاده میکند،
که برای Android نسخه ۳.۱ (سطح میانای برنامهسازی کاربردی ۱۲) و بالاتر دردسترس است.
در اینجا نمونهای از محو تدریجی متقاطع از نشانگر پیشرفت به محتوای نوشتاری آورده شده است:
ایجاد کردن بازدیدها
دو نمایی را که میخواهید درهمآمیزی کنید ایجاد کنید. مثال زیر یک نشانگر پیشرفت و نمای نوشتار پیمایشی ایجاد میکند:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_h>eight=<"match_parent"
ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="mat>ch_parent&<quot;
android:layout_height="match_parent"
TextView style="?android:textAppearanceMedium"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_heigh>t=&quo<t;wrap_cont>ent&qu<ot;
android:text="@string/lorem_ipsum"
android:padding="16dp" /
/ScrollView
ProgressBar android:id="@+id/loading_spinner"
style="?android:progress>Ba<rStyleLarge&>quot;
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" /
/FrameLayout
راهاندازی پویانمایی محو شدن تدریجی
برای راهاندازی پویانمایی محو تدریجی متقاطع، کارهای زیر را انجام دهید:
- متغیرهای عضو را برای نماهایی که میخواهید محو متقاطع شوند ایجاد کنید. بعداً هنگام اصلاح نماها درطول پویانمایی به این ارجاعها نیاز دارید.
- رؤیتپذیری نمایی را که درحال محو شدن است روی
GONEتنظیم کنید. این کار مانع از استفاده نما از فضای چیدمان میشود و آن را از محاسبات چیدمان حذف میکند، که باعث افزایش سرعت پردازش میشود - ویژگی سیستم
config_shortAnimTimeرا در متغیر عضو ذخیره کنید. این ویژگی مدتزمان استاندارد «کوتاه» را برای پویانمایی تعریف میکند. این مدت برای پویانماییهای ظریف یا پویانماییهایی که مکرراً اتفاق میافتند ایدهآل است.config_longAnimTimeوconfig_mediumAnimTimeنیز دردسترس است.
در اینجا مثالی بااستفاده از چیدمان تکهکد قبلی بهعنوان نمای محتوای فعالیت آورده شده است:
کاتلین
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_crossfade) contentView = findViewById(R.id.content) loadingView = findViewById(R.id.loading_spinner) // Initially hide the content view. contentView.visibility = View.GONE // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime) } ... }
جاوا
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_crossfade); contentView = findViewById(R.id.content); loadingView = findViewById(R.id.loading_spinner); // Initially hide the content view. contentView.setVisibility(View.GONE); // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = getResources().getInteger( android.R.integer.config_shortAnimTime); } ... }
محو کردن نماها
وقتی نماها بهدرستی تنظیم شدند، با انجام کارهای زیر آنها را درهمآمیزی کنید:
- برای نمایی که درحال ظاهر شدن است، مقدار آلفا را روی ۰ و نمایان بودن را روی
VISIBLEاز تنظیم اولیهGONEتنظیم کنید. این کار باعث میشود نما قابلمشاهده اما شفاف باشد. - برای نمایی که درحال محو شدن است، مقدار آلفای آن را از ۰ به ۱ پویانمایی کنید. برای نمایی که درحال محو شدن است، مقدار آلفا را از ۱ به ۰ پویانمایی کنید.
- بااستفاده از
onAnimationEnd()درAnimator.AnimatorListener، رؤیتپذیری نمایی را که درحال محو شدن است رویGONEتنظیم کنید. بااینکه مقدار آلفا ۰ است، تنظیم رؤیتپذیری نما رویGONEمانع از استفاده نما از فضای چیدمان میشود و آن را از محاسبات چیدمان حذف میکند، که این امر پردازش را سرعت میبخشد.
روش زیر مثالی از نحوه انجام این کار را نشان میدهد:
کاتلین
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... private fun crossfade() { contentView.apply { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. alpha = 0f visibility = View.VISIBLE // Animate the content view to 100% opacity and clear any animation // listener set on the view. animate() .alpha(1f) .setDuration(shortAnimationDuration.toLong()) .setListener(null) } // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration.toLong()) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { loadingView.visibility = View.GONE } }) } }
جاوا
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... private void crossfade() { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. contentView.setAlpha(0f); contentView.setVisibility(View.VISIBLE); // Animate the content view to 100% opacity and clear any animation // listener set on the view. contentView.animate() .alpha(1f) .setDuration(shortAnimationDuration) .setListener(null); // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { loadingView.setVisibility(View.GONE); } }); } }
ایجاد کردن پویانمایی برگرداندن کارت
جابهجایی کارتی با نشان دادن پویانماییای که پشتورو شدن کارت را شبیهسازی میکند، بین نماهای محتوا جابهجا میشود. پویانمایی پشتورو کردن کارت که در اینجا نشان داده شده است از
FragmentTransaction استفاده میکند.
در اینجا نحوه برگرداندن کارت نشان داده شده است:
ایجاد اشیای پویانما
برای ایجاد پویانمایی برگرداندن کارت، به چهار پویانما نیاز دارید. دو پویانما برای زمانی که جلوی کارت به بیرون و سمت چپ پویانمایی میشود و زمانی که به داخل و از سمت چپ پویانمایی میشود وجود دارد. دو پویانماساز دیگر برای زمانی است که پشت کارت از سمت راست پویانمایی میشود و زمانی که به سمت راست پویانمایی میشود.
card_flip_left_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/and>roid&<quot;
!-- Before rotating, immediately set the alph>a to <0. --
objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="al>pha&qu<ot;
an>droid<:duration="0" /
!-- Rotate. --
objectAnimator
android:valueFrom="-180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpol>ator/a<ccelerate_decelerate"
android:duration="@integer/card_f>lip_t<ime_full" /
!-- Halfway through the rotation, set the alpha to 1. See startOffset. --
objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
> < android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" /
/set
card_flip_left_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/and>roid&<quot;
!-- >Rotat<e. --
objectAnimator
android:valueFrom="0"
android:valueTo="180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
> andro<id:duration="@integer/card_flip_time_full" /
!-- Halfway t>hroug<h the rotation, set the alpha to 0. See startOffset. --
objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha">
< > android:startOffset="@integer/card_flip_time_half"
android:duration="1" /
/set
card_flip_right_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/and>roid&<quot;
!-- Before rotating, immediately set the alph>a to <0. --
objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="al>pha&qu<ot;
an>droid<:duration="0" /
!-- Rotate. --
objectAnimator
android:valueFrom="180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpol>ator/a<ccelerate_decelerate"
android:duration="@integer/card_f>lip_t<ime_full" /
!-- Halfway through the rotation, set the alpha to 1. See startOffset. --
objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
> < android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" /
/set
card_flip_right_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/and>roid&<quot;
!-- >Rotat<e. --
objectAnimator
android:valueFrom="0"
android:valueTo="-180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
> andro<id:duration="@integer/card_flip_time_full" /
!-- Halfway t>hroug<h the rotation, set the alpha to 0. See startOffset. --
objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha">
< > android:startOffset="@integer/card_flip_time_half"
android:duration="1" /
/set
ایجاد کردن بازدیدها
هر طرف کارت چیدمان جداگانهای است که میتواند حاوی هر محتوایی که میخواهید باشد، مثل دو نمای نوشتاری، دو تصویر، یا هر ترکیبی از نماها برای جابهجا شدن بین آنها. از دو چیدمان در قطعههایی که بعداً پویانمایی میکنید استفاده کنید. چیدمان زیر یک طرف کارت را ایجاد میکند که نوشتار را نشان میدهد:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#a6c"
a>ndroid<:padding="16dp"
android:gravity="bottom"
TextView android:id="@android:id/text1"
style="?android:textAppearanceLarge"
android:textStyle="bold"
android:textColor="#fff"
android:layout_widt>h=&quo<t;match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_title" /
TextView style="?android:textAppearanceSmall"
android:textAllCaps="true"
android:textColor="#80ffffff"
android:textStyle="bold"
> a<ndroid>:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_description" /
/LinearLayout
و چیدمان بعدی طرف دیگر کارت را ایجاد میکند که
ImageView را نمایش میدهد:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/image1"
android:scaleType="centerCrop"
android:>contentDescription="@string/description_image_1" /
ایجاد قطعات
کلاسهای قطعه را برای جلو و پشت کارت ایجاد کنید. در کلاسهای قطعه، طرحبندیهایی را که از روش
onCreateView()
ایجاد کردهاید برگردانید. سپس میتوانید نمونههایی از این قطعه را در فعالیت ولی ایجاد کنید
که میخواهید کارت را در آن نشان دهید.
مثال زیر کلاسهای قطعه تودرتو را در فعالیت والد نشان میدهد که از آنها استفاده میکند:
کاتلین
class CardFlipActivity : FragmentActivity() { ... /** * A fragment representing the front of the card. */ class CardFrontFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_front, container, false) } /** * A fragment representing the back of the card. */ class CardBackFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_back, container, false) } }
جاوا
public class CardFlipActivity extends FragmentActivity { ... /** * A fragment representing the front of the card. */ public class CardFrontFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_front, container, false); } } /** * A fragment representing the back of the card. */ public class CardBackFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_back, container, false); } } }
پویانمایی چرخاندن کارت
تکهها را در فعالیت والد نمایش دهید. برای انجام این کار، چیدمان
فعالیتتان را ایجاد کنید. مثال زیر یک
FrameLayout ایجاد میکند که میتوانید
تکهها را در زمان اجرا به آن اضافه کنید:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:>layout_height="match_parent" /
در کد فعالیت، نمای محتوا را روی چیدمانی که ایجاد میکنید تنظیم کنید. بهتر است هنگام ایجاد فعالیت، قطعه پیشفرضی نشان داده شود. فعالیت نمونه زیر نشان میدهد که چگونه جلوی کارت را بهطور پیشفرض نمایش دهید:
کاتلین
class CardFlipActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_activity_card_flip) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .add(R.id.container, CardFrontFragment()) .commit() } } ... }
جاوا
public class CardFlipActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_activity_card_flip); if (savedInstanceState == null) { getSupportFragmentManager() .beginTransaction() .add(R.id.container, new CardFrontFragment()) .commit(); } } ... }
وقتی جلوی کارت نشان داده میشود، میتوانید پشت کارت را با پویانمایی پشتورو کردن در زمان مناسب نشان دهید. روشی برای نشان دادن طرف دیگر کارت ایجاد کنید که کارهای زیر را انجام دهد:
- پویانماییهای سفارشیای را که برای گذارهای قطعه ایجاد کردهاید تنظیم میکند.
- قطعه نمایشدادهشده را با قطعه جدیدی جایگزین میکند و این رویداد را با پویانماییهای سفارشیای که ایجاد کردهاید پویانمایی میکند.
- Adds the previously displayed fragment to the fragment back stack, so when the user taps the Back button, the card flips back over.
کاتلین
class CardFlipActivity : FragmentActivity() { ... private fun flipCard() { if (showingBack) { supportFragmentManager.popBackStack() return } // Flip to the back. showingBack = true // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. supportFragmentManager.beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is tapped. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out ) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, CardBackFragment()) // Add this transaction to the back stack, letting users press // the Back button to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit() } }
جاوا
public class CardFlipActivity extends FragmentActivity { ... private void flipCard() { if (showingBack) { getSupportFragmentManager().popBackStack(); return; } // Flip to the back. showingBack = true; // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. getSupportFragmentManager() .beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is pressed. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, new CardBackFragment()) // Add this transaction to the back stack, letting users press // Back to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit(); } }
ایجاد پویانمایی آشکارسازی دایرهای
پویانماییهای آشکارسازی تداوم بصری را برای کاربران فراهم میکنند وقتیکه گروهی از عناصر میانای کاربر را نشان میدهید یا پنهان میکنید. روش
ViewAnimationUtils.createCircularReveal()
به شما امکان میدهد دایره برش را پویانمایی کنید تا نمایی را آشکار یا پنهان کنید. این پویانمایی در کلاس ViewAnimationUtils ارائه شده است که برای Android 5.0 (سطح میانای برنامهسازی کاربردی ۲۱) و بالاتر دردسترس است.
در اینجا مثالی آورده شده است که نشان میدهد چگونه میتوان نمای قبلاً نامرئی را آشکار کرد:
کاتلین
// A previously invisible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the final radius for the clipping circle. val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animator for this view. The start radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius) // Make the view visible and start the animation. myView.visibility = View.VISIBLE anim.start() } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.visibility = View.INVISIBLE }
جاوا
// A previously invisible view. View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the final radius for the clipping circle. float finalRadius = (float) Math.hypot(cx, cy); // Create the animator for this view. The start radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius); // Make the view visible and start the animation. myView.setVisibility(View.VISIBLE); anim.start(); } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.setVisibility(View.INVISIBLE); }
پویانمایی ViewAnimationUtils.createCircularReveal() پنج پارامتر دارد.
پارامتر اول نمایی است که میخواهید در صفحه پنهان یا نشان داده شود. دو پارامتر بعدی مختصات X و Y برای مرکز دایره برش هستند. معمولاً این نقطه مرکز نما است، اما میتوانید از نقطهای که کاربر تکضرب میزند نیز استفاده کنید تا پویانمایی از جایی که کاربر انتخاب میکند شروع شود. پارامتر چهارم شعاع شروع دایره برش است.
در مثال قبلی، شعاع اولیه روی صفر تنظیم شده است تا نمای درحال نمایش با دایره پنهان شود. پارامتر آخر شعاع نهایی دایره است. هنگام نمایش یک نما، شعاع نهایی را بزرگتر از نما قرار دهید تا نما بتواند قبلاز پایان پویانمایی بهطور کامل آشکار شود.
برای پنهان کردن نمایی که قبلاً نمایان بوده است، کارهای زیر را انجام دهید:
کاتلین
// A previously visible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the initial radius for the clipping circle. val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animation. The final radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f) // Make the view invisible when the animation is done. anim.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) myView.visibility = View.INVISIBLE } }) // Start the animation. anim.start() } else { // Set the view to visible without a circular reveal animation below // Android 5.0. myView.visibility = View.VISIBLE }
جاوا
// A previously visible view. final View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the initial radius for the clipping circle. float initialRadius = (float) Math.hypot(cx, cy); // Create the animation. The final radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f); // Make the view invisible when the animation is done. anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); myView.setVisibility(View.INVISIBLE); } }); // Start the animation. anim.start(); } else { // Set the view to visible without a circular reveal animation below Android // 5.0. myView.setVisibility(View.VISIBLE); }
دراینصورت، شعاع اولیه دایره برش بهاندازه نمای موردنظر تنظیم میشود تا نما قبلاز شروع پویانمایی قابلمشاهده باشد. شعاع نهایی روی صفر تنظیم میشود تا وقتی پویانمایی تمام شد، نما پنهان شود.
شنوندهای به پویانمایی اضافه کنید تا رؤیتپذیری نما بتواند روی
INVISIBLE تنظیم شود وقتی پویانمایی
تکمیل میشود.