web-dev-qa-db-ja.com

Androidで画像に角を丸くするにはどうすればよいですか?

読み込んだ画像の角を丸くしたいのですが。

ヒント、チュートリアル、知っているベストプラクティスはありますか?

38
iamkoa

より制御された方法については、丸みを帯びた長方形を描画し、ペイントのporter-duff Xferモードを使用して画像にマスクします。

最初にXfer Paintと丸みを帯びたビットマップを設定します。

Bitmap myCoolBitmap = ... ; // <-- Your bitmap you want rounded    
int w = myCoolBitmap.getWidth(), h = myCoolBitmap.getHeight();

// We have to make sure our rounded corners have an alpha channel in most cases
Bitmap rounder = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(rounder);    

// We're going to apply this Paint eventually using a porter-duff xfer mode.
// This will allow us to only overwrite certain pixels. RED is arbitrary. This
// could be any color that was fully opaque (alpha = 255)
Paint xferPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
xferPaint.setColor(Color.RED);

// We're just reusing xferPaint to Paint a normal looking rounded box, the 20.f
// is the amount we're rounding by.
canvas.drawRoundRect(new RectF(0,0,w,h), 20.0f, 20.0f, xferPaint);     

// Now we apply the 'magic sauce' to the Paint  
xferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

次に、このビットマップを画像の上に適用します。

Bitmap result = Bitmap.createBitmap(myCoolBitmap.getWidth(), myCoolBitmap.getHeight() ,Bitmap.Config.ARGB_8888);
Canvas resultCanvas = new Canvas(result)
resultCanvas.drawBitmap(myCoolBitmap, 0, 0, null);
resultCanvas.drawBitmap(rounder, 0, 0, xferPaint);

角が丸いビットマップが結果として存在するようになりました。

45
Ralphleon

ClipPathを使用しないのはなぜですか?

protected void onDraw(Canvas canvas) {
    Path clipPath = new Path();
    float radius = 10.0f;
    float padding = radius / 2;
    int w = this.getWidth();
    int h = this.getHeight();
    clipPath.addRoundRect(new RectF(padding, padding, w - padding, h - padding), radius, radius, Path.Direction.CW);
    canvas.clipPath(clipPath);
    super.onDraw(canvas);
}
29
Jerry

Romain Guy自身がこれについて 彼のブログ に書いています:

丸みを帯びた画像を生成するために、Canvas.drawRoundRect()を使用して角丸四角形を描画するカスタムDrawableを作成しました。トリックは、BitmapShaderでPaintを使用して、単純な色の代わりにテクスチャで丸みを帯びた長方形を塗りつぶすことです。コードは次のようになります。

BitmapShader shader;
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

Paint paint = new Paint();
Paint.setAntiAlias(true);
Paint.setShader(shader);

RectF rect = new RectF(0.0f, 0.0f, width, height);

// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// Paint contains the shader that will texture the shape
canvas.drawRoundRect(rect, radius, radius, Paint);

サンプルアプリケーションは少し進んで、BitmapShaderをRadialGradientと組み合わせることでビネット効果を偽装します。

17
Dheeraj V.S.

これは、ImageViewでそれを行うために私が発見した方法です。ここでの回答や同様の質問への回答など、他の方法を試しましたが、コーナーをビットマップに直接適用するのではなく、画像ビューに適用する必要があるため、うまく機能しないことがわかりました。コーナーもスケーリング/クロップ/パンされるため、ビットマップをスケーリング/クロップ/パンしている場合、ビットマップに直接適用しても機能しません。

public class RoundedCornersImageView extends ImageView {
    private final Paint restorePaint = new Paint();
    private final Paint maskXferPaint = new Paint();
    private final Paint canvasPaint = new Paint();

    private final Rect bounds = new Rect();
    private final RectF boundsf = new RectF();

    public RoundedCornersImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public RoundedCornersImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RoundedCornersImageView(Context context) {
        super(context);
        init();
    }

    private void init() {
        canvasPaint.setAntiAlias(true);
        canvasPaint.setColor(Color.argb(255, 255, 255, 255));
        restorePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
        maskXferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.getClipBounds(bounds);
        boundsf.set(bounds);

        canvas.saveLayer(boundsf, restorePaint, Canvas.ALL_SAVE_FLAG);
        super.onDraw(canvas);

        canvas.saveLayer(boundsf, maskXferPaint, Canvas.ALL_SAVE_FLAG);
        canvas.drawARGB(0, 0, 0, 0);
        canvas.drawRoundRect(boundsf, 75, 75, canvasPaint);

        canvas.restore();
        canvas.restore();
    }
}

以下は、最終的なレイヤーの合成にハードウェアレイヤーを使用する代替策です。

public class RoundedCornersImageView extends ImageView {
    private final Paint restorePaint = new Paint();
    private final Paint maskXferPaint = new Paint();
    private final Paint canvasPaint = new Paint();

    private final Rect bounds = new Rect();
    private final RectF boundsf = new RectF();

    public RoundedCornersImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public RoundedCornersImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RoundedCornersImageView(Context context) {
        super(context);
        init();
    }

