35

When extending an Android ViewGroup class, what is the purpose of the onLayout() override? I'm making a custom control in Android but for some reason the content (child View objects) isn't displaying. My approach was to extend the ViewGroup class, adding child-Views via the addView() method of ViewGroup. Then, in my main activity, I have the following code:

ChannelController myCC = new ChannelController(this); 
setContentView(myCC); 

ChannelController is the name of my custom class that extends ViewGroup. I must be doing something wrong because nothing is shown on the screen.

I understand that I must override and implement the onLayout() method, but with what? I know there's an entire page dedicated to this on the dev.android site, but it didn't help me much, mostly because I'm a newbie I guess. Any insight would be appreciated.

For reference, my ViewGroup extension looks like below:

public class ChannelController extends ViewGroup {

    final String TAG = "JAL"; 

    public ChannelController(Context c) 
    {   
        super(c); 
        init(c); 
    }

    public ChannelController(Context c, AttributeSet attibset)
    {
        super(c); 
        init(c); 
    }

    public ChannelController(Context c, AttributeSet attribset, int defStyle)
    {
        super(c); 
        init(c); 
    }

    public void init(Context c)
    {
        //RelativeLayout wrap = new RelativeLayout(c);
        RelativeLayout.LayoutParams wrapLP = new RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.MATCH_PARENT, 
                RelativeLayout.LayoutParams.WRAP_CONTENT); 

        RelativeLayout r1 = new RelativeLayout(c); 
        RelativeLayout.LayoutParams r1LP = new RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.MATCH_PARENT, 
                RelativeLayout.LayoutParams.WRAP_CONTENT); 

        RelativeLayout r2 = new RelativeLayout(c); 
        RelativeLayout.LayoutParams r2LP = new RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.MATCH_PARENT, 
                RelativeLayout.LayoutParams.WRAP_CONTENT); 

        TextView t = new TextView(c); 
        RelativeLayout.LayoutParams tlp = new RelativeLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 
        Button m = new Button(c); 
        RelativeLayout.LayoutParams mlp = new RelativeLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 
        Button s = new Button(c); 
        RelativeLayout.LayoutParams slp = new RelativeLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 
        SeekBar f = new SeekBar(c); 
        RelativeLayout.LayoutParams flp = new RelativeLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 

        t.setId(1); 
        m.setId(2); 
        s.setId(3); 
        f.setId(4); 
        r1.setId(5); 
        r2.setId(6); 

        t.setText("CHANNELNAME"); 
        t.setTextColor(Color.BLACK);
        tlp.setMargins(30, 0, 0, 0); 
        tlp.addRule(RelativeLayout.LEFT_OF, m.getId()); 
        tlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); 
        tlp.addRule(RelativeLayout.CENTER_VERTICAL); 
        tlp.addRule(RelativeLayout.CENTER_HORIZONTAL); 

        m.setText("M"); 
        m.setBackgroundColor(Color.rgb(237, 155, 31));
        m.setTextColor(Color.WHITE); 
        mlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); 
        mlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); 
        m.setTextSize(10); 

        flp.addRule(RelativeLayout.LEFT_OF, s.getId()); 
        flp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); 
        flp.addRule(RelativeLayout.CENTER_VERTICAL); 

        s.setText("S"); 
        s.setTextSize(10); 
        s.setBackgroundColor(Color.rgb(192, 48, 46)); 
        s.setTextColor(Color.WHITE); 
        slp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); 
        slp.addRule(RelativeLayout.ALIGN_PARENT_TOP); 

        r1.addView(t, tlp); 
        r1.addView(m, mlp); 
        r2.addView(f, flp);
        r2.addView(s, slp); 

        r1.setBackgroundColor(Color.rgb(233, 242, 251)); 
        r2.setBackgroundColor(Color.rgb(233, 242, 251)); 

        r1LP.addRule(RelativeLayout.ALIGN_PARENT_TOP); 
        r2LP.addRule(RelativeLayout.BELOW, r1.getId()); 

        this.addView(r1, r1LP); 
        this.addView(r2, r2LP); 

        this.setLayoutParams(wrapLP); 

        //this.addView(wrap); 

        Log.i(TAG, "ChannelController constructor was called"); 
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub
        //super.onLayout(changed, l, t, r, b); 

    }

}

