Viblo CTF
+1

Property Animation in Android.

Ở bài trước (http://viblo.framgia.vn/le.van.ban/posts/zoZVRgYQGmg5) chúng ta đã làm quen với việc sử dụng view animation để tạo ảnh động, hay các hiệu ứng một cách đơn giản. Lần này, chúng ta tiếp cận theo một khía cạnh khác, đó là sử dụng property animation để tạo các hiệu ứng, ảnh động. Khi sử dụng property thì chúng ta có thể tạo ra được các hiệu ứng một cách linh hoạt hơn, nhiều lựa chọn hơn.

Để hiểu rõ hơn về cách sử dụng property animation, chúng ta cùng làm một ví dụ nhỏ tại http://code.tutsplus.com/tutorials/android-sdk-creating-a-simple-property-animation--mobile-15022. Ở đây, mình sẽ trình bày lại theo ý hiểu của mình và sẽ giúp các bạn có một cách nhìn tổng quan hơn, tư duy một cách chính xác hơn khi có ý định thể hiện một màn hình sinh động với nhiều hình ảnh đan xen nhau…

https://www.youtube.com/watch?v=PothsN_ijh4&feature=youtu.be

Ở trên chính là kết quả của ví dụ nhỏ trên. Như video ta thấy ở đó, ta thấy bố cục của màn hình được chia làm 2 loại hình ảnh là ảnh tĩnh và ảnh động. Đối với tay lái ta thấy nó có thể chuyển động tròn và hiển thì lên trên cả khung màu xám bên ngoài mà khi mặt trời và đám mây chuyển động ra ngoài thì sẽ bị che đi. Đó chính là do sự sắp xếp thứ tự view với “RelativeLayout”. Khi sử dụng “RelativeLayout” thì các bạn nên chú ý, các hình hay view được viết hay chèn vào sau thì nó sẽ đè lên các hình đã được hiện thì trước đó. Trừ khi ta làm mờ chúng đi với thuộc tính alpha như bài trước ta đã đề cập.

Đối với các hình đơn giản (hình chữ nhật, tròn, vuông …) ta có thể sử dụng “drawables” để tạo ra chúng mà không cần tới các hình ảnh, việc này sẽ giúp ta tiết kiệm được bộ nhớ hay dung lượng của ứng dụng.

Với ví dụ trên, ta sử dụng file xml trong thư mục “drawables” để tạo ra các hình mặt trời, nền xanh lá cây, và khung cửa sổ màu xám.

drawalbe_sun.png

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:shape="oval" >

    <gradient
        android:endColor="#ffff6600"
        android:gradientRadius="150"
        android:startColor="#ffffcc00"
        android:type="radial"
        android:useLevel="false" />

    <size
        android:height="50dp"
        android:width="50dp" />

</shape>

drawalbe_ground.png

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:shape="rectangle" >

    <solid android:color="#339933" />

</shape>

drawalbe_window.png

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

    <solid android:color="#00000000" />

    <stroke
        android:width="40dp"
        android:color="#cccccc" />

</shape>

Còn đối với khung hình màu xanh da trời bên trên màu xanh dương, thì đó chính là màu nền và ta thay đổi độ sáng tối của nó để tạo ra hiệu ứng. Chính vì vậy mà ta không cần phải sử dụng drawables hay một hình ảnh nào đó để thay thế. Đối với hình phức tạp như tay lái và đám mây thì ta phải sử dụng hình ảnh để hiện thị(ImageView). Các bạn có thể down trực tiếp ở bài trên hay lưu lại 2 ảnh đưới đây.

cloud.png steering_wheel.png

Như vậy, chúng ta đã chuẩn bị được đầy đủ các view cần thiết để tạo ra ứng dụng đơn giản này. Việc cần làm bây giờ chính là sắp xếp chúng và tạo các hiệu ứng cho chúng để được kết quả như ý. Đầu tiên, chúng ta xem xét tới thứ tự xuất hiện của các view trong layout. Như chúng ta đã biết, với các hình ảnh 2D thì các vật ở gần sẽ che khuất các vật ở xa, như vậy thì các view ở càng xa so với hướng nhìn thì sẽ được đặt trước so với các vật ở gần. Đối với các view mà không có sự liên quan tới nhau hay là không chồng chéo lên nhau thì ta có thể để theo thứ tự nào cũng được. Ở đây ta thấy nền xanh lá cây ở dưới với mặt trời và mây thì ko có sự chuyển động chồng lên nhau nên ta có thể đổi vị trí của chúng. Chính vì vậy mà ta sẽ sắp xếp thứ tự các view trong layout này như sau:

Mặt trời -> mây -> nền xanh lá cây => khung cửa => bánh lái.

Ở ví dụ này, với các view ta đều sử dụng ImageView với thuộc tính android:src=”@drawable/xxx” như vậy thì ta tạo được một view dưới dạng hình ảnh được định nghĩa bởi một file có tên là xxx(có thể là một file xml hay một hình ảnh…) trong thư mục “drawables”. Một hình ảnh trong “drawables” có thể được sử dụng nhiều lần. Như vậy ta có file layout như sau:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/car_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#66ccff"
    tools:context=".PropertyAnimatedActivity" >

    <ImageView
        android:id="@+id/sun"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/sun"
        android:paddingLeft="100dp"
        android:paddingTop="45dp"
        android:src="@drawable/sun" />

    <ImageView
        android:id="@+id/cloud1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/cloud"
        android:paddingLeft="100dp"
        android:paddingTop="70dp"
        android:src="@drawable/cloud" />

    <ImageView
        android:id="@+id/cloud2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/cloud"
        android:paddingLeft="200dp"
        android:paddingTop="90dp"
        android:src="@drawable/cloud" />

    <ImageView
        android:id="@+id/ground"
        android:layout_width="fill_parent"
        android:layout_height="200dp"
        android:layout_alignParentBottom="true"
        android:contentDescription="@string/ground"
        android:padding="40dp"
        android:src="@drawable/ground" />

    <ImageView
        android:id="@+id/window"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:contentDescription="@string/window"
        android:src="@drawable/window" />

    <ImageView
        android:id="@+id/wheel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:contentDescription="@string/wheel"
        android:padding="3dp"
        android:src="@drawable/steering_wheel" />

</RelativeLayout>

Bây giờ, chúng ta sẽ tạo các hiệu ứng cho các ImageView đã tạo ra ở trên. Đối với property animation, ta thường sử dụng 3 class chính là “ValueAnimator”, “ObjectAnimator” và “AnimatorSet”. AnimatorSet cho phép ta load các hiệu ứng sẵn ở một file xml trong thư mục animator rồi gắn target (view hay đối tượng…) để cho target đó chạy các hiệu ứng đã được thiết lập sẵn.

VD: Hiệu ứng cho mặt trời di chuyển đi lại trong 3s và lặp đi lặp lại.

sun_swing.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:ordering="sequentially" >

    <objectAnimator
        android:duration="3000"
        android:propertyName="x"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueTo="-400"
        android:valueType="floatType" />

</set>
        ImageView sun = (ImageView)findViewById(R.id.sun);
        AnimatorSet sunSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.sun_swing);
        sunSet.setTarget(sun);
        sunSet.start();

