Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
i have one activity that has 3 text view. i want to animate these text views one by one.
first txt1 come from bottom to middle then txt2 come 10dp under txt1 and finally txt3.
please guide me how implement it
This can be achieved with translate and alpha animation.
Create XML file named down_to_top.xml in anim folder (create one in you "res" folder if it's not present)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="700">
<translate
android:fromYDelta="50%p"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:toYDelta="0" />
<alpha
android:fromAlpha="0"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:toAlpha="1" />
</set>
In your activity
Initialize your text views to tv1,tv2,tv3
Copy these three functions in your activity.
Call setAnimation() in your onCreate function of activity
private fun setAnimation() {
tv2.visibility = View.INVISIBLE // initially keeping t2 invisible
tv3.visibility = View.INVISIBLE // initially keeping t3 invisible
// Creating obect of Animation
val anim = AnimationUtils.loadAnimation(activity,
R.anim.down_to_up)
//Listener to get callback when animation of tv1 ends
val animListener1 = object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {
// when animation of first TextView ends we start
animation of textView 2 and make it visible
animateSecondView()
tv2.visibility = View.VISIBLE
}
override fun onAnimationRepeat(animation: Animation) {}
}
anim.setAnimationListener(animListener1)
tv1.startAnimation(anim)
}
private fun animateSecondView() {
val anim = AnimationUtils.loadAnimation(activity,
R.anim.down_to_up)
//Listener to get callback when animation of tv2 ends
val animListener2 = object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {
// when animation of TextView 2 ends we start animation of
textView 3 and make it visible
animateThirdView()
tv3.visibility = View.VISIBLE
}
override fun onAnimationRepeat(animation: Animation) {}
}
anim.setAnimationListener(animListener2)
tv2.startAnimation(anim)
}
private fun animateThirdView() {
val anim = AnimationUtils.loadAnimation(activity,R.anim.down_to_up)
tv3.startAnimation(anim)
}
You will have a result like this.
Here are a few ways you can achieve animations in android
Move a view using animation
A good place to start with the recommended way of writing animations in Android.
AndroidViewAnimations
A beginner-friendly pre-made animations library for Android
Animate all the things. Transitions in Android
A very good article explaining how to implement different types of translations on Views
Above mentioned links are a great place to get started.
Related
I'm trying to animate an ImageView with a list of pictures, iterating through them quickly, so that
it creates the impression of an animation. Sadly it does give the alert: Skipped 391 frames! The application may be doing too much work on its main thread.
This is my Main-Activity which implements the Animator-interface.
override fun onCreate(savedInstanceState: Bundle?) {
GlobalScope.launch {
show(Action.Idle)
}
}
My Animator show() function (runs the Runnables with android.os.Handler):
fun show(action: Action){
getHandler().removeCallbacksAndMessages(null)
when (action) {
Action.MoveRight -> getHandler().post(getRight())
Action.MoveUp -> getHandler().post(getUp())
Action.MoveDown -> getHandler().post(getDown())
Action.MoveLeft -> getHandler().post(getLeft())
Action.Idle -> getHandler().post(idle())
}
}
My idle function (anim.idle is an array of Ints/Drawables):
fun idle() = object : Runnable{
override fun run() {
val anim = getAnimation()
repeat(anim.idle!!.size){
getActivity().runOnUiThread {
binding.figure.setImageResource(anim.idle[it])
binding.figure.invalidate()
}
Thread.sleep(500)
}
getHandler().postDelayed(this,5000)
}
}
You can use AnimationDrawable to have the ImageView show a sequence of pictures, see also the guide to Animate drawable graphics
In a nutshell, you declare a animation-list drawable resource and set it as the View background (or foreground, according to your requirements). You can then obtain the AnimationDrawable from the View at runtime and use it to start the animation.
In my small example, I used some vector drawables with different tints
to create the animation-list in res/drawable/colored_androids.xml:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:drawable="#drawable/ic_android_blue_24dp" android:duration="250"/>
<item android:drawable="#drawable/ic_android_mint_24dp" android:duration="250"/>
<item android:drawable="#drawable/ic_android_green_24dp" android:duration="250"/>
<item android:drawable="#drawable/ic_android_mint_24dp" android:duration="250"/>
</animation-list>
My Activity has a field for the AnimationDrawable
private lateinit var coloredAndroidsAnimation: AnimationDrawable
and in onCreate() the following lines are required for the setup:
findViewById<ImageView>(R.id.iv_animated).apply {
setBackgroundResource(R.drawable.colored_androids)
coloredAndroidsAnimation = background as AnimationDrawable
setOnClickListener{
coloredAndroidsAnimation.start()
}
}
While I don't think you can change a view that is attached to the UI from a background thread, you can create/edit views on a background thread (I do this lots)
So a possible solution might be to programmatically create a series of ImageViews in the background. It might then be possible to measure them with the same parameters as the UI in the background thread as measurement can be costly (not sure if they would be re-measured when added to the UI)
Then in the UI thread your loop would add and remove Views from the parent view.
So Im supernew to Kotlin so im trying to create a simple game where if I press a button an Imageview will shift left or right. Below I tried using ObjectAnimator and it works. When I press the button it shifts to the right but it only does that only once on runtime. As it stands now I have one button programmed but I hope to have 4 directional buttons where I can move an Imageview around the screen. So How can I keep changing the position of an imageview when the app is running ?
Thank You!
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//val picture = findViewById<ImageView>(R.id.SpongeBob)
val position_button = findViewById<Button>(R.id.position_button)
position_button.setOnClickListener()
{
ObjectAnimator.ofFloat(SpongeBob, "TranslationX", 100f).apply {
duration = 200
start()
}
}
}
}
You can use picture.animate().setDuration(200).translationXBy(100f), be sure to type translationXBy and not translationX! You can also use "translationXBy" in your ObjectAnimator or use a variable instead of 100f.
What you are doing right now is moving the picture to x: 100 all the time, but what you really want is to move it by 100.
I made a small demo for you here.
I am able to go to my next destination with a circular reveal animation. My only problem is the previous fragment is gone, unless I set a fade out exit transition in my action app:exitAnim="#anim/exit_fade_out". I wanted to ask if anyone has been able to keep the previous fragment visible while doing the circular revealing.
this is the code for revealing and hiding the new fragment.. so far this works yet I cannot show the previous fragment as its masking the next one
private val maxScreenDimension : Float
get() {
return max(container.width, container.height).toFloat()
}
private fun circularReveal(point: Point) {
val viewTreeObserver = container.viewTreeObserver
if (viewTreeObserver.isAlive) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
doCircularReveal(point)
container.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
}
}
private fun doCircularReveal(point: Point) {
val initRadius = 0f
val finalRadius = maxScreenDimension
val circularReveal = ViewAnimationUtils.createCircularReveal(
container,
point.x,
point.y,
initRadius,
finalRadius
)
circularReveal.duration = resources.getInteger(R.integer.circular_animation_out).toLong()
circularReveal.start()
}
private fun circularHide(point: Point) {
val initRadius = maxScreenDimension
val finalRadius = 0f
val circularReveal = ViewAnimationUtils.createCircularReveal(
container,
point.x,
point.y,
initRadius,
finalRadius
)
circularReveal.duration = resources.getInteger(R.integer.circular_animation_in).toLong()
circularReveal.doOnEnd {
container.isVisible = false
findNavController().popBackStack()
}
circularReveal.start()
}
You can do a little hack to make Navigation component to leave the fragment in place during circular reveal animation by giving it a fake animation for exitAnim with the same duration that your circular reveal.
For example it can look like this:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="1.0" android:toAlpha="1.0"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
So in case you wonder how to retain the previous screen and do a circular reveal animation for the next screen. My solution was to simply add a new fragment. I don't think the Navigation component allows for this type of animation. Earlier I did a shared element transition which worked fine as long as I used a fade in/out transition between the two screens. In that way I was able to retain the previous screen while the shared element transition played out. I am thinking the Navigation component is constantly replacing the following fragment.
I want to implement shared element transition in my app, when one activity's recycler view item transforms into another activity like here: https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F15N3n1xwTt0briEbfIvFUG01pMv2d_xaT%2F02-focus-focalelement-do.mp4. (source: https://material.io/design/motion/choreography.html#using-a-focal-element)
Namely, the item is fading out and changes bounds then the new activity fade in. As far as I understand it is simple AutoTransition, but it doesn't work. Simple fading doesn't work as well.
Thus, for now I achieve only that the item gets background of new activity an then changes its bounds.
So, I ended up by adding recycler view item's layout in the resulting activity layout. The data (e.g. the title, etc.) of the clicked item is transferred to the next activity with intent.putExtra(). Shared elements in such case will be of course the item's root view and resulting activity's root view. When activity starts I set the data of the item to matching views in activity via SharedElementCallback, e.g.:
setEnterSharedElementCallback(
object : SharedElementCallback() {
override fun onSharedElementStart(...) {
val title = intent.getStringExtra(TITLE)
activity_item_title.text = title
........
}
override fun onSharedElementEnd(...) {
}
})
This allows to show exactly the same item view at the beginning of the transition. Then it should start change its bounds, fading out this item's view at the same time. And at some moment (e.g. in the middle of the transition) when the initial view completely fades out, the laouyt of the activity shows up, fading in gradually. To do this we need to hide item's view in the middle of the transition (View.visibility = View.GONE) and make activity views visible. Probably this is not the best way, but I solve this by adding a listener to shared element enter transition and used Handler().postDelayed:
window.sharedElementEnterTransition.addListener(
object : android.transition.Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition) {}
override fun onTransitionResume(transition: Transition) {}
override fun onTransitionPause(transition: Transition) {}
override fun onTransitionCancel(transition: Transition) {}
override fun onTransitionStart(transition: Transition) {
Handler().postDelayed({
activity_item.visibility = View.GONE
activity_view_1.visibility = View.VISIBLE
activity_view_2.visibility = View.VISIBLE
.............
.............
// Also you could e.g. set the background to your activity here, ets.
activity_view_root.background = backgroundDrawable
}, 150) //suppuse that the whole transition length is 300ms
}
}
})
The transition animations themselves could look as follows:
<transitionSet>
<targets>
<target android:targetId="#id/activity_root_view"/>
</targets>
<transition
class="com.organization.app.utils.FadeOutTransition"
android:duration="150"/>
<transition
class="com.organization.app.utils.FadeInTransition"
android:startDelay="150"/>
<changeBounds android:duration="300"/>
</transitionSet>
Here, custom FadeOutTransition and FadeInTransition were used since simple android <fade/> animation doesn't work with shared elements. These classes are similar to that given in the answer here: Why Fade transition doesn't work on Shared Element.
The steps for creating return transition are similar.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I'm trying to make an horizontal list with a RecyclerView, that when I put the focus on an item, increase the size of this. I want to make this effect:
Do you have any ideas to accomplish this?
I'm imagining something like this:
Create the horizontal RV
When binding the ViewHolder, attach a FocusChangeListener to the root view of the item
When the item gains focus, scale it to make it slightly bigger; when the focus is lost, revert the animation.
static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View root) {
// bind views
// ...
// bind focus listener
root.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
// run scale animation and make it bigger
} else {
// run scale animation and make it smaller
}
}
});
}
}
Thanks to dextor answer, I could figure out this answer.
I've used the FocusChangeListener and added animation states to change the size of the view:
static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(final View root) {
// bind views
// ...
// bind focus listener
root.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
// run scale animation and make it bigger
Animation anim = AnimationUtils.loadAnimation(context, R.anim.scale_in_tv);
root.startAnimation(anim);
anim.setFillAfter(true);
} else {
// run scale animation and make it smaller
Animation anim = AnimationUtils.loadAnimation(context, R.anim.scale_out_tv);
root.startAnimation(anim);
anim.setFillAfter(true);
}
}
});
}
And the code for the anims:
scale_in_tv:
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXScale="100%"
android:fromYScale="100%"
android:toXScale="110%"
android:toYScale="110%"
android:pivotX="50%"
android:pivotY="50%">
</scale>
scale_out_tv:
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="100"
android:fromXScale="110%"
android:fromYScale="110%"
android:toXScale="100%"
android:toYScale="100%"
android:pivotX="50%"
android:pivotY="50%">
</scale>