0

I have a LinearLayout that is occupying the entire lower bottom of the screen, just like Mac OSX dock or Windows' taskbar. I'm creating it programmatically without xml as the content will be dynamic. However, I'm having problem with the positioning of the icons (which is ImageView objects). I'd like to specify the distance between the icons (I want some icons to be closer to each other, and some farther away.. also optionally to retain some space in the beginning or the end), but setMargins, setGravity, etc all failed with no apparent noticable visual changes.

This is how it is currently looked like:

5 icons distributed evenly across the whole LinearLayout, when weight is specified in LayoutParams

this is applied to the LinearLayout:

setOrientation( LinearLayout.HORIZONTAL );

this is applied to the ImageView (icons within the LinearLayout):

LayoutParams lp = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, (1) ); // weight has to be specified, otherwise only will show 1 icon
imageIcon.setGravity( Gravity.CENTER_HORIZONTAL );

If weight is not specified:

5 icons occupying the same space, when weight is not specified in LayoutParams

this is applied to the LinearLayout:

setOrientation( LinearLayout.HORIZONTAL );

this is applied to the ImageView (icons within the LinearLayout):

LayoutParams lp = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT /* , (1)  */); // weight has to be specified, otherwise only will show 1 icon
imageIcon.setGravity( Gravity.CENTER_HORIZONTAL );

The question:

  1. How do I control the distance between the ImageView inside the LinearLayout? Would you please explain about the code used to control it? I've been searching in the Android doc in vain...

  2. Could anyone explain the magical thing the "weight" param in LayoutParams do? I've spent very long time debugging the missing ImageView and find through trial and error the magic "weight" param, but still can't figure out why it does such powerful thing without clear explanation in the documentation

EDIT: Inserted code for the LinearLayout (The green Container)

public class GameSliderView extends LinearLayout {

    private Context                 mContext;
    private Vector<GameEntryView>   mGameEntries;

    public GameSliderView(Context context) {
        super(context);
        mContext = context;
        mGameEntries = new Vector<GameEntryView>();
        setOrientation( LinearLayout.HORIZONTAL );
    }

    public void addGameEntry( String szIconUrl ) {
        GameEntryView gameEntry = new GameEntryView(mContext);
        LayoutParams lp = new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT );
        this.setGravity( Gravity.CENTER_HORIZONTAL );
//      lp.setMargins( 0, 0, 0, 0 );
        gameEntry.setLayoutParams( lp );
        gameEntry.loadIcon( szIconUrl );
        gameEntry.requestLayout();
        mGameEntries.add( gameEntry );
        addView( gameEntry );
    }

}

The Game Icon:

public class GameEntryView extends RelativeLayout {

    private Context         mContext;
    private GameIconView    mGameIcon;
//  private ImageView       mNewIcon;

    private String          mIconUrl;

