// 头像信息类,记录大小、位置等信息
private static class DrawableInfo {
int mId = View.NO_ID;
Drawable mDrawable;
// 中心点位置
float mCenterX;
float mCenterY;
// 头像上缺口弧所在圆上的圆心位置,其实就是下一个相邻头像的中心点
float mGapCenterX;
float mGapCenterY;
boolean mHasGap;
// 头像边界
final RectF mBounds = new RectF();
// 圆形蒙板路径,把头像弄成圆形
final Path mMaskPath = new Path();
}
private void layoutDrawables() {
mSteinerCircleRadius = 0;
mOffsetY = 0;
int width = getWidth() - getPaddingLeft() - getPaddingRight();
int height = getHeight() - getPaddingTop() - getPaddingBottom();
mContentSize = Math.min(width, height);
final List<DrawableInfo> drawables = mDrawables;
final int N = drawables.size();
float center = mContentSize * .5f;
if (mContentSize > 0 && N > 0) {
// 图像圆的半径。
final float r;
if (N == 1) {
r = mContentSize * .5f;
} else if (N == 2) {
r = (float) (mContentSize / (2 + 2 * Math.sin(Math.PI / 4)));
} else if (N == 4) {
r = mContentSize / 4.f;
} else {
r = (float) (mContentSize / (2 * (2 * Math.sin(((N - 2) * Math.PI) / (2 * N)) + 1)));
final double sinN = Math.sin(Math.PI / N);
// 以所有图像圆为内切圆的圆的半径
final float R = (float) (r * ((sinN + 1) / sinN));
mOffsetY = (float) ((mContentSize - R - r * (1 + 1 / Math.tan(Math.PI / N))) / 2f);
}
// 初始化第一个头像的中心位置
final float startX, startY;
if (N % 2 == 0) {
startX = startY = r;
} else {
startX = center;
startY = r;
}
// 变换矩阵
final Matrix matrix = mLayoutMatrix;
// 坐标点临时数组
final float[] pointsTemp = this.mPointsTemp;
matrix.reset();
for (int i = 0; i < drawables.size(); i++) {
DrawableInfo drawable = drawables.get(i);
drawable.reset();
drawable.mHasGap = i > 0;
// 缺口弧的中心
if (drawable.mHasGap) {
drawable.mGapCenterX = pointsTemp[0];
drawable.mGapCenterY = pointsTemp[1];
}
pointsTemp[0] = startX;
pointsTemp[1] = startY;
if (i > 0) {
// 以上一个圆的圆心旋转计算得出当前圆的圆位置
matrix.postRotate(360.f / N, center, center + mOffsetY);
matrix.mapPoints(pointsTemp);
}
// 取出中心点位置
drawable.mCenterX = pointsTemp[0];
drawable.mCenterY = pointsTemp[1];
// 设置边界
drawable.mBounds.inset(-r, -r);
drawable.mBounds.offset(drawable.mCenterX, drawable.mCenterY);
// 设置“蒙板”路径
drawable.mMaskPath.addCircle(drawable.mCenterX, drawable.mCenterY, r, Path.Direction.CW);
drawable.mMaskPath.setFillType(Path.FillType.INVERSE_WINDING);
}
// 设置第一个头像的缺口,头像数量少于3个的时候没有
if (N > 2) {
DrawableInfo first = drawables.get(0);
DrawableInfo last = drawables.get(N - 1);
first.mHasGap = true;
first.mGapCenterX = last.mCenterX;
first.mGapCenterY = last.mCenterY;
}
mSteinerCircleRadius = r;
}
invalidate();
}
drawable.mMaskPath.addCircle(drawable.mCenterX, drawable.mCenterY, r, Path.Direction.CW); drawable.mMaskPath.setFillType(Path.FillType.INVERSE_WINDING);
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
...
canvas.translate(0, mOffsetY);
final Paint paint = mPaint;
final float gapRadius = mSteinerCircleRadius * (mGap + 1f);
for (int i = 0; i < drawables.size(); i++) {
DrawableInfo drawable = drawables.get(i);
RectF bounds = drawable.mBounds;
final int savedLayer = canvas.saveLayer(0, 0, mContentSize, mContentSize, null, Canvas.ALL_SAVE_FLAG);
// 设置Drawable的边界
drawable.mDrawable.setBounds((int) bounds.left, (int) bounds.top,
Math.round(bounds.right), Math.round(bounds.bottom));
// 绘制Drawable
drawable.mDrawable.draw(canvas);
// 绘制“蒙板”路径,将Drawable绘制的图像“剪”成圆形
canvas.drawPath(drawable.mMaskPath, paint);
// “剪”出弧形的缺口
if (drawable.mHasGap && mGap > 0f) {
canvas.drawCircle(drawable.mGapCenterX, drawable.mGapCenterY, gapRadius, paint);
}
canvas.restoreToCount(savedLayer);
}
}
public interface Callback {
/**
* 当drawable需要重新绘制时调用。此时的view应该使其自身失效(至少drawable展示部分失效)
* @param who 要求重新绘制的drawable
*/
void invalidateDrawable(@NonNull Drawable who);
/**
* drawable可以通过调用该方法来安排动画的下一帧。
* @param who 要预定的drawable
* @param what 要执行的动作
* @param when 执行的时间(以毫秒为单位),基于android.os.SystemClock.uptimeMillis()
*/
void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when);
/**
* drawable可以通过调用该方法来取消先前通过scheduleDrawable(Drawable, Runnable, long)调度的动作。
* @param who 要取消预定的drawable
* @param what 要取消执行的动作
*/
void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what);
}
protected boolean verifyDrawable(@NonNull Drawable who) {
// ...
return who == mBackground || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
}
private boolean hasSameDrawable(Drawable drawable) {
for (DrawableInfo d : mDrawables) {
if (d.mDrawable == drawable) {
return true;
}
}
return false;
}
@Override
protected boolean verifyDrawable(@NonNull Drawable drawable) {
return hasSameDrawable(drawable) || super.verifyDrawable(drawable);
}
// 状态改变时被调用
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
boolean invalidate = false;
for (DrawableInfo drawable : mDrawables) {
Drawable d = drawable.mDrawable;
// 判断Drawable是否支持状态并更新状态
if (d.isStateful() && d.setState(getDrawableState())) {
invalidate = true;
}
}
if (invalidate) {
invalidate();
}
}
// 这个方法主要针对状态改变时有过渡动画的Drawable
@Override
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
for (DrawableInfo drawable : mDrawables) {
drawable.mDrawable.jumpToCurrentState();
}
}
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有