I have a LinearLayout (LayoutContentView) which can contain one or two view (SubView1 and SubView2) (they are eitherTextView or ImageView).
This layout is in another LinearLayout (MyScreenLayout).
I want to make an animation on LayoutContentView so it can move and show just a part of it in MyScreenLayout.
Both of these layouts have setClipChildren(false); so it can let it's children draw outside it-self.
Depending on different parameters, I can change the size of the content, and the size of the content I will show.
Basically, I expend from top to bottom to show the two subviews and unexpend for bottom to top to show only the second subview. Before I expend, I increase the size of the LayoutContentView, so it can show the two subviews, and after I unexpend, I decrease the size of the LayoutContentView, so it can only show the second subview, and it let space on the screen for other elements.
Here is my method for expending and un-expending LayoutContentView :
mLayoutContentView.clearAnimation();
float yFrom = 0.0F;
float yTo = 0.0F;
float xFrom = 0.0F;
float xTo = 0.0F;
if (expend) { // if we expend
// I change the height of my LayoutContentView so it we can show it's two subviews
final android.view.ViewGroup.LayoutParams lp = mLayoutContentView.getLayoutParams();
lp.height = subView1H + subView2H;
setLayoutParams(lp);
invalidate();
// we start the animation so it shows only the second subview
yFrom = -subView1H;
// and we animate from top to bottom until it shows the two subviews
yTo = 0;
} else { // if we un-expend
// we animate from bottom to top starting by showing the two subviews
yFrom = 0;
// and progressively hiding the first subview and showing only the second subview
yTo = -subView1H;
}
Animation anim = new TranslateAnimation(xFrom, xTo, yFrom, yTo);
anim.setDuration(1000);
anim.setFillAfter(true);
anim.setFillEnabled(true);
anim.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
if (!expend) {
// if we un expend at the end of the animation we can set the size of LayoutContentView to the size of the second subview again
final android.view.ViewGroup.LayoutParams lp = mLayoutContentView.getLayoutParams();
lp.height = subView2H;
mLayoutContentView.setLayoutParams(lp);
invalidate();
}
}
});
mLayoutContentView.startAnimation(anim);
The way I made my animation I need it to apply on LayoutContentView, the layout which contain two subview, and with startAnimation() it doesn't do the animation.
I tried to use a LayoutAnimationController, but instead of doing the animation on the LayoutContentView, it does it on each of its children...
I also tried to do the animation on each children myself, but I don't know why, the second subview isn't shown.
Each time I've tried to use HierarchyViewer, but it's does see the change made by the animation.
Does anyone know a solution or have faced the same problem and found a good solution ?
EDIT :
Well it seems that if you set a background color to your TextView and move them with animation, the background move but even if you have set the fill after parameter to your animation, the background moves back to it's original position or something like that, and therefore as I set a background color to both of my SubViews, somewhat one of the SubView's background hide the background of the other...
And there also a problem if after the animation, one of the SubView is still outside the its layout, there is also a problem during the animation, so I add a limitation to what I intended to here too.
Related
I have a View that has an OnClickListener. When clicked, the view translates up to a certain position on the page. This is no problem, the view goes where it should. When the view is clicked again, I would like to position it somewhere else, but this is not the case. After a little bit of trouble shooting, I found that my View's getTop() method returns the same value - even after the translation animation has moved the view to a different part of the screen. For the second animation, it is not using the current position (as I would expect), it instead uses the initial position.
Few things that I am doing: I am using the ObjectAnimation class rather than the TranslationAnimation class, since I wanted to keep the OnClickListener functioning. With the TranslationAnimation class, I found that the view was correctly moved, but the OnClickListener was only working in the area that the View started from. Using the ObjectAnimation class, I was able to easily get the translation to work AND the OnClickListener functions correctly - it is triggered where the view currently is on the screen.
Here's what I have so far:
final LinearLayout child = layouts.get(i); //ArrayList containing some dynamic layouts
final int offset = target - child.getTop();
ObjectAnimator anim = ObjectAnimator.ofFloat(child,"translationY",offset);
anim.setDuration(250);
anim.start();
This is what happens when the view is clicked the first time. It translates up along the Y axis, where the offset determines how far the View needs to move from its current position.
Now, here's what happens on the second click. The goal here was to align the view with the parent's base.
target = parent.getBottom();
offset = target - child.getTop();
anim = ObjectAnimator.ofFloat(child, "translationY",offset);
anim.setDuration(250);
anim.start();
prev = child;
This is where things fall apart - child.getTop() returns the Y coordinate of the view's ORIGINAL position. Not the current position. So after the animation, the view is placed well below the bottom of the parent. I read a different question which stated that I should use child.getY() instead, which is supposed to give me the translationY position plus the top position, but this didn't lead to any better results. I can't seem to get this to work just right. I'd simply like to move the view from its current position to the bottom of the screen, but this appears to be a hard thing to accomplish. Any help would be appreciated.
EDIT
I have added an animation listener:
ObjectAnimator anim = ObjectAnimator.ofFloat(child,"translationY",offset);
anim.setDuration(250);
anim.addListener(new ObjectAnimator.AnimatorListener(){
#Override
public void onAnimationStart(Animator animation) {
System.out.println("start: " + child.getTop() + " " + child.getY());
}
#Override
public void onAnimationEnd(Animator animation) {
System.out.println("end: " + child.getTop() + " " + child.getY() + " " + child.getTranslationY());
child.setTop((int)child.getY());
System.out.println(child.getTop());
}
#Override
public void onAnimationCancel(Animator animation) {}
#Override
public void onAnimationRepeat(Animator animation) {}
});
anim.start();
Here I am setting the listener to try to change where the Top of the view is located. Behaviour is again not working as expected. The view is actually sent up above the screen when I do this. Output of the System.out looks like this:
start: 2008 2008.0
end: 2008 478.0 -1530.0
478
So calling child.getTop() after the animation is complete and setting a new position returns a positive integer, but the view is not actually completely on screen. It is above the screen, partly visible. The height of the view itself is about 700px. I am still so confused as to why this is such a hard thing to accomplish.
EDIT 2
I have also tried setting layoutparams inside the onAnimationEnd method:
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)child.getLayoutParams();
params.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
params.topMargin = (int)child.getY();
child.setLayoutParams(params);
Result: child.getTop() still returns the original position of 2008.
You can get the very bottom of the screen coordinates like this :
float bottomOfScreen = getResources().getDisplayMetrics().heightPixels;
but you probably want it minus the height of your LinearLayout or else your LinearLayout will be cut off by the bottom :
float bottomOfScreen = getResources().getDisplayMetrics().heightPixels
- child.getHeight();
// if you want a little more space to the bottom
// try something like - child.getHeight()*2;
Then use ViewPropertyAnimator to animate your LL like this :
child.animate()
.translationY(bottomOfScreen)
.setInterpolator(new AccelerateDecelerateInterpolator())
.setDuration(250);
The Interpolator is just to make the animation more realistic.
In the case that child.getHeight() returns 0 , your Linear Layout has not been finished setting up by the system, in that case you might want to do something like :
child.post(new Runnable() {
#Override
public void run() {
float bottomOfScreen = getResources().getDisplayMetrics().heightPixels
- child.getHeight()*2;
child.animate()
.translationY(bottomOfScreen)
.setInterpolator(new AccelerateDecelerateInterpolator())
.setDuration(250);
}
});
Remember that a duration of 250 milliseconds is very fast, and does usually not look cool translating stuff on the screen, so you might want to set it a little higher, but thats just a matter of taste.
I'm looking to implement an animation that will slide open or expand a ListView in android. At the moment, I'm using a scale animation (shown below)
Animation slideDown = new ScaleAnimation(1.0f, 1.0f, 0.0f, 1.0f);
slideDown.setDuration(1000);
but this doesn't work for 2 reasons:
1) I want the first item to fade in, and then the list to drop down. If I do a fade in animation, the entire list shows up and then jumps back up to the top and scales down to full size
2) I'm not really looking to scale, I would like to just reveal the ListView by animating down the bottom (if that makes sense).
Is there a way that I can do a TranslateAnimation on just the bottom margin or something similar? I looked into the drawable Clip resource, but that didn't seem to fit my needs here (I'm not sure how to apply it to a ListView). And I'm using a normal ListView, not an ExpandableListView, because I just have one group of items.
So I started searching for different things and it turns out that animating the height was my solution. Essentially I start with a dummy object in the list (so that it has at least 1 row), then I animate it from the height of that row to the height of the full list. Below is the code for the runnable that I am now calling.
private Runnable slideDownList1 = new Runnable() {
#Override
public void run() {
final ListView detail = (ListView)getView().findViewById(R.id.detail_menu_1);
ValueAnimator va = ValueAnimator.ofInt(detail.getHeight(), detail.getHeight()*mItem.containedObjects.size());
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
detail.getLayoutParams().height = value.intValue();
detail.requestLayout();
}
});
va.setDuration(1000);
va.start();
}
};
The XML is just a basic ListView with the id of detail_menu_1.
The new element Recycler View include animations. You can use it with Support Library
https://developer.android.com/training/material/lists-cards.html
I have 3 views on a screen in android, assume like set of buttons, map, list and another information view. these are all present vertically.
So, if i click on list, map should get updated & also buttons color should change. Some time if i click on button information should display.
In this scenario, is it good to use fragments? or Relative layout?. suggest
Thanks
I would go with a Activity that consists of a RelativeLayout, with 3 FrameLayouts inside the RelativeLayout. I would then add Fragments in code, to the FrameLayouts. 1 fragment to each. This way you can easily move the fragments in any way you desire. Sliding menus, top sliding, side sliding, over and under, so easy. So easy if you set it up like this.
When the far left is selected you can shove ther other 2 the right, when the center receives focus slide the left one to the left and right one right, and when the far right one gets focus, slide the other to to the left.
Or you can do top to bottom.
Or you can just have all 3 have equal space, at all times.
Or you can always shove the non-focused ones to the right, left, top, bottom, the possibilities are endless. You can shrink the unfocused 2 to 1/4 size and shove them to one side of the screen, one o top and one on bottom.
See where I'm going?
otherwise do a linearlayout, with 3 framelayouts, and set each framelayout to weight = 1 (may have to toggle a few other options to keep them perfectly even at all times), then add your fragments.
public void swapfragment(int fragId, Bundle args, boolean slide)
{
FragmentTransaction ft = getFragmentManager().beginTransaction();
switch (fragId)
{
case FRAGID_DEVICE:
currentFrag = new FragmentDevice();
currentFrag.setArguments(args);
((FragmentDevice) currentFrag).initialize();
break;
case FRAGID_NETWORK:
currentFrag = new FragmentNetwork();
currentFrag.setArguments(args);
((FragmentNetwork) currentFrag).initialize();
break;
}
ft.replace(R.id.flFragHost, currentFrag).commit();
if (slide)
slideFragment();
}
private void slideFragment()
{
final Point displaySize = new Point();
getWindowManager().getDefaultDisplay().getSize(displaySize);
if (isFragmentOut)
{
isFragmentOut = false;
Animation slideOutAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_right_out_80);
AnimationListener listener = new Animation.AnimationListener()
{
public void onAnimationStart(Animation animation)
{
}
public void onAnimationRepeat(Animation animation)
{
}
public void onAnimationEnd(Animation animation)
{
int pushback = (int) (displaySize.x * .8f);
rlFragHost.clearAnimation();
RelativeLayout.LayoutParams FragContainerParams;
FragContainerParams = (android.widget.RelativeLayout.LayoutParams) rlFragHost.getLayoutParams();
FragContainerParams.setMargins(pushback, 0, pushback * -1, 0);
rlFragHost.setLayoutParams(FragContainerParams);
}
};
slideOutAnimation.setAnimationListener(listener);
rlFragHost.startAnimation(slideOutAnimation);
}
else
{
isFragmentOut = true;
Animation slideInAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_left_in_80);
AnimationListener listener = new Animation.AnimationListener()
{
public void onAnimationStart(Animation animation)
{
}
public void onAnimationRepeat(Animation animation)
{
}
public void onAnimationEnd(Animation animation)
{
rlFragHost.clearAnimation();
RelativeLayout.LayoutParams FragContainerParams;
FragContainerParams = (android.widget.RelativeLayout.LayoutParams) rlFragHost.getLayoutParams();
FragContainerParams.setMargins(0, 0, 0, 0);
rlFragHost.setLayoutParams(FragContainerParams);
}
};
slideInAnimation.setAnimationListener(listener);
rlFragHost.startAnimation(slideInAnimation);
}
}
I know this isn't exactly what you need, but it should get you started. this is how I do sliding menus.
I think a lot of it is personal preference. My particular favorite is the linear layout. You mentioned that you want to present the items vertically, well, just set android:orientation="vertical", put your items in the xml file in order, and there you go. I've never used fragments personally, so I can't speak to their usefulness, but the linear layout has yet to let me down.
As I've a master in MS Paint, I will just upload a picture selfdescripting what I'm trying to achieve.
I've searched, but I'm not really sure what do I've to search. I've found something called Animations. I managed to rotate, fade, etc an element from a View (with this great tutorial http://www.vogella.com/articles/AndroidAnimation/article.html)
But this is a bit limited for what I'm trying to achieve, and now, I'm stuck, because I don't know how is this really called in android development. Tried words like "scrollup layouts" but I didn't get any better results.
Can you give me some tips?
Thank you.
You can see a live example, with this app: https://play.google.com/store/apps/details?id=alexcrusher.just6weeks
Sincerely,
Sergi
Use something like this as your layout (Use Linear, Relative or other layout if you wish):
<LinearLayout
android:id="#+id/lty_parent">
<LinearLayout
android:id="#+id/lyt_first" />
<LinearLayout
android:id="#+id/lyt_second"/>
</LinearLayout>
And then in an onClick method on whatever you want to use to control it, set the Visibility between Visible and Gone.
public void buttonClickListener(){
((Button) findViewById(R.id.your_button))
.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (lyt_second.getVisibility() == View.GONE) {
lyt_second.setVisibility(View.VISIBILE);
}
else {
lyt_second.setVisibility(View.GONE);
}
});
Which is fine if you just want a simple appear/disappear with nothing fancy. Things get a little bit more complicated if you want to animate it, as you need to play around with negative margins in order to make it appear to grow and shrink, like so:
We use the same onClick method that we did before, but this time when we click it starts up a custom SlideAnimation for the hidden/visible view.
#Override
public void onClick(View v) {
SlideAnimation slideAnim = new SlideAnimation(lyt_second, time);
lyt_second.startAnimation(slideAnim);
}
The implementation of the SlideAnimation is based on a general Animation class, which we extend and then Override the transformation.
public SlideAnimation(View view, int duration) {
//Set the duration of the animation to the int we passed in
setDuration(duration);
//Set the view to be animated to the view we passed in
viewToBeAnimated = view;
//Get the Margin Parameters for the view so we can edit them
viewMarginParams = (MarginLayoutParams) view.getLayoutParams();
//If the view is VISIBLE, hide it after. If it's GONE, show it before we start.
hideAfter = (view.getVisibility() == View.VISIBLE);
//First off, start the margin at the bottom margin we've already set.
//You need your layout to have a negative margin for this to work correctly.
marginStart = viewMarginParams.bottomMargin;
//Decide if we're expanding or collapsing
if (marginStart == 0){
marginEnd = 0 - view.getHeight();
}
else {
marginEnd = 0;
}
//Make sure the view is visible for our animation
view.setVisibility(View.VISIBLE);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
// Setting the new bottom margin to the start of the margin
// plus the inbetween bits
viewMarginParams.bottomMargin = marginStart
+ (int) ((marginEnd - marginStart) * interpolatedTime);
// Request the layout as it happens so we can see it redrawing
viewToBeAnimated.requestLayout();
// Make sure we have finished before we mess about with the rest of it
} else if (!alreadyFinished) {
viewMarginParams.bottomMargin = marginEnd;
viewToBeAnimated.requestLayout();
if (hideAfter) {
viewToBeAnimated.setVisibility(View.GONE);
}
alreadyFinished = true;
}
hideAfter = false;
}
}
EDIT: If anyone had used this code before and found that if you click on the button that starts the animation more than once before the animation was finished, it would mess up the animation from then on, causing it to always hide the view after the animation finished. I missed the reset of the hideAfter boolean near the bottom of the code, added it now.
you can do this manually by using setvisibility feature on the event onClick()
or
use this
dynamically adding two views one below other
I have a LinearLayout, I'm applying a translation animation to it. I'm filling the animation before and after. Visually it works fine. The animation ends by translating the view off screen. But if I click an x,y coordinate on screen that happens to be where the view was at some point during its animation, a button on the view has its click listener fire.
The only solution I've found is to add an animation listener, and when the animation ends, mark the buttons on the (now out of view) layout to visibility=gone, enabled=false. This seems bizarre - the view is no longer on screen, but it's still responding to click events. Is this a known thing, I'm probably not setting the animation up correctly?
Thanks
----- Update --------
I refactored my animation a little. Instead of using animation.setFillAfter(true), I set the layout's visibility to GONE when the animation is complete. Now it doesn't register clicks when off-screen. Still interested to know if this is a known thing, as it'd be easier to simply not have to add an animation listener etc.
Translate Animations on lower level API( below honey comb) changes where the button is drawn, but not where the button physically exists within the container. So, you are on your own to handle this situation. For more information about this you can refer to this link. One way is to actually change the location of the button in the layout(not by animation). Here is how you can achieve this:
params = (LayoutParams) mBtn.getLayoutParams();
TranslateAnimation animation = new TranslateAnimation(0, 0, 0, 400);
animation.setDuration(2000);
animation.setAnimationListener(mAnimationListener);
mBtn.startAnimation(animation);
....
....
private AnimationListener mAnimationListener = new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
params.topMargin = params.topMargin + 400;
mButton.setLayoutParams(params);
}
};
Here by changing the layout params we are changing the physical position of the button.
In your case as view is going off the screen so you just need to change the visibility of the button(View.GONE) on animation end.