طراحی انیمیشن های ساده برای اپلیکیشن های اندرویدی — به زبان ساده
انیمیشنهای اندروید روش جالبی برای متمایز ساختن UI محسوب میشوند و برای اطلاعرسانی زمان رخداد تغییر در UI به کاربر نیز مفید هستند. انواع بسیار مختلفی از انیمیشن در اندروید وجود دارند که میتوانند بسیار پیچیده باشند. به همین دلیل در این راهنما قصد داریم به توضیح روش طراحی انیمیشن های ساده برای اپلیکیشن های اندرویدی بپردازیم.
اندروید از انیمیشنها هم در view-ها و هم گذار بین activity-ها پشتیبانی میکند. با این حال افزودن گذار بین اکتیویتی ها نسبتاً امکان جدیدی محسوب میشود. سه نوع سیستم انیمیشن وجود دارند که در حالتهای مختلف به طرز متفاوتی کار میکنند:
- انیمیشنهای مشخصه (Property Animations): این نوع رایجترین نوع انیمیشن محسوب میشود و در اندروید 3.0 معرفی شده است. از آن برای تغییر مشخصههای یک شیء استفاده میشود. اگر میخواهید هنگام کلیک روی یک نما، انیمیشن را مدیریت کنید، میتوانید از انیمیشنهای مشخصه استفاده کنید، زیرا حالت و همچنین رفتار را تغییر میدهند.
- انیمیشنهای نما (View Animations): برای انیمیشنهای سادهای مانند تغییر دادن اندازه، موقعیت و چرخش مورد استفاده قرار میگیرند. ساخت این انیمیشنها آسان است و در عین حال کندتر بوده و انعطافپذیری کمتری نسبت به انیمیشنهای مشخصه دارند. مشکل انیمیشنهای نما این است که گرچه حالت تغییر مییابد، اما مشخصه آن همچنان در موقعیت اصلی باقی میماند. به همین جهت، انیمیشنهای نما از زمان معرفی انیمیشنهای مشخصه دیگر کمتر مورد استفاده قرار میگیرند.
- انیمیشنهای گذار (Transition Animations): این انیمیشن آخرین نوع انیمیشن است که در نسخه اندروید 4.0 معرفی شده است. فریمورک Transitions API امکان تغییر لیآوت را درون یک اکتیویتی فراهم میسازد. گذارهایی از یک نما، یک اکتیویتی یا یک فرگمان وجود دارند. گذارهای اکتیویتی اقدام به انیمیت گذار در زمان ورود یک اکتیویتی به صفحه و هنگامی که یک Intent اجرا میشود میکنند. از سوی دیگر گذارهای فرگمان اقدام به انیمیشن گذار در زمان ورود یا خروج یک فرگمان به صفحه میکنند.
به کمک این فریمورکهای انیمیشن دو نوع دیگر از انیمیشن نیز ارائه شدهاند که کمتر شناخته شدهاند و استفاده میشوند:
- انیمیشنهای لیآوت (Layout Animations): این نوع از انیمیشن به ما امکان میدهد که انیمیشن را روی یک ViewGroup مانند LinearLayout، یک RelativeLayout یا یک ListView اجرا کنیم. با استفاده از Transitions API میتوان انیمیشنهایی برای تغییر نما میتوان معرفی کرد. در نسخههای پایینتر، انیمیشنهای لیآوت همچنان میتوانند فعل باشند، اما روش دیگری برای تعیین شیوه رخداد گذار نمیتوان تعیین نمود.
- انیمیشنهای Drawable: از این نوع انیمیشن برای نمایش drawable-ها در یک توالی سریع استفاده میشود.
انیمیشنهای مشخصه
انگیزه اصلی از معرفی انیمیشنهای مشخصه این بوده است که تا پیش از آن تنها امکان ارائه انیمیشن برای نماهای دارای دکمه، TextView ،LinearLayout و غیره وجود داشت. در این وضعیت تنها امکان جابجایی، چرخش، تغییر مقیاس و فِید کردن یک نما وجود داشت. از این رو ایجاد امکان انیمیت برای مشخصههای یک شیء به جز نما قابلیتی است که انیمیشنهای مشخصه در اختیار ما قرار داده است.
جدول زیر مشخصههایی که به طور معمول روی نماها در انیمیشن مشخصه انیمیت میشوند نشان میدهد:
مشخصه | توضیح |
---|---|
alpha | Fade in or out |
rotation, rotationX, rotationY | Spin or flip |
scaleX, scaleY | Grow or shrink |
x,y,z | Position |
translationX, translationY, translationZ | Offset from Position |
انیمیشن در روش انیمیشن مشخصه به وسیله ValueAnimator (+) اجرا میشود. این کلاس مدت انیمیشن و مقادیر آغاز و پایان مشخصهای که انیمیشن میشود را ردگیری میکند. تصویر زیر دیاگرام UML شیوه تعامل این کلاس را نمایش میدهد:
همچنان که در دیاگرام فوق میبینید، یک ValueAnimator روی دو مشخصه مهم TimeInterpolator و TypeEvaluator عمل میکند. مشخصه TimeInterpolater نرخ تغییرات انیمیشن را ردگیری میکند که امکان داشتن حرکتهای غیرخطی را فراهم میسازد. انواع مختلفی از میانیابیها وجود دارند:
- Accelerate Decelerate (+) – نرخ تغییرات به کندی آغاز و پایان مییابد، اما در میانه شتاب بیشتری دارد.
- Accelerate (+) – نرخ تغییرات با کندی آغاز میشود و سپس شتاب میگیرد.
- Anticipate (+) – تغییرات به سمت عقب آغاز میشود و سپس به سمت جلو نوسان میکند.
- Anticipate Overshoot (+) – تغییرات به سمت عقب آغاز میشود و سپس به سمت جلو نوسان میکند پس از عبور از مقصد نهایی مجدداً به مقدار نهایی بازمیگردد.
- Bounce (+) – تغییرات در انتها دچار جهش میشود.
- Decelerate (+) – نرخ تغییرات به سرعت آغاز میشود و سپس از شتاب آن کاسته میشود.
- Fast Out Linear In (+) – متناظر با fast_out_linear_in (+) در R.interpolator است.
- Fast Out Slow In (+) – متناظر با fast_out_slow_in (+) در R.interpolator است.
- Linear (+) – نرخ تغییرات ثابت است.
- Linear Out Slow In (+) – متناظر با linear_out_slow_in (+) در R.interpolator است.
- Overshoot (+) – تغییرات به سمت جلو آغاز میشود و پس از گذر از مقدار آخر دوباره سمت مقصد نهایی بازمیگردد.
TypeEvaluator (+) اینترفیسی است که امکان ایجاد انیمیشنها روی انواع دلخواه مشخصهها مانند int, float, rgb و غیره یا استفاده از TypeEvaluator سفارشی را میدهد.
انیمیشن با استفاده از ValueAnimator
ValueAnimator به توسعهدهنده امکان داشتن کنترل بیشتری روی انیمیشنها و سفارشیسازی آنها در هر گام اجرا را میدهد. برای مقداردهی اولیه ValueAnimator باید یکی از متدهای زیر را فراخوانی کنید:
- ()ofInt
- ()ofFloat
- ()ofObject
به مثال زیر توجه کنید:
1ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 100f);
در ادامه میتوانید حالت نما را با تنظیم مدت، تکرارها، میانیابی و دیگر مشخصههای ذکر شده فوق که در کد زیر میبینید، تغییر دهید:
1import android.animation.Animator;
2import android.animation.ObjectAnimator;
3import android.animation.ValueAnimator;
4import android.support.v7.app.AppCompatActivity;
5import android.os.Bundle;
6import android.view.View;
7import android.view.animation.AccelerateDecelerateInterpolator;
8import android.view.animation.DecelerateInterpolator;
9import android.widget.Button;
10import android.widget.TextView;
11
12public class MainActivity extends AppCompatActivity {
13
14 @Override
15 protected void onCreate(Bundle savedInstanceState) {
16 super.onCreate(savedInstanceState);
17 setContentView(R.layout.activity_main);
18 final TextView animateTextView = (TextView)findViewById(R.id.animation);
19 // construct the value animator and define the range
20
21 ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 500f);
22 //repeats the animation 2 times
23 valueAnimator.setRepeatCount(2);
24 valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); // increase the speed first and then decrease
25 // animate over the course of 700 milliseconds
26 valueAnimator.setDuration(700);
27// define how to update the view at each "step" of the animation
28 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
29 @Override
30 public void onAnimationUpdate(ValueAnimator animation) {
31 float progress = (float) animation.getAnimatedValue();
32 animateTextView.setRotationX(progress);
33
34 }
35 });
36 valueAnimator.start();
37
38
39 }
40}
همین نتیجه با استفاده از یک فایل resource در مسیر res/animator/valueanimator/ به صورت زیر قابل حصول است:
1<?xml version="1.0" encoding="utf-8"?>
2<animator xmlns:android="http://schemas.android.com/apk/res/android"
3android:duration="700"
4android:interpolator="@android:anim/accelerate_decelerate_interpolator"
5android:valueFrom="0f"
6android:valueTo="500f"
7android:repeat="2"
8android:valueType="floatType" />
در ادامه میتوانید اکتیویتی که میخواهید انیمیت کنید را بنویسید:
1ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(
2 this, R.animator.valueanimator);
3
4valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
5 @Override
6 public void onAnimationUpdate(ValueAnimator animation) {
7 float animatedValue = (float) animation.getAnimatedValue();
8 animateTextView.setRotationX(animatedValue);
9 }
10});
11
12valueAnimator.start();
انیمیشن با استفاده از ObjectAnimator
ObjectAnimator (+) یک کلاس فرعی از ValueAnimator است و از انیمیت کردن مشخصهها روی شیء هدف پشتیبانی میکند. سازندههای ObjectAnimator شیئی را که هدفگیری شده و انیمیت خواهد شد را میگیرند. همچنین نام مشخصهای که قرار است روی شیء اجرا شود را میگیرد. متدهای set و get نیز برای انیمیت بیشتر شیء استفاده میشوند و انیمیشن این متدها را فرا میخواند.
کد زیر نمونهای از شیوه مقداردهی و استفاده از یک ObjectAnimator برای ایجاد دکمهای که با کلیک کردن فید میشود را نمایش میدهد:
1import android.animation.ObjectAnimator;
2import android.support.v7.app.AppCompatActivity;
3import android.os.Bundle;
4import android.view.View;
5import android.view.animation.AccelerateDecelerateInterpolator;
6import android.view.animation.BounceInterpolator;
7import android.view.animation.DecelerateInterpolator;
8import android.widget.Button;
9import android.widget.TextView;
10
11public class MainActivity extends AppCompatActivity {
12
13 @Override
14 protected void onCreate(Bundle savedInstanceState) {
15 super.onCreate(savedInstanceState);
16 setContentView(R.layout.activity_main);
17 final Button animateButton = (Button) findViewById(R.id.buttonanimate);
18
19
20 animateButton.setOnClickListener(new View.OnClickListener() {
21 @Override
22 public void onClick(View v) {
23 ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(animateButton, "alpha",0f);
24 // alpha fades in the button
25 buttonAnimator.start();
26 }
27 });
28
29 }
30}
مشخصههای مورد پشتیبانی از سوی ObjectAnimation شامل ALPHA ،ROTATION ،ROTATION_X ،SCLAE_X ،SCALE_Y ،TRANSLATION_X ،TRANSLATION_Y ،TRANSLATION_Z هستند. مقادیر X ،Y و Z برای بهبود عملکرد این انیمیشنها استفاده میشوند. به مثال زیر توجه کنید:
1ObjectAnimator fade = ObjectAnimator.ofFloat(textView, View.ALPHA, 0f);
2fade.start();
با استفاده از یک روش میانیابی دیگر میتوانیم به این انیمیشن دست پیدا کنیم:
که با کد زیر به دست میآید:
1animateButton.setOnClickListener(new View.OnClickListener() {
2 @Override
3 public void onClick(View v) {
4 ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(animateButton, "translationX",0f, 400f);
5 buttonAnimator.setDuration(3000);
6 buttonAnimator.setInterpolator(new BounceInterpolator());
7 buttonAnimator.start();
8 }
9});
همین وضعیت کد فوق را میتوان با استفاده از یک فایل resource در مسیر /res/animator/objectanimator/ به صورت زیر نمایش داد:
1<?xml version="1.0" encoding="utf-8"?>
2<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
3android:duration="3000" android:interpolator="@android:anim/bounce_interpolator"
4android:propertyName="translationX"
5android:valueFrom="0f"
6android:valueTo="400f"
7android:valueType="floatType" />
سپس در اکتیویتی میتوانید فایل resource را اتصال داده و انیمیشنی مانند زیر به دست آورید:
1ObjectAnimator objectAnimator = (ObjectAnimator) AnimatorInflator.loadAnimator(this, R.animator.objectanimator);
2objectAnimator.setTarget(animateButton);
3objectAnimator.start();
از API 23 به بعد امکان استفاده از PropertyValuesHolder (+) و keyframe در فایلهای resource برای ایجاد انیمیشنهای پیچیدهتر فراهم شده است. PropertyValuesHolders به انیماتورها امکان میدهند که چندین مشخصه را همراه با همدیگر انیمیت کنند. انیمیشنها با استفاده از کیفریمها میتوانند مسیرهای پیچیدهتری را از مقدار آغاز تا پایان طی کنند. به علاوه یک میانیاب اختیاری نیز میتوان ذکر کرد. میانیاب روی بازه بین کیفریم که میانیاب روی آن تنظیم شده و کیفریم قبلی اعمال میشود. زمانی که هیچ میانیابی ارائه نشده باشد، AccelerateDecelerateInterpolator پیشفرض که ابتدا سرعت را افزایش و سپس کاهش میدهد مورد استفاده قرار خواهد گرفت.
گذارها
گذارها در اندروید کیتکت به همراه Scenes و Transitions API معرفی شدند. همه این موارد با استفاده از Transitions API (+) اجرا میشود که اطلاعاتی در مورد انیمیشنهایی که در طی تغییر صحنه (Scene) روی هدف اجرا خواهند شد نگهداری میکند. منظور از صحنه یا Scene، حالت همه نماها در کانتینر لیآوت است. یک گذار به مجموعهای از انیماتورها گفته میشود که روی نماها در صحنه معین اعمال میشوند تا گذار روانی از یک صحنه به صحنه دیگر اجرا کنند. گذارها بین اکتیویتیها و فرگمانها در اندروید 5.0 معرفی شدهاند و مفهوم نسبتاً جدیدی محسوب میشود. از این رو در این بخش روی گذارهای ساده روی نماها تمرکز میکنیم. نمودار زیر شیوه ایجاد یک انیمیشن از سوی فریمورک گذار را نشان میدهد.
برای کارکرد صحیح API گذار حتماً API شما باید 19 یا بالاتر باشد، چون در غیر این صورت کار نخواهد کرد. برای مثال فرض کنید میخواهیم زمانی که دکمهای کلیک میشود، متنی ظاهر شود:
1<?xml version="1.0" encoding="utf-8"?>
2
3<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
4 android:id="@+id/transitions"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 android:gravity="center">
8
9 <Button
10 android:id="@+id/animatebutton"
11 android:layout_width="wrap_content"
12 android:layout_height="wrap_content"
13 android:text="Click Me!"/>
14
15 <TextView
16 android:id="@+id/animatetext"
17 android:layout_width="wrap_content"
18 android:layout_height="wrap_content"
19
20 android:text="Transitions are awesome!"
21 android:visibility="gone"/>
22
23</LinearLayout>
نمایانی TextView را روی gone قرار میدهیم، زیرا نمیخواهیم این متن را تا زمانی که دکمه کلیک نشده است ببینیم. سپس در اکتیویتی کد زیر را وارد میکنیم:
1import android.support.v7.app.AppCompatActivity;
2import android.os.Bundle;
3import android.view.View;
4import android.view.ViewGroup;
5import android.view.Window;
6import android.view.animation.AccelerateDecelerateInterpolator;
7import android.view.animation.BounceInterpolator;
8import android.view.animation.DecelerateInterpolator;
9import android.widget.Button;
10import android.widget.TextView;
11import android.transition.TransitionManager;
12
13public class MainActivity extends AppCompatActivity {
14
15 @Override
16 protected void onCreate(Bundle savedInstanceState) {
17 super.onCreate(savedInstanceState);
18 setContentView(R.layout.activity_main);
19 final ViewGroup transitions = (ViewGroup) findViewById(R.id.transitions);
20 final TextView textView = (TextView) findViewById(R.id.animatetext);
21 final Button transitionbutton = (Button) findViewById(R.id.animatebutton);
22
23 transitionbutton.setOnClickListener(new View.OnClickListener() {
24
25 boolean isVisible;
26
27 @Override
28 public void onClick(View v) {
29 TransitionManager.beginDelayedTransition(transitions);
30 isVisible = !isVisible;
31 textView.setVisibility(isVisible ? View.VISIBLE : View.GONE);
32 }
33
34 });
35}
همانند کاری که در مورد ViewAnimator و ObjectAnimator انجام دادیم، میتوانیم مدت (setDuration(int))، میانیابی (setInterpolator(TimeInterpolator)) و تأخیر (setStartDelway(int)) گذار را تنظیم کنیم.
پکیج جدیدی وجود دارد که امکان پیادهسازی گذارهای بیشتر را روی نماهایی که در Transition API اندروید نیستند فراهم میسازد. این پکیج transitioneverywhere (+) نام دارد و با قرار دادن کد زیر در فایل grade پیادهسازی میشود:
1dependencies {
2 implementation "com.andkulikov:transitionseverywhere:1.8.1"
3}
سپس باید در اکتیویتیهایی که از Transitions API استفاده میکنند، به جای ایمپورت android.transition.* اقدام به ایمپورت کردن com.transitionseverywhere.* بکنید.
گذارهایی که میتوان در این پکیج جدید اجرا کرد، شامل موارد فهرست زیر میشوند:
- ChangeBounds: تغییراتی در موردی موقعیت و اندازه نما انیمیت میکند. در مثال زیر ChangeBounds دکمه را بسته به این که متن نمایش یافته باشد یا نه، جابجا میکند.
- Fade: کلاس Visibility را بسط داده و انیمیشنهای fade in و fade out را اجرا میکند. در مثال زیر اقدام به نمایان و پنهان ساختن TextView میکند.
- TransitionSet: یک گذار است که شامل مجموعهای از گذارهای دیگر میشود. همه آنها میتواند به صورت همزمان و یا به صورت ترتیبی آغاز شوند. برای تغییر دادن آن setOrdering() را فراخوانی کنید.
- AutoTransition: یک کلاس فرعی از TransitionSet است که یک گذار پیشفرض ایجاد میکند که به صورت خودکار نماها را در زمان تغییر یافتن صحنه، فید یا جابجا کرده و تغییر اندازه میدهد.
- Slide: مانند گذار Fade کلاس Visibility را بسط میدهد. بدین ترتیب امکان لغزش یک نما در زمان تغییر یافتن صحنه از یکی از سمتها به داخل صفحه را فراهم میسازد. برای نمونه میتوانید Slide(Gravity.LEFT) را اجرا کنید. این انیمیشن نیازمند API 21 یا بالاتر است. امکان لغزاندن متن از چپ در صورت تنظیم مقدار اولیه visibility روی gone نیز میسر است:
1TransitionManager.beginDelayedTransition(transitionsContainer, new Slide(Gravity.LEFT));
2textView.setVisibility(View.VISIBLE);
- ChangeImageTransform: این مقدار یک تصویر را در موقعیتی که scaleType یک ImageView تغییر مییابد انیمیت میکند. در اغلب موارد به همراه ChangeBounds برای انیمیت موقعیت، اندازه و scaleType یک تصویر استفاده میشود.
- ChangeText: میتوانید متن را با یک کلیک دکمه به صورت زیر نیز تغییر دهید:
1final ViewGroup transitionsContainer = (ViewGroup) findViewById(R.id.transitions);
2final TextView textView = (TextView) findViewById(R.id.animatetext);
3final Button transitionbutton = (Button) findViewById(R.id.animatebutton);
4
5transitionbutton.setOnClickListener(new View.OnClickListener() {
6
7
8 String texts[] = {"First Text", "This is a blog post", "Now we end"};
9 int currentCount = 0;
10
11 @Override
12 public void onClick(View v) {
13 TransitionManager.beginDelayedTransition(transitionsContainer,
14 new ChangeText().setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT));
15 currentCount++;
16 if(currentCount == texts.length){
17 currentCount = 0;
18 }
19 textView.setText(texts[currentCount]);
20
21 }
22
23});
جمعبندی
چنان که در این مقاله در مورد انیمیشنهای اندروید دیدیم، اجرای آنها میتواند کاملاً ساده و جالب باشد. این مقاله تنها بخش کوچکی از انیمیشنها و گذارهای مختلف ممکن در اندروید را معرفی میکند، با این حال همین مقدار نیز به شما کمک میکند که بتوانید به شروع کار با انیمیشنها اقدام کنید. به مستندات اندروید در مورد انیمیشنها و Transition API مراجعه کنید، تا با انیمیشنها پیچیدهتر و حتی گذار بین اکتیویتیها بهتر آشنا شوید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی اندروید
- گنجینه برنامهنویسی اندروید (Android)
- مجموعه آموزشهای برنامهنویسی
- ساخت کتابخانه اندروید و انتشار آن — به زبان ساده
- ساده سازی کد کاتلین با Ktlint — راهنمای کاربردی
==
بسیار عالی.ممنون از توضیحات خوبتون.برای من بسیار مفید بود