    public GameEntryView(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    protected void onLayout( boolean changed, int l, int t, int r, int 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());
        }
    }

    @Override
    protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) {
        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)
            );
        }
    }

    public void loadIcon( String szUrl ) {
        mGameIcon = new GameIconView( mContext );
        addView( mGameIcon );

        ImageManager.getInstance( mContext ).get( szUrl, new OnImageReceivedListener() {

            @Override
            public void onImageReceived(String source, Bitmap bitmap) {
                mGameIcon.setImageBitmap( bitmap );
                mGameIcon.setLayoutParams( new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT ) );
                postInvalidate();
            }
        });
    }

    public String getIconUrl() {
        return mIconUrl;
    }

    /**
     *  @author Hakim Hauston
     *  @desc   Resizable ImageView - surprised that Android library did not include this?
     *  @ref    http://stackoverflow.com/questions/5554682/android-imageview-adjusting-parents-height-and-fitting-width
     */
    class GameIconView extends ImageView {

        public GameIconView(Context context) {
            super(context);
        }

        @Override
        protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) {
            Drawable d = getDrawable();
            if ( d != null ) {
                float fScale = 0.90f;
                int iHeight = (int) (MeasureSpec.getSize( heightMeasureSpec ) * fScale);
                int iWidth = (int) ((iHeight * d.getIntrinsicWidth() / d.getIntrinsicHeight()) * fScale);
                setMeasuredDimension( iWidth,  iHeight );
                Log.d("GameEntryView", "GameEntryView.onMeasure: measureSpec: (" + widthMeasureSpec + ", " + heightMeasureSpec + ");");
                Log.d("GameEntryView", "GameEntryView.onMeasure: intrinsic: (" + d.getIntrinsicWidth() + ", " + d.getIntrinsicHeight() + ");");
                Log.d("GameEntryView", "GameEntryView.onMeasure: calculated: (" + iWidth + ", " + iHeight + ");");
            } else {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        }

    }

}
Zennichimaro
  • 5,236
  • 6
  • 54
  • 78
  • show some code how u r dynamically adding views.Then i will explain u . – TheFlash Jul 12 '13 at 06:26
  • Why not use a relative layout? It's much better with positioning. As for the weight, With layout_weight you can specify a size ratio between multiple views. For example if you want something to use half of the screen or 3/4 of the screen etc.. – nedaRM Jul 12 '13 at 06:28
  • the weight param is used to distribute the spaces in case of total of widths/heights is greater than layout, so put 0dp for widths of imageviews and you are the one who will customize the ratio between images, assume you have 5 images with both have 1 as each layout weight, than your layout will be divided into 5 pieces in terms of width, but if you increase or decrease weight for only one of them its width will increase or decrease, so my suggesstion is first give 1 for each of them and 0dp for width and then tweak weights how you want them – Onur A. Jul 12 '13 at 06:33
  • @Pratik: I inserted the entire class which may contain unrelated but necessary code. won't the setOrientation, setLayoutParams, setMargins and setGravity be enough? I may be missing set* other tough – Zennichimaro Jul 12 '13 at 07:01
  • @OnurA. what if I need all the 5 pieces to stick with each other? (or separated by a specified px/dp)? in xml, I just specify: android:layout_width="wrap_content" and the next View added just atomatically snapped to it... I can't have the same in Java where the next View will overlap it unless I specify the weight.. – Zennichimaro Jul 12 '13 at 07:05
  • *I'd like to specify the distance between the icons (I want some icons to be closer to each other, and some farther away.. also optionally to retain some space in the beginning or the end),* - this should be very easy to make: set padding left/right on the `LinearLayout` for the end spaces, for space between the buttons use margin left/right(there is no need for gravity or weight for your case). – user Jul 12 '13 at 07:42
  • @zennichimaro you can use padding/margin via LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); lp.setMargins(left, top, right, bottom); imageView.setLayoutParams(lp); or use imageViewSetPadding(left, top, right, bottom) – Onur A. Jul 12 '13 at 07:50
  • @Luksprog if I don't set the weight, the second and subsequent ImageViews will not be shown (ie. like 2nd image: http://i.stack.imgur.com/MVVtL.png). I don't know why as if I do in XML without setting the weight, everything is shown perfectly. Do you have any idea about this? – Zennichimaro Jul 12 '13 at 08:20
  • @OnurA. setting the margin / padding will do, but since it still needs "weight" on the LayoutParams object (otherwise, second and subsequent ImageView will not be shown), the padding will just make the ImageViews become more separated to each other.. – Zennichimaro Jul 12 '13 at 08:23
  • It's normal, in the `addGameEntry()` method you create `LayoutParams` for the children with width set to `MATCH_PARENT`. You may want to use `WRAP_CONTENT` so the first children doesn't fill the `LinearLayout` pushing everything else outside. – user Jul 12 '13 at 08:23
  • use `WRAP_CONTENT` or 0dip for width and also make sure the orientation is `horizontal` – Onur A. Jul 12 '13 at 08:52
  • you are right, it should be WRAP_CONTENT. I changed it but all the second and subsequent ImageView still pushed to the right of the screen! (I getLocationOnScreen() and it is just after the right bound of the screen!) I also set the ImageView and the GameEntryView that contains it to WRAP_CONTENT :( and yes, the LinearLayout is horizontal.. do I miss a postInvalidate (similar call, but for re-layout rather than re-draw)? – Zennichimaro Jul 12 '13 at 09:16
  • I figured out that if I do not set the weight, I'll need to set the width and height, and if I set the weight, I've gotta set the width and height to 0 (or not set it as it default to 0)... so many trial and error to do... will continue to investigate... – Zennichimaro Jul 12 '13 at 10:20

1 Answers1

0

Thanks to @Luksprog and @OnurA. for the insights and help in the comment. I finally set the children width and height to WRAP_CONTENT, LinearLayout to horizontal, and retain the weight set to 1. Then I set the width and height of the LayoutParam on the children to the desired with and height and that way, I managed to control the positioning for the children.

Thanks for the help, everyone!

Zennichimaro
  • 5,236
  • 6
  • 54
  • 78