Android如何将布局背景图片的角落变成圆形,只有顶部或底部的角落?

15

我希望能够拥有一个包含多个RelativeLayout的屏幕,并且我想让顶部布局和底部布局都有圆角,即顶部布局的上方2个角是圆角,底部布局的下方2个角也是圆角。

我的问题是,我在网上找到的所有示例都使用shape.xml创建了圆角并给它设置了渐变效果,但这还不够好,因为我想给relativeLayout设置一个背景图像,并使该图像具有圆角,但我似乎无法同时实现两者。

任何帮助都将不胜感激!

编辑-开始奖金

好吧,我已经在这个问题上苦苦思索很长时间了。目前我主要在测试一个叫做UITableView的第三方工具,它类似于iPhone中的表格。

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

它以与iPhone表格类似的方式设置了tableView,我希望能够给每一行设置一个背景图像,并使顶部和底部行呈弧形。在这个UITableView类中,在提交时调用了以下代码:

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);
    }
}
他已经用XML设置了顶部、中部和底部行的布局样式,以及顶部和底部圆角。但问题是,我想为每一行添加一个图像。所以我加入了这段代码。

他已经用XML设置了顶部、中部和底部行的布局样式,以及顶部和底部圆角。但问题是,我想为每一行添加一个图像。所以我加入了这段代码

tempItemView.setBackgroundResource(R.drawable.background);
但问题是,这将删除顶部和底部行的曲线角,因为使用XML和白色渐变来实现圆角,而不是图片。我需要能够填充布局,然后弯曲顶部和底部角。我已经查看了许多剪裁角的示例,甚至尝试了不同的第三方工具,但仍然没有找到一个单独的示例,可以将背景图应用于容器,然后将其角变成圆角。
有人有任何关于如何做到这一点的想法吗??
编辑:
在iPhone上,您可以这样做
UIColor *color = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"image.png"]];

在哪里可以将图像转换为颜色?Android有对应的功能吗?

编辑:

感谢ACheese提供的答案,我修改了他的代码并将其分成了3个方法:一个用于顶部圆角、一个用于完全圆角,以及一个用于底部圆角,并得出了以下结果。

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));
    }

我使用指标来设置视图的四舍五入程度,以便它可以根据不同的屏幕尺寸进行缩放。

希望这对那些遇到这个问题的人有所帮助!


所以您需要在活动中为主要布局设置圆角背景,对吗?或者即使不在活动中,也要为将容纳其他视图的主容器设置圆角背景? - hardartcore
如果您想让活动的背景具有圆角,则应首先使该活动透明。然后,将圆形图像设置为底部视图的背景。 - Lumis
我传递的图像并不是圆形的,我想传递一个背景图像,该图像将用于表格的每一行,并且我想使用代码来使顶部行的角变圆,并使底部行的角变圆。 - AdamM
@AdamM 你想要在圆形布局内拥有圆形背景图片,是吗? - Android Stack
我有一张非圆角的背景图片,我想手动将其圆角化,然后将其设置为布局的背景图片。这样我就可以传入任何我想要的背景图片,并使用代码将其圆角化。我已经可以很容易地在iPhone上做到这一点,但在Android上,没有简单的解决方案。 - AdamM
显示剩余2条评论
4个回答

3

你是否尝试过类似这样的做法:

例如,这是你的主布局:

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

您需要执行以下操作:MyMainRelLAyout.setBackgroundResource(R.drawable.mydrawable);

其中mydrawable.xml的内容如下:

    <?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>

根据下面的评论,我可以向您建议这个链接:Romain Guy - image with rounded corners,在那里您可以找到一个答案,我认为会帮助您如何做到这一点。
这是另一个有用的库,它使用ImageView,但您可以更改它并将其用于任何类型的View,链接:RoundedImageView

