I am trying to move Imageview in curved path to a particular point. So I make use of this library. I have to move the imageview in the curved path and the image view should return to its original position. This is one cycle. But it should repeat indefinitely until I press a button in that activity.
It works as expected but when the imageview finishes one cycle and about to start the next cycle, the image view is shaken little bit at the starting point (This is not happening for the first time but it happens for second and so forth) and then the animation proceeds normally.
private fun startAnimation(thermView: ImageView, thermAnimIdentifierView: TextView) {
Log.d(TAG, "****************************************************Start Animation")
val arcAnimator = ArcAnimator.createArcAnimator(thermView, thermAnimIdentifierView, 360f, Side.RIGHT)
arcAnimator.setDuration(5000)
arcAnimator.setInterpolator(CycleInterpolator(0.5f))
arcAnimator.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {
}
override fun onAnimationEnd(animation: Animator) {
val arcAnimator = ArcAnimator.createArcAnimator(thermView, thermAnimIdentifierView, 360f, Side.RIGHT)
arcAnimator.setStartDelay(500)
arcAnimator.setDuration(5000)
arcAnimator.setInterpolator(CycleInterpolator(0.5f))
arcAnimator.addListener(this)
arcAnimator.start()
}
override fun onAnimationCancel(animation: Animator) {
}
override fun onAnimationRepeat(animation: Animator) {
}
})
arcAnimator.start()
}
Related
What I am trying to do is retrieve data from the server when I click a button. When I click the button, I want to show my "Loading..." TextView for 2 seconds, and only then show the data I got from the server. How can I do this?
For now my animation is working, but the data is showing almost instantly. I want to delay that. Using Thread.sleep(2000) causes both the data and Loading to be delayed.
val loadingAnimation :TextView = findViewById<TextView>(R.id.loadingAnimationTextView)
val alphaAnim = AlphaAnimation(1.0f, 0.0f)
alphaAnim.startOffset = 0
alphaAnim.duration = 2000
alphaAnim.setAnimationListener(object : AnimationListener {
override fun onAnimationRepeat(animation: Animation?) {
//not sure what code to put here
}
override fun onAnimationEnd(animation: Animation) {
// make invisible when animation completes, you could also remove the view from the layout
loadingAnimation.setVisibility(View.INVISIBLE)
}
override fun onAnimationStart(animation: Animation?) {
loadingAnimation.setVisibility(View.VISIBLE)
}
})
loadingAnimation.setAnimation(alphaAnim)
Thread.sleep(2000)
You can use the handler for this task.
Handler(Looper.getMainLooper()).postDelayed({
// Show you data here
loadingAnimation.setVisibility(View.INVISIBLE)
}, 2000)
Here, 2000 = 2 seconds
It's probably easier to use the ViewPropertyAnimator stuff:
loadingAnimation.animate()
.alpha(0)
.duration(2000)
.withEndAction {
// data displaying code goes here
}.start()
but honestly I don't think there's anything wrong with populating an invisible list, and just making it visible when you want to display it. But that up there is a way to chain runnable code and animations, one after the other
I have a simple fade out animation function for my android app, it works, but the issue I am having is that, after the fade out animation has run, the view (TextView in this case) does not stay faded out, the alpha value becomes 1 again.
Here is the fadeOut function code:
fun fadeOut(duration: Long = 100) : AlphaAnimation{
val fadeOut = AlphaAnimation(1f, 0f)
fadeOut.interpolator = DecelerateInterpolator()
fadeOut.duration = duration
return fadeOut
}
And I use it like this:
myTextView.startAnimation(fadeOut(500))
Any help or advice will be highly appreciated.
I think Animation::setFillAfter function would do the trick for you, the code would be like this:
val animation = fadeOut(500)
animation.fillAfter = true
myTextView.startAnimation(animation)
Although, this solution only preserves the alpha value of the view after the animation ends, if you want to change the visibility of the view, you need to change it when the animation ends using Animation.AnimationListener interface, the code would be like this:
myTextView.startAnimation(fadeOut(500).apply {
setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation?) {
}
override fun onAnimationEnd(animation: Animation?) {
myTextView.visibility = View.GONE
}
override fun onAnimationRepeat(animation: Animation?) {
}
})
})
You can set visibility in handler after running animation
myTextView.startAnimation(fadeOut(500))
Handler().postDelayed({
myTextView.visibility = View.GONE
},500)
Is it possible to pause Motion layout transition and then resume this transition?(for example I show dialog fragment and I need to pause this motion scene and after dismissing dialog I want to resume transition)
I launch transition by code like as MotionLayout.setTransitionToEnd()
You should be able to get the current progress and set to that value... then when you are ready to resume, you can call the transitionToEnd() again and it will resume from that value... so for example
private void pauseAnimation() {
float currentProgress = motionLayout.getProgress()
motionLayout.setProgress(currentProgress())
}
private void resumeAnimation() {
motionLayout.transitionToEnd()
}
That should do what you are looking to accomplish
I found the public method in MotionLayout class setProgress(float) and setInterpolatedPreogress(float) https://developer.android.com/reference/android/support/constraint/motion/MotionLayout.html#setProgress(float)
https://developer.android.com/reference/android/support/constraint/motion/MotionLayout.html#setInterpolatedProgress(float)
which allows to resume animation
You can store the position of progress that you left before pausing the fragment like so:
private var outState: Bundle? = null
const val KEY_CURRENT_POSITION_LIST_CATEGORY = "KEY_CURRENT_POSITION_LIST_CATEGORY"
override fun onPause() {
super.onPause()
outState = Bundle()
outState!!.putFloat(KEY_CURRENT_POSITION_LIST_CATEGORY, motion_layout.progress)
}
And onResume, you can just set the progress of the animation in the motion_layout
override fun onResume() {
super.onResume()
if (outState != null) {
motion_layout.progress=outState!!.getFloat(KEY_CURRENT_POSITION_LIST_CATEGORY)
}
}
I have a method called showProgress which runs a fade animation and switches the current view with a page that shows just a loading spinner (ProgressBar).
fun showProgress(show: Boolean, progressView: ProgressBar, layoutView: View): Completable {
val showView = if (show) progressView else layoutView
val hideView = if (show) layoutView else progressView
return fadeAnimation(showView, hideView)
}
private fun fadeAnimation(showView: View, hideView: View): Completable {
return Completable.create {
// animateSemaphore.acquire()
Log.d("beforeFade", "showView: ${showView.visibility}; hideView: ${hideView.visibility}")
val animTime: Long = showView.context.applicationContext.resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
showView.alpha = 0f
handler.post {
showView.visibility = View.VISIBLE
showView.animate()
.alpha(1f)
.setDuration(animTime)
.setListener(null)
hideView.animate()
.alpha(0f)
.setDuration(animTime)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
hideView.visibility = View.GONE
Log.d("fade", "showView: ${showView.visibility}; hideView: ${hideView.visibility}")
it.onComplete()
}
})
}
}
}
I use it when I'm waiting for a network request to complete with RxJava and I want to show the ProgressBar while the app is waiting for the request to finish. However, sometimes the network request will return before showProgress is finished and will cause the animation to run twice causing progressView and layoutView to both be set to View.GONE.
My question is how do I make sure that when showProgress is called while another showProgress is still running, the showProgress being called will wait till the one running is finished? Just an FYI, semaphores won't exactly work since showProgress runs on the main thread and will cause a running showProgress to freeze.
I have tried the new BottomSheetBehaviour with design library 23.0.2 but i think it too limited. When I change state with setState() method, the bottomsheet use ad animation to move to the new state.
How can I change state immediately, without animation? I don't see a public method to do that.
Unfortunately it looks like you can't. Invocation of BottomSheetBehavior's setState ends with synchronous or asynchronous call of startSettlingAnimation(child, state). And there is no way to override these methods behavior cause setState is final and startSettlingAnimation has package visible modifier. Check the sources for more information.
I have problems with the same, but in a bit different way - my UI state changes setHideable to false before that settling animation invokes, so I'm getting IllegalStateException there. I will consider usage of BottomSheetCallback to manage this properly.
If you want to remove the show/close animation you can use dialog.window?.setWindowAnimations(-1). For instance:
class MyDialog(): BottomSheetDialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.setDimAmount(0f) // for removing the dimm
dialog.window?.setWindowAnimations(-1) // for removing the animation
return dialog
}
}
If you really need it, then you can resort to reflection:
fun BottomSheetBehavior.getViewDragHelper(): ViewDragHelper? = BottomSheetBehavior::class.java
.getDeclaredField("viewDragHelper")
.apply { isAccessible = true }
.let { field -> field.get(this) as? ViewDragHelper? }
fun ViewDragHelper.getScroller(): OverScroller? = ViewDragHelper::class.java
.getDeclaredField("mScroller")
.apply { isAccessible = true }
.let { field -> field.get(this) as? OverScroller? }
Then you can use these extension methods when the state changes:
bottomSheetBehavior.setBottomSheetCallback(object : BottomSheetCallback() {
override fun onSlide(view: View, offset: Float) {}
override fun onStateChanged(view: View, state: Int) {
if (state == STATE_SETTLING) {
try {
bottomSheetBehavior.getViewDragHelper()?.getScroller()?.abortAnimation()
} catch(e: Throwable) {}
}
}
})
I will add that the code is not perfect, getting fields every time the state changes is not efficient, and this is done for the sake of simplicity.