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.
Related
I am creating my own custom viewgroup in android. I have two children. One is a linearLayout (it's the first child and covers half of the screen) with some background image and buttons over it and other is a extension of View (it's the second child and covers whole screen) where I draw something using my finger.
I want the (first child) Linear Layout to be hidden under the (second child) extension of view so that I can use some gesture to swipe the second child to the right hand side (kind of like slide of google,youtube) and see the first child (LinearLayout). The problem is inside onLayout I place the children in certain order but the first child (LinearLayout) always comes in front no matter what I do.
secondchild.layout(0,0,top,bottom);
firstchild.layout(0,0,top/2,bottom);
I also tried
firstchild.layout(0,0,top/2,bottom);
secondchild.layout(0,0,top,bottom);
But the first child always comes on top.
Code for Costume ViewGroup:
public class RootViewLayout extends ViewGroup {
private View mDrawView;
private View mSlideView;
private int mTop;
private int mDragRange;
public RootViewLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onFinishInflate() {
mDrawView = findViewById(R.id.content_frame_white);
mSlideView = findViewById(R.id.slide_frame);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom){
bringChildToFront(mDrawView);
mDrawView.layout(0, 0, right, bottom);
mSlideView.layout(0, 0, right/2, bottom);
}
}
XML Code :
<com.example.drawapp.RootViewLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/Root_View_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
<com.example.drawapp.DrawView
android:id="#+id/content_frame_white"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/whitepaperwithcoffeestain">
</com.example.drawapp.DrawView>
<LinearLayout
android:id="#+id/slide_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#drawable/slidebackgrd"
android:orientation="vertical">
<Button
android:id="#+id/pen"
android:layout_width="100dip"
android:layout_height="wrap_content"
android:background="#drawable/pic"/>
</LinearLayout>
</com.example.drawapp.RootViewLayout>
When I don't put
bringChildToFront(mDrawView);
the blank is placed with proper black background, but that's not what I actually want. I want whole screen to be covered with DrawView (whose background is white with coffee stain over it).
Is there any specific way to tell the children to be placed one on top of other?
You need to change the z-order of the child views. You should probably use bringChildToFront(), i.e.
parentLayout.bringChildToFront(secondChild);
However, the effect depends on the type of the parent layout (e.g. if it's a LinearLayout then the views would be swapped). Since you're overlaying I guess it means it's a RelativeLayout, and then it should work as you want.
I see that in your case you're using a custom ViewGroup. If it's only to achieve the "full width/half width" children, then I would suggest swapping it for a RelativeLayout. Add secondchild with match_parent and firstchild as right of a centered 0dp view as in Adjust width to half screen
Or another option, possibly simpler, is to just change the visibility on the child that goes on top (VISIBLE or GONE).
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.
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)
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.)
Can anyone give me a tip for how to get the position of a view what is a child of an AbsoluteLayout? I want to do this for drag and drop the selected view.
Thank you!
AbsoluteLayout is deprecated, so probably you would have to provide your own drag&drop layout by extending ViewGroup. In general, layout it is responsible for positioning children widgets. This is done in onLayout() method which you would have to override. It will be probably something like this:
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for(int i=0; i<count; i++){
final View child = getChildAt(i);
if(GONE != child.getVisibility()){
//position child
child.layout(left, top, right, bottom);
}
}
}
So, by implementing your own DragAndDropLayout - you know the position of your children.
But, maybe there is simplier solution.
Regards!
To know where a child is in its parent, simply call getLeft() and getTop(). Also, do not use AbsoluteLayout :)