I'm trying to slide this view in and out using Transition + Transition manager, however when hitting the hide button to make the view GONE it doesn't have the sliding animation. However, the show button does have the sliding in animation to make the view VISIBLE again.
#OnClick(R.id.testBtn)
public void onTestBtnClick(){
//hide
Transition transition = new Slide(Gravity.START);
transition.setDuration(600);
TransitionManager.beginDelayedTransition(mParentLayout, transition);
mLayout.setVisibility(View.GONE);
}
#OnClick(R.id.testBtn2)
public void onTestBtn2Click(){
//show
Transition transition = new Slide(Gravity.START);
transition.setDuration(600);
TransitionManager.beginDelayedTransition(mParentLayout, transition);
mLayout.setVisibility(View.VISIBLE);
}
I've tried changing the gravity of testBtn2 to Gravity.END, but that causes it to slide all the way starting from the right side of the screen.
Here's the layout:
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/main_activity_root_view"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="#+id/testBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:text="Hide"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:id="#+id/testBtn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="#id/testBtn"
android:text="show"
/>
<LinearLayout
android:id="#+id/layout"
android:orientation="vertical"
android:layout_width="100dp"
android:layout_height="250dp"
android:background="#drawable/background_side_bar_corners"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Not sure whats wrong with your code. i have created a sample, just try the code below it working fine. make sure to add android:visibility="gone" for panel view in layout so that its hidden at first launch.
public class MainActivity extends AppCompatActivity {
boolean isShowing = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_damm_activty);
findViewById(R.id.testBtn).setOnClickListener(v -> {
showSlidingPanel(!isShowing);
});
}
private void showSlidingPanel(boolean show) {
ViewGroup parent = findViewById(R.id.main_activity_root_view);
View layout = findViewById(R.id.layout);
Transition transition = new Slide(Gravity.START);
transition.setDuration(450);
transition.addTarget(R.id.layout);
transition.setInterpolator(new AccelerateDecelerateInterpolator());
TransitionManager.beginDelayedTransition(parent, transition);
layout.setVisibility(show ? View.VISIBLE : View.GONE);
isShowing = show;
}
}
Create these animations
slide up
<translate
android:duration="400"
android:fromYDelta="100%"
android:toYDelta="0" />
slide down
<translate
android:duration="400"
android:fromYDelta="0"
android:toYDelta="100%" />
When you want to set the layout visible
binding.musicOptionsLayout.setVisibility(View.GONE); binding.musicOptionsLayout.startAnimation(AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_down));
When you want to set the layout Gone
binding.musicOptionsLayout.setVisibility(View.GONE); binding.musicOptionsLayout.startAnimation(AnimationUtils.loadAnimation(v.getContext(), R.anim.slide_down));
set android:visibility="gone" on XML..
Hope this will fix your issues...
The excellent answer here is very similar to this question.
I have noticed the constraint layout needs to be set up very specifically - if the view you are setting to GONE is constrained to it's sibling, those constraints seem to take precedence over any transition manager animation, i.e. setting a view to GONE just instantly kills it instead of smoothly animating, because the other view's constraints kick in immediately.
My solution was to use a guideline and have two views constrained to it, and set the GuidelineBegin for the transition manager. Just another option that may help:
val rootView = binding.constraintLayout
val constraintSet = ConstraintSet()
constraintSet.clone(rootView)
val transition = AutoTransition()
transition.duration = 150L
transition.excludeTarget(R.id.orders_recycler_view, true)
constraintSet.setGuidelineBegin(binding.guideline.id, if (showingDetailView) 0.px else 64.px)
TransitionManager.beginDelayedTransition(rootView as ViewGroup, transition)
constraintSet.applyTo(rootView)
Kotlin solution
Call the method wherever you want. Use FALSE to Slide out and TRUE
to Slide in.
targetView = View you want to slide in / out
rootLayout = Main layout where the targetView is located
In this example, it will slide from RIGHT to LEFT. If you want LEFT
to RIGHT, change Gravity.END to Gravity.START
private fun shareSlideInOut(show: Boolean) {
val slide = Slide(Gravity.END)
slide.duration = 450
slide.addTarget(targetView)
slide.interpolator = AccelerateDecelerateInterpolator()
TransitionManager.beginDelayedTransition(rootLayout, slide)
shareCodeLayout.visibility = if (show) View.VISIBLE else View.GONE
}
Related
I have a fragment with a navigation menu at the top-left corner. At the start of the activity, I want to gradually slide a view (let's call it black_view) out of the menu icon.
Here's a rough breakdown of how I want the animation to be in accordance with the images below:
Activity starts as the first image with black_view being invisible.
black_view gradually slides out from behind the menu icon length by length until it gets to the point of the second image.
>>>
What I've tried:
I tried achieving this by using a TranslateAnimation. However, the whole length of black_view shows up at the start of the animation and this is not what I want. I also saw a couple of sliding animation code snippets like this and this, but they all follow the TranlateAnimation model (with the whole length of black_view showing instantly).
How can I achieve this?
PS: If there's any important detail that I failed to add, kindly let me know.
It can be done easily with Slide transition from Transition API. Just use TransitionManager.beginDelayedTransition method then change visibility of black view from GONE to VISIBLE.
import androidx.appcompat.app.AppCompatActivity;
import androidx.transition.Slide;
import androidx.transition.Transition;
import androidx.transition.TransitionManager;
public class MainActivity extends AppCompatActivity {
private ViewGroup parent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
parent = findViewById(R.id.parent);
parent.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
parent.getViewTreeObserver().removeOnPreDrawListener(this);
animate();
return true;
}
});
}
private void animate() {
View textView = findViewById(R.id.text);
Transition transition = new Slide(Gravity.LEFT);
transition.setDuration(2000);
transition.setStartDelay(1000);
TransitionManager.beginDelayedTransition(parent, transition);
textView.setVisibility(View.VISIBLE);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Button" />
<FrameLayout
android:id="#+id/parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/button">
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000"
android:text="hello world"
android:textColor="#fff"
android:textSize="22sp"
android:visibility="gone" />
</FrameLayout>
</RelativeLayout>
Result:
All classes here are from androix package so code is backward compatible.
I would like to do something like this:
https://storage.googleapis.com/spec-host/mio-staging%2Fmio-design%2F1563837804615%2Fassets%2F1XlKhaQFU9aS84ACmF-EDjVKDgI4pPldv%2F02-overflowmenu.mp4
I would like to squeeze a "ViewGroup" as you can see in the video. In the meantime, I want to fade out the content in the ViewGroup at its original position. i.e. not pushing the content to the right.
Any idea how to implement this?
Thanks!
You can do such animations with Transition API. Declare two ViewGroups: first one is horizontal with cut/copy buttons, second is vertical with search/share buttons. Then switch these ViewGroups with TransitionManager. Then just provide Transition which describes how views on first and second ViewGroups appears and disappers.
import androidx.appcompat.app.AppCompatActivity;
import androidx.transition.ChangeBounds;
import androidx.transition.Fade;
import androidx.transition.Scene;
import androidx.transition.Slide;
import androidx.transition.Transition;
import androidx.transition.TransitionManager;
import androidx.transition.TransitionSet;
public class MainActivity extends AppCompatActivity {
private ViewGroup mSceneRoot;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
mSceneRoot = findViewById(R.id.sceneRoot);
showPopup1();
}
private void showPopup1() {
ViewGroup popup1 = (ViewGroup) getLayoutInflater().inflate(R.layout.popup_1, mSceneRoot, false);
popup1.findViewById(R.id.btnGo).setOnClickListener(v -> {
showPopup2();
});
Scene scene = new Scene(mSceneRoot, popup1);
TransitionManager.go(scene, getTransition(false));
}
private void showPopup2() {
ViewGroup popup2 = (ViewGroup) getLayoutInflater().inflate(R.layout.popup_2, mSceneRoot, false);
popup2.findViewById(R.id.btnBack).setOnClickListener(v -> {
showPopup1();
});
Scene scene = new Scene(mSceneRoot, popup2);
TransitionManager.go(scene, getTransition(true));
}
}
activity_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sceneRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom|right"
android:orientation="vertical"
android:paddingBottom="150dp"
android:paddingRight="50dp"
android:clipToPadding="false"
android:clipChildren="false"/>
popup_1.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/popup1"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:transitionName="bg"
app:cardElevation="10dp">
<Button
android:id="#+id/btnGo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="go"
android:transitionName="btn_go"/>
</androidx.cardview.widget.CardView>
popup_2.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/popup2"
android:layout_width="wrap_content"
android:layout_height="250dp"
android:transitionName="bg"
app:cardElevation="10dp">
<Button
android:id="#+id/btnBack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="back"
android:transitionName="btn_back"/>
</androidx.cardview.widget.CardView>
Last thing here is getTransition method. You can just use AutoTransition, it can handle easy layout changes.
private Transition getTransition(boolean open) {
return new AutoTransition();
}
Result:
Also just for demostration I wrote more complex transition where button on first viewgroup appear/dissapear with Slide animation.
private Transition getTransition(boolean open) {
Slide btnGo = new Slide(Gravity.RIGHT);
btnGo.addTarget("btn_go");
ChangeBounds bgBounds = new ChangeBounds();
bgBounds.addTarget("bg");
Fade btnBack = new Fade();
btnBack.addTarget("btn_back");
TransitionSet set = new TransitionSet();
set.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
if (open) {
set.addTransition(btnGo);
set.addTransition(bgBounds);
set.addTransition(btnBack);
} else {
set.addTransition(btnBack);
set.addTransition(bgBounds);
set.addTransition(btnGo);
}
return set;
}
Result:
Note that transitionName for background should be same in both layouts.
All transition classes here are from androix package so code is backward compatible. But if you need support pre Lollipop devices you should set transitionName via ViewCompat.setTransitionName method.
You can check my another answer with more difficult/custom transition.
I am needing to animate a layout from bottom to the top of the screen when the user taps on an EditText within that layout. Here is my code for doing this:
myEditText.setOnFocusChangeListener() { _, focused ->
if (focused) {
val animation = object: Animation() {
override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
val params = myLayout.getLayoutParams() as RelativeLayout.LayoutParams
params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE)
myLayout.setLayoutParams(params)
}
}
animation.duration = 1000
myEditText.startAnimation(animation)
}
}
When I tap on the EditText, the "myLayout" properly goes to the top of the screen. However, two issues:
1) There is no animation, it just jumps to the top. I am wanting it to "slide" up to the top.
2) More importantly, the views that are set to be "below" and relative to the myLayout do not go up with it. I know those are set properly, since when I add in the top alignment in the XML, they go up with it. But programatically it seems they stay down below the screen, still hidden.
I tried a different approach using ObjectAnimator, manually moving all the views up:
val anims = AnimatorSet();
val anim1 = ObjectAnimator.ofFloat(myLayout, View.TRANSLATION_Y, -1000f)
val anim2 = ObjectAnimator.ofFloat(layoutBelow, View.TRANSLATION_Y, -1000f)
val anim3 = ObjectAnimator.ofFloat(layoutBelowBelow, View.TRANSLATION_Y, -1000f)
anims.playTogether(anim1, anim2, anim3)
anims.start()
Doing that makes the animation work, but the views below "myLayout" are still invisible! I'm curious if it makes a difference that they are off-screen initially (?)
Thoughts on what I may be missing? If there's a better way to handle this, I'm open to different approaches as well.
This kind of animations can be done via Transition API which is now available in androix package.
Here is example where Button is centerVertical in RelativeLayout. TextView aligned to be below Button. After button click I remove centerVertical rule and add alignParentTop rule to Button. All animations can be done in almost 1 line.
import androidx.transition.AutoTransition;
import androidx.transition.TransitionManager;
....
#Override
protected void onCreate(Bundle savedInstanceState) {
....
View button = findViewById(R.id.button);
button.setOnClickListener(btn -> {
animate(btn);
});
}
private void animate(View button) {
RelativeLayout parent = findViewById(R.id.relativeLayout);
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) button.getLayoutParams();
lp.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
lp.removeRule(RelativeLayout.CENTER_VERTICAL);
AutoTransition transition = new AutoTransition();
transition.setDuration(2000);
TransitionManager.beginDelayedTransition(parent, transition);
button.setLayoutParams(lp);
}
layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Button" />
<TextView
android:id="#+id/bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/button"
android:layout_centerHorizontal="true"
android:text="bottom text" />
</RelativeLayout>
Result:
Anyone know how i can move an imageview to another imageview ?
im thinking at this kind of method, or maybe is another one that i dont know... no problem, im glad to learn it
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:interpolator="#android:anim/linear_interpolator">
<translate
android:duration="800"
android:fromXDelta="0%p"
android:toXDelta="75%p" />
i know that fromXDelta (is the starting position) and the toXDelta is the possition where should arrive.. but? how i can know what is my starting position and arrive position looking at my picture example?
Added details:
There are 4 different layouts in here, but only 3 are with weight value.
The bar from top where are buttons is a layout but not have weight like the rest.
So laying cards from up are in the layout1
My desired position to arrive are in the layout2
Playing cards from down are in the layout3
Also i use onClick methods in xml not onClickListeners. Thanks
You can do this programmatically like this :
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View target = findViewById(R.id.target);
final View viewToMove = findViewById(R.id.viewToMove);
viewToMove.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
translate(viewToMove, target);
}
});
}
private void translate(View viewToMove, View target) {
viewToMove.animate()
.x(target.getX())
.y(target.getY())
.setDuration(1000)
.start();
}
Here the XML :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="#+id/activity_main"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/target"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:background="#color/colorAccent"/>
<ImageView
android:id="#+id/viewToMove"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#color/colorPrimary"
android:src="#mipmap/ic_launcher"/>
</FrameLayout>
Finnaly i have succeded with the help of user Francois L.
Was a lot of work to do, i don't say no, because i needed to redesign all my layouts, but this is no problem cause i learned something new and i appreciate all the help i can receive.
If anyone want to move a view (any type of view, imageview, buttonview, textview etc ) to another view it is necessary that both views to be in the same layout, that is the most important part.
And the code to achieve this is:
//here is the part of initialization
ImageView poz1Inamic = (ImageView) findViewById(inamic_pozitia1);
ImageView poz1InamicSpateCarte = (ImageView) findViewById(inamic_pozitia1spatecarte);
//other code
poz1InamicSpateCarte.setVisibility(View.INVISIBLE);
//here is the initialization of the arrive position
ImageView cartePusaInamic = (ImageView) findViewById(R.id.cartePusaInamic);
//the code for moving from start position to arrive position
poz1Inamic.animate()
.x(cartePusaInamic.getX())
.y(cartePusaInamic.getY())
.setDuration(333)
.start();
I have a 9 patch image of a shadow that I want to add to the bottom of a RelativeLayout. The layout fills the screen, but slides up then the user taps a button. So, I would like to have the shadow image be below the RelativeLayout (pulled down with a negative bottom margin) so that when the layout sides up, the shadow is on the bottom edge of the layout, giving it a layered effect.
<ImageView
android:id="#+id/shadow"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="-10dp"
android:src="#drawable/shadow" />
I am sliding up the frame using:
ObjectAnimator mover = ObjectAnimator.ofFloat(mainView, "translationY", !historyShown ? -ph : 0);
mover.setDuration(300);
mover.start();
Strangely, when I add the image to the layout and it give it a negative margin, it just isn't shown, like it is cutting off anything outside the bounds of the layout.
Is there a way around this?
I can think of one way of doing it, but I'm sure there must be a cleaner way.
Make the layout in which the mainView resides a FrameLayout (or RelativeLayout), and include the ImageView in that, making it a sibling of mainView, but list it before mainView. Set it to be at the bottom using layout_gravity="bottom" (or layout_alignParentBottom="true" if using a RelativeLayout).
Now change the target in the ObjectAnimator to something above the mainView (so either its container View or the Activity/Fragment), and change the property to something like "scrollUp", then create a method named setScrollUp(float) in the target. In this method set the translation of the mainView and shadow using setTranslationY(). Offset the shadow by its height.
Works for me here in a simple app using solid colours:
XML:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#f00">
<View
android:id="#+id/shadow"
android:layout_width="match_parent"
android:layout_height="20px"
android:layout_gravity="bottom"
android:background="#00f"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0f0"
android:id="#+id/container"/>
</FrameLayout>
Activity code:
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
container = findViewById(R.id.container);
shadow = findViewById(R.id.shadow);
container.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(final View v)
{
final float translationTo = (open) ? 0 : -300;
final float translationFrom = (open) ? -300 : 0;
open = !open;
ObjectAnimator anim = ObjectAnimator.ofFloat(MyActivity.this, "scrollUp", translationFrom, translationTo);
anim.setDuration(500);
anim.start();
}
});
}
public void setScrollUp(final float position)
{
container.setTranslationY(position);
shadow.setTranslationY(position + shadow.getHeight());
}