2009-11-10 2 views

Antwort

41

Für eine kontrolliertere Methode zeichnen Sie ein abgerundetes Rechteck und maskieren Sie es auf Ihrem Bild mit dem Porter-Duff Xfer-Modus der Farbe.

Erste Einrichtung der Xfer malen und die abgerundete Bitmap:

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

nun diese Bitmap anwenden ontop des Bildes:

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

Bitmap mit abgerundeten Ecken und befindet sich nun im Ergebnis.

+0

Wow, sehr nett! Meine einzige Sorge mit dieser Methode ist Geschwindigkeit - wenn ich diese Technik auf neun Bilder gleichzeitig anwenden würde, würde die App tuckern? – iamkoa

+1

Ich verwende diese Technik in einer Hochleistungs-Grafik-Situation - und habe kein Problem damit gesehen (auch nach dem Profiling)! Stellen Sie nur sicher, dass Sie das xferPaint und das Allround-Bitmap als ein Feld in Ihrer Klasse speichern. Das Erstellen einer Bitmap ist langsam, aber das Zeichnen ist ziemlich schnell. – Ralphleon

+0

Es wäre großartig, wenn Sie einige Erklärungen zu den Parametern, die Sie in Ihrem Beitrag verwenden, bearbeiten könnten. Der Beispielcode sieht gut aus, aber das Verständnis ist nicht so einfach, wenn Sie nicht bereits mit diesen Klassen gearbeitet haben. – Janusz

5

Wie wäre es mit einem NinePatchDrawable Bild, das gerade abgerundete Ecken hat und einen transparenten Körper hat. Überlagern Sie Ihr Bild mit einer entsprechend großen Version Ihres NinePatchDrawable.

+1

Sehr gute Idee, aber was ist, wenn mein gerundetes Bild über einen Hintergrund mit Farbverlauf gelegt wird? Würde das noch funktionieren? – iamkoa

+1

Ich stimme zu, dass dies der weniger hack-ish Weg ist, dies zu erreichen. Sie können das Bild und den Bildrahmen einfach in ein FrameLayout setzen (in dieser Reihenfolge) und fertig. – Matthias

+0

@iamkoa: Stellen Sie sicher, dass die leeren Teile der abgerundeten Ecken transparent sind. Wenn Sie in Ihren Ecken einen Farbverlauf verwenden, verwenden Sie den Alphakanal des Pixels. (Ich benutze Gimp und setze einen Farbverlauf, um von transparent zu deiner Farbe zu gehen.) – idbrii

28

Warum nicht clipPath verwenden?

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

Habe das noch nicht getestet, aber es hört sich so an. Danke Jerry! – iamkoa

+2

Hinweis, nach dem Ausprobieren habe ich gefunden: 1 - Sie clip Leinwand zuerst dann Bitmap 2 zeichnen - es ist nicht schneller als mit einer Bitmap-Maske und der Xfer-Modus-Overlay 3 - man kann ClipPath nicht anti-alias, während man anti-alias die Farbe, die eine Maske für die Überlagerung des Überlagerungsmodus zeichnet. – Lumis

+0

Das funktionierte gut für den Fall, den ich suchte, obwohl es keine Bitmap mit abgerundeten Ecken zeichnete, sondern eine ganze Ansicht. – Jes

4
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; 
    } 
} 
+0

Dieser Code hilft Ihnen, gerundetes Bild zu erhalten. – Swati

+1

aber dieser Code hilft nicht, abgerundete Ecken zu bekommen, wenn wir sie transparent brauchen. Weißt du, wie man das erreicht? – Thiago

15

Romain Guy selbst schreibt darüber in his blog:

Um die abgerundeten Bilder zu erzeugen ich einfach eine benutzerdefinierte Drawable schrieb, dass ein abgerundetes Rechteck zeichnet Canvas.drawRoundRect() verwenden. Der Trick ist , eine Farbe mit einem BitmapShader zu verwenden, um das abgerundete Rechteck mit einer Textur anstelle einer einfachen Farbe zu füllen. Hier ist, was der Code wie folgt aussieht:

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

Die Beispielanwendung etwas weiter geht und täuscht eine Vignette Effekt durch die BitmapShader mit einem RadialGradient kombiniert.

6

Hier ist ein Weg, wie ich es mit einem ImageView getan habe. Ich habe andere Methoden ausprobiert, einschließlich der Antworten hier und bei ähnlichen Fragen, aber ich fand, dass sie nicht gut für mich funktionierten, da ich die Ecken auf die Bildansicht und nicht direkt auf die Bitmap anwenden musste. Das direkte Anwenden auf die Bitmap funktioniert nicht, wenn Sie diese Bitmap skalieren/zuschneiden/schwenken, da die Ecken ebenfalls skaliert/zugeschnitten/geschwenkt werden.

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

Hier ist eine Alternative, die Hardware-Schichten für die endgültige Schichtverbund verwendet:

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

Anfang war ich nicht in der Lage, es zu bekommen mit dieser Methode zu arbeiten, weil meine Ecken schwarz wurden immer; Ich erkannte später, was das Problem nach dem Lesen dieser Frage war: Android how to apply mask on ImageView?. Es stellt sich heraus, dass das Ändern des Alphas in der Leinwand direkt auf dem Bildschirm "herauskratzt" und ein Loch in das darunter liegende Fenster gestanzt wird, das schwarz ist. Aus diesem Grund werden zwei Ebenen benötigt: eine zum Anwenden der Maske und eine weitere zum Anwenden des zusammengesetzten Bildes auf den Bildschirm.

+1

funktioniert wie ein Zauber – Softlion