您的位置:首页 > Web前端

自定义圆角图片控件(Xfermode方式)

2016-01-11 16:35 399 查看
苹果都放弃自己的棱角了。。。

看惯了方方正正的图片,乍一看到圆角图片觉得挺漂亮的。可当满世界都是圆角图片的时候,我又开始怀念那些棱角了~之前仓促的写过一个,今天拿过来又修改了一下,现在贴在这里,以方便以后ctrl+c、ctrl+v~~~~~

一、目标

自定义一个图片控件,有圆形和圆角两种选择。控件的行为和ImageView一致!

二、思路

扩展ImageView控件,重写其onDraw方法。一开始还想重写onMeasure方法,如果显示圆形图片强制宽高相等,没能行得通(代码中会说明)。圆角图片以Xfermode方式实现,关于这个知识点大家可以参考这篇文章《setXfermode属性》。

三、实现

控件类RoundImageView:

/**
* 圆角或者圆形图片控件扩展自ImageView<br/>
* 说明:<br/>
* 1.仅重写了绘制流程,即onDraw()方法<br/>
* 2.如果使用圆形图片需要设置固定的尺寸
* 3.支持普通、圆形、圆角三种样式
* 4.可设置圆角半径  默认是10dp
* 5.无法为背景添加圆角
* Created by 95 on 2016/1/11.
*/
public class RoundImageView extends ImageView
{
private static final String TAG = RoundImageView.class.getSimpleName();
private Paint mPaint;
private WeakReference<Bitmap> mWeakBitmap;

/**
* 图片的类型,圆形or圆角
*/
private int type;
public static final int TYPE_NORMAL = 0;
public static final int TYPE_CIRCLE = 1;
public static final int TYPE_ROUND = 2;

/**
* 圆角大小的默认值
*/
private static final int DEFAULT_RADIUS = 10;
/**
* 圆角的大小
*/
private int mRadius;

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

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

mPaint = new Paint();
mPaint.setAntiAlias(true);

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

mRadius = a.getDimensionPixelSize(R.styleable.RoundImageView_radius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_RADIUS, getResources().getDisplayMetrics()));// 默认为10dp
type = a.getInt(R.styleable.RoundImageView_type, TYPE_NORMAL);// 默认为normal
a.recycle();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

/**
* 如果类型是圆形,则强制改变view的宽高一致,以小值为准
* 这种写法是有问题的
* 我们本来是想在正常测量的基础上取最短边
* 可下面的操作插入到了正常执行的流程当中   致使最终的结果出了问题
*/
//        if (type == TYPE_CIRCLE)
//        {
//            int min = Math.min(getMeasuredWidth(), getMeasuredHeight());
//            setMeasuredDimension(min, min);
//        }
}

@Override
protected void onDraw(Canvas canvas)
{
//普通模式  调用原生的绘制方法
if (type == TYPE_NORMAL)
{
super.onDraw(canvas);
return;
}
//如果当前未设置图片  直接返回
if (getDrawable() == null) return;
//在缓存中取出bitmap
Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();
//如果bitmap已经被回收,就重新创建
if (bitmap == null || bitmap.isRecycled())
{
bitmap = createDesiredBitmap(drawableToBitmap(getDrawable()), type, getMeasuredWidth(), getMeasuredHeight(), mRadius);
mWeakBitmap = new WeakReference<Bitmap>(bitmap);
}
//以为我们所有的绘制使用了同一个Paint  所以每次使用之前都要考虑这个状态
mPaint.setXfermode(null);
//把最终结果绘制到画布上
canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
}

/**
* 生成圆角图片 使用Xfermode方式
*
* @param src           原图
* @param type          round or circle
* @param desiredWidth  控件的宽
* @param desiredHeight 控件的高
* @param radius        圆角半径
* @return
*/
private Bitmap createDesiredBitmap(Bitmap src, int type, int desiredWidth, int desiredHeight, int radius)
{
Bitmap target = Bitmap.createBitmap(desiredWidth, desiredHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(target);
mPaint.setXfermode(null);
if (TYPE_CIRCLE == type)
{
canvas.drawCircle(desiredWidth * 1.0f / 2, desiredWidth * 1.0f / 2, desiredWidth * 1.0f / 2, mPaint);
} else
{
RectF rectF = new RectF(0.0f, 0.0f, desiredWidth, desiredHeight);
canvas.drawRoundRect(rectF, radius * 1.0f, radius * 1.0f, mPaint);
}
//缩放比例  保证图片的宽和高大于控件的宽和高
float ratio = Math.max(desiredWidth * 1.0f / src.getWidth(), desiredHeight * 1.0f / src.getHeight());
Bitmap tem = getScaledBitmap(src, ratio);
//设置Xfermode
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//保证控件展示的位置即为图片正中间的内容
canvas.drawBitmap(tem, (desiredWidth - tem.getWidth()) * 1.0f / 2, (desiredHeight - tem.getHeight()) * 1.0f / 2, mPaint);
//释放内存
tem.recycle();
return target;
}

/**
* 缩放图片
*
* @param src   原图
* @param ratio 缩放比例
* @return
*/
private Bitmap getScaledBitmap(Bitmap src, float ratio)
{
Matrix matrix = new Matrix();
matrix.setScale(ratio, ratio, 0.0f, 0.0f);
Bitmap target = Bitmap.createBitmap((int) (src.getWidth() * ratio), (int) (src.getHeight() * ratio), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(target);
canvas.drawBitmap(src, matrix, mPaint);
return target;
}

/**
* drawable 转 bitmap
*
* @param drawable
* @return
*/
private Bitmap drawableToBitmap(Drawable drawable)
{
if (drawable instanceof BitmapDrawable)
{
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap target = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(target);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.draw(canvas);
return target;
}
}


自定义属性:

<declare-styleable name="RoundImageView">
<attr name="type" format="enum">
<enum name="normal" value="0"/>
<enum name="circle" value="1"/>
<enum name="round" value="2"/>
</attr>

<attr name="radius" format="dimension"/>
</declare-styleable>


四、使用

直接在布局文件当中使用:

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">

<!--原图-->
<com.hsji.testptr.widget.RoundImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/lena"/>

<!--圆角效果,圆角半径是15dp-->
<com.hsji.testptr.widget.RoundImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginTop="10dp"
android:src="@drawable/lena"
app:radius="15dp"
app:type="round"/>

<!--非正方形圆角效果,圆角半径60dp-->
<com.hsji.testptr.widget.RoundImageView
android:layout_width="120dp"
android:layout_height="80dp"
android:layout_marginTop="10dp"
android:src="@drawable/lena"
app:radius="60dp"
app:type="round"/>

<!--圆形效果-->
<com.hsji.testptr.widget.RoundImageView
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginTop="10dp"
android:src="@drawable/lena"
app:type="circle"/>
</LinearLayout>


下面是效果图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: