มาทำ Animation ด้วย ObjectAnimator บน Android แล้วมาขิง iOS กันเถอะ

Android Sep 4, 2020
มีคนคามิน้องขอมา แต่เอาจริงๆ ความสดใสยังไงก็บังไม่มิดอ่ะ https://www.facebook.com/bnk48official.music/

บล็อกนี้จริงๆไม่ได้อยู่ในแพลนที่จะเขียนนะ แต่เกิดมาจากความที่ iOS เขามาขิงเราล้วนๆ

คือ มีอยู่วันนึงฝั่ง iOS เขาขิงพร้อมโชว์ให้ดูว่า เขาสร้าง Animation ใน SpriteView ซึ่งสามารถปรับอะไรต่างๆได้ผ่าน Xcode ได้เลย โดยไม่ต้องเขียนโค้ด จริงๆเจ้าตัวนี้ไว้ทำ animation ในเกมส์ แต่ก็ประยุกต์ใช้กับแอพได้ด้วยเช่นกัน

https://developer.apple.com/documentation/spritekit

เราในฐาแนะ เอ้ยยย ฐานะ Android Developer รู้สึกว่าทำไม Android Studio ไม่มีบ้างนะ ทำไมนะ ทำไมกัน และมีอะไรขิงคืนได้บ้างนะ

น้องในทีม Android ของเราไม่ยอมเช่นกัน พยายามหาตัวที่คล้ายกันให้มากที่สุด เจอตัวนี้เข้า แต่โชคร้ายที่เขาเลิกทำ library ตัวนี้ไปแล้ว เราจะไปทำต่อก็ยังไงอยู่เนอะ 555

andersonlucasg3/SpriteKit-Android
(DISCONTINUED) SpriteKit port for Android. Contribute to andersonlucasg3/SpriteKit-Android development by creating an account on GitHub.

เลยไปตั้งกระทู้ถามใน Thailand Android Developer ว่ามีอะไรคล้ายกันไหม ได้คำตอบมาว่าที่ใกล้เคียงสุดก็คือ OpenGL ES ผู้ซึ่งทำได้ทั้ง 2D และ 3D

เลยลองเขียนดูตาม Tutorial ใน Android Developer มา

https://developer.android.com/training/graphics/opengl

ตัวโครงสร้างจะเป็นแบบนี้

Activity → GLSurfaceView → GLRenderer → Triangle, Square

ตัว Activity จะมีเจ้า GLSurfaceView ไปแปะ แล้วมี GLRenderer ที่มีรูปสามเหลี่ยมที่วาดอีกทีนึง

ในนั้นจะเล่าว่าแต่ละตัวคืออะไร ต้องปรับอะไรตรงไหน มี animation นิดหน่อยด้วยว่าใช้นิ้วจิ้มให้หมุนไปมุมไหน

อันนี้เป็น Document ของ GLSurfaceView เนอะ

https://developer.android.com/reference/kotlin/android/opengl/GLSurfaceView

https://developer.android.com/guide/topics/graphics/opengl

ด้วยตัว SpriteView มีพื้นฐานเป็น OpenGL อยู่แล้ว แต่เนื้อหาเจ้า OpenGL มันเยอะจนเป็นวิชาเรียนหนึ่งวิชาในมหาวิทยาลัยได้เลย แล้วเราจะไป research เพิ่มทำไมกันเล่า เนื้อหาอื่นๆที่ต้องศึกษาก็เยอะจนงง แน่นอน learning curve ก็สูงด้วย

กลับมาที่ตัวงานของเรา ต้องการอะไรกันนะ?

งานของเราต้องการแค่กดปุ่ม แล้วมีรูปภาพวิ่งออกมาจากตำแหน่งของปุ่มมุมขวามือไปตรงกลางจอ โดยเริ่มต้นขนาด 50% และพอถึง location ที่สิ้นสุด ขนาดจะเพิ่มเป็น 100%

ถ้าอย่างงั้นมีตัวไหนบ้างนะที่ทำได้ใน Android?

Object Animator ไง ในบล็อกคุณเอกที่นอนน้อยๆอ่ะ

มาทำความรู้จักกับ Object Animator กันดีกว่า~!
วันนี้ขอกลับเข้าสู่เรื่องการทำ Animation กันอีกครั้ง ซึ่งในอดีตกาลเมื่อนานมาแล้วเจ้าของบล็อกได้ทำบทความเกี่ยวกับคลาส Animation ไปแล้วแต่ทว่าคลาสดังกล่าวนั้นเป็นอะไรที่เก่าไปแล้วล่ะ เพราะว่าหลังจากที่ Android 3.0Honeycomb (API 11) ได้เปิดตัว ก็มีการประกาศ Animation APIตัวใหม่ขึ้นมาที่มีชื่อว่า Ob…

ซึ่งเขาก็เขียนไว้ครบถ้วนสมบูรณ์แล้ว งั้นจบบล็อกนี้แล้วกันเนอะ

ได้หรอม? ไม่ได้สิ

ตัว Android Developer ในส่วน Training จะเป็นอย่างนี้นะ อันนี้แปะไว้เฉยๆอ่ะ

https://developer.android.com/training/animation

หรือจะเรียนผ่าน codelab ก็ได้น้า

โดย codelab ที่เราจะเริ่มศึกษา Object Animator ก็จะมี 2 codelab ด้วยกัน คือ

  • Property Animation ตัวอย่างเป็นแอพสร้างน้องดาวขึ้นมา มี control position, size, rotation, and translucency ในบล็อกนี้พูดถึงตัวนี้
Property Animation | Google Developer Profile | google.dev
In this codelab, you'll build an Android Kotlin app that performs simple UI animations by varying property values on views using ObjectAnimator.
  • Animation with MotionLayout อันนี้ของใหม่ ยกไปบล็อกต่อๆๆๆๆๆๆไปดีกว่า
Animation with MotionLayout | Google Developer Profile | google.dev
In this codelab you'll use MotionLayout to build an Android Kotlin app with dynamic animations.

ทั้งสองจะอยู่ในชุดของ Advanced Android in Kotlin นะ

ทำความรู้จักกับ ObjectAnimator กันเถอะ

ก่อนอื่นมาทำความรู้จักกันก่อนว่า ObjectAnimator คืออะไร?

ObjectAnimator เป็น sub-class ของ ValueAnimator ที่ support การแสดง animation ใน view ที่เราต้องการ สามารถสร้างได้แบบ resource file ที่เป็น xml หรือเขียนด้วย code ก็ได้นะ ซึ่งเราจะกล่าวกันในบล็อกนี้เนอะ

https://developer.android.com/reference/android/animation/ObjectAnimator

หลักการคร่าวๆในการทำ animation แบบเบื้องต้น (ยังไม่ใช่ท่า advance)

จะมีประมาณ 3 ขั้นตอน คือ

1) สร้าง ObjectAnimator ขึ้นมา ว่าให้ view นี้ animate อย่างไร เช่น ให้ view ที่เป็นรูปดาวหมุน

val animator = ObjectAnimator.ofFloat(star, View.ROTATION, -360f, 0f)
  • target ใส่ view ที่ต้องการให้มัน animate
  • property จะให้ view นั้น animate ไปแบบไหน
  • values จะให้ทำไปเท่าไหร่ ถ้าตัวอย่างก็คือหมุนจาก -360 ไป 0 องศา คือหมุนตามเข็มนาฬิกานั่นแหละ

2) setting เจ้า ObjectAnimator ที่เราเพิ่งสร้างไปทำอะไรเพิ่ม เช่น

มันจะมีบางเคส เช่น ให้ขยับด้านขวาไป 200 นะ แล้วช่วยกลับมาหน่อย โดยให้ repeat รอบนึง และให้ reverse จากเดิม

animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE

เราสามารถกำหนดเวลาการแสดงผลได้ดังนี้

animator.duration = 500

ระหว่างที่แสดง animation จะให้ปุ่มไม่สามารถกดได้เมื่อ animation แสดง เพื่อไม่ให้แสดง animation ที่ไม่ต่อเนื่องออกไป

private fun ObjectAnimator.disableViewDuringAnimation(view: View) {
    addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationStart(animation: Animator?) {
            view.isEnabled = false
        }

        override fun onAnimationEnd(animation: Animator?) {
            view.isEnabled = true
        }
    })
}

สามารถเอาไปใช้ได้ดังนี้

animator.disableViewDuringAnimation(rotateButton)

3) แสดง animation ขึ้นมาได้เล้ยยย

animator.start()

Animation ที่ทำใน codelab จะมีดังนี้

  • ROTATE หมุนวัตถุ ในที่นี้ก็กล่าวถึงไปข้างต้นแล้ว ว่าให้หมุนดาวเป็นตามเข็มนาฬิกา
val animator = ObjectAnimator.ofFloat(star, View.ROTATION, -360f, 0f)
animator.duration = 1000
animator.disableViewDuringAnimation(rotateButton)
animator.start()
  • TRANSLATE ย้ายที่วัตถุไปตรงไหน

ตัวอย่างใน codelab จะบอกว่าให้เลื่อนไปทางขวา 200 นะ จากนั้นขอ repeat รอบนึงโดยรอบนี้ช่วย reverse มานะ ตามที่เพิ่งกล่าวไป

val animator = ObjectAnimator.ofFloat(star, View.TRANSLATION_X, 200f)
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.disableViewDuringAnimation(translateButton)
animator.start()

และเราลองทำเพิ่มว่าถ้าเลื่อนไปจากเดิมไปทางเฉียงหล่ะ จากการทดสอบโดยการเปลี่ยนค่าในการ translation ทั้งแกน x และ y จะได้ข้อสรุปแบบนี้

จากรูป แกน x บวกซ้าย ลบขวา, แกน y บวกลง ลบขึ้น, ของเดิมอยู่ตรงกลาง

ถ้าเราอยากให้ไปทางเฉียงๆนั้น จะต้องกำหนดแกน x ค่าเป็นลบ และแกน y ค่าเป็นลบเช่นกัน และ view นี้จะใช้ property 2 ตัว คือ TRANSLATION_X และ TRANSLATION_Y แต่การสร้าง ObjectAnimator มันใส่ property ได้อันเดียวนี่นา ทำไงดีน้าาา?

ใช้ PropertyValuesHolder ในการเพิ่ม property ของ animation ว่ามี value เท่าไหร่

val transitionX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, -200f)
val transitionY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, -200f)

พอได้มาหลายๆอันก็เอามาสร้าง ObjectAnimator

val animator = ObjectAnimator.ofPropertyValuesHolder(star, transitionX, transitionY)
  • target เหมือนเดิมเลย ใส่ view ที่ต้องการให้มัน animate
  • values ใส่ PropertyValuesHolder ที่เราสร้างมาเมื่อกี้

จากนั้นใส่ของเพิ่มคล้ายๆเดิม

animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.disableViewDuringAnimation(translateButton)
animator.start()
  • SCALE ขยายขนาดของวัตถุ

ในที่นี้คือ ช่วยขยายใหญ่เป็น 4 เท่าจากของเดิมให้หน่อย แล้วช่วย reverse กลับมาให้หน่อย

แน่นอนว่ามันควร scale ทั้งแกน x และ y เท่ากัน ถ้าไม่เท่ามันจะมีด้านนึงยืด มันจะแปลกๆเนอะ

ส่วนการย่อขนาดลงไม่รู้ว่าต้องทำยังไง เพราะพอใส่ค่าเป็นลบแล้วมันขยายแต่แกนมันแปลกๆนะ

val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 4f)
val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 4f)