这只会给它加上背景颜色,我已经知道如何做了,但我希望能够传入一张图片,让图片呈圆形。 - AdamM
你需要添加一个自定义的圆形图像,而不仅仅是单色的吗? - hardartcore
我拥有的图像将是矩形图像,我希望能够传入任何图像作为背景图像,并使用代码来圆角传入的任何图像。上面的代码只会将容器的背景颜色设置为白色。 - AdamM
我下载了那个 RoundedImageView 的示例,现在正在测试它。 - AdamM
那个库不错,但如果你使用代码来将可绘制图像进行圆角处理,然后将该图像设置为容器的背景图像,它会遗憾地恢复成矩形形状。 - AdamM

2

请检查我的解决方案是否适用于您的情况: 通过扩展RelativeLayout来定义自己的布局。您只需要添加以下代码:

@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));

}

在您的活动中调用此方法。您不能从onCreate()调用此方法,因为getMeasuredWidth()和getMeasuredHeight()尚未准备好。通过设置自己的drawable id来覆盖并从onWindowFocusChanged(boolean hasFocused)调用。这将重复设置带有圆角的图像作为背景。


你的方法似乎是迄今为止最成功的。我会接受这个答案。我对这些方法唯一的问题是,Android的画布模式似乎需要相当多的资源,如果屏幕上有几个视图,当从一个包含多个这些方法调用的屏幕转换到另一个屏幕时,它可能会导致明显的UI延迟。必须设置屏幕转换,然后调用此方法。你有注意到这个问题吗?不知道你是否有任何解决方案?再次感谢你的帮助。 - AdamM
对于决定使用这种方法的其他人,请确保将Shader.TileMode.Repeat更改为Shader.TileMode.MIRROR,以防止图像在您拥有相当大的视图时重复。 - AdamM
我使用了这种方法,不是用于背景,而是用于一些自定义的ImageView。我没有注意到任何显著的UI延迟。你是在onDraw()或onMeasure()中调用它的吗?我认为多次调用是可以的。但如果真的是这样,太多实例化大型位图肯定会占用很多内存,从而导致UI延迟。 - Acheese
我稍微修复了一下我的代码,我还忘记删除一个旧的方法调用,导致出现了问题。现在没问题了 :) 我会在原帖中添加一个编辑,这样就可以添加我现在正在使用的三个版本的方法,一个用于完全圆形图像,一个用于仅圆角顶部,另一个用于仅圆角底部。再次感谢!! - AdamM
请解释如何调用这个函数以及从哪里调用。我正在从适配器中调用它,但出现了 IllegalArgumentException: 宽度和高度必须大于0 的错误。 - Pranav Mahajan

1

好的,我终于找到了一个解决方案。为了将顶部角落变圆,使用这种方法。

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;
  }

如果您只想将底部圆角化,可以使用此方法

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;
      }

此外,如果您传入不同尺寸的图像,我建议您首先使用这种方法。
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;
    }

将图像放大到相同大小后再应用圆角方法,否则如果传入不同宽度/高度的图像,则根据传入的图像而异,圆角将看起来非常不同。如果先将图像放大,则意味着无论传入什么图像,都应该看起来相当一致。
然后你可以像这样做
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);

希望这能帮助到遇到相同问题的其他人!

应该指出,这些方法可能会占用大量内存,从而导致问题。正在努力改进这些方法。 - AdamM
尝试使用您上面的getTopRoundedCorners方法时,我在“metrics.heightPixels”上遇到了无法解析为变量的错误。有什么遗漏吗? - Phil
没事了,我通过在方法顶部添加“DisplayMetrics metrics = new DisplayMetrics”解决了。 - Phil
是的,我正准备更新它,我忘记包含那一部分代码了。 - AdamM

0

使用LayerList怎么样?您可以设置位图、圆角,然后将其设置为相对布局的背景。

文档中包含了详细的步骤说明。


我对那段代码的问题在于,这意味着每当我想要更改容器的背景图像时,我都需要三张图片,一张用于图像中心,一张用于顶部角落,一张用于底部角落。我希望使用一张图片,并使用代码来圆角上部或下部。 - AdamM
可以引用一张图片,但有三种不同的样式并可以切换它们。因此,创建一个带有三种不同圆角的样式XML,并在需要时应用它们。http://developer.android.com/guide/topics/resources/style-resource.html - Hrafn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接