    private void init() {
        canvasPaint.setAntiAlias(true);
        canvasPaint.setColor(Color.argb(255, 255, 255, 255));
        restorePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
        maskXferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));

        setLayerType(View.LAYER_TYPE_HARDWARE, restorePaint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.getClipBounds(bounds);
        boundsf.set(bounds);

        super.onDraw(canvas);

        canvas.saveLayer(boundsf, maskXferPaint, Canvas.ALL_SAVE_FLAG);
        canvas.drawARGB(0, 0, 0, 0);
        canvas.drawRoundRect(boundsf, 75, 75, canvasPaint);

        canvas.restore();
    }
}

角が黒くなったため、最初はこの方法で機能させることができませんでした。この質問を読んだ後、私は後で問題が何であるかを理解しました: Android ImageViewにマスクを適用する方法? 。キャンバスでアルファを変更することは、実際には画面上で直接「引っかき」、黒い下にあるウィンドウに穴を開けることです。そのため、2つのレイヤーが必要です。1つはマスクを適用し、もう1つは合成画像を画面に適用します。

7
Learn OpenGL ES

角が丸く、ボディが透明な NinePatchDrawable 画像を作成してみませんか。適切にサイズ変更されたバージョンのNinePatchDrawableで画像をオーバーレイします。

5
Navin
package com.pkg;

import Java.io.File;
import Java.io.FileInputStream;
import Java.io.FileNotFoundException;

import Android.app.Activity;
import Android.graphics.Bitmap;
import Android.graphics.BitmapFactory;
import Android.graphics.Canvas;
import Android.graphics.Paint;
import Android.graphics.PorterDuffXfermode;
import Android.graphics.Rect;
import Android.graphics.RectF;
import Android.graphics.Bitmap.Config;
import Android.graphics.PorterDuff.Mode;
import Android.os.Bundle;
import Android.os.Environment;
import Android.widget.ImageView;

public class RoundedImage extends Activity {
    /** Called when the activity is first created. */
    ImageView imag;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        imag=(ImageView)findViewById(R.id.image);

        //ImageView img1=(ImageView)findViewById(R.id.imageView1);
        BitmapFactory.Options bitopt=new BitmapFactory.Options();
        bitopt.inSampleSize=1;
        // String img=Environment.getExternalStorageDirectory().toString();
        // String filepath =Environment.getExternalStorageDirectory().toString();
        String filepath ="/mnt/sdcard/LOST.DIR";
        File imagefile = new File(filepath + "/logo.jpg");
        FileInputStream fis = null;
        try 
        {
        fis = new FileInputStream(imagefile);
        }  
        catch (FileNotFoundException e1)
        {
        // TODO Auto-generated catch block
        e1.printStackTrace();
        }
        Bitmap bi = BitmapFactory.decodeStream(fis);
        if(bi!=null){
            imag.setImageBitmap(getRoundedCornerBitmap(bi));
        }

    }

    public static Bitmap getRoundedCornerBitmap(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);
    final float roundPx = 12;

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

次に、ImageViewを使用した、丸められた別のPath実装を示します。パフォーマンスは優れていますが、特定の条件では、ハードウェアの描画が原因でエミュレータにいくつかのバグが発生する場合があります。

public class RoundImageView extends ImageView {

    private Path mPath;
    private RectF mRect;
    private Paint mPaint;

    private int mCornerRadius;
    private float mImageAlpha;
    private boolean mIsCircular;

    public RoundImageView(Context context) {
        this(context, null);
    }

    public RoundImageView(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.roundImageViewStyle);
    }

    public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.RoundImageView, defStyle, 0);

        mCornerRadius = a.getDimensionPixelSize(R.styleable.RoundImageView_cornerRadius, 0);
        mIsCircular = a.getBoolean(R.styleable.RoundImageView_isCircular, false);
        mImageAlpha = a.getFloat(R.styleable.RoundImageView_imageAlpha, 1);

        a.recycle();

        setAlpha((int) (mImageAlpha * 255));

        // Avoid expensive off-screen drawing
        setLayerType(LAYER_TYPE_HARDWARE, null);

        mPath = new Path();

        mRect = new RectF();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    }

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

        mPath.reset();

        if (mIsCircular) {
            float halfWidth = canvas.getWidth() / 2;
            float halfHeight = canvas.getHeight() / 2;
            float radius = Math.max(halfWidth, halfHeight);

            mPath.addCircle(halfWidth, halfHeight, radius, Path.Direction.CW);
        } else {
            mRect.right = canvas.getWidth();
            mRect.bottom = canvas.getHeight();

            mPath.addRoundRect(mRect, mCornerRadius, mCornerRadius, Path.Direction.CW);
        }

        canvas.drawPath(mPath, mPaint);
    }
}

追伸 OpenGL ESを学ぶ 最高のソリューションを提供しました。それは非常にスムーズで、エミュレータでも動作します。

0
Nikolai

私が見つけた最善の解決策->

1)丸い角のドローアブルを作成します。そして、背景としてimageviewに設定します。

<shape xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:shape="rectangle" >
<corners
    Android:radius="10dp" /></shape>

2)次に、setClipToOutline(true)のイメージビューオブジェクトプロパティをJavaコードで設定します。

imageview.setClipToOutline(true);

それは魅力のように機能します

0
Yogesh Alai