package wangdaye.com.geometricweather.ui.widget.astro; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import androidx.annotation.ColorInt; import android.util.AttributeSet; import android.view.View; import wangdaye.com.geometricweather.utils.DisplayUtils; public class MoonPhaseView extends View { private Paint paint; private RectF foregroundRectF = new RectF(); private RectF backgroundRectF = new RectF(); private float surfaceAngle; // head of light surface, clockwise. @ColorInt private int lightColor; @ColorInt private int darkColor; @ColorInt private int strokeColor; private float LINE_WIDTH = 1; public MoonPhaseView(Context context) { this(context, null); } public MoonPhaseView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MoonPhaseView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initialize(); initPaint(); } private void initialize() { setColor(Color.WHITE, Color.BLACK, Color.GRAY); setSurfaceAngle(0); // from 0 -> phase : 🌑 (new) foregroundRectF = new RectF(); backgroundRectF = new RectF(); LINE_WIDTH = DisplayUtils.dpToPx(getContext(), (int) LINE_WIDTH); } private void initPaint() { paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeCap(Paint.Cap.ROUND); } public void setColor(@ColorInt int lightColor, @ColorInt int darkColor, @ColorInt int strokeColor) { this.lightColor = lightColor; this.darkColor = darkColor; this.strokeColor = strokeColor; } public void setSurfaceAngle(float surfaceAngle) { this.surfaceAngle = surfaceAngle; if (this.surfaceAngle >= 360) { this.surfaceAngle %= 360; } invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); int padding = (int) DisplayUtils.dpToPx(getContext(), 4); backgroundRectF.set( padding, padding, getMeasuredWidth() - padding, getMeasuredHeight() - padding ); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); paint.setStyle(Paint.Style.FILL); if (surfaceAngle == 0) { // 🌑 drawDarkCircle(canvas); } else if (surfaceAngle < 90) { // 🌒 drawLightCircle(canvas); paint.setColor(darkColor); canvas.drawArc(backgroundRectF, 90, 180, true, paint); float halfWidth = (float) ( backgroundRectF.width() / 2 * Math.cos(Math.toRadians(surfaceAngle)) ); foregroundRectF.set( backgroundRectF.centerX() - halfWidth, backgroundRectF.top, backgroundRectF.centerX() + halfWidth, backgroundRectF.bottom ); canvas.drawArc(foregroundRectF, 270, 180, true, paint); } else if (surfaceAngle == 90) { // 🌓 drawDarkCircle(canvas); paint.setColor(lightColor); canvas.drawArc(backgroundRectF, 270, 180, true, paint); } else if (surfaceAngle < 180) { // 🌔 drawDarkCircle(canvas); paint.setColor(lightColor); canvas.drawArc(backgroundRectF, 270, 180, true, paint); float halfWidth = (float) ( backgroundRectF.width() / 2 * Math.sin(Math.toRadians(surfaceAngle - 90)) ); foregroundRectF.set( backgroundRectF.centerX() - halfWidth, backgroundRectF.top, backgroundRectF.centerX() + halfWidth, backgroundRectF.bottom ); canvas.drawArc(foregroundRectF, 90, 180, true, paint); } else if (surfaceAngle == 180) { // 🌕 drawLightCircle(canvas); } else if (surfaceAngle < 270) { // 🌖 drawDarkCircle(canvas); paint.setColor(lightColor); canvas.drawArc(backgroundRectF, 90, 180, true, paint); float halfWidth = (float) ( backgroundRectF.width() / 2 * Math.cos(Math.toRadians(surfaceAngle - 180)) ); foregroundRectF.set( backgroundRectF.centerX() - halfWidth, backgroundRectF.top, backgroundRectF.centerX() + halfWidth, backgroundRectF.bottom ); canvas.drawArc(foregroundRectF, 270, 180, true, paint); } else if (surfaceAngle == 270) { // 🌗 drawDarkCircle(canvas); paint.setColor(lightColor); canvas.drawArc(backgroundRectF, 90, 180, true, paint); } else { // surface angle < 360. 🌘 drawLightCircle(canvas); paint.setColor(darkColor); canvas.drawArc(backgroundRectF, 270, 180, true, paint); float halfWidth = (float) ( backgroundRectF.width() / 2 * Math.cos(Math.toRadians(360 - surfaceAngle)) ); foregroundRectF.set( backgroundRectF.centerX() - halfWidth, backgroundRectF.top, backgroundRectF.centerX() + halfWidth, backgroundRectF.bottom ); canvas.drawArc(foregroundRectF, 90, 180, true, paint); } paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(LINE_WIDTH); if (surfaceAngle < 90 || 270 < surfaceAngle) { paint.setColor(darkColor); canvas.drawLine( backgroundRectF.centerX(), backgroundRectF.top, backgroundRectF.centerX(), backgroundRectF.bottom, paint ); } else if (90 < surfaceAngle && surfaceAngle < 270) { paint.setColor(lightColor); canvas.drawLine( backgroundRectF.centerX(), backgroundRectF.top, backgroundRectF.centerX(), backgroundRectF.bottom, paint ); } paint.setColor(strokeColor); canvas.drawCircle( backgroundRectF.centerX(), backgroundRectF.centerY(), backgroundRectF.width() / 2, paint ); } private void drawLightCircle(Canvas canvas) { paint.setColor(lightColor); canvas.drawCircle( backgroundRectF.centerX(), backgroundRectF.centerY(), backgroundRectF.width() / 2, paint ); } private void drawDarkCircle(Canvas canvas) { paint.setColor(darkColor); canvas.drawCircle( backgroundRectF.centerX(), backgroundRectF.centerY(), backgroundRectF.width() / 2, paint ); } }