2015-12-03 16 views
6

Ho realizzato questo Drawable personalizzato che dovrebbe ritagliare qualsiasi Drawable in circle. Ma con la mia implementazione, il drawable passato è l'output nella forma originale non nella forma circolare.Android: Circular Drawable

public class CircularDrawable extends Drawable { 
    Paint mPaint,xfermodePaint; 
    Drawable mDrawable; 
    int[] vinylCenter = new int[2]; 
    int radius; 
    Bitmap src; 
    PorterDuffXfermode xfermode; 
    Rect rect; 
    Canvas testCanvas; 
    public CircularDrawable(Drawable drawable) { 
     mDrawable = drawable; 
     mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mPaint.setColor(0xffffffff); 
     mPaint.setStyle(Paint.Style.FILL); 
     xfermodePaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     xfermode=new PorterDuffXfermode(PorterDuff.Mode.SRC_IN); 
     xfermodePaint.setXfermode(xfermode); 
     testCanvas=new Canvas(); 
    } 

    @Override 
    public void setBounds(Rect bounds) { 
     super.setBounds(bounds); 
     mDrawable.setBounds(bounds); 
    } 

    @Override 
    public void setBounds(int left, int top, int right, int bottom) { 
     super.setBounds(left, top, right, bottom); 
     mDrawable.setBounds(left, top, right, bottom); 
    } 

    @Override 
    protected void onBoundsChange(Rect bounds) { 
     super.onBoundsChange(bounds); 
     vinylCenter[0] = bounds.width()/2; 
     vinylCenter[1] = bounds.height()/2; 
     radius = (bounds.right - bounds.left)/2; 
     src = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888); 
     testCanvas.setBitmap(src); 
    } 


    @Override 
    public void draw(Canvas canvas) { 
     canvas.save(); 
     canvas.drawARGB(0, 0, 0, 0); 
     canvas.drawCircle(vinylCenter[0],vinylCenter[1],radius,mPaint); 
     mDrawable.draw(testCanvas); 
     canvas.drawBitmap(src,0f,0f,xfermodePaint); 
    } 



    @Override 
    public void setAlpha(int alpha) {/*ignored*/} 

    @Override 
    public void setColorFilter(ColorFilter colorFilter) {/*ignored*/} 

    @Override 
    public int getOpacity() { 
     /*ignored*/ 
     return 0; 
    } 
} 

Qual è il mio errore?

Anche io sto usando un oggetto SquareImageview per visualizzare questo drawable che rende la vista quadrata in onMeasure. Questa domanda non è pensata per la visualizzazione di immagini circolare.

+2

vedere [questo ] (http://androidxref.com/6.0.0_r1/xref/frameworks/support/v4/donut/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java) come riferimento – pskink

+0

@pskink ma questa è un'altra implementazione. Voglio sapere qual è il mio errore nell'implementazione Porter/Duff. –

+0

se è quadrata significa che stai disegnando l'intera area "" DST "' – pskink

risposta

3

La tela non ha il canale alfa attivato per impostazione predefinita quando si utilizza la modalità Porter/Duff xfermode (come per la mia esperienza con il mio questo in Android). Questo è il motivo per cui l'intera immagine (src) è l'output. Porter/Duff xfermode agisce sulla base di Alpha Channel.

In molte implementazioni di Circular Drawable 2 vengono acquisiti Canvas, da cui viene fornita una bitmap di immagine RGB e ad altri viene fornita solo bitmap di canale alfa con maschera circolare. Porter/Duff xfermodeis applicato e il risultato viene disegnato sulla tela principale.

L'errore è che non sto specificando la tela della funzione di disegno che il canale alfa deve essere considerato quando si utilizza il livello sull'area di disegno per xfermode.

La funzione di disegno diventerà

@Override 
public void draw(Canvas canvas) { 
    int sc = canvas.saveLayer(null, null, 
        Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | 
        ); 
    canvas.drawCircle(vinylCenter[0],vinylCenter[1],radius,mPaint); 
    mDrawable.draw(testCanvas); 
    canvas.drawBitmap(src,0f,0f,xfermodePaint); 
    canvas.restoreToCount(sc); 
} 

Salvataggio strato per indicare il canale alfa con la bandiera e quindi utilizzando l'xfermode. Infine ripristinandolo al suo conteggio di salvataggio.

3

Quindi, in pratica ho utilizzare questo metodo per ottenere immagini ad angolo arrotondato

public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) { 
    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.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 = pixels; 

    paint.setAntiAlias(true); 
    canvas.drawARGB(0, 0, 0, 0); 
    paint.setColor(color); 
    canvas.drawRoundRect(rectF, roundPx, roundPx, paint); 

    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); 
    canvas.drawBitmap(bitmap, rect, rect, paint); 

    return output; 

