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;
}
Related
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.
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 am new to android and i want to fix a pop up dialogue box relative to another button. I am inflating the popup window using code. How can i get current position of the button so that i can use these values for inflating the pop up window.
This is the code I use to get the current x/y coordinate of any View Object:
/**
* #return the absolute x and y coordinates of the given view
*/
public Point currentPosition(View view)
{
int[] loc = new int[2];
view.getLocationOnScreen(loc);
return new Point(loc[0], loc[1]);
}
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.