最近项目过程中踩坑记录

本文介绍了如何通过自定义NiceImageView类实现图片圆角、边框以及内层边框的效果,包括设置圆角半径、边框宽度和颜色等属性。同时提到了在GridLayoutManager中处理item间距和边距的问题,强调了在项目开发中进行代码和设计思考的重要性。
摘要由CSDN通过智能技术生成

1、一个View就会有一个背景,ImageView也是一样的,假设你图片边缘是白色的,你背景也是白色的,你就看不到圆角效果,但是不要直接给ImageView一个背景,而是可以给ImageView的父布局来一层背景,并且给相应弧度圆角效果。下面贴出一个适用性很强的图片圆角类。
public class NiceImageView extends AppCompatImageView {
private Context context;

private boolean isCircle; // 是否显示为圆形,如果为圆形则设置的corner无效
private boolean isCoverSrc; // border、inner_border是否覆盖图片
private int borderWidth; // 边框宽度
private int borderColor = Color.WHITE; // 边框颜色
private int innerBorderWidth; // 内层边框宽度
private int innerBorderColor = Color.WHITE; // 内层边框充色

private int cornerRadius; // 统一设置圆角半径,优先级高于单独设置每个角的半径
private int cornerTopLeftRadius; // 左上角圆角半径
private int cornerTopRightRadius; // 右上角圆角半径
private int cornerBottomLeftRadius; // 左下角圆角半径
private int cornerBottomRightRadius; // 右下角圆角半径

private int maskColor; // 遮罩颜色

private Xfermode xfermode;

private int width;
private int height;
private float radius;

private float[] borderRadii;
private float[] srcRadii;

private RectF srcRectF; // 图片占的矩形区域
private RectF borderRectF; // 边框的矩形区域

private Paint paint;
private Path path; // 用来裁剪图片的ptah
private Path srcPath; // 图片区域大小的path

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

public NiceImageView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public NiceImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    this.context = context;

    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.NiceImageView, 0, 0);
    for (int i = 0; i < ta.getIndexCount(); i++) {
        int attr = ta.getIndex(i);
        if (attr == R.styleable.NiceImageView_is_cover_src) {
            isCoverSrc = ta.getBoolean(attr, isCoverSrc);
        } else if (attr == R.styleable.NiceImageView_is_circle) {
            isCircle = ta.getBoolean(attr, isCircle);
        } else if (attr == R.styleable.NiceImageView_border_width) {
            borderWidth = ta.getDimensionPixelSize(attr, borderWidth);
        } else if (attr == R.styleable.NiceImageView_border_color) {
            borderColor = ta.getColor(attr, borderColor);
        } else if (attr == R.styleable.NiceImageView_inner_border_width) {
            innerBorderWidth = ta.getDimensionPixelSize(attr, innerBorderWidth);
        } else if (attr == R.styleable.NiceImageView_inner_border_color) {
            innerBorderColor = ta.getColor(attr, innerBorderColor);
        } else if (attr == R.styleable.NiceImageView_corner_radius) {
            cornerRadius = ta.getDimensionPixelSize(attr, cornerRadius);
        } else if (attr == R.styleable.NiceImageView_corner_top_left_radius) {
            cornerTopLeftRadius = ta.getDimensionPixelSize(attr, cornerTopLeftRadius);
        } else if (attr == R.styleable.NiceImageView_corner_top_right_radius) {
            cornerTopRightRadius = ta.getDimensionPixelSize(attr, cornerTopRightRadius);
        } else if (attr == R.styleable.NiceImageView_corner_bottom_left_radius) {
            cornerBottomLeftRadius = ta.getDimensionPixelSize(attr, cornerBottomLeftRadius);
        } else if (attr == R.styleable.NiceImageView_corner_bottom_right_radius) {
            cornerBottomRightRadius = ta.getDimensionPixelSize(attr, cornerBottomRightRadius);
        } else if (attr == R.styleable.NiceImageView_mask_color) {
            maskColor = ta.getColor(attr, maskColor);
        }
    }
    ta.recycle();

    borderRadii = new float[8];
    srcRadii = new float[8];

    borderRectF = new RectF();
    srcRectF = new RectF();

    paint = new Paint();
    path = new Path();

    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
        xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    } else {
        xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
        srcPath = new Path();
    }

    calculateRadii();
    clearInnerBorderWidth();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    width = w;
    height = h;

    initBorderRectF();
    initSrcRectF();
}

