/*
* Copyright (C) 2015 Thomas Robert Altstidl
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package com.tr4android.support.extension.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.support.v7.widget.AppCompatDrawableManager;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.util.Log;
import com.tr4android.appcompat.extension.R;
import com.tr4android.support.extension.drawable.PlaceholderDrawable;
import com.tr4android.support.extension.utils.ThemeUtils;
/**
* A supercharged ImageView that displays images as circles and creates placeholder images
*/
public class CircleImageView extends AppCompatImageView {
private static final String LOG_TAG = "CircleImageView";
// whether the image should be clipped to a circle
private boolean mIsCircleImageEnabled = true;
// whether an image drawable is being resolved
private boolean mIsResolvingDrawable;
// the placeholder drawable used for drawing the placeholder
private PlaceholderDrawable mPlaceholderDrawable;
// default color for circle
private int mPlaceholderCircleDefaultColor;
public CircleImageView(Context context) {
this(context, null);
}
public CircleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyleAttr, 0);
mIsCircleImageEnabled = a.getBoolean(R.styleable.CircleImageView_circleImageEnabled, true);
mPlaceholderCircleDefaultColor = a.getColor(R.styleable.CircleImageView_placeholderCircleColor, ThemeUtils.getThemeAttrColor(getContext(), R.attr.colorAccent));
int textColorPrimaryInverse = ThemeUtils.getThemeAttrColor(getContext(), android.R.attr.textColorPrimaryInverse);
int mPlaceholderTextSize = a.getDimensionPixelSize(R.styleable.CircleImageView_placeholderTextSize, getResources().getDimensionPixelSize(R.dimen.defaultPlaceholderTextSize));
int mPlaceholderTextColor = a.getColor(R.styleable.CircleImageView_placeholderTextColor, textColorPrimaryInverse);
int mPlaceholderImageSize = a.getDimensionPixelSize(R.styleable.CircleImageView_placeholderIconSize, getResources().getDimensionPixelSize(R.dimen.defaultPlaceholderImageSize));
int mPlaceholderImageColor = a.getColor(R.styleable.CircleImageView_placeholderIconColor, textColorPrimaryInverse);
a.recycle();
mPlaceholderDrawable = new PlaceholderDrawable(
mPlaceholderTextSize, mPlaceholderTextColor, mPlaceholderImageSize, mPlaceholderImageColor);
}
@Override
public void setImageBitmap(Bitmap bm) {
if (mIsCircleImageEnabled) {
setImageDrawable(getCircleBitmapDrawable(getContext(), bm));
} else {
super.setImageBitmap(bm);
}
}
@Override
public void setImageDrawable(Drawable drawable) {
if (mIsCircleImageEnabled && drawable != null &&
!(drawable instanceof RoundedBitmapDrawable) && !(drawable instanceof PlaceholderDrawable)) {
setImageDrawable(getCircleBitmapDrawable(getContext(), getBitmapFromDrawable(drawable)));
} else {
super.setImageDrawable(drawable);
}
}
@Override
public void setImageResource(@DrawableRes int resId) {
if (mIsCircleImageEnabled) {
mIsResolvingDrawable = true;
super.setImageResource(resId);
mIsResolvingDrawable = false;
setImageDrawable(getCircleBitmapDrawable(getContext(), getBitmapFromDrawable(getDrawable())));
} else {
super.setImageResource(resId);
}
}
@Override
public void setImageURI(Uri uri) {
if (mIsCircleImageEnabled) {
mIsResolvingDrawable = true;
super.setImageURI(uri);
mIsResolvingDrawable = false;
setImageDrawable(getCircleBitmapDrawable(getContext(), getBitmapFromDrawable(getDrawable())));
} else {
super.setImageURI(uri);
}
}
/**
* Set whether the image should be transformed to a circle or not
*
* @param enabled pass true to enable, false to disable circular images
*/
public void setCircleImageEnabled(boolean enabled) {
mIsCircleImageEnabled = enabled;
}
/**
* Check whether the image is transformed to a circle or not
*
* @return true if circular images are enabled, false if they are disabled
*/
public boolean isCircleImageEnabled() {
return mIsCircleImageEnabled;
}
/**
* Set a placeholder with a text. This will use the default circle color.
* <p/>
* Note: This usually should be the first letter of a name. You can use the helper {@link #retrieveLetter(String)}
* to extract the first letter of a given {@link String}.
*
* @param placeholderText Placeholder text to display in the middle of the circle
*/
public void setPlaceholder(String placeholderText) {
setPlaceholder(placeholderText, mPlaceholderCircleDefaultColor);
}
/**
* Set a placeholder with a text and circle color.
* <p/>
* Note: This usually should be the first letter of a name. You can use the helper {@link #retrieveLetter(String)}
* to extract the first letter of a given {@link String}.
*
* @param placeholderText Placeholder text to display in the middle of the circle
* @param circleColor Color to use for the circle
*/
public void setPlaceholder(String placeholderText, @ColorInt int circleColor) {
mPlaceholderDrawable.setPlaceholder(placeholderText, circleColor);
setImageDrawable(mPlaceholderDrawable);
}
/**
* Set a placeholder with an image. This will use the default circle color.
*
* @param resId The resource id of the placeholder drawable
*/
public void setPlaceholder(@DrawableRes int resId) {
setPlaceholder(AppCompatDrawableManager.get().getDrawable(getContext(), resId), mPlaceholderCircleDefaultColor);
}
/**
* Set a placeholder with an image. This will use the default circle color.
*
* @param drawable The placeholder drawable
*/
public void setPlaceholder(Drawable drawable) {
setPlaceholder(drawable, mPlaceholderCircleDefaultColor);
}
/**
* Set a placeholder with an image and circle color.
*
* @param resId The resource id of the placeholder drawable
* @param circleColor Color to use for the circle
*/
public void setPlaceholder(@DrawableRes int resId, @ColorInt int circleColor) {
setPlaceholder(AppCompatDrawableManager.get().getDrawable(getContext(), resId), circleColor);
}
/**
* Set a placeholder with an image and circle color.
*
* @param drawable The placeholder drawable
* @param circleColor Color to use for the circle
*/
public void setPlaceholder(Drawable drawable, @ColorInt int circleColor) {
mPlaceholderDrawable.setPlaceholder(drawable, circleColor);
setImageDrawable(mPlaceholderDrawable);
}
/**
* Small Helper for extracting the first letter of a {@link String}.
*
* @param text the full text representing e.g. a name
* @return the first letter uppercase
*/
public static String retrieveLetter(String text) {
return String.valueOf(text.charAt(0)).toUpperCase();
}
@Override
public void onDraw(Canvas canvas) {
if (mIsResolvingDrawable) return;
super.onDraw(canvas);
}
/**
* Helper for creating a circle bitmap drawable using the {@link android.support.v4.graphics.drawable.RoundedBitmapDrawable}
*
* @param bitmap The bitmap which should be converted to a circle bitmap drawable
* @return the {@link android.support.v4.graphics.drawable.RoundedBitmapDrawable} containing the bitmap
*/
public static RoundedBitmapDrawable getCircleBitmapDrawable(Context context, Bitmap bitmap) {
RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(context.getResources(), bitmap);
drawable.setCornerRadius(Math.max(bitmap.getWidth() / 2, bitmap.getHeight() / 2));
drawable.setAntiAlias(true);
return drawable;
}
/**
* Helper for creating a bitmap from a drawable
*
* @param drawable The drawable which should be converted to a bitmap
* @return the bitmap containing the drawable
*/
public static Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) return null;
if (drawable instanceof BitmapDrawable) {
Log.w(LOG_TAG, "For better performance consider using setImageBitmap() instead!");
return ((BitmapDrawable) drawable).getBitmap();
} else {
Bitmap bitmap = Bitmap.createBitmap(Math.max(2, drawable.getIntrinsicWidth()), Math.max(2, drawable.getIntrinsicHeight()), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}
}