0

I am having problem inflating my CustomView programmatically from the XML layout that I specify.

I have a CustomView which extends RelativeLayout and contains another RelativeLayout which in turns contain 2 ImageView and 1 LinearLayout. The ImageViews are arrow Icons which I put to the left and right of the parent by android:layout_alignParentLeft="true" and android:layout_alignParentRight="true" respectively, and the LinearLayout is used to fill all the space in between.

To make it clear, here is the xml layout view in the Eclipse Layout Designer, which is what I intended it to be...

my_xml_layout - which is what I'd like it to be

If I setContentView(R.layout.my_xml_layout); directly from the Activity, everything appears as shown in Eclipse Layout Designer, however, if I inflate the R.layout.my_xml_layout from my CustomView's constructor, there is a stubborn margin to the left and right of the ImageView that cannot go away.

This is done in java code, and is problematic:

As you can see, there is the stubborn spacing to the left of the left ImageView and to the right of the right ImageView

Any help will be highly appreciated! Thanks in advance!

my_xml_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

  <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">

        <ImageView
            android:id="@+id/imageLeftArrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:src="@drawable/dock_leftarrow" />

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >

        </LinearLayout>

        <ImageView
            android:id="@+id/imageRightArrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:src="@drawable/dock_rightarrow" />

    </RelativeLayout>

</RelativeLayout>

I inflate it in the CustomView's Constructor through this line:

View.inflate( mContext, R.layout.my_xml_layout, this );

My CustomView's onLayout:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // Do nothing. Do not call the superclass method--that would start a layout pass
    // on this view's children. PieChart lays out its children in onSizeChanged().
    Log.e("DrawView", "DrawView.onLayout: " + l + ", " + t + ", " + r + ", " + b);

    int iChildCount = this.getChildCount();
    for ( int i = 0; i < iChildCount; i++ ) {
        View pChild = this.getChildAt(i);
        pChild.layout(0, 0, pChild.getMeasuredWidth(), pChild.getMeasuredHeight());
    }
}

My CustomView's onMeasure:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // Try for a width based on our minimum
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    Log.d("DockView", "DockView.onMeasure: width: " + widthMeasureSpec + " getWidth: " + MeasureSpec.getSize(widthMeasureSpec));
    Log.d("DockView", "DockView.onMeasure: height: " + heightMeasureSpec + " getHeight: " + MeasureSpec.getSize(heightMeasureSpec));
    Log.d("DockView", "DockView.onMeasure: getPaddingLeft: " + getPaddingLeft() + " getPaddingRight: " + getPaddingRight());
    Log.d("DockView", "DockView.onMeasure: getPaddingTop: " + getPaddingTop() + " getPaddingBottom: " + getPaddingBottom());

    // http://stackoverflow.com/a/17545273/474330
    int iParentWidth = MeasureSpec.getSize(widthMeasureSpec);
    int iParentHeight = MeasureSpec.getSize(heightMeasureSpec);
    this.setMeasuredDimension(iParentWidth, iParentHeight);

    int iChildCount = this.getChildCount();
    for ( int i = 0; i < iChildCount; i++ ) {
        View pChild = this.getChildAt(i);
        this.measureChild( pChild, 
                MeasureSpec.makeMeasureSpec(iParentWidth, MeasureSpec.EXACTLY), 
                MeasureSpec.makeMeasureSpec(iParentHeight, MeasureSpec.EXACTLY)
        );
    }
}
Zennichimaro
  • 5,236
  • 6
  • 54
  • 78
  • Do you have any padding or somehting? – Shobhit Puri Jul 16 '13 at 07:06
  • no, no padding and no margin at all... I doubt it is the onMeasure, because if I comment it out, the ImageView will not show at all... I got the code from another question in StackOverflow and do not really comprehend the way it (MeasureSpec etc) works... – Zennichimaro Jul 16 '13 at 07:09
  • Remove this line `xmlns:android="http://schemas.android.com/apk/res/android"` from your second `RelativeLayout`. – GrIsHu Jul 16 '13 at 07:57
  • I removed it, and it doesn't make any difference :( – Zennichimaro Jul 17 '13 at 09:28

1 Answers1

1

For the time being, I am resorting to a hack.

I only add the LinearLayout as subview of my CustomView. Then I manually render the two ImageViews in onDraw(Canvas c); function of my CustomView. And in order to get the LinearLayout to fit into the remaining space between the two ImageViews, I calculate the margin of the LinearLayout in my CustomView's onLayout.

horizontal_dock_view.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white" >

            <ImageView 
                android:id="@+id/imageLauncher"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/ic_launcher"/>

        </LinearLayout>

</RelativeLayout>

CustomView.java

    /**
     * Initialize the control. This code is in a separate method so that it can be
     * called from both constructors.
     */
    private void init() {
        setWillNotDraw( false );

        // Load the arrow bitmap
        mArrowBitmap = ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.dock_leftarrow)).getBitmap();

        ViewGroup pRootView = (ViewGroup) View.inflate( mContext, R.layout.horizontal_dock_view, this );
        Log.d("DockView", "DockView.init: " + pRootView.getClass().getCanonicalName());
        mIconContainerView = (LinearLayout) ((RelativeLayout)pRootView.getChildAt(0)).getChildAt(0);

        Log.d("DockView", "DockView.init: " + mIconContainerView.getClass().getCanonicalName());