@Override
protected void onDraw(Canvas canvas) {
    // 使用图形混合模式来显示指定区域的图片
    canvas.saveLayer(srcRectF, null, Canvas.ALL_SAVE_FLAG);
    if (!isCoverSrc) {
        float sx = 1.0f * (width - 2 * borderWidth - 2 * innerBorderWidth) / width;
        float sy = 1.0f * (height - 2 * borderWidth - 2 * innerBorderWidth) / height;
        // 缩小画布,使图片内容不被borders覆盖
        canvas.scale(sx, sy, width / 2.0f, height / 2.0f);
    }
    super.onDraw(canvas);
    paint.reset();
    path.reset();
    if (isCircle) {
        path.addCircle(width / 2.0f, height / 2.0f, radius, Path.Direction.CCW);
    } else {
        path.addRoundRect(srcRectF, srcRadii, Path.Direction.CCW);
    }

    paint.setAntiAlias(true);
    paint.setStyle(Paint.Style.FILL);
    paint.setXfermode(xfermode);
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
        canvas.drawPath(path, paint);
    } else {
        srcPath.addRect(srcRectF, Path.Direction.CCW);
        // 计算tempPath和path的差集
        srcPath.op(path, Path.Op.DIFFERENCE);
        canvas.drawPath(srcPath, paint);
        //android9 一次圆一次方,失效的bug解决 https://github.com/SheHuan/NiceImageView/issues/22
        srcPath.reset();
    }
    paint.setXfermode(null);

    // 绘制遮罩
    if (maskColor != 0) {
        paint.setColor(maskColor);
        canvas.drawPath(path, paint);
    }
    // 恢复画布
    canvas.restore();
    // 绘制边框
    drawBorders(canvas);
}

private void drawBorders(Canvas canvas) {
    if (isCircle) {
        if (borderWidth > 0) {
            drawCircleBorder(canvas, borderWidth, borderColor, radius - borderWidth / 2.0f);
        }
        if (innerBorderWidth > 0) {
            drawCircleBorder(canvas, innerBorderWidth, innerBorderColor, radius - borderWidth - innerBorderWidth / 2.0f);
        }
    } else {
        if (borderWidth > 0) {
            drawRectFBorder(canvas, borderWidth, borderColor, borderRectF, borderRadii);
        }
    }
}

private void drawCircleBorder(Canvas canvas, int borderWidth, int borderColor, float radius) {
    initBorderPaint(borderWidth, borderColor);
    path.addCircle(width / 2.0f, height / 2.0f, radius, Path.Direction.CCW);
    canvas.drawPath(path, paint);
}

private void drawRectFBorder(Canvas canvas, int borderWidth, int borderColor, RectF rectF, float[] radii) {
    initBorderPaint(borderWidth, borderColor);
    path.addRoundRect(rectF, radii, Path.Direction.CCW);
    canvas.drawPath(path, paint);
}

private void initBorderPaint(int borderWidth, int borderColor) {
    path.reset();
    paint.setStrokeWidth(borderWidth);
    paint.setColor(borderColor);
    paint.setStyle(Paint.Style.STROKE);
}

/**
 * 计算外边框的RectF
 */
private void initBorderRectF() {
    if (!isCircle) {
        borderRectF.set(borderWidth / 2.0f, borderWidth / 2.0f, width - borderWidth / 2.0f, height - borderWidth / 2.0f);
    }
}

/**
 * 计算图片原始区域的RectF
 */
private void initSrcRectF() {
    if (isCircle) {
        radius = Math.min(width, height) / 2.0f;
        srcRectF.set(width / 2.0f - radius, height / 2.0f - radius, width / 2.0f + radius, height / 2.0f + radius);
    } else {
        srcRectF.set(0, 0, width, height);
        if (isCoverSrc) {
            srcRectF = borderRectF;
        }
    }
}

/**
 * 计算RectF的圆角半径
 */