What do I need to do in the onLayout method override?

Lavekush Agrawal
  • 6,040
  • 7
  • 52
  • 85
JLindsey
  • 700
  • 2
  • 7
  • 14

2 Answers2

53

In onLayout you need to call layout method on each child of this ViewGroup and provide desired position (relatively to parent) for them. You can check source code of FrameLayout (one of the simpliest subclasses of ViewGroup) to find out how it works.

Although, if you don't need any "special" layouting, you have other options:

  • Extend some another subclass of ViewGroup instead (FrameLayout for example)
  • Use LayoutInflater if you just need your control to look exactly as in XML (which, I think, is exactly your case)
Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146
  • Hi Dmitry Zaitsev.. I am facing same problem and not able to resolve even after using your solution. I have specified my problem in this link: http://stackoverflow.com/questions/19311549/after-inflating-and-assigning-a-layout-to-view-the-elements-inside-layout-not-s/19311818?noredirect=1#19311818. Please help – Sushil Oct 11 '13 at 09:19
  • Where do you inflate an XML layout, if you're extending `ViewGroup`, please? – Marcel Bro Jul 14 '15 at 21:37
  • 1
    @anoniim inside constructor of your custom `ViewGroup` – Dmitry Zaytsev Jul 14 '15 at 22:01
  • That way we still need to implement `onLayout()` calculating position of its child `Views`, right? – Marcel Bro Jul 14 '15 at 22:42
  • @anoniim not exactly. Inflating is not saving you from layouting. Layouting just assigns positions to views which are added to your container. Inflate == add all views from XML to your `ViewGroup`. If you're extending not `ViewGroup` directly but, say, `FrameLayout`, it will just work (since onLayout is already implemented there). If you're extending `ViewGroup` and writing your `onLayout` - you have to layout all those views which you added by yourself. – Dmitry Zaytsev Jul 15 '15 at 07:45
  • That's what I meant, I believe. Or are you saying I need to layout only the `View`s added programatically (not those inflated from XML)? – Marcel Bro Jul 15 '15 at 09:11
  • @anoniim there is no difference between Views added programmatically or via XML. They are all the same Views and play by the same rules. So, you need to layout all views within container. – Dmitry Zaytsev Jul 15 '15 at 09:13
1

Actually it helps in positioning children of a view group. following code may help

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
MarginLayoutParams layoutParams = (MarginLayoutParams) icon.getLayoutParams();

// Figure out the x-coordinate and y-coordinate of the icon.
int x = getPaddingLeft() + layoutParams.leftMargin;
int y = getPaddingTop() + layoutParams.topMargin;

// Layout the icon.
icon.layout(x, y, x + icon.getMeasuredWidth(), y + icon.getMeasuredHeight());

// Calculate the x-coordinate of the title: icon's right coordinate +
// the icon's right margin.
x += icon.getMeasuredWidth() + layoutParams.rightMargin;

// Add in the title's left margin.
layoutParams = (MarginLayoutParams) titleView.getLayoutParams();
x += layoutParams.leftMargin;

// Calculate the y-coordinate of the title: this ViewGroup's top padding +
// the title's top margin
y = getPaddingTop() + layoutParams.topMargin;

// Layout the title.
titleView.layout(x, y, x + titleView.getMeasuredWidth(), y + titleView.getMeasuredHeight());

// The subtitle has the same x-coordinate as the title. So no more calculating there.

// Calculate the y-coordinate of the subtitle: the title's bottom coordinate +
// the title's bottom margin.

...

you can find more detail about custom views here custom views

Irfan Ul Haq
  • 1,065
  • 1
  • 11
  • 19