Hiệu ứng cho bánh lái quay đi quay lại trong 3s

wheel_spin.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:ordering="sequentially" >
    <objectAnimator
        android:duration="3000"
        android:propertyName="rotation"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueTo="180"
        android:valueType="floatType" />
</set>
ImageView wheel = (ImageView)findViewById(R.id.wheel);
        AnimatorSet wheelSet = (AnimatorSet) AnimatorInflater.loadAnimator(getApplicationContext(),
            R.animator.wheel_spin);
        wheelSet.setTarget(wheel);
        wheelSet.start();

ValueAnimator là lớp chính của property để tính toán một cách chính xác các giá trị của một hiệu ứng nào đó, nó chứa thông tin chi tiết về thời gian, sự lặp lại, lắng nghe các sự kiện để cập nhật, và khả năng thiết lập các loại tùy chỉnh để đánh giá.

ObjectAnimator là một lớp con của ValueAnimator và được sử dụng một cách dễ dàng hơn ValueAnimator. Hầu hết các trường hợp chúng ta đều có thẻ sử dụng ObjectAnimator.

VD về việc sử dụng ValueAnimatorObjectAnimator

        ValueAnimator skyAnim = ObjectAnimator.ofInt
            (findViewById(R.id.car_layout), "backgroundColor",
                Color.rgb(0x00, 0x00, 0x00), Color.rgb(0x00, 0x66, 0x99));
        skyAnim.setDuration(3000);
        skyAnim.setRepeatCount(ValueAnimator.INFINITE);
        skyAnim.setRepeatMode(ValueAnimator.REVERSE);
        skyAnim.setEvaluator(new ArgbEvaluator());
        skyAnim.start();

        ObjectAnimator cloudAnim = ObjectAnimator.ofFloat(findViewById(R.id.cloud1), "x", -350);
        cloudAnim.setDuration(3000);
        cloudAnim.setRepeatCount(ValueAnimator.INFINITE);
        cloudAnim.setRepeatMode(ValueAnimator.REVERSE);
        cloudAnim.start();

        ObjectAnimator cloudAnim2 = ObjectAnimator.ofFloat(findViewById(R.id.cloud2), "x", -300);
        cloudAnim2.setDuration(3000);
        cloudAnim2.setRepeatCount(ValueAnimator.INFINITE);
        cloudAnim2.setRepeatMode(ValueAnimator.REVERSE);
        cloudAnim2.start();

