Android自定View:可以设置宽高比例和圆角图片的ImageView

发布于 2018-06-24  172 次阅读


昨天有个朋友让我折腾一个这样的ImageView,
他说“我想要一个既可以圆角又可以设置宽高比的imageview”
所以我就折腾了下。

大概是这样的
效果图

1.attributes

这里说一下:如果参考边是:宽,那么你高度的设置其实是没有效果的
因为,代码里面 我直接把高度重新计算了

<declare-styleable name="SakuraImage">
   <!--参考边-->
   <attr name="refer" format="enum">
        <enum name="宽" value="1"/>
        <enum name="高" value="2"/>
    </attr>
    <!--长宽比例-->
    <attr name="ratio" format="float"/>
    <!--圆弧度-->
    <attr name="radius" format="float"/>
</declare-styleable>

2.xml

<mos.kos.cache.widget.SakuraImage
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:scaleType="centerCrop"
    android:src="@drawable/image"
    sakura:radius="20"
    sakura:ratio="1"
    sakura:refer="高"/>

3.Code

3.1.长宽按比例设置

这一步主要是在onMeasure里面完成的
具体的看下面的代码和代码里的注释

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (ratio > 0) {
        //参考边为:宽
        if (referEdge == 1) {
            ratioW = MeasureSpec.getSize(widthMeasureSpec);
            ratioH = (int) (ratioW * ratio);//根据宽来计算高
        //参考边为:高
        } else {
            ratioH = MeasureSpec.getSize(heightMeasureSpec);
            ratioW = (int) (ratioH * ratio);//根据高来计算宽
        }
        //丢给父类处理
        super.onMeasure(MeasureSpec.makeMeasureSpec(ratioW, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(ratioH, MeasureSpec.EXACTLY));
    } else {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    Logger.v("宽高1:ratioW=" + ratioW + ",ratioH=" + ratioH);
}

3.2.设置带圆弧的图片

这里稍微麻烦一点,不过主要还是在onDraw里面处理的

  • 1.先找到图片的锚点
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    rectF.top = 0;
    rectF.left = 0;
    rectF.right = getWidth(); // 宽度
    rectF.bottom = getHeight(); // 高度
    setBitmapShader();
}
  • 2.关键的一步:设置图片的透明区域Shader
    > 下面是一段伪代码,把这一块的关键点都整到一起的
//先整一个画笔
Paint bmpPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
bmpPaint.setStyle(Paint.Style.FILL);
bmpPaint.setAntiAlias(true);

//这里的bmp是搞到的Bitmap图片
BitmapShader bmpShader = new BitmapShader(bmp, 
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//bmpShader 就是圆弧所裁剪调的透明区域
bmpPaint.setShader(bmpShader);

//这个矩阵主要是对裁剪后的图片做缩放、还有位置的处理
matrix.set(null);//设为默认值,固定为CENTER_CROP
// 缩放
float scale = Math.max(getWidth() * 1f / bmp.getWidth(), getHeight() * 1f / bmp.getHeight());
// 居中
float dx = (getWidth() - bmp.getWidth() * scale) / 2;
float dy = (getHeight() - bmp.getHeight() * scale) / 2;
matrix.setScale(scale, scale);
matrix.postTranslate(dx, dy);
bmpShader.setLocalMatrix(matrix);
invalidate();//刷新
  • 3.最后绘制
    > 这里我踩了个坑,记录一下:
    > 绘制的时候最好用Canvas 的路径绘制canvas.drawPath(),就是第1步打的那4个锚点,这样也是最简单的
    >
    > 我之前尝试过直接用画布来绘制矩形canvas.drawRoundRect(或者绘制图形canvas.drawBitmap(
    > 虽然也成功了,但是效果不怎么理想。
@Override
protected void onDraw(Canvas canvas) {
    if (bmp != null) {
        path.reset();
        path.addRoundRect(rectF, radius, radius, Path.Direction.CW);
        canvas.drawPath(path, bmpPaint);
    }
}

最后丢再几张效果图

  • 1.宽高比为1的时候
    这里写图片描述
  • 2.这里是以宽为参考边
    这里写图片描述
  • 3.这里是以高为参考边
    这里写图片描述

源码下载


赤夜染尽 千樱散落 零时夜雨 无茵之音