15

I want to be able to have a screen with multiple RelativeLayouts, and I want the top layout and the bottom layout to have rounded corners, so top layout would have top 2 corners rounded, and bottom layout would have bottom 2 corners rounded.

My issue is, all the examples I find online are using shape.xml to create a rounded corner and gave it a gradient and that is not good enough, because I want to to give the relativeLayout a background image, and have that image rounded, and I can't seem to do both.

Any help would be much appreciated!!

EDIT - Bounty Started

Okay, I have been banging my head against the wall for ages on this one. I am using a 3rd party tool called UITableView at the moment, mainly just testing something out.

https://github.com/thiagolocatelli/android-uitableview

It sets up tableView similar to the way iPhone table is, and I want to be able to give each row a background image, and have the top and bottom rows curved. In this UITableView class, under commit, this code is called

public void commit()
    {
        mIndexController = 0;

        if (mItemList.size() > 1)
        {
            // when the list has more than one item
            for (IListItem obj : mItemList)
            {
                View tempItemView;
                if (mIndexController == 0)
                {
                    //tempItemView = new RoundedView(context_i, this);
                    tempItemView = mInflater.inflate(R.layout.list_item_top,null);


        } 
            else if (mIndexController == mItemList.size() - 1)
            {
                tempItemView = mInflater.inflate(R.layout.list_item_bottom,null);
            } 
            else
            {
                tempItemView = mInflater.inflate(R.layout.list_item_middle,null);
            }
            setupItem(tempItemView, obj, mIndexController);
            tempItemView.setClickable(obj.isClickable());
            mListContainer.addView(tempItemView);
            mIndexController++;

        }
    } 
    else if (mItemList.size() == 1)
    {
        // when the list has only one item
        View tempItemView = mInflater.inflate(R.layout.list_item_single,
                null);
        IListItem obj = mItemList.get(0);
        setupItem(tempItemView, obj, mIndexController);
        tempItemView.setClickable(obj.isClickable());
        mListContainer.addView(tempItemView);
    }
}

He has a layout style for top middle and bottom rows, top and bottom rounded used XML, but the problem is, I want to give each row an image. So I added this code

tempItemView.setBackgroundResource(R.drawable.background);

But the problem is, this removes the curved corners for the top and bottom rows as the corners are rounded using XML and using white gradients, not an image. I need to be able to inflate the layout, then curve the top and bottom corners. I have looked at so many examples of cropping corners, and even tried out different 3rd party tools, but have yet to find a single example which shows applying a background image to a container, and then rounding the corners.

Does anyone have any ideas on how to do this??

Edit:

On iPhone, you can do something like this

UIColor *color = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"image.png"]];

Where you convert an image into a colour. Does Android have an equivalent?

Edit:

Thanks to ACheese for the answer, I modified his code and separated it into 3 methods, one for top rounded corners, one for fully rounded corners, and one for bottom rounded corners, and came up with this

public void setBackgroundRounded(int resID, int w, int h, View v)
    {
        DisplayMetrics metrics = getResources().getDisplayMetrics();
        double dH = (metrics.heightPixels / 100) * 1.5;
        int iHeight = (int)dH;

        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmp);
        Shader shader = new BitmapShader(BitmapFactory.decodeResource(
                getResources(), resID), Shader.TileMode.MIRROR,
                Shader.TileMode.MIRROR);

        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
        paint.setAntiAlias(true);
        paint.setShader(shader);
        RectF rec = new RectF(0, 0, w, h);
        c.drawRoundRect(rec, iHeight, iHeight, paint);

        v.setBackgroundDrawable(new BitmapDrawable(getResources(), bmp));
    }

    public void setTopRounded(int resID, int w, int h, View v)
    {
        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmp);
        Shader shader = new BitmapShader(BitmapFactory.decodeResource(
                getResources(), resID), Shader.TileMode.MIRROR,
                Shader.TileMode.MIRROR);

        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
        paint.setAntiAlias(true);
        paint.setShader(shader);
        RectF rec = new RectF(0, 0, w, h - 20);
        c.drawRect(new RectF(0, 20, w, h), paint);
        c.drawRoundRect(rec, 20, 20, paint);
        v.setBackgroundDrawable(new BitmapDrawable(getResources(), bmp));
    }

    public void setBottomRounded(int id, int w, int h, View v)
    {
        DisplayMetrics metrics = getResources().getDisplayMetrics();
        double dH = (metrics.heightPixels / 100) * 1.5;
        int iHeight = (int)dH;

        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmp);
        Shader shader = new BitmapShader(BitmapFactory.decodeResource(
                getResources(), id), Shader.TileMode.MIRROR,
                Shader.TileMode.MIRROR);
        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
        paint.setAntiAlias(true);
        paint.setShader(shader);
        RectF rec = new RectF(0, 0, w, h);
        c.drawRoundRect(rec, iHeight, iHeight, paint);
        c.drawRect(new RectF(0, 0, w, h-iHeight), paint);

        v.setBackgroundDrawable(new BitmapDrawable(getResources(), bmp));
    }