val animator = ObjectAnimator.ofPropertyValuesHolder(star, scaleX, scaleY)
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.disableViewDuringAnimation(scaleButton)
animator.start()
  • FADE ทำให้วัถตุจางหายหรือเข้มขึ้น

ในที่นี้ให้จางลงจนมองไม่เห็น แล้วช่วย reverse กลับมาให้หน่อย อันนี้ไม่ค่อยมีอะไร

val animator = ObjectAnimator.ofFloat(star, View.ALPHA, 0f)
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.disableViewDuringAnimation(fadeButton)
animator.start()
  • BACKGROUND COLOR เปลี่ยนสีพื้นหลังก็ทำได้ด้วยนะ

โดย target view ที่เราจะใช้คือ parent ของ ImageView รูปดาวนั่นเอง และใช้ propertyName ที่ชื่อว่า "backgroundColor" โดย values สีเริ่มต้นจากสีดำ ไปสีแดง

val animator = ObjectAnimator.ofInt(star.parent, "backgroundColor", Color.BLACK, Color.RED)

ผลที่ได้ก็คือสีมันไม่ smooth มันจะมีอาการกระตุก จึงต้องใช้ ofArgb แทน โดยตัวนี้นั้น minSdk 21

val animator = ObjectAnimator.ofArgb(star.parent, "backgroundColor", Color.BLACK, Color.RED)
animator.duration = 500
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.disableViewDuringAnimation(colorizeButton)
animator.start()

และการเปลี่ยนพื้นหลังจะไม่กระตุกอีกต่อไป

ต่อมาเป็นแบบ Advance

  • SHOWER ในตัวอย่างทำเป็นน้องดาวตกลงมาจากฟากฟ้าหลายๆดวง

Step 1: A star is born

set ขนาดพ่อแม่น้องดาวก่อน

val container = star.parent as ViewGroup
val containerW = container.width
val containerH = container.height
var starW: Float = star.width.toFloat()
var starH: Float = star.height.toFloat()

จากนั้นสร้างดาว โดยการสร้าง ImageView ขึ้นมาอันนึง แล้วใส่รูปดาวลงไปในนั้น และกำหนด layoutParams ให้เป็น wrap_content ทั้งด้าน width และ height แล้วเพิ่มไปที่ parent view

val newStar = AppCompatImageView(this)
newStar.setImageResource(R.drawable.ic_star)
newStar.layoutParams = FrameLayout.LayoutParams(
    FrameLayout.LayoutParams.WRAP_CONTENT,
    FrameLayout.LayoutParams.WRAP_CONTENT)
container.addView(newStar)

Step 2: Size and position the star

random ขนาดน้องดาวที่เพิ่งสร้าง และ set scale ให้เท่ากับทั้งความกว้างและความสูง

https://google.dev/codelabs/advanced-android-kotlin-training-property-animation#8
newStar.scaleX = Math.random().toFloat() * 1.5f + .1f
newStar.scaleY = newStar.scaleX
starW *= newStar.scaleX
starH *= newStar.scaleY

และ random ตำแหน่งในแนวแกน x เพื่อวางน้องดาว

https://google.dev/codelabs/advanced-android-kotlin-training-property-animation#8
newStar.translationX = Math.random().toFloat() * containerW - starW / 2

Step 3: Create animators to for star rotation and falling

สร้าง animators ให้น้องดาวตกและหมุนลงไป

https://google.dev/codelabs/advanced-android-kotlin-training-property-animation#8
val mover = ObjectAnimator.ofFloat(newStar, View.TRANSLATION_Y, -starH, containerH + starH)
mover.interpolator = AccelerateInterpolator(1f)
val rotator = ObjectAnimator.ofFloat(newStar, View.ROTATION, (Math.random() * 1080).toFloat())
rotator.interpolator = LinearInterpolator()
  • AccelerateInterpolator "interpolator" that you are setting on the star causes a gentle acceleration motion
  • LinearInterpolator ตกลงมาแบบ linear แบบบนลงล่าง

