Generar Imágenes Redondas

En este nuevo post os queremos enseñar como generar de manera automática imágenes redondas al más puro estilo Google Plus, como os vamos a explicar a continuación veréis que es un proceso realmente sencillo. Manos a la obra!!!

 

 

 

 

El objetivo de este post es enseñaros como extender un ImageView de Android para que sea capaz de hacer la transformación que os mostramos a continuación:

Image_001 Image_002
Imagen Original Imagen Procesada

Paso 1:

  • Creamos una nueva clase en nuestro proyecto y la llamamos por ejemplo “RoundImageView.java“.

Paso 2:

  • Extendemos esta clase de la clase “ImageView” de la API de Android, tal que así.
public class RoundImageView extends ImageView

Paso 3:

  •  Sobrescribimos el método “onDraw” de la clase para implementar aquí nuestra lógica.
@Override
protected void onDraw(Canvas pCanvas)

Paso 4:

  • Generamos un nuevo Bitmap, tomando como origen la imagen que hayamos configurado en el atributo “android:src” de la vista.
if (scaledBitmap == null) {
scaledBitmap = Bitmap.createScaledBitmap(((BitmapDrawable)getDrawable()).getBitmap(), size, size, true);
}
  • Generamos un nuevo Bitmap en blanco sobre el que dibujaremos la imagen procesada y un Canvas asociado a este que usaremos después para dibujar sobre el Bitmap.
if (resultBitmap == null) {
resultBitmap = Bitmap.createBitmap(pCanvas.getWidth(), pCanvas.getHeight(), scaledBitmap.getConfig());
}
  • Generamos un Path sobre el que dibujaremos el circulo de nuestra imagen y el que más tarde invertiremos, para ilustrar esto hemos generados la imágenes que os mostramos a continuación.
if (maskPath == null) {
maskPath = new Path();
maskPath.addCircle((size / 2), (size / 2), (size / 2), Direction.CCW);
maskPath.toggleInverseFillType();
maskPath.close();
}
Image_001 Image_002
Path Original Path Invertido
  • Dibujamos sobre el Bitmap en blanco la imagen original.
//Draw background image.
maskedCanvas.drawBitmap(scaledBitmap, 0, 0, null);
  • Eliminamos el contenido de los bordes de la imagen de la siguiente manera, no entraremos en explicar el porque, ya que la documentación de la API es muy extensa y siempre está disponible en la web de Android Dev.
//Draw transparent area masking the original bitmap.
maskedCanvas.clipPath(maskPath);
maskedCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
  • Dibujamos la imagen procesada sobre el objecto Canvas que nos llego como parámetro al método onDraw.
pCanvas.drawBitmap(resultBitmap, 0, 0, null);

Con esto ya tendréis lo necesario para que vuestras aplicaciones luzcan imágenes redondas, como nota a todo esto, decir que podemos hacer que las imágenes tomen cualquier otra forma únicamente variando el objeto Path. Os dejo el código completo de la clase para que podáis ver todo el proceso de a una.

/**
 Copyright Mob&Me 2013 (@MobAndMe)

Licensed under the GPL General Public License, Version 3.0 (the "License"),
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

http://www.gnu.org/licenses/gpl.html

Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 Website: http://mobandme.com
 Contact: Txus Ballesteros <txus.ballesteros@mobandme.com>
*/

package com.mobandme.toolkit;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.graphics.drawable.BitmapDrawable;
import com.mobandme.toolkit.helpers.ExceptionsHelper;

public class RoundImageView extends ImageView {

private int size = 0;
 private Bitmap resultBitmap;
 private Bitmap scaledBitmap;
 private Path maskPath;
 private Canvas maskedCanvas;

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

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
 int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
 size = Math.min(parentWidth, parentHeight);

 resultBitmap = null;
 scaledBitmap = null;
 maskedCanvas = null;
 maskPath = null;
 }

 @Override
 protected void onDraw(Canvas pCanvas) {
 if (scaledBitmap == null) {
 scaledBitmap = Bitmap.createScaledBitmap(((BitmapDrawable)getDrawable()).getBitmap(), size, size, true);
 }

 if (resultBitmap == null) {
 resultBitmap = Bitmap.createBitmap(pCanvas.getWidth(), pCanvas.getHeight(), scaledBitmap.getConfig());
 }

 if (maskPath == null) {
 maskPath = new Path();
 maskPath.addCircle((size / 2), (size / 2), (size / 2), Direction.CCW);
 maskPath.toggleInverseFillType();
 maskPath.close();
 }

if (maskedCanvas == null) {
 maskedCanvas = new Canvas(resultBitmap);
 //Draw background image.
 maskedCanvas.drawBitmap(scaledBitmap, 0, 0, null);

 //Draw transparent area masking the original bitmap.
 maskedCanvas.clipPath(maskPath);
 maskedCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
}

 pCanvas.drawBitmap(resultBitmap, 0, 0, null);
 }

 @Override
 protected void onDetachedFromWindow() {
 resultBitmap = null;
 scaledBitmap = null;
 maskedCanvas = null;
 maskPath = null;
 }
}

3 Comentarios

  1. Eric says:

    Nota: a partir de Android 3.0, y cuando se tiene la aceleración por hardware activada, la función clipPath() no está soportada.

    Así lo indica la guía:

    Podemos conseguir el mismo resultado con total compatibilidad utilizando los PorterDuff.Mode:

  2. Eric says:

    PD: El comentario anterior está mal editado. Aquí el bueno :-)

    Nota: a partir de Android 3.0, y cuando se tiene la aceleración por hardware activada, la función clipPath() no está soportada.

    Así lo indica la guía:

    http://developer.android.com/intl/es/guide/topics/graphics/hardware-accel.html#unsupported

    Podemos conseguir el mismo resultado con total compatibilidad utilizando los PorterDuff.Mode:

    http://developer.android.com/intl/es/reference/android/graphics/PorterDuff.Mode.html

    • Mob&Me says:

      Tienes toda la razón Eric,

      Pero esta es la manera más fácil de conseguirlo, pese a que hay otras tantas técnicas.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>