0

I wanted to create a custom LinearLayout (and later a custom ImageButton) that could take percentage values for both dimensions of size based on its parent's size regardless of the parent type (Relative or Linear). I was following this post: How to size an Android view based on its parent's dimensions, and it was very helpful, but I have a problem that those answers don't address.

When I place my Custom LinearLayout inside another LinearLayout, everything works as expected. My Custom LinearLayout covers the expected space (80% of the parent's width in the example below).

However if I place it inside a RelativeLayout, my screen always shows empty, I am not sure why this happens.

Here is my class:

public class ButtonPanel extends LinearLayout {

    public ButtonPanel(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int newWidth = (int) Math.ceil(parentWidth * 0.8);

        this.setMeasuredDimension(newWidth, parentHeight);
        this.setLayoutParams(new RelativeLayout.LayoutParams(newWidth,parentHeight));

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

And here is my testing layout for the activity.

<RelativeLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="horizontal"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent">

        <com.android.tests.views.ButtonPanel
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:orientation="horizontal"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="@drawable/inner_panel"
                android:gravity="center_horizontal"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true">
        </com.android.tests.views.ButtonPanel>
    </RelativeLayout>

In my activity all I do is set the Content View to the above layout.

(Incidentally, does anybody now how I could get the type of the parent dynamically for setting the new LayoutParameters? Above you'll see the parent type (RelativeLayout) hard-coded into the Custom View onMeasure function)

Thanks in advance for any help!

Community
  • 1
  • 1
RobertoCuba
  • 881
  • 9
  • 25

3 Answers3

0

Is this exposed to be a problem?

this.setLayoutParams(new RelativeLayout.LayoutParams(newWidth,parentHeight)); // <-- a RelativeLayout params?
Pete Houston
  • 14,931
  • 6
  • 47
  • 60
  • I am not sure what you mean. Admittedly I am implementing this half-blind and what I know from that line is that the layout parameters that any given View or Viewgroup has, they have to be an instance of the layout parameters of their parent container. It was about this line that I asked my minor question at the end so that I wouldn't have to hard-code RelativeLayout as the parent's type. It seems that blessenm gave me the answer to that though. – RobertoCuba Oct 04 '11 at 16:52
0

In the onMeasure function you could use something like this to know what class is the parent of the view.

this.getParent().getClass().getName()

This should also work

a instanceof B

or

B.class.isAssignableFrom(a.getClass())

When using "instanceof", you need to know the class of "B" at compile time. When using "isAssignableFrom" it can be dynamic and change during runtime.

If you are not compfortable with string comparison, you could also use enums.

blessanm86
  • 31,439
  • 14
  • 68
  • 79
  • Thank you, I'll try that later today. – RobertoCuba Oct 04 '11 at 16:55
  • Tried this, but made a slight modification, getSimpleName() gets me the class name alone, turns out getName() gets the entire package (android.widget.RelativeLayout). It seems strange to me that I would have to resort to String comparisons to tell one parent from another, but I guess for my purposes it works. Thanks for the pointer! – RobertoCuba Oct 05 '11 at 01:30
  • Hi, thanks for the extra info, but through further testing and by fixing the main bug I was having I discovered that I don't really need to know the parent type. Turns out that I can edit the height and width of the LayoutParams of my view directly without having to instantiate a new LayoutParams object at all. In any case, I didn't accept the answer because your solution, while definitely helpful, was for the minor problem I was having and not the main issue I posted about. I have updated the post to reflect the fix to that main problem I was having. Thanks again! – RobertoCuba Oct 08 '11 at 23:57
0

Turns out my two inquiries in this post were more related than expected.

I realized that by setting my view's LayoutParams to a completely new instance, I was overwriting the layout positioning information needed by the Relative Layout to position my view.

By 'zeroing out' that information, my view has the right dimensions, but the layout doesn't know where to place it, so it simply doesn't.

The following code for the new onMeasure shows how just directly modifying the height and width of the LayoutParams already attached to my view I avoid both overwriting the layout position information and having to create new LayoutParams based on the parent's type.

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int specWidth = MeasureSpec.getSize(widthMeasureSpec);
        int specHeight = MeasureSpec.getSize(heightMeasureSpec);

        int newWidth = (int) Math.ceil(parentWidth * 0.8);
        int newHeight = (int) Math.ceil(parentHeight * 0.8);

        this.setMeasuredDimension(newWidth, newHeight);

        this.getLayoutParams().height = newHeight;
        this.getLayoutParams().width = newWidth;

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

Now, I'll be honest and say that this code is still not bug-free. Bringing the activity to the foreground and background multiple times constantly reduces the size of this custom view. The 0.8 reduction factor gets applied over and over each time the activity is brought up (I suspect the setting of the LayoutParams has to do with it, it might actually be unnecessary, but I haven't has time to test).

BUT, this still answered the question concerning this post, namely, why was my view not appearing at all despite having the right dimensions.

RobertoCuba
  • 881
  • 9
  • 25