圆角相对布局 RCRelativeLayout
【代码】圆角相对布局 RCRelativeLayout。
·

<com.android.song.view.RCRelativeLayout
android:id="@+id/iv_heart_rc"
android:layout_width="@dimen/dp_84"
android:layout_height="@dimen/dp_138"
android:layout_marginStart="@dimen/dp_6"
android:scaleType="centerCrop"
android:shadowDx="0"
android:shadowDy="2"
android:shadowRadius="4"
android:src="@mipmap/img_assistant_author"
app:clip_background="true"
app:layout_constraintBottom_toBottomOf="@+id/iv_bg"
app:layout_constraintStart_toStartOf="@+id/iv_bg"
app:layout_constraintTop_toTopOf="@+id/iv_bg"
app:round_corner="@dimen/dp_14"
app:stroke_width="@dimen/dp_1">
<ImageView
android:id="@+id/iv_heart"
android:layout_width="@dimen/dp_84"
android:layout_height="@dimen/dp_138"
android:scaleType="centerCrop"
android:src="@mipmap/img_assistant_author" />
</com.android.song.view.RCRelativeLayout>
package com.android.song.view;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Checkable;
import android.widget.RelativeLayout;
import com.android.song.utils.RCAttrs;
import com.android.song.utils.RCHelper;
/**
* 作用:圆角相对布局
*/
public class RCRelativeLayout extends RelativeLayout implements Checkable, RCAttrs {
RCHelper mRCHelper;
public RCRelativeLayout(Context context) {
this(context, null);
}
public RCRelativeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RCRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mRCHelper = new RCHelper();
mRCHelper.initAttrs(context, attrs);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRCHelper.onSizeChanged(this, w, h);
}
@Override
protected void dispatchDraw(Canvas canvas) {
canvas.saveLayer(mRCHelper.mLayer, null, Canvas.ALL_SAVE_FLAG);
super.dispatchDraw(canvas);
mRCHelper.onClipDraw(canvas);
canvas.restore();
}
@Override
public void draw(Canvas canvas) {
if (mRCHelper.mClipBackground) {
canvas.save();
canvas.clipPath(mRCHelper.mClipPath);
super.draw(canvas);
canvas.restore();
} else {
super.draw(canvas);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN && !mRCHelper.mAreaRegion.contains((int) ev.getX(), (int) ev.getY())) {
return false;
}
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP) {
refreshDrawableState();
} else if (action == MotionEvent.ACTION_CANCEL) {
setPressed(false);
refreshDrawableState();
}
return super.dispatchTouchEvent(ev);
}
//--- 公开接口 ----------------------------------------------------------------------------------
public void setClipBackground(boolean clipBackground) {
mRCHelper.mClipBackground = clipBackground;
invalidate();
}
public void setRoundAsCircle(boolean roundAsCircle) {
mRCHelper.mRoundAsCircle = roundAsCircle;
invalidate();
}
public void setRadius(int radius) {
for (int i = 0; i < mRCHelper.radii.length; i++) {
mRCHelper.radii[i] = radius;
}
invalidate();
}
public void setTopLeftRadius(int topLeftRadius) {
mRCHelper.radii[0] = topLeftRadius;
mRCHelper.radii[1] = topLeftRadius;
invalidate();
}
public void setTopRightRadius(int topRightRadius) {
mRCHelper.radii[2] = topRightRadius;
mRCHelper.radii[3] = topRightRadius;
invalidate();
}
public void setBottomLeftRadius(int bottomLeftRadius) {
mRCHelper.radii[6] = bottomLeftRadius;
mRCHelper.radii[7] = bottomLeftRadius;
invalidate();
}
public void setBottomRightRadius(int bottomRightRadius) {
mRCHelper.radii[4] = bottomRightRadius;
mRCHelper.radii[5] = bottomRightRadius;
invalidate();
}
public void setStrokeWidth(int strokeWidth) {
mRCHelper.mStrokeWidth = strokeWidth;
invalidate();
}
public void setStrokeColor(int strokeColor) {
mRCHelper.mStrokeColor = strokeColor;
invalidate();
}
@Override
public void invalidate() {
if (null != mRCHelper)
mRCHelper.refreshRegion(this);
super.invalidate();
}
public boolean isClipBackground() {
return mRCHelper.mClipBackground;
}
public boolean isRoundAsCircle() {
return mRCHelper.mRoundAsCircle;
}
public float getTopLeftRadius() {
return mRCHelper.radii[0];
}
public float getTopRightRadius() {
return mRCHelper.radii[2];
}
public float getBottomLeftRadius() {
return mRCHelper.radii[4];
}
public float getBottomRightRadius() {
return mRCHelper.radii[6];
}
public int getStrokeWidth() {
return mRCHelper.mStrokeWidth;
}
public int getStrokeColor() {
return mRCHelper.mStrokeColor;
}
//--- Selector 支持 ----------------------------------------------------------------------------
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
mRCHelper.drawableStateChanged(this);
}
@Override
public void setChecked(boolean checked) {
if (mRCHelper.mChecked != checked) {
mRCHelper.mChecked = checked;
refreshDrawableState();
if (mRCHelper.mOnCheckedChangeListener != null) {
mRCHelper.mOnCheckedChangeListener.onCheckedChanged(this, mRCHelper.mChecked);
}
}
}
@Override
public boolean isChecked() {
return mRCHelper.mChecked;
}
@Override
public void toggle() {
setChecked(!mRCHelper.mChecked);
}
public void setOnCheckedChangeListener(RCHelper.OnCheckedChangeListener listener) {
mRCHelper.mOnCheckedChangeListener = listener;
}
}
package com.android.song.utils;
public interface RCAttrs {
void setClipBackground(boolean clipBackground);
void setRoundAsCircle(boolean roundAsCircle);
void setRadius(int radius);
void setTopLeftRadius(int topLeftRadius);
void setTopRightRadius(int topRightRadius);
void setBottomLeftRadius(int bottomLeftRadius);
void setBottomRightRadius(int bottomRightRadius);
void setStrokeWidth(int strokeWidth);
void setStrokeColor(int strokeColor);
boolean isClipBackground();
boolean isRoundAsCircle();
float getTopLeftRadius();
float getTopRightRadius();
float getBottomLeftRadius();
float getBottomRightRadius();
int getStrokeWidth();
int getStrokeColor();
}
package com.android.song.utils;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Checkable;
import com.android.song.R;
import com.android.song.utils.RCAttrs;
import java.util.ArrayList;
/**
* 作用:圆角辅助工具
*/
public class RCHelper {
public float[] radii = new float[8]; // top-left, top-right, bottom-right, bottom-left
public Path mClipPath; // 剪裁区域路径
public Paint mPaint; // 画笔
public boolean mRoundAsCircle = false; // 圆形
public int mDefaultStrokeColor; // 默认描边颜色
public int mStrokeColor; // 描边颜色
public ColorStateList mStrokeColorStateList;// 描边颜色的状态
public int mStrokeWidth; // 描边半径
public boolean mClipBackground; // 是否剪裁背景
public Region mAreaRegion; // 内容区域
public RectF mLayer; // 画布图层大小
public void initAttrs(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RCAttrs);
mRoundAsCircle = ta.getBoolean(R.styleable.RCAttrs_round_as_circle, false);
mStrokeColorStateList = ta.getColorStateList(R.styleable.RCAttrs_stroke_color);
if (null != mStrokeColorStateList) {
mStrokeColor = mStrokeColorStateList.getDefaultColor();
mDefaultStrokeColor = mStrokeColorStateList.getDefaultColor();
} else {
mStrokeColor = Color.WHITE;
mDefaultStrokeColor = Color.WHITE;
}
mStrokeWidth = ta.getDimensionPixelSize(R.styleable.RCAttrs_stroke_width, 0);
mClipBackground = ta.getBoolean(R.styleable.RCAttrs_clip_background, false);
int roundCorner = ta.getDimensionPixelSize(R.styleable.RCAttrs_round_corner, 0);
int roundCornerTopLeft = ta.getDimensionPixelSize(
R.styleable.RCAttrs_round_corner_top_left, roundCorner);
int roundCornerTopRight = ta.getDimensionPixelSize(
R.styleable.RCAttrs_round_corner_top_right, roundCorner);
int roundCornerBottomLeft = ta.getDimensionPixelSize(
R.styleable.RCAttrs_round_corner_bottom_left, roundCorner);
int roundCornerBottomRight = ta.getDimensionPixelSize(
R.styleable.RCAttrs_round_corner_bottom_right, roundCorner);
ta.recycle();
radii[0] = roundCornerTopLeft;
radii[1] = roundCornerTopLeft;
radii[2] = roundCornerTopRight;
radii[3] = roundCornerTopRight;
radii[4] = roundCornerBottomRight;
radii[5] = roundCornerBottomRight;
radii[6] = roundCornerBottomLeft;
radii[7] = roundCornerBottomLeft;
mLayer = new RectF();
mClipPath = new Path();
mAreaRegion = new Region();
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mPaint.setAntiAlias(true);
}
public void onSizeChanged(View view, int w, int h) {
mLayer.set(0, 0, w, h);
refreshRegion(view);
}
public void refreshRegion(View view) {
int w = (int) mLayer.width();
int h = (int) mLayer.height();
RectF areas = new RectF();
areas.left = view.getPaddingLeft();
areas.top = view.getPaddingTop();
areas.right = w - view.getPaddingRight();
areas.bottom = h - view.getPaddingBottom();
mClipPath.reset();
if (mRoundAsCircle) {
float d = areas.width() >= areas.height() ? areas.height() : areas.width();
float r = d / 2;
PointF center = new PointF(w / 2, h / 2);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
mClipPath.addCircle(center.x, center.y, r, Path.Direction.CW);
mClipPath.moveTo(0, 0); // 通过空操作让Path区域占满画布
mClipPath.moveTo(w, h);
} else {
float y = h / 2 - r;
mClipPath.moveTo(areas.left, y);
mClipPath.addCircle(center.x, y + r, r, Path.Direction.CW);
}
} else {
mClipPath.addRoundRect(areas, radii, Path.Direction.CW);
}
Region clip = new Region((int) areas.left, (int) areas.top,
(int) areas.right, (int) areas.bottom);
mAreaRegion.setPath(mClipPath, clip);
}
public void onClipDraw(Canvas canvas) {
if (mStrokeWidth > 0) {
// 支持半透明描边,将与描边区域重叠的内容裁剪掉
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(mStrokeWidth * 2);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(mClipPath, mPaint);
// 绘制描边
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
mPaint.setColor(mStrokeColor);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(mClipPath, mPaint);
}
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawPath(mClipPath, mPaint);
} else {
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
final Path path = new Path();
path.addRect(0, 0, (int) mLayer.width(), (int) mLayer.height(), Path.Direction.CW);
path.op(mClipPath, Path.Op.DIFFERENCE);
canvas.drawPath(path, mPaint);
}
}
//--- Selector 支持 ----------------------------------------------------------------------------
public boolean mChecked; // 是否是 check 状态
public OnCheckedChangeListener mOnCheckedChangeListener;
public void drawableStateChanged(View view) {
if (view instanceof RCAttrs) {
ArrayList<Integer> stateListArray = new ArrayList<>();
if (view instanceof Checkable) {
stateListArray.add(android.R.attr.state_checkable);
if (((Checkable) view).isChecked())
stateListArray.add(android.R.attr.state_checked);
}
if (view.isEnabled()) stateListArray.add(android.R.attr.state_enabled);
if (view.isFocused()) stateListArray.add(android.R.attr.state_focused);
if (view.isPressed()) stateListArray.add(android.R.attr.state_pressed);
if (view.isHovered()) stateListArray.add(android.R.attr.state_hovered);
if (view.isSelected()) stateListArray.add(android.R.attr.state_selected);
if (view.isActivated()) stateListArray.add(android.R.attr.state_activated);
if (view.hasWindowFocus()) stateListArray.add(android.R.attr.state_window_focused);
if (mStrokeColorStateList != null && mStrokeColorStateList.isStateful()) {
int[] stateList = new int[stateListArray.size()];
for (int i = 0; i < stateListArray.size(); i++) {
stateList[i] = stateListArray.get(i);
}
int stateColor = mStrokeColorStateList.getColorForState(stateList, mDefaultStrokeColor);
((RCAttrs) view).setStrokeColor(stateColor);
}
}
}
public interface OnCheckedChangeListener {
void onCheckedChanged(View view, boolean isChecked);
}
}
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)