How do I force a ViewGroup to draw off screen? - android

I have several LinearLayouts that get filled with downloaded images or text within a ScrollView. The LinearLayouts have a LayoutAnimation applied to them, so each one "slides" into place when drawn. Is there a way to force the offscreen LinearLayouts to draw so that by the time the user scrolls to them, the animation has already completed? I've tried measuring each view like so: (container is the ViewGroup)
int measuredWidth = View.MeasureSpec.makeMeasureSpec(LayoutParams.FILL_PARENT, View.MeasureSpec.AT_MOST);
int measuredHeight = View.MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED);
container.measure(measuredWidth, measuredHeight);
container.layout(0, 0, container.getMeasuredWidth(), container.getMeasuredHeight());
container.requestLayout();
But they still won't draw until they appear on screen during scrolling (which normally is fine but the animation makes it.. er, not fine)

If you don't want to run the animation why don't you simply remove the animation? The framework will apply the animation because you tells it to.
Also note that none of your code causes a redraw. To draw you need to call invalidate() or draw().

For any future readers, here's what I ended up doing: I subclassed LinearLayout and overrode onLayout to only apply animation if the layout is currently on screen at the moment it is populated:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
super.onLayout(changed, left, top, right, bottom);
// only animate if viewgroup is currently on screen
int[] xy = new int[2];
this.getLocationOnScreen(xy);
int yPos = xy[1];
if (yPos < availableScreenHeight && bottom > 200)
{
Animation slide_down = AnimationUtils.loadAnimation(getContext(), R.anim.container_slide_down);
LayoutAnimationController controller = new LayoutAnimationController(slide_down, 0.25f);
this.setLayoutAnimation(controller);
}
}
This actually saves some cycles since I'm not applying animation across the board then removing it from views that don't need it. (BTW "availableScreenHeight" is just that, and "200" is simply a threshold that I know a populated view will never be smaller than. Your case may vary.)

Related

Remove all children overflowed of their parent bounds

In a special android application, I want to remove all child views from their parents if entire or a part of them was overflowed of their parents bounds (on window focus changed).
I have tried to write manual calculations on children and parent widths but I am looking for an object oriented solution to be applicable on all kinds of views.
This would probably the easiest by just extending the ViewGroup you are using and removing the views after layouting them. (ViewGroups include LinearLayout, RelativeLayout, ...)
By overwriting onLayout you could iterate over your children and remove any views with bounds not within of their parent.
Things to note: This will probably trigger another layout call, so be sure to properly handle things.
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// call super to layout children and have their bounds set
super.onLayout(changed, left, top, right, bottom);
// Iterate over all children
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getLeft() < left
// ... add top / bottom
|| child.getRight() > right) {
removeView(child);
}
}
}
And if you want to handle things without extending the ViewGroup you can always handle things by using the same principle after the view got layouted.

Android: call requestLayout() after update v.layout parameter causes layout reverts back to it's original position

I have framelayout which contains two relative layouts, one is on top of the other. When user clicks a button, the one on the top move 80% off the screen to the right. Then one on the bottom becomes clickable. This is what it looks like.
FrameLayout
RelativeLayout (bottom) RelativeLayout (top)
FilterWidgets Open/close button, ListView
It's really easy to achieve on 3.0+ with the new animation api which is Property base Animation. For the pre 3.0, because animation is view based. So I end up manually modify the layout property on onAnimationEnd. The call requestLayout to make it permanent, but only to find out the layout reverts back to original position. Anybody know how to move layout permanently?
see my other post if you want to see the whole picture:
https://stackoverflow.com/questions/14541265/changecursor-cause-layout-container-of-the-listview-to-reposition
theTranslationX.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator nullPointer) {
v.clearAnimation();
int theL = isMenuOn() ? 0 : v.getLeft() + getFilterWidth();
int theR = isMenuOn() ? v.getWidth() : v.getLeft() + getFilterWidth() + v.getWidth();
int theHeight = v.getHeight();
int theT = 0;
v.layout(theL, theT, theR, theHeight);
v.requestLayout();
}
});
This is 9 months late but try using:
yourView.layout(left,top,right,bottom); //all parameters are type int
However I don't think this is permanent, the position of the view will still be reset when you call requestLayout(), but give it a try.

Android Viewgroup: fade/blend two views on top of each other

