I'm trying to animate two sets of views simultaneously using the MaterialContainerTransform transition. However, with the code below, only the first transition runs correctly, while the second jumps from the start to the end state unanimated. When tested separately, both run as they should. After searching the internet for many hours, I have so far been unable to find any solution to this. Any help would be appreciated!
val transform = TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
addTransition(MaterialContainerTransform().apply {
startView = startViewA
endView = endViewA
addTarget(endViewA)
pathMotion = MaterialArcMotion()
scrimColor = Color.TRANSPARENT
})
addTransition(MaterialContainerTransform().apply {
startView = startViewB
endView = endViewB
addTarget(endViewB)
pathMotion = MaterialArcMotion()
scrimColor = Color.TRANSPARENT
})
}
TransitionManager.beginDelayedTransition(viewContainer, transform)
startViewA?.visibility = View.GONE
endViewA?.visibility = View.VISIBLE
endViewB?.visibility = View.INVISIBLE
startViewB?.visibility = View.VISIBLE
MaterialContainerTransform transition, under the hood, uses a drawable to animate between two states. The drawable is added as an overlay to android.R.id.content by default ( root view ).
My guess is, as both drawables are added as an overlay to same view, one is completely overriden. I would need to take a look at view overlay system to make sure.
BUT there is a method in MaterialContainerTransform that lets you set which view is going to use the drawable as an overlay.
/**
* Set the id of the View whose overlay this transition will be added to.
*
* <p>This can be used to limit the bounds of the animation (including the background scrim) to
* the bounds of the provided drawing view, and also have the animation drawn at the relative
* z-order of the drawing view.
*
* <p>By default, the {#code drawingViewId} will be {#code android.R.id.content}. Additionally, if
* {#code drawingViewId} is the same as the end View's id, {#code MaterialContainerTransform} will
* add the transition's drawable to the {#code drawingViewId}'s parent instead.
*
* <p>If the drawing view cannot be found during the initialization of the {#code
* MaterialContainerTransform}, then an {#link IllegalArgumentException} will be thrown.
*/
public void setDrawingViewId(#IdRes int drawingViewId) {
this.drawingViewId = drawingViewId;
}
Use this method to set a different 'drawing view' for one of your transitions and I think that would solve it.
Related
In my GridView I show a Popupwindow when user makes a long tap. But according to documentation, if there's no room, the Popupwindow tries to scroll the view's parent. And this's what I want to avoid.
showAsDropDown(View anchor) If there is not enough room on screen to show
* the popup in its entirety, this method tries to find a parent scroll
* view to scroll.
I looked into Popupwindow documentation, and I found the following method to achieve my goal (To avoid scrolling the parent), but it's unsupported for app usage.
/**
* Allow PopupWindow to scroll the anchor's parent to provide more room
* for the popup. Enabled by default.
*
* #param enabled True to scroll the anchor's parent when more room is desired by the popup.
*/
#UnsupportedAppUsage
void setAllowScrollingAnchorParent(boolean enabled) {
mAllowScrollingAnchorParent = enabled;
}
For future readers, here's how I solved GridView being forced to be scrolled by the Popupwindow.
I couldn't find an approach to disable GridView's scrolling before showing the Popupwindow. So I made sure that Popupwindow won't appear near the bottom edge.
public void showDropDownMenu(View aView, PopupWindow aPopupWindow, int aMnuItemsNum){
int[] loc = new int[2];
aView.getLocationOnScreen(loc);
int popHeight = (toPixels(getMnuItemHeightDip()) * aMnuItemsNum) + aView.getHeight();
if(getResources().getDisplayMetrics().heightPixels - loc[1] > popHeight){
aPopupWindow.showAsDropDown(aView);
} else {
aPopupWindow.showAsDropDown(aView, 0, - popHeight, Gravity.START | Gravity.TOP);
}
}
I have an special use case where I have an an custom FrameLayout which contains 2 custom views. I will call it MyWrapperFL.
I have another custom view, which is outside this custom FrameLayout.
All of this is wrapped inside an basic FrameLayout.
So I have the following:
CustomView 1
MyWrapperFL
CustomView 2
CustomView 3
Now I want to change the order of the views like this:
CustomView 3 (top most)
CustomView 1
CustomView 2 (bottom most)
I've tried using the following utility method I've created:
/**
* Reorder the position of the views on their Z axis.
* <br/>
* The order of the views is important. The first in the list will be the top most view while the last in the list
* will be the bottom most view.
*
* #param views
* The list of views which to reorder. Can be {#code null}.
*/
private void updateViewsOrder(#Nullable View... views) {
if (null == views) {
// No views specified for reorder - exit early
return;
}
List<View> viewList = Arrays.asList(views);
int translationZ = viewList.size();
for (View view : viewList) {
if (null != view) {
view.bringToFront();
view.getParent().requestLayout();
view.invalidate();
}
}
}
But result is not the desired one. Instead of the expected result I get the following:
CustomView 1 (top most)
CustomView 3
CustomView 2 (bottom most)
I'm guessing this is because they have different parents. Can anyone help me with this? Is there a way to do it?
I want to implement a sliding Menu like FB or G+ app and I have found some sample code from FB Menu Demo and https://github.com/jfeinstein10/SlidingMenu
These are good to begin with, But I need something extra from them. Like here it works only on the click of the menu button but I want to move it by gestures as well.
I want to have the behavior that there is a center view and on moving that center towards the right, one view will appear and on moving that towards left, the menu will appear. Say there are three views A,B,C and when I swipe C towards left then A appear and when I swipe C towards right then B appear. C is in the middle of A and B.
1.Middle view moves towards right
Move towards right
2.Move the middle view towards left side
Move towards left
Now my question is: What are the best practices to develop the views like that. I have heard from someone that I should use fragments and View pager as well. So how can I develop this? Is there any sample implementation done by anyone ? Any help and suggestions are appreciated.
For reference see this app which uses this type of sliding b/w views Skout app
The simplest solution may be to use android-undergarment, which has bezel swiping built in, based on the project README:
The user will also be able to control the drawer by bezel swiping from the left side of the screen to open the drawer and doing the same from the right to close it. If you want to prevent this touch functionality, you can call setDrawerEnabled(false).
You can simply use TranslateAnimationon the view which you wish to move along with a pop up for fade and another pop up window for you menu. I have implemented it in my application, and it works like a charm.
Code:
public class SlidingOptionsMenuActivity extends Activity {
/**
* Signifies that the menu is already visible
*/
boolean alreadyShowing = false;
/**
* Width of the current window
*/
private int windowWidth;
/**
* Height of the current window
*/
private int windowHeight;
/**
* Reference of the {#link PopupWindow} which dims the screen
*/
private PopupWindow fadePopup;
/**
* The translate animation
*/
private Animation ta;
/**
* The view which needs to be translated
*/
private RelativeLayout baseView;
/**
* Reference of the {#link LayoutInflater}
*/
LayoutInflater inflater;
/*
* (non-Javadoc)
*
* #see android.app.Activity#onCreate(android.os.Bundle)
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Display display = getWindowManager().getDefaultDisplay();
windowWidth = display.getWidth();
windowHeight = display.getHeight();
inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
findViewById(R.id.btnOptions).setOnClickListener(new OnClickListener() {
/*
* (non-Javadoc)
*
* #see android.view.View.OnClickListener#onClick(android.view.View)
*/
#Override
public void onClick(View v) {
if(!alreadyShowing){
alreadyShowing = true;
openSlidingMenu();
}
}
});
}
/**
* Fades the entire screen, gives a dim background
*/
private void showFadePopup() {
final View layout = inflater.inflate(R.layout.fadepopup, (ViewGroup) findViewById(R.id.fadePopup));
fadePopup = new PopupWindow(layout, windowWidth, windowHeight, false);
fadePopup.showAtLocation(layout, Gravity.NO_GRAVITY, 0, 0);
}
/**
* Opens the sliding Menu
*/
private void openSlidingMenu() {
showFadePopup();
// The amount of view which needs to be moved out. equivalent to the
// width of the menu
int width = (int) (windowWidth * 0.6f);
translateView((float) (width));
int height = LayoutParams.FILL_PARENT;
// creating a popup
final View layout = inflater.inflate(R.layout.option_popup_layout,(ViewGroup) findViewById(R.id.popup_element));
final PopupWindow optionsPopup = new PopupWindow(layout, width, height, true);
optionsPopup.setBackgroundDrawable(new PaintDrawable());
optionsPopup.showAtLocation(layout, Gravity.NO_GRAVITY, 0, 0);
optionsPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
public void onDismiss() {
//Removing the fade effect
fadePopup.dismiss();
//to clear the previous animation transition in
cleanUp();
//move the view out
translateView(0);
//to clear the latest animation transition out
cleanUp();
//resetting the variable
alreadyShowing = false;
}
});
}
/**
* This method is responsible for view translation. It applies a translation
* animation on the root view of the activity
*
* #param right The position to translate to
*/
private void translateView(float right) {
ta = new TranslateAnimation(0f, right, 0f, 0f);
ta.setDuration(300);
ta.setFillEnabled(true);
ta.setFillAfter(true);
baseView = (RelativeLayout) findViewById(R.id.baseView);
baseView.startAnimation(ta);
baseView.setVisibility(View.VISIBLE);
}
/**
* Basic cleanup to avoid memory issues. Not everything is release after
* animation, so to immediately release it doing it manually
*/
private void cleanUp(){
if (null != baseView) {
baseView.clearAnimation();
baseView = null;
}
if (null != ta) {
ta.cancel();
ta = null;
}
fadePopup = null;
}
} //END of Class
//END of file
Hope this would help.
Another open source library I've found that's been very good is SlidingMenu. It should suit your needs as you can open and close the drawer by a "Menu" click and by bezel swiping. I found integrating this along with an Actionbar library like ActionBarSherlock or johannilsson's android-actionbar library is simply a matter of changing a line or two of code in the library project. The Readme for the SlidingMenu library explains how to integrate with the ABSherlock library.
One thing worth noting is that the SlidingMenu example project demonstrates a number of different drawer open-close animations. These are some of the best animations I've seen for this style of menu/navigation.
There is an Official Way ... useful and light (by use of v4 Support Library):
Creating a Navigation Drawer:
https://developer.android.com/training/implementing-navigation/nav-drawer.html
How can I recreate the layout animation affects you see in the Gmail app on Android 3.0+
I know you can use PropertyAnimators, ObjectAnimators, etc., but in my scenario, I have a several fragments on the screen which can be interchanged, so they're all contained within their own FrameLayouts, and all the FrameLayouts are in a RelativeLayout.
I can't figure out how to change the width of the FrameLayouts, since you have to edit that through the LayoutParams object.
Any ideas?
My take on this can be viewed on GitHub here
It demonstrates one way of hiding a fragment with an animation, like Gmail does.
The animation isn't perfect as the left fragment is moving slightly faster than the right fragment is.
The method that does the hiding defines the animation in code and looks as follows:
/**
* Besides showing/hiding the left fragment, this method specifies that a
* layout animation should be used. It is defined as a sliding animation.
* The right fragment will use the default animation, which is a sliding
* animation also.
*
* If the left fragment's animation is removed from this method, the default
* animation will be used which is a fading animation.
*
* Please note that this method will only have an effect in those screen
* configurations where the list is hideable; by default, a width between
* 600 and 1024 dip which corresponds to a portrait view on tablets. Change
* the boolean value in layout_constants.xml to allow for it in other screen
* sizes.
*
* #param visible
*/
protected void setLeftFragmentVisible(boolean visible) {
if (leftFragment != null && (leftFragment.isVisible() || visible)
&& getResources().getBoolean(R.bool.leftHideable)) {
final float listWidth = getLeftFragment().getView().getWidth();
ViewGroup container = (ViewGroup) findViewById(R.id.dual_layout);
// Don't clip the children, we want to draw the entire fragment even
// if it is partially off-screen.
container.setClipChildren(false);
final LayoutTransition trans = container.getLayoutTransition();
/**
* This specifies the delay before the leftFragment will appear.
* Change if you want the right fragment to move before.
*/
trans.setStartDelay(LayoutTransition.APPEARING, 0);
/**
* This is the delay before the right fragment will start to occupy
* the space left by the left fragment
*/
trans.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 100);
/**
* Adding, specifies that the left fragment should animate by
* sliding into view.
*/
ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "x",
-listWidth, 0f).setDuration(
trans.getDuration(LayoutTransition.CHANGE_APPEARING));
trans.setAnimator(LayoutTransition.APPEARING, animIn);
/**
* Removing, specifies that the left fragment should animate by
* sliding out of view.
*/
ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "x", 0f,
-listWidth).setDuration(
trans.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
trans.setAnimator(LayoutTransition.DISAPPEARING, animOut);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
if (getLeftFragment().isVisible()) {
fragmentTransaction.hide(getLeftFragment());
} else {
fragmentTransaction.show(getLeftFragment());
}
// The hiding/showing will automatically initiate the animations
// since
// we have specified that we want layout animations in the layout
// xml
fragmentTransaction.commit();
/*
* Display home as up to be able to view the list
*/
getActionBar().setDisplayHomeAsUpEnabled(!visible);
}
}
To make this work, you need to define that you want layout transition to be animated. One line in the top of your layout's xml structure will do that:
android:animateLayoutChanges="true"
If you don't want custom animations, you can remove everything before FragmentManager fragmentManager = getFragmentManager().
Now in my example I use fragments but the same principle should apply to any view.
Edit: Instead of changing the widths of your views manually, you should use layout weights instead to allow automatic resizing.
I have an activity with a couple of fragments inside. I'm using Frame Layout for each of the fragment and have Relative Layout as parent.
I've been having headaches on getting the exact location of a fragment in parent. But seems like I am only able to relative values.
So, is there anyway to get an exact location of a fragment within an activity?
I haven't done this with Fragments but with the contents of fragments.
If you use findById() on the activity that will find the id of anything inflated in that activity (including fragment contents etc).
You can then use something like View.getLocationOnScreen(int[]) this should give you the x and y of the View on screen. Never tried this on a fragment, but if you fragment is wrapped by a Layout then should be no problem.
Hope this helps?
Actually this might help, might need changing to take in two views as this method is inside a custom RelativeLayout I made:
/**
* Will return the X,Y of the Anchor view relative to this relative layout.. So It doesn't
* matter if the anchor view is nested, or any any other layout, this WILL find its location
* relative too this {#link HelpRelativeLayout}.
*
* #author chris.jenkins
* #param anchor
* #return
*/
private int[] calculateAnchorViewPosition(View anchor)
{
int[] loc = new int[2];
int[] conLoc = new int[2];
// getLocationInWindow(conLoc);
getLocationOnScreen(conLoc);
// anchor.getLocationInWindow(loc);
anchor.getLocationOnScreen(loc);
// get correct top left pos + half the h/w of the anchor
loc[0] = (loc[0] - conLoc[0]);
loc[1] = (loc[1] - conLoc[1]);
MyLog.d(loc[0] + " " + loc[1]);
return loc;
}