I use metrics to set up how much to round the view, so it scales with different screen sizes.

Hope that helps others who were having this issue!!

AdamM
  • 4,400
  • 5
  • 49
  • 95
  • So you need to set rounded background to a main layout in your activity, is that correct ..or even it's not in activity, to the main container which will hold other views? – hardartcore Mar 13 '13 at 12:12
  • Correct, I need to be able to gave that container a background image, and have 2 of the corners of that container rounded, either top 2 corners or bottom 2 corners – AdamM Mar 13 '13 at 12:15
  • If you want the background of an activity to have rounded corners then this activity should be made transparent first. Then you set your rounded image as the background of the bottom view. – Lumis Mar 19 '13 at 23:13
  • The image I am passing it is not rounded though, I want to pass in ONE background image which will be used for each row of the table, and I want to use code to round the top rows corners, and round the bottom row corners – AdamM Mar 20 '13 at 08:41
  • @AdamM you want to have round background image inside round layout , is it correct – Android Stack Mar 25 '13 at 12:36
  • I have a non rounded background image, and I want to manually round it and then set it as the background image of the layout. That way I can pass in any background image I want and it will be rounded using code. I can already very easily do this for the iPhone, but on Android, there is no easy solution – AdamM Mar 25 '13 at 13:27
  • I could'nt properly understand what to pass to the argument (int resID)...I'm using a ListView...each element has an Image at the left & text at the right...I want the image to be curved from the left ..right now I'm getting IllegalArgumentExceptiom: width & height must be greater than 0 – Pranav Mahajan Jun 13 '14 at 11:46

4 Answers4

3

So did you tried something like this :

For example this is your main layout :

RelativeLayout myMainRelLAyout = (RelativeLayout) findViewById(R.id.my_layout);

and do MyMainRelLAyout.setBackgroundResource(R.drawable.mydrawable);

where mydrawable.xml look like this :

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

    <solid android:color="@android:color/white" />

    <corners
        android:bottomLeftRadius="0dip"
        android:bottomRightRadius="0dip"
        android:topLeftRadius="5dip"
        android:topRightRadius="5dip" />
    </shape>

Depending on the comments below I can suggest you this link : Romain Guy - image with rounded corners , where you can find an answer which I think will help you how to do this.

And here is another useful library which is using ImageView, but you can change it and use it for any kind of View, link : RoundedImageView .

hardartcore
  • 16,886
  • 12
  • 75
  • 101
  • All that will do is give it a background colour, I know how to do that already, but I want to be able to pass in an image, so the image is rounded – AdamM Mar 13 '13 at 12:18
  • you need to add a custom rounded image, not only with one color? – hardartcore Mar 13 '13 at 12:19
  • The images I have will be rectangle images, I want to be able to pass in any image to use as a background image, and use code to round the corners of whatever image is passed in. The code above will just set the background colour of the container to be white – AdamM Mar 13 '13 at 12:20
  • I downloaded that RoundedImageView example, testing it out now – AdamM Mar 13 '13 at 12:32
  • That library is nice, but if you use the code to round a drawable image, then set that drawable as the background image of the container, it reverts back to the rectangle shape sadly – AdamM Mar 13 '13 at 14:31
2

Check if my solution work for your case : Define your own layout by extending RelativeLayout. You only need to add the following code

@SuppressWarnings("deprecation")
public void setBackground(int id){
    Bitmap bmp = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(bmp);
    Shader shader = new BitmapShader(BitmapFactory.decodeResource(getResources(), id), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);

    Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
    paint.setAntiAlias(true);
    paint.setShader(shader);
    RectF rec = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());
//  you may need this for only top round corner
//  RectF rec = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()-20);
//  c.drawRect(new RectF(0, 20, getMeasuredWidth(), getMeasuredHeight()),      paint);
    c.drawRoundRect(rec, 20, 20, paint);
    this.setBackgroundDrawable(new BitmapDrawable(getResources(), bmp));

}

call this method from your activity. You cannot call from onCreate() because getMeasuredWidth() and getMeasuredHeight() are not ready. Override and call from onWindowFocusChanged(boolean hasFocused) by setting your own drawable id. This will repeating setting your image as the background with round corners.

Acheese
  • 135
  • 1
  • 11
  • Your method seems to be the most successful so far. Will accept answer. My only issue with these methods is that the Androids canvas mode seems to be quite resource intensive, if you have several views on the screen it can cause a noticeable UI lag when transitioning from one screen to another which contains several of these method calls. Will have to set it up so screen transitions in, then this method is called. Do you notice this at all? Don't suppose you have any solution to it? Thanks again for your help – AdamM Mar 26 '13 at 11:03
  • Oh to other people who decide to use this method, make sure you change Shader.TileMode.Repeat to Shader.TileMode.MIRROR to prevent image being repeated if you have quite a large view – AdamM Mar 26 '13 at 11:50
  • I used this method not for a background but for some custom ImageView. I did not notice any significant UI lags. Did you call it in onDraw() or onMeasure()? I think several calls are fine. But if that is really the case, too many instances of large Bitmaps could definitely take up a lot memory which will lead to UI lag. – Acheese Mar 26 '13 at 15:10
  • I fixed up my code a bit, I had an old method call still in place which I forgot to take out which was causing an issue. It is fine now :) Will add an edit in my original post so can add in the 3 versions of your method I am now using, one for fully rounded image, one for rounded top corners only, and one for rounded corners bottom only. Thanks again!! – AdamM Mar 26 '13 at 15:18
  • Please explain how to call this function & from where. I'm calling it from my adapter & it gives me IllegalArgumentException: width & height must be greater than 0 – Pranav Mahajan Jun 13 '14 at 11:52