Step 4: Run the animations in parallel with AnimatorSet

เมื่อกี้เราสร้าง animator ไป 2 ตัว คือ ย้ายตำแหน่งแกน y ลงมา พร้อมกับการหมุนดาว ซึ่งจะทำพร้อมกัน หลังจากสร้างแล้ว setting animator แล้ว เราสร้าง AnimatorSet() ขึ้นมา และใส่ animator ที่สร้างไว้เมื่อกี้ ใน playTogether แล้วก็ random ระยะเวลาการแสดง animator set นี้

val set = AnimatorSet()
set.playTogether(mover, rotator)
set.duration = (Math.random() * 1500 + 500).toLong()

และเมื่อเล่นเสร็จแล้ว ช่วยลบ view น้องดาวที่สร้างออกไปนะ

set.addListener(object : AnimatorListenerAdapter() {
    override fun onAnimationEnd(animation: Animator?) {
        container.removeView(newStar)
    }
})

พร้อมแล้ว เริ่มกันเลยจ้า

set.start()

ผลที่ได้จะเป็นดังนี้

สรุปโค้ดแบบยาวๆในการสร้างดาวตกแบบหมุนๆ

ปล. ถ้าเราใช้เจ้า ObjectAnimator ในจำนวนที่มากจนเกินไปจะทำให้ animation view ของเรานั้นเกิดอาการหน่วงได้จ้า

ถ้าลองเปลี่ยนเป็นหิมะตกหล่ะ

Step 1: A star is born

ขั้นตอนแรกยังคงเดิม เปลี่ยนจากการสร้างน้องดาวน้อยมาเป็นหิมะแทน

val container = star.parent as ViewGroup
val containerW = container.width
val containerH = container.height
var starW: Float = star.width.toFloat()
var starH: Float = star.height.toFloat()

val newSnow = AppCompatImageView(this)
newSnow.setImageResource(R.drawable.ic_snow)
newSnow.alpha = Math.random().toFloat()
newSnow.layoutParams = FrameLayout.LayoutParams(
    FrameLayout.LayoutParams.WRAP_CONTENT,
    FrameLayout.LayoutParams.WRAP_CONTENT)
container.addView(newSnow)

สิ่งที่ต่างไป แน่นอนสิ่งแรก drawable ที่ต่างกัน หิมะก็ drawable สีขาวกลมๆนั่นแหละ

newSnow.setImageResource(R.drawable.ic_snow)

และเราต้องการให้ random ตัว alpha หน่อยเนอะ ซึ่งค่า alpha จะมี range ระหว่าง 0 - 1 จ้า

newSnow.alpha = Math.random().toFloat()

Step 2: Size and position the star ส่วน size และ position ก็ลอกของเดิมมา

Step 3: Create animators to for star rotation and falling

เราจะให้หิมะตกลงมา แล้วให้มันเลี้ยวๆเบ้ๆ ไม่ให้ตกลงมาตรงๆ จะให้ตกแบบเฉียงๆนิดๆ แบบโค้งหน่อยๆ

val directionX = ((Math.random() * 2 - 1) * 100).toFloat()
val moverX = ObjectAnimator.ofFloat(newSnow, View.TRANSLATION_X, newSnow.translationX, newSnow.translationX + directionX)
moverX.interpolator = AccelerateInterpolator(1f)
val moverY = ObjectAnimator.ofFloat(newSnow, View.TRANSLATION_Y, -starH, containerH + starH)
moverY.interpolator = LinearInterpolator()

แล้วเราจะกำหนด เบ้ซ้าย เบ้ขวายังไง จากก่อนหน้านี้ที่สรุปเรื่องแกนไว้ให้ ถ้าไปทางซ้ายค่าเป็นลบ และไปทางขวาค่าเป็นบวก ดังนั้นเราจะ random ค่าระหว่าง 0 ถึง 2 ออกมา แล้วหักไป 1 เพื่อนเลื่อนแกน เราจะได้ค่าเป็นลบออกมาถ้ามันได้ค่าน้อยกว่า 1 แล้วเอาไปคูณ 100 เข้าไปโดยไม่มีเหตุผล

