Android ViewGroup: what should I do in the onLayout() override?

Jared Lindsey picture Jared Lindsey · Jul 7, 2013 · Viewed 27.5k times · Source

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?

Answer

Dmitry Zaytsev picture Dmitry Zaytsev · Jul 7, 2013

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)