I want to fade from one view to another on in a ViewGroup.
At the moment I'm doing the transition using setAlpha, but the problem is that only one view is being rendered, the one that was on top and is fading out.
Is the view-array inside ViewGroup an order by z-axis?
Is only the top view being rendered?
My layout method looks like this:
#Override
protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
L.debug("laying out {} children", this.getChildCount());
for (int i = 0; i < this.getChildCount(); i++) {
L.debug("layout out {}", i);
View view = this.getChildAt(0);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
}
}
Why don't you want to use ViewSwitcher? It does exactly what you want. Here is an example.
Is the view-array inside ViewGroup an order by z-axis?
There is no such thing like Z-order in android. Views are drawn in oder they were added to ViewGroup. First added draws first.
Is only the top view being rendered
No, android will draw all views in visible rect even if they are totally overlaped by others.
I think you should fix this place this.getChildAt(0) and layout all childrens in your ViewGroup.

Android: Notify Scrollview that it's child's size has changed: how?

When I enlarge the size of the content of a scrollview, the scrollview takes a while to get to "know" this size change of it's child. How can I order the ScrollView to check it's child immediately?
I have an ImageView in a LinearLayout in a ScrollView.
In my ScaleListener.onScale, I change the size of my LinearLayout. I then try to order a scroll on the scrollview. In the ScaleListener.onScale:
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) imageView.getLayoutParams();
params.width = (int) (startX * scaleFactor);
params.height = (int) (startY * scaleFactor);
imageView.setLayoutParams(params);
(...)
scrollView.scrollBy(scrollX, scrollY);
However, no scrolling occurs when in the situation before the scaling scrolling was not possible because the view was too small to scroll. After the setLayoutParams, the view should be larger, but no scrolling occurs because the scrollview thinks the child is still small.
When a fes ms later the onScroll is called again, it does scroll fine, it somehow found out that the child is larger and scrollable.
How can I notify the scrollview immediately, that the child's size has changed? So that scrollBy will work right after setLayoutParams on it's child?
I found a solution after trying just about every onXXX() method. onLayout can be used. You can plan the scroll and do it later in onLayout().
Extend your scrollview, and add:
private int onLayoutScrollByX = 0;
private int onLayoutScrollByY = 0;
public void planScrollBy(int x, int y) {
onLayoutScrollByX += x;
onLayoutScrollByY += y;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
doPlannedScroll();
}
public void doPlannedScroll() {
if (onLayoutScrollByX != 0 || onLayoutScrollByY != 0) {
scrollBy(onLayoutScrollByX, onLayoutScrollByY);
onLayoutScrollByX = 0;
onLayoutScrollByY = 0;
}
}
Now, to use this in your code, instead of scrollBy(x,y) use planScrollBy(x,y). It will do the scroll at a time when the new size of the child is "known", but not displayed on screen yet.
When you use a horizontal or vertical scrollview, of course you can only scroll one way, so you will have to change this code it a bit (or not, but it will ignore the scroll on the other axis). I used a TwoDScrollView, you can find it on the web.
You can call:
scrollView.updateViewLayout(childView, childLayout)

hierarchy viewer makes height changes appear

it's my first question.
I have built a custom component: a RelativeLayout with a TextView on the bottom and two ImageView above that, acting as a 2-columns clickable element of an histogram. To set the height of a bar, i get the "available height" in onLayout(), as container's height minus label's one:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mAvailHeight = getHeight()-findViewById(R.id.label).getHeight(); // it works
and then assign it (multiplied by a 0.-1. value) as a layout parameter to the ImageView:
View bar = findViewById(R.id.bigBar);
RelativeLayout.LayoutParams rlp = (RelativeLayout.LayoutParams) bar.getLayoutParams();
rlp.height = Math.round((float)mAvailHeight * mBigBarHeight);
}
The mBigBarHeight variable (0.-1.) can be set via this function:
public void setBigBarHeight(float value, float max) {
mBigBarHeight = value / max;
requestLayout(); //
invalidate(); // do these help? i find no difference
}
Now. When i add one of these "HistogramBar" in onCreate() and set the heights and label everything works as I expect. If i try to modify them later, say onClickSomething:
bar.setBigBarHeight(25, 100);
bar.setSmallBarHeight(50, 100);
bar.setLabel("jjk");
only the label changes. I checked with Hierarchy Viewer and actually the LayoutParams did change. If i click again changes appear.
The funny thing is that even if i do "Load View Hierarchy" from the tool changes get displayed (on the emulator)!! What happens? Is it strange? I want to do that in my code so that it works!
I couldn't find any similar question. Thanks.
When you load a hierarchy from the tool, a relayout/redraw happens to measure performance. You are probably not calling requestLayout() when you should.

Categories

Resources