Android Custom View Tutorial (Part 4) – Animation

http://www.intertech.com/Blog/android-custom-view-tutorial-part-4-animation/

Android Custom View Tutorial (Part 4) – Animation

Custom View 2This post is part of a series written by our developers that documents their journey in a cross-platform mobile app development project. They set out to improve an internal process, create educational content for the developer community, and explore new craft beers and a vintage arcade. Sound interesting? Read more about this series and their efforts in our introductory series post.

 

This post is part of an Android custom view tutorial series for creating custom views in Android.  All of the code for this tutorial is available in the example application, which demonstrates the custom views.

In the last post, we talked about saving and restoring state of a custom view.  We used the example application to demonstrate interaction between the two views that we have created: ValueSelector and ValueBar.  More specifically, clicking the update button will update the ValueBar with the value from the ValueSelector:

customViews_beforeRotate

When I ran this app using only the code that we have covered so far, there was one aspect that left me feeling empty inside: when the bar’s value was updated, it would simply jump to the new value.  Seeing this in action made me realize that it would be much more natural if the indicator would “slide” to the new value.  What we are talking about here is animation, and Android provides ways that we can accomplish this.

Animation in Android

In Android, there is more than one way to create animations.  We will be using the Property Animation system, which is not to be confused with the View Animation system.  The View Animation system lets you perform animations on the view as a whole, such as moving, rotating, or scaling the view.  That is not what we want here.  We only want to animate a portion of our view: the value indicator.  The Property Animation system allows us to accomplish that.

Property Animation

The primary class for property animation is the ValueAnimator.  The basic usage is not complicated at all.  You simply provide start and end values, specify a duration, and start the animation.  For example:

This ValueAnimator will “animate” from 0 to 100 over the span of 1 second.  I put animate in quotes because this code, as is, does’t have anything to do with our view.  It is up to us to connect the ValueAnimator to the property we are animating.  We do so by adding an AnimatorUpdateListener, and overriding onAnimationUpdate.  This method is called at each interval during the animation.

ValueAnimator has several configuration options, including the refresh interval and interpolation.  The refresh interval can be set using setFrameDelay.  This specifies the interval between each frame of the animation (i.e. how often onAnimationUpdate is called).  Interpolation refers to how the intermediate values are calculated as a function of time.  For example, linear interpolation means that the value is updated by the same amount each time.  The default frame delay is 10 ms and the default interpolator is AccelerateDecelerateInterpolator.  Both of these are sufficient for our purpose.

Now that we have some understanding how the Property Animation system works, let’s look at how we can incorporate it into our ValueBar.

Adding Animation Inside a Custom View

First, let’s add a few properties to configure the view’s animation:

The animated property will control whether or not to animate the view at all.  The duration specifies how long the animation should run.  We also keep a reference to the ValueAnimator that is currently running.  This is in case an animation is still running when we need to start a new one, i.e. the bar’s value is updated while an animation for a previous update is still in progress.  In that scenario, we need to cancel the running animation before starting a new one.

Now we can add logic to animate the bar’s value when it changes.  To do so, we’re going to use a separate variable to indicate what value the bar should display, separate from the bar’s current value. If we updated the bar’s actual value during each frame of the animation, and the user were to call getValue while the animation was in progress, they would get the current value of the animation, and not the actual value of the ValueBar.  To put it another way, if I call setValue(50) and then immediately call getValue(), I would expect to get 50, not some intermediate value that represents the current state of the animation.

Here is what our updated setValue method looks like:

valueToDraw is updated during each frame of the animation, and that is what our onDraw method will now use to draw the bar (that is the only change to onDraw, so the code is not shown).  If animated is false, we simply set valueToDraw to currentValue.  Note that it is important to call invalidate() for each frame of the animation to let Android know that our view needs to be redrawn, otherwise our efforts to animate will fail.

We also need to make a change to onRestoreInstanceState.  In addition to setting currentValue to the saved value, we also need to set valueToDraw.  We are not doing any animation when restoring from a saved state, so these two values need to be the same:

This is looking pretty good.  Our view can be configured to animate changes in its value, and the duration is configurable.    There is still one thing that I don’t like, however: the same duration is used regardless of how much the value has changed.  If the duration is set to one second, our animation will run for one second regardless of whether the bar’s value changed by 1 or 100.  This can make small changes look too slow, and large changes too fast.  We have put so much effort into making a great view, it would be a shame to get a bad rap for such a petty reason.  Fixing this only takes a few lines of code:

The duration for the animation is now dependent on how much the value has changed.  This means the circle indicator will always travel at the same speed.  It also means that the specified duration is the time that it takes for the value to traverse the entire length of the bar, not the duration of each animation.  I think that’s fine, as long as it is well documented.

We have successfully added animation to our view.  The user just needs to call setAnimated(true), and everything else is taken care of by the ValueBar class.  Using property animation inside our custom view is a great way to encapsulate the logic that animates our view.  However, this does have cost: it adds complication to our view class.  Also, the user has no way to configure the animation, other than what is provided by the view class.  In our case, this is not a big deal because there are only so many ways you can animate ValueBar, and it looks pretty good with the options that we provided (after all, ValueBar only has one value).  But what if you were creating a more complex view, that could potentially be animated in several different ways?  It might become too burdensome to try to incorporate many different animation options inside your view.  Instead, you may decide to let the user of your view decide how to animate.  Lucky for you, Android provides a way to do this: Object Animation via the ObjectAnimator class.

Object Animation

ObjectAnimator works similar to ValueAnimator, but instead of providing a listener that is notified every frame of the animation, you provide an object and the name of the property to animate.  Suppose we did not have animation built in to the ValueBar class.  In the MainActivity of the example application, we could animate the ValueBar as follows:

By specifying “value” as the property to animate, our object needs to have the appropriate setter method getValue(), otherwise this will not work.  Other than that small caveat, this sure looks a whole lot simpler than all of that logic we just added to ValueBar, doesn’t it?

The answer is yes, it is definitely simpler. However, there are also advantages to having everything inside the view class. Remember the logic we added so that the indicator will always travel at the same speed, regardless of how much the value changes?  That, and any other logic needed to make things work nicely would need to be repeated everywhere the view is used.

Which method to use is up to you.  Placing animation logic inside the view class makes it easier to use, but can significantly complicate the view class, and limits the options to only those that you provide.  Leaving it out means that every user of the view that needs animation has to add the logic to do so.  This may or may not be a big deal, depending on how complicated it is to make the animation work well, and how many places you intend the view to be used.

Wrap Up

This concludes our Android custom view tutorial series. For your convenience, other posts in this series are listed below. There are many more aspects to explore, but hopefully you are now well on your way to creating awesome custom views in Android.

More Android Custom View tutorials in this series:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值