private void calculateRadii() {
    if (isCircle) {
        return;
    }
    if (cornerRadius > 0) {
        for (int i = 0; i < borderRadii.length; i++) {
            borderRadii[i] = cornerRadius;
            srcRadii[i] = cornerRadius - borderWidth / 2.0f;
        }
    } else {
        borderRadii[0] = borderRadii[1] = cornerTopLeftRadius;
        borderRadii[2] = borderRadii[3] = cornerTopRightRadius;
        borderRadii[4] = borderRadii[5] = cornerBottomRightRadius;
        borderRadii[6] = borderRadii[7] = cornerBottomLeftRadius;

        srcRadii[0] = srcRadii[1] = cornerTopLeftRadius - borderWidth / 2.0f;
        srcRadii[2] = srcRadii[3] = cornerTopRightRadius - borderWidth / 2.0f;
        srcRadii[4] = srcRadii[5] = cornerBottomRightRadius - borderWidth / 2.0f;
        srcRadii[6] = srcRadii[7] = cornerBottomLeftRadius - borderWidth / 2.0f;
    }
}

private void calculateRadiiAndRectF(boolean reset) {
    if (reset) {
        cornerRadius = 0;
    }
    calculateRadii();
    initBorderRectF();
    invalidate();
}

/**
 * 目前圆角矩形情况下不支持inner_border,需要将其置0
 */
private void clearInnerBorderWidth() {
    if (!isCircle) {
        this.innerBorderWidth = 0;
    }
}

public void isCoverSrc(boolean isCoverSrc) {
    this.isCoverSrc = isCoverSrc;
    initSrcRectF();
    invalidate();
}

public void isCircle(boolean isCircle) {
    this.isCircle = isCircle;
    clearInnerBorderWidth();
    initSrcRectF();
    invalidate();
}

public void setBorderWidth(int borderWidth) {
    this.borderWidth = BYSystemHelper.Dp2Px( borderWidth);
    calculateRadiiAndRectF(false);
}

public void setBorderColor(@ColorInt int borderColor) {
    this.borderColor = borderColor;
    invalidate();
}

public void setInnerBorderWidth(int innerBorderWidth) {
    this.innerBorderWidth = BYSystemHelper.Dp2Px( innerBorderWidth);
    clearInnerBorderWidth();
    invalidate();
}

public void setInnerBorderColor(@ColorInt int innerBorderColor) {
    this.innerBorderColor = innerBorderColor;
    invalidate();
}

public void setCornerRadius(int cornerRadius) {
    this.cornerRadius = BYSystemHelper.Dp2Px(cornerRadius);
    calculateRadiiAndRectF(false);
}

public void setCornerTopLeftRadius(int cornerTopLeftRadius) {
    this.cornerTopLeftRadius = BYSystemHelper.Dp2Px( cornerTopLeftRadius);
    calculateRadiiAndRectF(true);
}

public void setCornerTopRightRadius(int cornerTopRightRadius) {
    this.cornerTopRightRadius = BYSystemHelper.Dp2Px( cornerTopRightRadius);
    calculateRadiiAndRectF(true);
}

public void setCornerBottomLeftRadius(int cornerBottomLeftRadius) {
    this.cornerBottomLeftRadius = BYSystemHelper.Dp2Px( cornerBottomLeftRadius);
    calculateRadiiAndRectF(true);
}

public void setCornerBottomRightRadius(int cornerBottomRightRadius) {
    this.cornerBottomRightRadius = BYSystemHelper.Dp2Px( cornerBottomRightRadius);
    calculateRadiiAndRectF(true);
}

public void setMaskColor(@ColorInt int maskColor) {
    this.maskColor = maskColor;
    invalidate();
}

}
当然你也是使用CardView ,还能做一些边框阴影效果。
2.GridLayoutManager spanCount布局思考总结。
如何布局协调间距合理,须知,GLM会将你item等比例放大,比如你划分4等分,那么会将你的item划分为4块,不管你如何设置item的大小和父布局大小,GLM都会让你的item的宽度均分1/4。那么问题来了。如果你想让item的左右边距是10dp。你如何做?首先你可以直接给rv的paddingLeft和paddingRight为10dp,然后在item的父布局中让item居中。这其实是个挺简单的思路。体现了均分的思想。
偏偏项目中遇到了一个奇葩的做法。由于对GLM的布局思想理解不深,走了弯路。具体怎么做的呢?
首先将item设置一个leftMargin。然后内容部分居中。其次给RV设置一个paddingRight。这样其实也是利用了GLM均分的思想。只不过这个均分不容易看出来均分在哪儿。margin和padding一起使用,达到左右边距相等的目的。一个项目维护过久,实际更多的精力已经是踩坑和梳理了。项目就是一个代码屎山,改的越来越多,逻辑叠加越来越多,一言难尽。
3.项目中更多的时间应该投入精力去做代码和设计思考,而不是啥都没想清楚就投入进去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值