1

Okay, I found a solution at long last. In order to round top corners, use this method

public Bitmap getTopRoundedCorner(Bitmap bitmap, DisplayMetrics metrics) {

    //Using this so it scales with different screen sizes
    double dH = (metrics.heightPixels / 100.0) * 3;
    int iHeight = (int) dH;

    //Subtract this from bitmap height
    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
                bitmap.getHeight()-iHeight, Config.ARGB_8888);

    Canvas canvas = new Canvas(output);

    final int color = 0xff424242;
    final Paint paint = new Paint();
    final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

    //Again used so it scales with diff screen sizes
    //Can play around with this value, depending on how rounded you wanted the corner
    dH = (metrics.heightPixels / 100.0) * 3.5;
   iHeight = (int) dH;

    final RectF rectF = new RectF(rect);
    final float roundPx = iHeight;

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
    canvas.drawBitmap(bitmap, rect, rect, paint);

    return output;
  }

If you only want to round bottom corner, use this method

public Bitmap getBottomRoundedCorner(Bitmap bitmap) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
                    bitmap.getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);

        //Again play around with this to get the rounded value you require
        double dH = (metrics.heightPixels / 100.0) * 2.5;
        int iHeight = (int) dH;
        final float roundPx = iHeight;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        //Draw second rectangle over the top of the first one
        //So it hides the top rounded corners
        iHeight = (int) dH;

        final int color2 = 0xff424242;
        final Paint paint2 = new Paint();
        Canvas canvas2 = new Canvas(output);
        final Rect testRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()-iHeight);
        final RectF testF = new RectF(testRect);

        paint2.setAntiAlias(true);
        canvas2.drawARGB(0, 0, 0, 0);
        paint2.setColor(color2);
        canvas2.drawRoundRect(testF, roundPx, roundPx, paint2);
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas2.drawBitmap(bitmap, testRect, testRect, paint2);
        return output;
      }

Also if you passing in different images which are different sizes, I would recommend you use this method first

public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
        int width = bm.getWidth();
        int height = bm.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // CREATE A MATRIX FOR THE MANIPULATION
        Matrix matrix = new Matrix();
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight);

        // "RECREATE" THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
        return resizedBitmap;
    }

Scale the images up to be the same size before applying the rounded corners method, otherwise if you pass in a different image with different width/height, the rounded corners will look quite different depending on what image is passed in. If you scale the image up first, it means no matter what image you pass in, should look fairly consistent.

Then you can just do something like this

Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.tablebackground);
        bmp = getResizedBitmap(bmp, 200, 300);
        bmp = getTopRoundedCorner(bmp);
        BitmapDrawable backgroundDrawable = new BitmapDrawable(getResources(),bmp);
        subLayout = (RelativeLayout) findViewById(R.id.subLayout);
        subLayout.setBackgroundDrawable(backgroundDrawable);

Hope that helps any others who were having the same issue!!

AdamM
  • 4,400
  • 5
  • 49
  • 95
  • Should point out, that these methods can be quite memory intensive, which can cause issues. Trying to work on improving these methods – AdamM Mar 25 '13 at 09:12
  • When trying to use your method above for getTopRoundedCorners, I get an unable to resolve to variable error on "metrics.heightPixels". Is there something missing? – Phil Aug 08 '14 at 14:44
  • nevermind, I got it by adding 'DisplayMetrics metrics = new DisplayMetrics' at top of method – Phil Aug 08 '14 at 14:46
  • Yeah was about to update it, I forgot to include that bit of code – AdamM Aug 08 '14 at 14:47
0

What about using a LayerList where you set the bitmap, its corner rounding, and then set it as your Relative layouts background?

The documentation includes a step by step of how to do it.

Hrafn
  • 2,867
  • 3
  • 25
  • 44
  • My issue with that code is it means anytime I wanted to change the background image of the container, I would require 3 images, one for the centre of the image, one for the top corners and one for the bottom corners. I was hoping to use the one image, and use code to either round the top corners or bottom corners – AdamM Mar 13 '13 at 12:25
  • Could reference one image, but have 3 different styles and switch them out. So create a style xml with three different corner roundings and apply them when you need them. http://developer.android.com/guide/topics/resources/style-resource.html – Hrafn Mar 13 '13 at 12:27