ma come hai fatto notare a volte non funziona, bene fa il lavoro, ma ciò che accade il più delle volte è il vostro imageview potrebbe avere il suo scalino impostato su centerCrop e altri tipi, quello che faccio è che prima comprimo la mia altezza dell'immagine alla stessa altezza e larghezza della mia vista di immagine in modo che l'intera immagine si adatterà alla visualizzazione di immagini e non si verificherà alcun ridimensionamento o calcolo solo quanto dell'altezza dell'angolo da arrotondare utilizzando questa funzione in base all'altezza

private int calculatePercentage(int percentage, int target) 
{ 
    int k = (int)(target*(percentage/100.0f)); 
    return k; 

} 

per cui uso il mio codice come: new Bitmap(getRoundedCornerBitmap(bmp, calculatePercentage(5, bmp.getHeight()))); where 5 is 5 dell'altezza dell'immagine questo mi dà stesse curve per tutte le immagini che si tratti di alcune immagini sono più grandi di altri `

+0

Un'altra soluzione alternativa non è stata richiesta. L'ipotesi che hai fatto riguardo allo scaletype è sbagliata. Lo scalimetro predefinito è fitCenter. –

+0

@Paritosh Tonk potresti aiutarmi o no in ogni caso il mio funziona perfettamente –

+0

Apprezzo il tuo aiuto. Il pensiero non riguarda il lavoro. Riguarda il concetto e perché questo accade. –

2
For rounded imageview you can use this code 

    public class RoundedImageView extends ImageView { 

    public RoundedImageView(Context context) { 
     super(context); 
     // TODO Auto-generated constructor stub 
    } 

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

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

    @Override 
    protected void onDraw(Canvas canvas) { 

     Drawable drawable = getDrawable(); 

     if (drawable == null) { 
      return; 
     } 

     if (getWidth() == 0 || getHeight() == 0) { 
      return; 
     } 
     Bitmap b = ((BitmapDrawable) drawable).getBitmap(); 
     Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true); 

     int w = getWidth();//, h = getHeight(); 

     Bitmap roundBitmap = getCroppedBitmap(bitmap, w); 
     canvas.drawBitmap(roundBitmap, 0, 0, null); 

    } 

    public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) 
    { 
     Bitmap sbmp; 
     if (bmp.getWidth() != radius || bmp.getHeight() != radius) 
      sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false); 
     else 
      sbmp = bmp; 
     Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(),Config.ARGB_8888); 
     Canvas canvas = new Canvas(output); 

//  final int color = 0xffa19774; 
     final Paint paint = new Paint(); 
     final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight()); 

     paint.setAntiAlias(true); 
     paint.setFilterBitmap(true); 
     paint.setDither(true); 
     canvas.drawARGB(0, 0, 0, 0); 
//  paint.setColor(Color.parseColor("#BAB399")); 
     paint.setColor(Color.parseColor("#FF0000")); 
     canvas.drawCircle(sbmp.getWidth()/2 + 0.7f, sbmp.getHeight()/2 + 0.7f, sbmp.getWidth()/2 + 0.1f, paint); 
     paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 
     canvas.drawBitmap(sbmp, rect, rect, paint); 

     return output; 
    } 
} 
4

È implementazione hanno già ottimizzati per drawable circolare Supporto biblioteca v4:

Google reference

/** 
* A Drawable that wraps a bitmap and can be drawn with rounded corners. You can create a 
* RoundedBitmapDrawable from a file path, an input stream, or from a 
* {@link android.graphics.Bitmap} object. 
* <p> 
* Also see the {@link android.graphics.Bitmap} class, which handles the management and 
* transformation of raw bitmap graphics, and should be used when drawing to a 
* {@link android.graphics.Canvas}. 
* </p> 
*/ 
public abstract class RoundedBitmapDrawable extends Drawable .... 
3
For circular imageview use the below code 

public class RoundedImageView extends ImageView { 

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

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

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

    @Override 
    protected void onDraw(Canvas canvas) { 
     BitmapDrawable drawable = (BitmapDrawable) getDrawable(); 

     if (drawable == null) { 
      return; 
     } 

     if (getWidth() == 0 || getHeight() == 0) { 
      return; 
     } 

     Bitmap fullSizeBitmap = drawable.getBitmap(); 

     int scaledWidth = getMeasuredWidth(); 
     int scaledHeight = getMeasuredHeight(); 

     /* 
     * scaledWidth = scaledWidth/2; scaledHeight = scaledHeight/2; 
     */ 

     Bitmap mScaledBitmap; 
     if (scaledWidth == fullSizeBitmap.getWidth() 
       && scaledHeight == fullSizeBitmap.getHeight()) { 
      mScaledBitmap = fullSizeBitmap; 
     } else { 
      mScaledBitmap = Bitmap.createScaledBitmap(fullSizeBitmap, 
        scaledWidth, scaledHeight, true /* filter */); 
     } 

     // Bitmap roundBitmap = getRoundedCornerBitmap(mScaledBitmap); 

     // Bitmap roundBitmap = getRoundedCornerBitmap(getContext(), 
     // mScaledBitmap, 10, scaledWidth, scaledHeight, false, false, 
     // false, false); 
     // canvas.drawBitmap(roundBitmap, 0, 0, null); 

     Bitmap circleBitmap = getCircledBitmap(mScaledBitmap); 

     canvas.drawBitmap(circleBitmap, 0, 0, null); 

    } 

    public Bitmap getRoundedCornerBitmap(Context context, Bitmap input, 
      int pixels, int w, int h, boolean squareTL, boolean squareTR, 
      boolean squareBL, boolean squareBR) { 

     Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888); 
     Canvas canvas = new Canvas(output); 
     final float densityMultiplier = context.getResources() 
       .getDisplayMetrics().density; 

     final int color = 0xff424242; 

     final Paint paint = new Paint(); 
     final Rect rect = new Rect(0, 0, w, h); 
     final RectF rectF = new RectF(rect); 

     // make sure that our rounded corner is scaled appropriately 
     final float roundPx = pixels * densityMultiplier; 

     paint.setAntiAlias(true); 
     canvas.drawARGB(0, 0, 0, 0); 
     paint.setColor(color); 
     canvas.drawRoundRect(rectF, roundPx, roundPx, paint); 

     // draw rectangles over the corners we want to be square 
     if (squareTL) { 
      canvas.drawRect(0, 0, w/2, h/2, paint); 
     } 
     if (squareTR) { 
      canvas.drawRect(w/2, 0, w, h/2, paint); 
     } 
     if (squareBL) { 
      canvas.drawRect(0, h/2, w/2, h, paint); 
     } 
     if (squareBR) { 
      canvas.drawRect(w/2, h/2, w, h, paint); 
     } 

     paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); 
     canvas.drawBitmap(input, 0, 0, paint); 

     return output; 
    } 

    Bitmap getCircledBitmap(Bitmap bitmap) { 

     Bitmap result = Bitmap.createBitmap(bitmap.getWidth(), 
       bitmap.getHeight(), Bitmap.Config.ARGB_8888); 

     Canvas canvas = new Canvas(result); 

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

     paint.setAntiAlias(true); 
     canvas.drawARGB(0, 0, 0, 0); 
     paint.setColor(color); 
     // canvas.drawRoundRect(rectF, roundPx, roundPx, paint); 
     canvas.drawCircle(bitmap.getWidth()/2, bitmap.getHeight()/2, 
       bitmap.getHeight()/2, paint); 

     paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 
     canvas.drawBitmap(bitmap, rect, rect, paint); 

     return result; 
    } 

} 
0

provare a disabilitare l'accelerazione hardware se si utilizza PorterDuffXfermode:

Nel manifestare per tutta l'attività:

<activity android:hardwareAccelerated="false" /> 

o in una vista specifica su cui si sta utilizzando xfermode:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); 
Problemi correlati