Hi: When the user clicks on another tab an animation should appear to the next view? What is the best to do this?
By default, no animation is applied, at least on honeycomb. The view will just pop on.
You could do something like this if you wanted to animate it:
Find the child of the tab and play an animation on it.
Example:
First set a listener:
exampleTabhost.setOnTabChangedListener(new OnTabChangeListener()
{
#Override
public void onTabChanged(String tabId)
{
refreshTabHostUI(exampleTabhost);
}
});
In your listener play the animation on your tab:
View tab1 = th.findViewById(R.id.tab1);
if( tab1 != null )
playAnim(tab1, getBaseContext(), android.R.anim.fade_in, 500);
Play anim function:
public Animation playAnim( View v, Context con, int animationid, int startOffset )
{
if( v != null )
{
Animation animation = AnimationUtils.loadAnimation(con, animationid );
animation.setStartOffset(startOffset);
v.startAnimation(animation);
return animation;
}
return null;
}
Related
I am trying to use the AHBottomNavigation (https://github.com/aurelhubert/ahbottomnavigation) and I am following its demo app for make my own.
In the demo app it uses ViewPager and FragmentPagerAdapter to change between fragments, and everything is working perfectly.
My problem is, I want to add a fade-in fade-out transition between the fragments, I had tried to change this:
viewPager.setCurrentItem(position, false);
for
viewPager.setCurrentItem(position);
And I had added this in my MainActivity:
viewPager.setPageTransformer(false, new FadePageTransformer());
...
public class FadePageTransformer implements ViewPager.PageTransformer {
public void transformPage(View view, float position) {
view.setTranslationX(view.getWidth() * -position);
if(position <= -1.0F || position >= 1.0F) {
view.setAlpha(0.0F);
} else if( position == 0.0F ) {
view.setAlpha(1.0F);
} else {
// position is between -1.0F & 0.0F OR 0.0F & 1.0F
view.setAlpha(1.0F - Math.abs(position));
}
}
}
It actually works, but if I want to go from the first fragment to the last one, it shows all the fragments between them, like a scroll view.
It has a method in the Fragment class (DemoFragment) that I think I can use for a fade-in fade-out transition, but I have no idea of how to implement it, this is the method:
/**
* Called when a fragment will be displayed
*/
public void willBeDisplayed() {
// Do what you want here, for example animate the content
}
Sorry, I am new in android, I still learning, and I hope you can help me, thanks!
UPDATE:
I want to add a cross-fade animation between the fragments, like the example in Material Design (https://www.google.com/design/spec/components/bottom-navigation.html#bottom-navigation-behavior)
Transition between active and inactive views using a cross-fade
animation.
Since viewpagers transition through the pages inbetween, its animated page change should not be used according to the material design guidelines. Instead, you should use setCurrentItem(int, boolean) with the boolean parameter as false (this removes the transition through the inbetween elements) and perform the animation inside onTabSelected:
private Animation fadeOut;
private Animation fadeIn;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
fadeOut = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
fadeOut.setDuration(150);
fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
fadeIn.setDuration(150);
...
bottomNavigation.setOnTabSelectedListener(new AHBottomNavigation.OnTabSelectedListener() {
#Override
public boolean onTabSelected(int position, boolean wasSelected) {
if(!wasSelected) {
Fragment oldFrag = getCurrentFragment();
if(oldFrag.getView() != null) {
oldFrag.getView().startAnimation(fadeOut);
}
mViewPager.setCurrentItem(position, false);
// The following line might not be necessary
getSupportFragmentManager().executePendingTransactions();
Fragment newFrag = getCurrentFragment();
if(newFrag.getView() != null) {
newFrag.getView().startAnimation(fadeIn);
}
} else {
getCurrentFragment().onReopen();
}
return true;
}
});
}
So in my android app I have a HorizontalScrollView that will display images. All images are downloaded an added to the View before it is avalible to the user. However when it does apear I want each image to animate in seperatly. Ive tried a LayoutTransition object attached to my layout with this code to show the views:
transition = new LayoutTransition();
transition.setStagger(LayoutTransition.APPEARING, 500);
transition.setDuration(1000);
transition.setAnimateParentHierarchy(true);
for (int i = 0; i < mGallery.getChildCount(); i++) {
mGallery.getChildAt(i).setVisibility(View.VISIBLE);
transition.showChild(mGallery, mGallery.getChildAt(i));
}
I have also tried this method using the the AnimationEndListener, and a recursive animateView() method
private void animateView(final int index) {
if (mGallery.getChildAt(index) == null)
return;
final View child = mGallery.getChildAt(index);
final Animation an = AnimationUtils.loadAnimation(mRootView.getContext(), R.anim.slideup);
AnimationEndListener listener = new AnimationEndListener() {
#Override
public void onAnimationEnd(Animation animation) {
animateView(index + 1);
}
};
an.setAnimationListener(listener);
child.setAnimation(an);
child.setVisibility(View.VISIBLE);
an.start();
}
The first method is preferable however it always animates in all my images at the same time. The second method kind of works, however it will apply the animation to the first view and the all subsequent views will appear in sequence without the animation playing.
I have a main activity which keeps 2 fragments.
On the first fragment i call an animation from the main activity.
If i call it from a button it animates and everything is ok
If i try to call it automatically (like if(x>3) animate()) it doesn't show the animation at all and in addition if i push the button it continues not to showing the animation. the code is below
Parent
public void showAnimation()
{
AnimationSet as = new AnimationSet(true);
Animation up = AnimationUtils.loadAnimation(this.getBaseContext(), R.anim.slide_up);
up.setStartOffset(2000);
Animation down = AnimationUtils.loadAnimation(this.getBaseContext(), R.anim.slide_down);
down.setStartOffset(6000);
as.addAnimation(up);
as.addAnimation(down);
Log.v("FFF","ok");
test.setVisibility(View.VISIBLE);
test.startAnimation(as);
test.setVisibility(View.GONE);
}
Child
if(!settings.getBoolean(BADGE_D7, false)){
if (days >= 7){
days7.setImageResource(R.drawable.days7);
Log.v("FFF","done");
parent.showAnimation();
}
}
and in the constructor of the child i have
public Child(MainActivity p) {
this.parent = p;
}
by the way the log messages are shown up the only problem is the animation. What is the problem??
Well, i kinda found out a solution, it's not the best but it works.
First i put all the code in the child activity and i use it from there. I access the textView from the child and i added a treeObserver
test = (TextView) parent.findViewById(R.id.test1);
ViewTreeObserver vto = test.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
ready = true;
return true;
}
});
and I edited the showAnimation() like this:
public void showAnimation()
{
as = new AnimationSet(true);
Animation up = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_up);
Animation down = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_down);
down.setStartOffset(4000);
as.addAnimation(up);
as.addAnimation(down);
Log.v("FFF","ok");
while(!ready){} //Added this line
test.setVisibility(View.VISIBLE);
test.startAnimation(as);
test.setVisibility(View.GONE);
}
so while the TextView isn't in place it is stucks in a loop until everything is ok.
I had a view that I declared at the end of my layout to keep its Z index above its siblings. I had to touch the page to make the animation work.
So I set the Z index again through Java and it worked.
view.bringToFront();
I have made a PopupWindow that pops up when a user selects an item in a list. And then when he swipes left and right on the popup window, the data (in the popup) is change to the previous and next items in the list, respectively.
And I have made it so that it looks like a new popup window is sliding in from the left as the current one goes out from the right of the screen when the user slides his finger to the right ("Next" motion). And vice versa for the "Previous" motion.
But the issue is, as I have made 2 styles for the two animations (left-to-middle while middle-to-right and right-to-middle while middle-to-left), and as PopupWindows does not get updated while it is showing, when the user swipes to the right ("Next") and then to the left ("Previous"), the animation looks choppy as the 2 animations overlap. The same happens when the swipes are reversed again.
Now, if I call update() after changing the Animation, then it again slides in to the middle. That's even worse. So is there anything I can do to achieve the behavior I want?
Thanks.
EDIT: Source Code
Here is the code I used to show the popup for the first time:
popup1 = new PopupWindow(popupLayout, popup_width, popup_height, true);
popup1.setAnimationStyle(R.style.AnimationPopup);
popup1.showAtLocation(this.findViewById(R.id.main_container), Gravity.CENTER, 0, 0);
And this is the next() method's code:
if(popup2 != null) popup1 = popup2;
if(popup1 != null) {
popup2 = new PopupWindow(popupLayout, popup_width, popup_height, true);
popup2.setAnimationStyle(R.style.AnimationPopup);
popup1.dismiss();
popup2.showAtLocation(rootLayout, Gravity.CENTER, 0, 0);
}
And this is the prev() method's code:
if(popup2 != null) popup1 = popup2;
if(popup1 != null) {
popup2 = new PopupWindow(popupLayout, popup_width, popup_height, true);
popup2.setAnimationStyle(R.style.AnimationPopupReverse);
popup1.setAnimationStyle(R.style.AnimationPopupReverse);
popup1.dismiss();
popup2.showAtLocation(rootLayout, Gravity.CENTER, 0, 0);
}
That's all for the Java code. The AnimationPopup is a XML with a simple show of slide-from-right-to-center and a hide of slide-from-center-to-left animations. And the AnimationPopupReverse is the reverse of the above animations.
I have provided my first try of the codes. Here, the changed animations take effect after one more popup has been shown.
All animations appear much cleaner if you use android:hardwareAccelerated="true" in the manifest under the application tag. Even regular slide left/right animation will seem choppy without being hardware accelerated.
An easy solution would be to:
Disable swiping until the current animation ends.
Use android:duration="#android:integer/config_mediumAnimTime" making it pretty standard and sort of fast.
You can also implement some sort of dragging:
Catch Touch Events and change the location of the Popup accordingly. When the user removes his finger, either dispatch the swipe and show the next/previous popup window. This would actually involve some head scratching.
The best would be to use a ViewPager that just shows the popup windows with all the functionality of swiping implemented:
Example:
yourViewPager.setAdapter(new PagerAdapter() {
#Override
public Object instantiateItem(ViewGroup container, int position) {
View view;
//intialise view to a PopupWindow or some Layout with a PopupWindow in the location you want
container.addView(view, 0);
return view;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return (view==object);
}
#Override
public int getCount() {
//return the count
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
#Override
public void finishUpdate(ViewGroup arg0) {
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
#Override
public Parcelable saveState() {
return null;
}
#Override
public void startUpdate(ViewGroup arg0) {
}
});
I have a ViewFlipper implementation that needs to be improved. This ViewFlipper has three child views. Basically, I want an indicator on which child view is currently active. My ViewFlipper is just a part of a complex layout which also has list views, etc.
Switching of views is also automatic and done in a specified interval.
From Android's SDK reference, I haven't seen any listener for when the ViewFlipper changes the child view.
Do you guys know of a way I can have a listener for that event?
Or are there alternative ways I can implement this feature besides using ViewFlipper ?
Thanks!
If you apply animation (out or in animation) on view switching, you can set listener to an animation and, for example, act on animation end.
viewFlipper.getInAnimation().setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationRepeat(Animation animation) {}
public void onAnimationEnd(Animation animation) {}
});
I find one way to detect which child is actived :
addOnLayoutChangeListener to ViewFlipper, and getCurrentView of ViewFlipper, then compare with childs of ViewFlipper.
remember to removeOnLayoutChangeListener when activity onDestory
private View page1, page2, page3, page4;
private ViewFlipper viewFlipper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.flipper);
page1 = findViewById(R.id.MyFlipper_page01);
page2 = findViewById(R.id.MyFlipper_page02);
page3 = findViewById(R.id.MyFlipper_page03);
page4 = findViewById(R.id.MyFlipper_page04);
viewFlipper = (ViewFlipper) findViewById(R.id.MyFlipper_flipper);
viewFlipper.addOnLayoutChangeListener(onLayoutChangeListener_viewFlipper);
}
View.OnLayoutChangeListener onLayoutChangeListener_viewFlipper = new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if(viewFlipper.getCurrentView() == page1)
Log.d("test", "change to flipper_page1");
else if(viewFlipper.getCurrentView() == page2)
Log.d("test", "change to flipper_page2");
else if(viewFlipper.getCurrentView() == page3)
Log.d("test", "change to flipper_page3");
else if(viewFlipper.getCurrentView() == page4)
Log.d("test", "change to flipper_page4");
}
};
#Override
protected void onDestroy() {
super.onDestroy();
viewFlipper.removeOnLayoutChangeListener(onLayoutChangeListener_viewFlipper);
}
I have created an extended ViewFlipper which does exactly that: DecentViewFlipper
While this is an old question I found a decent approach that works.
public class MainLaunch extends Activity {
... main setup and code
int currentIndex = 0;
int maxIndex = 3;
// set specific animations for the view flipper on showNext
// only showNext while in valid index
public void showNext() {
if( currentIndex < maxIndex )
{
currentIndex++;
viewFlipper.setInAnimation(getBaseContext(), R.anim.slide_in_left);
viewFlipper.setOutAnimation(getBaseContext(), R.anim.slide_out_right);
viewFlipper.showNext();
}
}
// set specific animations for the view flipper on showPrevious
// only showPrevious while in valid index
public void showPrevious() {
if( currentIndex > 0 )
{
currentIndex--;
viewFlipper.setInAnimation(getBaseContext(), R.anim.slide_in_right);
viewFlipper.setOutAnimation(getBaseContext(), R.anim.slide_out_left);
viewFlipper.showPrevious();
}
}
// get current flipped view
public View getCurrentView() {
return viewFlipper.getChildAt(currentIndex);
}
}
Then to use the ViewFlipper you call showNext() or showPrevious anywhere and can get the currently active view by calling getCurrentView(). This helps in setting different animations for left and right flipping and for easily getting current working views.
I know this question already has an answer. But here's an alternative which is a method ViewFlipper inherited from ViewGroup and which seems to be the best from my experience.
Code Snippets Below:
ViewFlipper viewFlipper = findViewById (R.id.myViewFlipperId);
viewFlipper.setOnHierarchyChangeListener(ViewGroup.OnHierarchyChangeListener);
The onChildViewAdded(View, View) method in the callback listener will be invoked whenever a child view is added to the ViewGroup.
Which you can use to detect whenever the ViewFlipper flips.
See ViewGroup.html#setOnHierarchyChangeListener API Docs