Views within a Custom ViewGroup are not showing

triggs picture triggs · Jan 2, 2012 · Viewed 7.2k times · Source

I have recently delved into creating custom ViewGroups and have encountered a problem I can't figure out.

I have 2 ViewGroups - ViewManager and Article

ViewManager simply lays out an Article below the previous Article (ie like a vertical LinearLayout)

Article arranges several TextViews and an ImageView as you can see in the image. Creating a single Article and adding it to ViewManager works fine and everything shows up but when I add a second Article none of the content of the Article is visible.

Illustrates second view not showing

So why are none of the TextViews or the ImageView showing up (note the blue bar is drawn with canvas.drawRect() in onDraw()). I have also printed out (via logcat) the the bound values of the child views(ie getLeft()/Top()/Right()/Bottom() in drawChild() and they all seem to look fine

FIRST TextView
FIRST left 5 right 225 top 26 bottom 147
FIRST TextView
FIRST left 5 right 320 top 147 bottom 198
FIRST TextView
FIRST left 5 right 180 top 208 bottom 222
FIRST ImageView
FIRST left 225 right 315 top 34 bottom 129
SECOND TextView
SECOND left 10 right 53 top 238 bottom 257
SECOND TextView
SECOND left 5 right 325 top 257 bottom 349
SECOND TextView
SECOND left 5 right 320 top 349 bottom 400
SECOND TextView
SECOND left 5 right 180 top 410 bottom 424

So does anyone have an idea of what I've done wrong?

the Article onMeasure method

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);

    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
    specWidth = widthSpecSize;
    int totalHeight=0;

    int width = 0;
    if(mImage!=null&&mImage.getParent()!=null){
        measureChild(mImage,MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST));
        width=widthSpecSize-mImage.getWidth();
        imageWidth = mImage.getMeasuredWidth();
    }
    for(int i = 0;i<this.getChildCount();i++){
        final View child = this.getChildAt(i);

        //get the width of the available view minus the image width
        if(imageWidth > 0)
            width =widthSpecSize- imageWidth; 
        else
            width = widthSpecSize;

        //measure only the textviews
        if(!(child instanceof ImageView)){
            measureChild(child, MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), heightMeasureSpec);
            //calculate total height of the views, used to set the dimension
            totalHeight+=child.getMeasuredHeight();
        }
    }
    //if the title height is greater than the image then the snippet
    //can stretch to full width
    if(mTitle.getMeasuredHeight()>mImage.getMeasuredHeight())
        measureChild(mSnippet, MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.AT_MOST), heightMeasureSpec);

    //measure source to make it full width
    measureChild(mSource,MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.AT_MOST), heightMeasureSpec);
    setMeasuredDimension(widthSpecSize, totalHeight);
}

and the onLayout method

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

    int newLeft = left+outerMargin;
    int newTop = top+marginTop;
    int prevHeight = 0;

    if(mEngine!=null){

        int height = mEngine.getMeasuredHeight();
        int childRight = newLeft+mEngine.getMeasuredWidth(); 
        mEngine.layout(newLeft+marginLeft, newTop+prevHeight+2, childRight+marginLeft, newTop+height+prevHeight+2);
        maxRight = Math.max(maxRight, childRight);
        prevHeight += height+2;
        topBarHeight = mEngine.getMeasuredHeight()+(marginLeft*2);
        maxBottom = Math.max(maxBottom, mEngine.getBottom());
    }
    if(mTitle!=null){
        int height = mTitle.getMeasuredHeight();
        int childRight = newLeft+mTitle.getMeasuredWidth();
        mTitle.layout(newLeft, newTop+prevHeight, childRight, newTop+height+prevHeight);
        maxRight = Math.max(maxRight, childRight);
        prevHeight += height;
        maxBottom = Math.max(maxBottom, mTitle.getBottom());
    }
    if(mSnippet!=null){
        int height = mSnippet.getMeasuredHeight();
        int childRight = newLeft+mSnippet.getMeasuredWidth();
        mSnippet.layout(newLeft, newTop+prevHeight, right, newTop+height+prevHeight);
        maxRight = Math.max(maxRight, childRight);
        prevHeight += height;
        maxBottom = Math.max(maxBottom, mSnippet.getBottom());
    }
    if(mSource !=null){
        int height = mSource.getMeasuredHeight();
        int childRight = newLeft+mSource.getMeasuredWidth();
        mSource.layout(newLeft, newTop+prevHeight+(marginTop*2), childRight, newTop+height+prevHeight+(marginTop*2));
        maxRight = Math.max(maxRight, childRight);
        prevHeight += height;
        maxBottom = Math.max(maxBottom, mSource.getBottom());

    }
    if(mImage!=null){
        int height = mImage.getMeasuredHeight();
        log("mxW "+maxRight);
        int childRight = maxRight+mImage.getMeasuredWidth();
        mImage.layout(right-mImage.getMeasuredWidth()+5, newTop+topBarHeight, right-5, height+topBarHeight);
        totalWidth = childRight;
        maxBottom = Math.max(maxBottom, mImage.getBottom());
    }else
        totalWidth = maxRight;

}

On a side note is it okay to use LayoutParams.WRAP_CONTENT as a parameter for makeMeasureSpec() like this?

MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.AT_MOST)

Answer

Ted Hopp picture Ted Hopp · Jan 2, 2012

In onLayout, the children should be positioned with respect to their parent. You are adding top (and left) to the coordinates of the children. That's not a problem for the first Article because top and left are zero. For the second Article, left is still zero but top is the top of the second article in its ViewManager. Just get rid of newTop and newLeft and use, respectively, marginTop and outerMargin in their place.

Regarding your aside: the first argument to makeMeasureSpec is supposed to be a dimension. By using WRAP_CONTENT, you are setting the maximum dimension to -2, which is probably not what you want to do.