เดี๋ยวสิ

เพราะเราลองแล้วพบว่าถ้าไม่ขยายค่าเข้าไป แบบจะมองไม่ออกว่ามันเบ้ไปข้างนึงไหม เลยคูณ 100 เข้าไป ดังนั้นจะเบ้ซ้ายขวาระหว่าง 0 ถึง 100 นั่นเอง จะได้เห็นกันชัดๆ

Step 4: Run the animations in parallel with AnimatorSet

จับการเคลื่อนไหวทั้งสองแกนมัดรวมให้แสดงพร้อมกัน จะได้ลงมาเฉียงๆหน่อย

val set = AnimatorSet()
set.playTogether(moverX, moverY)
set.duration = (Math.random() * 2500 + 1000).toLong()

set.addListener(object : AnimatorListenerAdapter() {
    override fun onAnimationEnd(animation: Animator?) {
        container.removeView(newSnow)
    }
})
set.start()

ผลที่ได้ขอแบบกดรัวๆ

แน่นอนว่าแบ่งแบบนี้มันไม่สวยดูยาก งั้นจัด gist ให้เลยจ้า ยาวไปยาวไป

มาประยุกต์ใช้กับงานของเรากันเถอะ

กลับมาที่เรื่องงาน เราต้องการให้รูปภาพของเรา เคลื่อนจากปุ่มที่อยู่ล่างขวา และเลื่อนเฉียงไปในแนวแกน x ที่กึ่งกลาง และแกน y ลงจากขอบจอมาประมาณนึง และพอถึง location ที่สิ้นสุด ขนาดจะเพิ่มเป็น 2 เท่าจากเดิม

ก่อนอื่นสร้างน้องดาวกันก่อนเช่นเคย

val newStar = AppCompatImageView(this)
newStar.setImageResource(R.drawable.ic_star)
newStar.layoutParams = FrameLayout.LayoutParams(
    FrameLayout.LayoutParams.WRAP_CONTENT,
    FrameLayout.LayoutParams.WRAP_CONTENT)

ต่อมาพ่อแม่น้องดาวเช่นเคย แล้วเอาน้องดาวไปให้ parent view ซะ

val container = star.parent as ViewGroup
val containerW = container.width
val containerH = container.height
container.addView(newStar)

จากนั้นวางตำแหน่งน้องดาวไว้มุมล่างขวา คนกดมาอ่านและทำตามสามารถปรับตำแหน่งได้ตามใจชอบนะ คือ -150f เนี่ยยย ไม่มีนัยนะอะไรทั้งสิ้น ใส่มั่วไปก่อน แบบไม่อยากให้ติดมุมเกินไป

newStar.translationX = containerW - 150f
newStar.translationY = containerH - 150f

action แรกของเราก็คือ เคลื่อนน้องดาวไปตรงกลางจอในแนวแกน x และลงจากขอบจอมาหน่อย ตัวเลข 300f ไม่มีเหตุผลอีกหล่ะ รบกวนไปปรับกันเองตามใจชอบแล้วกันเน้อ

val transitionX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, (containerW / 2).toFloat())
val transitionY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 300f)
val moveAnimator = ObjectAnimator.ofPropertyValuesHolder(newStar, transitionX, transitionY)

ต่อมา พอถึงตำแหน่งที่เราต้องการแล้วไซร์ ก็ให้น้องดาวนั้นใหญ่เป็น 2 เท่าไปเล้ยยยย

val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 2f)
val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 2f)
val scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(newStar, scaleX, scaleY)
scaleAnimator.duration = 500