//      if ( mArrowBitmap != null ) {
//          // set the icon container margin        
//          float fWidth = this.getWidth();     // View's width
//          float fHeight = this.getHeight();   // View's height
//          float fScale = fHeight / mArrowBitmap.getHeight();
//          float fArrowWidth = mArrowBitmap.getWidth() * fScale;
//          float fArrowHeight = mArrowBitmap.getHeight() * fScale;
//          Log.d("DockView", "DockView.init: " + fArrowWidth + ", " + fArrowHeight );
//          ((RelativeLayout.LayoutParams)mIconContainerView.getLayoutParams()).setMargins((int)fArrowWidth, 0, (int)fArrowWidth, 0);
//      }
    }

CustomView.onLayout:

    @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // Do nothing. Do not call the superclass method--that would start a layout pass
    // on this view's children. PieChart lays out its children in onSizeChanged().
    Log.e("DrawView", "DrawView.onLayout: " + l + ", " + t + ", " + r + ", " + b);

    if ( mIconContainerView != null && mArrowBitmap != null ) {
        // set the icon container margin    
        float fHeight = this.getHeight();
        float fScale = fHeight / mArrowBitmap.getHeight();
        float fArrowWidth = mArrowBitmap.getWidth() * fScale;
        float fArrowHeight = mArrowBitmap.getHeight() * fScale;
        Log.d("DockView", "DockView.init: " + fArrowWidth + ", " + fArrowHeight );
        ((RelativeLayout.LayoutParams)mIconContainerView.getLayoutParams()).setMargins((int)fArrowWidth, 0, (int)fArrowWidth, 0);
        this.requestLayout();
    }

    int iChildCount = this.getChildCount();
    for ( int i = 0; i < iChildCount; i++ ) {
        View pChild = this.getChildAt(i);
        pChild.layout(0, 0, pChild.getMeasuredWidth(), pChild.getMeasuredHeight());
    }
}

CustomView.onDraw

    @Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

//      mBgColor = Color.CYAN;
//      Log.e("DockView", "DockView.onDraw: " + mBgColor);
    Log.e("DockView", "DockView.onDraw: width: " + this.getWidth() + " height: " + this.getHeight());

//      debugChildren( (RelativeLayout) ((RelativeLayout)this.getChildAt(0)).getChildAt(0) );
    debugChildren( ((RelativeLayout)this.getChildAt(0)) );

    // draw the background
    canvas.drawColor( mBgColor );

    float fWidth = this.getWidth();     // View's width
    float fHeight = this.getHeight();   // View's height

    {
        // draw the dock
        float fTop = (2 * fHeight) / 3.0f;
        Shader shader = new LinearGradient( 0, fTop, 0, fHeight, mDockTopGradientColor, mDockBottomGradientColor, TileMode.CLAMP );
        Paint paint = new Paint();
        paint.setShader(shader);
        canvas.drawRect( new RectF( 0, fTop, fWidth, fHeight ), paint );
    }

// moved to onLayout
//      if ( mIconContainerView != null && mArrowBitmap != null ) {
//          // set the icon container margin        
//          float fScale = fHeight / mArrowBitmap.getHeight();
//          float fArrowWidth = mArrowBitmap.getWidth() * fScale;
//          float fArrowHeight = mArrowBitmap.getHeight() * fScale;
//          Log.d("DockView", "DockView.init: " + fArrowWidth + ", " + fArrowHeight );
//              ((RelativeLayout.LayoutParams)mIconContainerView.getLayoutParams()).setMargins((int)fArrowWidth, 0, (int)fArrowWidth, 0);
//          this.requestLayout();
//      }

    if ( mArrowBitmap != null ) {
        // draw the arrow
//          canvas.drawBitmap(mArrowBitmap, 0, 0, null);
        float fScale = fHeight / mArrowBitmap.getHeight();
        float fDrawnWidth = mArrowBitmap.getWidth() * fScale;
        float fDrawnHeight = mArrowBitmap.getHeight() * fScale;
//          float fLeft = fWidth - fDrawnWidth;
//          float fTop = 0.0f;
//          float fRight = fWidth;
//          float fBottom = fDrawnHeight;
//          Log.d("DockView", "DockView.onDraw: (" + fLeft + ", " + fTop + ", " + fRight + ", " + fBottom + ")");
        canvas.drawBitmap(mArrowBitmap, null, new RectF(0, 0, fDrawnWidth, fDrawnHeight), null);    // Left arrow
        Log.d("DockView", "DockView.onDraw: (" + 0 + ", " + 0 + ", " + fDrawnWidth + ", " + fDrawnHeight + ")");
        canvas.save();
        canvas.scale(-1,1);
        canvas.translate(-fWidth, 0);
//          canvas.drawBitmap(mArrowBitmap, null, new RectF(fLeft, fTop, fRight, fBottom), null);
        canvas.drawBitmap(mArrowBitmap, null, new RectF(0, 0, fDrawnWidth, fDrawnHeight), null);    // Right arrow, flipped
        canvas.restore();
    }

}
Zennichimaro
  • 5,236
  • 6
  • 54
  • 78