Với các dòng lệnh trên, ta đã thực hiện việc thay đổi màu sắc của hình nền trong 3s giữa màu 000000 với 000699. Với 2 đám mây ta cho chúng di chuyển lệch khỏi vị trí đầu 350dp, sau đó quay trở lại cũng trong thời gian là 3s.

Các thay đổi trên đều lặp đi lặp lại vô hạn.

Chúng ta để ý tới các dòng lệnh:

ValueAnimator skyAnim = ObjectAnimator.ofInt
        (findViewById(R.id.car_layout), "backgroundColor",
            Color.rgb(0x00, 0x00, 0x00), Color.rgb(0x00, 0x66, 0x99));
ObjectAnimator cloudAnim = ObjectAnimator.ofFloat(findViewById(R.id.cloud1), "x", -350);

Ta thấy tham số thứ 2 chính là cái ta muốn thay đổi như "backgroundColor" hay "x" (tọa độ x) của đối tượng. Đó chính là thuộc tính của đối tượng. Khác với view animation, ta chỉ có thể thay đổi được 1 thuộc tính như alpha, tọa độ x-y, hay scale... với một animation, còn với property animation thì ta có thể thay đổi bất kỳ thuộc tính nào vào bất cứ lúc nào. Như vậy việc sử dụng hiệu ứng sẽ linh hoạt hơn rất nhiều.

Tổng kết

  1. Khi thiết kế một màn hình, ta nên chú ý tới thứ tự xuất hiện của các đối tượng (sắp xếp theo thứ tự từ xa tới gần với hướng nhìn - xa ở trước, gần ở sau)
  2. Đối với các view có hiệu ứng đơn giản, hoặc các đối tượng có hiệu ứng giống nhau với một thuộc tính thay đổi thì nên sử dụng view animation, còn các đối tượng có hiệu ứng đa dạng, phức tạp thì ta nên sử dụng property animation.

Tham khảo


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.