เนื่องจาก animator ของเรานั้น มันจะเริ่มจาก 1 ไป 2 มันจะไม่พร้อมกันเหมือนน้องดาวตก ดังนั้นเราจะสร้าง AnimatorSet() ขึ้นมา และใส่ animator ที่สร้างไว้เมื่อกี้ ใน playSequentially เพื่อให้เล่นตามลำดับที่เราวางไว้ แน่นอนเล่นเสร็จช่วยออกไปด้วยนะน้องดาว

val set = AnimatorSet()
set.playSequentially(moveAnimator, scaleAnimator)
set.addListener(object : AnimatorListenerAdapter() {
    override fun onAnimationEnd(animation: Animator?) {
        container.removeView(newStar)
    }
})
set.start()

เท่านี้ก็เรียบร้อยแล้ว ในทีนี้จะไม่ disable button เมื่อ animate ยังไม่เสร็จน้าาา จะให้กดรัวๆได้ โดยน้องดาวจะสร้างใหม่และดับไปเนอะ

ปรับ gif แล้วได้แค่นี้แหละแก ฮืออออ

แล้วฝั่ง iOS เดินมาถามอีกว่า มีแบบดึ๋งๆไหม โดย scale จะเป็น 0 → 1.5 เท่า → 1.2 เท่า ในระยะเวลาทั้งหมด 200 ms อะเครจัดปายยย

step แรกเลยคือ จาก 0 จะค่อยขยายรูปถึง 1.5 เท่า

และ step ถัดมาคือ ลดขนาดลงเป็น 1.2 เท่า

val scaleX1 = PropertyValuesHolder.ofFloat(View.SCALE_X, 0f, 1.5f)
val scaleY1 = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f, 1.5f)
val animator1 = ObjectAnimator.ofPropertyValuesHolder(star, scaleX1, scaleY1)

val scaleX2 = PropertyValuesHolder.ofFloat(View.SCALE_X, 1.5f, 1.2f)
val scaleY2 = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.5f, 1.2f)
val animator2 = ObjectAnimator.ofPropertyValuesHolder(star, scaleX2, scaleY2)

val set = AnimatorSet()
set.playSequentially(animator1, animator2)
set.duration = 200
set.start()

ตอนแรกลองใส่ duration แยกระหว่าง animator แต่ละตัว พบว่าไม่รอดจ้า มองแทบไม่ทัน พอมาใส่ใน set พบว่ามันดีกว่าและ smooth กว่ามากเลยแหะ

ปัญหาที่เกิดขึ้นในงานจริงคือ การแสดง animation มากกว่า 1 view เช่น กดปุ่มแล้ว ImageView ช่วยวิ่งไปหน่อยนะครับ สักพักน้อง TextView ช่วยแสดงวิบวับๆตอนที่ ImageView ตัวโตนะครับ แล้วที่นี้ทำไงดีน้า

อ่านกันต่อกับ MotionLayout ที่ยังไม่ได้พูดเรื่อง solve ปัญหาด้านบนแต่อย่างใด

มาแต่งซิ่งไปกับ MotionLayout บน Android แล้วมาขิง iOS กันเถอะ
พอดีเม้ากับพี่เอก เขาแนะนำว่าใช้ MotionLayout เถอะ (เห็นเอาไปใช้ใน LINE MAN แล้วนะแต่ตอนนี้เรายังหาไม่เจอว่าตรงไหนนะ) อ่ะยังไงเราก็ win อ่ะ เพราะเหมือน iOS จะยังไม่มีมั้งนะ

เขียนบล็อกเสร็จแล้ว อ่ะฝากร้านหน่อย

อย่าลืมกด like กด share บทความกันด้วยนะคะ :)

Posted by MikkiPastel on Sunday, 10 December 2017

และฝากช่องทางใหม่ ทาง Twitter ฮับ

Tags

Minseo Chayabanjonglerd

I am a full-time Android Developer and part-time contributor with developer community and web3 world, who believe people have hard skills and soft skills to up-skill to da moon.