1:修改K歌房

2:修改房间展示每日任务
3:修改页面跳转
4:遗留问题在进入首页的时候出现首页刷新
This commit is contained in:
2025-10-27 20:09:12 +08:00
parent 8631fdbdbf
commit a4032c76ad
106 changed files with 6593 additions and 396 deletions

View File

@@ -0,0 +1,25 @@
plugins {
alias(libs.plugins.android.library)
}
android {
compileSdk 35
namespace 'app.dinus.com.loadingdrawable'
defaultConfig {
minSdkVersion 24
targetSdkVersion 35
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation 'androidx.core:core:1.5.0' // or later
implementation 'androidx.annotation:annotation:1.6.0'
implementation libs.androidx.interpolator
}

17
Loadinglibrary/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/dinus/workspace/android-sdk-macosx/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.dinus.com.loadingdrawable">
<application android:allowBackup="true" android:label="@string/app_name">
</application>
</manifest>

View File

@@ -0,0 +1,11 @@
package app.dinus.com.loadingdrawable;
import android.content.Context;
public class DensityUtil {
public static float dip2px(Context context, float dpValue) {
float scale = context.getResources().getDisplayMetrics().density;
return dpValue * scale;
}
}

View File

@@ -0,0 +1,77 @@
package app.dinus.com.loadingdrawable;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import app.dinus.com.loadingdrawable.render.LoadingDrawable;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
import app.dinus.com.loadingdrawable.render.LoadingRendererFactory;
public class LoadingView extends ImageView {
private LoadingDrawable mLoadingDrawable;
public LoadingView(Context context) {
super(context);
}
public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
}
private void initAttrs(Context context, AttributeSet attrs) {
try {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LoadingView);
int loadingRendererId = ta.getInt(R.styleable.LoadingView_loading_renderer, 0);
LoadingRenderer loadingRenderer = LoadingRendererFactory.createLoadingRenderer(context, loadingRendererId);
setLoadingRenderer(loadingRenderer);
ta.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
public void setLoadingRenderer(LoadingRenderer loadingRenderer) {
mLoadingDrawable = new LoadingDrawable(loadingRenderer);
setImageDrawable(mLoadingDrawable);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
startAnimation();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopAnimation();
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
if (visible) {
startAnimation();
} else {
stopAnimation();
}
}
private void startAnimation() {
if (mLoadingDrawable != null) {
mLoadingDrawable.start();
}
}
private void stopAnimation() {
if (mLoadingDrawable != null) {
mLoadingDrawable.stop();
}
}
}

View File

@@ -0,0 +1,89 @@
package app.dinus.com.loadingdrawable.render;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class LoadingDrawable extends Drawable implements Animatable {
private final LoadingRenderer mLoadingRender;
private final Callback mCallback = new Callback() {
@Override
public void invalidateDrawable(Drawable d) {
invalidateSelf();
}
@Override
public void scheduleDrawable(Drawable d, Runnable what, long when) {
scheduleSelf(what, when);
}
@Override
public void unscheduleDrawable(Drawable d, Runnable what) {
unscheduleSelf(what);
}
};
public LoadingDrawable(LoadingRenderer loadingRender) {
this.mLoadingRender = loadingRender;
this.mLoadingRender.setCallback(mCallback);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
this.mLoadingRender.setBounds(bounds);
}
@Override
public void draw(Canvas canvas) {
if (!getBounds().isEmpty()) {
this.mLoadingRender.draw(canvas);
}
}
@Override
public void setAlpha(int alpha) {
this.mLoadingRender.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
this.mLoadingRender.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void start() {
this.mLoadingRender.start();
}
@Override
public void stop() {
this.mLoadingRender.stop();
}
@Override
public boolean isRunning() {
return this.mLoadingRender.isRunning();
}
@Override
public int getIntrinsicHeight() {
return (int) this.mLoadingRender.mHeight;
}
@Override
public int getIntrinsicWidth() {
return (int) this.mLoadingRender.mWidth;
}
}

View File

@@ -0,0 +1,124 @@
package app.dinus.com.loadingdrawable.render;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
public abstract class LoadingRenderer {
private static final long ANIMATION_DURATION = 1333;
private static final float DEFAULT_SIZE = 56.0f;
private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener
= new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
computeRender((float) animation.getAnimatedValue());
invalidateSelf();
}
};
/**
* Whenever {@link LoadingDrawable} boundary changes mBounds will be updated.
* More details you can see {@link LoadingDrawable#onBoundsChange(Rect)}
*/
protected final Rect mBounds = new Rect();
private Drawable.Callback mCallback;
private ValueAnimator mRenderAnimator;
protected long mDuration;
protected float mWidth;
protected float mHeight;
public LoadingRenderer(Context context) {
initParams(context);
setupAnimators();
}
@Deprecated
protected void draw(Canvas canvas, Rect bounds) {
}
protected void draw(Canvas canvas) {
draw(canvas, mBounds);
}
protected abstract void computeRender(float renderProgress);
protected abstract void setAlpha(int alpha);
protected abstract void setColorFilter(ColorFilter cf);
protected abstract void reset();
protected void addRenderListener(Animator.AnimatorListener animatorListener) {
mRenderAnimator.addListener(animatorListener);
}
void start() {
reset();
mRenderAnimator.addUpdateListener(mAnimatorUpdateListener);
mRenderAnimator.setRepeatCount(ValueAnimator.INFINITE);
mRenderAnimator.setDuration(mDuration);
mRenderAnimator.start();
}
void stop() {
// if I just call mRenderAnimator.end(),
// it will always call the method onAnimationUpdate(ValueAnimator animation)
// why ? if you know why please send email to me (dinus_developer@163.com)
mRenderAnimator.removeUpdateListener(mAnimatorUpdateListener);
mRenderAnimator.setRepeatCount(0);
mRenderAnimator.setDuration(0);
mRenderAnimator.end();
}
boolean isRunning() {
return mRenderAnimator.isRunning();
}
void setCallback(Drawable.Callback callback) {
this.mCallback = callback;
}
void setBounds(Rect bounds) {
mBounds.set(bounds);
}
private void initParams(Context context) {
mWidth = DensityUtil.dip2px(context, DEFAULT_SIZE);
mHeight = DensityUtil.dip2px(context, DEFAULT_SIZE);
mDuration = ANIMATION_DURATION;
}
private void setupAnimators() {
mRenderAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
mRenderAnimator.setRepeatCount(Animation.INFINITE);
mRenderAnimator.setRepeatMode(Animation.RESTART);
mRenderAnimator.setDuration(mDuration);
//fuck you! the default interpolator is AccelerateDecelerateInterpolator
mRenderAnimator.setInterpolator(new LinearInterpolator());
mRenderAnimator.addUpdateListener(mAnimatorUpdateListener);
}
private void invalidateSelf() {
mCallback.invalidateDrawable(null);
}
}

View File

@@ -0,0 +1,71 @@
package app.dinus.com.loadingdrawable.render;
import android.content.Context;
import android.util.SparseArray;
import java.lang.reflect.Constructor;
import app.dinus.com.loadingdrawable.render.animal.FishLoadingRenderer;
import app.dinus.com.loadingdrawable.render.animal.GhostsEyeLoadingRenderer;
import app.dinus.com.loadingdrawable.render.circle.jump.CollisionLoadingRenderer;
import app.dinus.com.loadingdrawable.render.circle.jump.DanceLoadingRenderer;
import app.dinus.com.loadingdrawable.render.circle.jump.GuardLoadingRenderer;
import app.dinus.com.loadingdrawable.render.circle.jump.SwapLoadingRenderer;
import app.dinus.com.loadingdrawable.render.circle.rotate.GearLoadingRenderer;
import app.dinus.com.loadingdrawable.render.circle.rotate.LevelLoadingRenderer;
import app.dinus.com.loadingdrawable.render.circle.rotate.MaterialLoadingRenderer;
import app.dinus.com.loadingdrawable.render.circle.rotate.WhorlLoadingRenderer;
import app.dinus.com.loadingdrawable.render.goods.BalloonLoadingRenderer;
import app.dinus.com.loadingdrawable.render.goods.WaterBottleLoadingRenderer;
import app.dinus.com.loadingdrawable.render.scenery.DayNightLoadingRenderer;
import app.dinus.com.loadingdrawable.render.scenery.ElectricFanLoadingRenderer;
import app.dinus.com.loadingdrawable.render.shapechange.CircleBroodLoadingRenderer;
import app.dinus.com.loadingdrawable.render.shapechange.CoolWaitLoadingRenderer;
public final class LoadingRendererFactory {
private static final SparseArray<Class<? extends LoadingRenderer>> LOADING_RENDERERS = new SparseArray<>();
static {
//circle rotate
LOADING_RENDERERS.put(0, MaterialLoadingRenderer.class);
LOADING_RENDERERS.put(1, LevelLoadingRenderer.class);
LOADING_RENDERERS.put(2, WhorlLoadingRenderer.class);
LOADING_RENDERERS.put(3, GearLoadingRenderer.class);
//circle jump
LOADING_RENDERERS.put(4, SwapLoadingRenderer.class);
LOADING_RENDERERS.put(5, GuardLoadingRenderer.class);
LOADING_RENDERERS.put(6, DanceLoadingRenderer.class);
LOADING_RENDERERS.put(7, CollisionLoadingRenderer.class);
//scenery
LOADING_RENDERERS.put(8, DayNightLoadingRenderer.class);
LOADING_RENDERERS.put(9, ElectricFanLoadingRenderer.class);
//animal
LOADING_RENDERERS.put(10, FishLoadingRenderer.class);
LOADING_RENDERERS.put(11, GhostsEyeLoadingRenderer.class);
//goods
LOADING_RENDERERS.put(12, BalloonLoadingRenderer.class);
LOADING_RENDERERS.put(13, WaterBottleLoadingRenderer.class);
//shape change
LOADING_RENDERERS.put(14, CircleBroodLoadingRenderer.class);
LOADING_RENDERERS.put(15, CoolWaitLoadingRenderer.class);
}
private LoadingRendererFactory() {
}
public static LoadingRenderer createLoadingRenderer(Context context, int loadingRendererId) throws Exception {
Class<?> loadingRendererClazz = LOADING_RENDERERS.get(loadingRendererId);
Constructor<?>[] constructors = loadingRendererClazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes != null
&& parameterTypes.length == 1
&& parameterTypes[0].equals(Context.class)) {
constructor.setAccessible(true);
return (LoadingRenderer) constructor.newInstance(context);
}
}
throw new InstantiationException();
}
}

View File

@@ -0,0 +1,262 @@
package app.dinus.com.loadingdrawable.render.animal;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.DisplayMetrics;
import android.view.animation.Interpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class FishLoadingRenderer extends LoadingRenderer {
private Interpolator FISH_INTERPOLATOR = new FishInterpolator();
private static final float DEFAULT_PATH_FULL_LINE_SIZE = 7.0f;
private static final float DEFAULT_PATH_DOTTED_LINE_SIZE = DEFAULT_PATH_FULL_LINE_SIZE / 2.0f;
private static final float DEFAULT_RIVER_HEIGHT = DEFAULT_PATH_FULL_LINE_SIZE * 8.5f;
private static final float DEFAULT_RIVER_WIDTH = DEFAULT_PATH_FULL_LINE_SIZE * 5.5f;
private static final float DEFAULT_FISH_EYE_SIZE = DEFAULT_PATH_FULL_LINE_SIZE * 0.5f;
private static final float DEFAULT_FISH_WIDTH = DEFAULT_PATH_FULL_LINE_SIZE * 3.0f;
private static final float DEFAULT_FISH_HEIGHT = DEFAULT_PATH_FULL_LINE_SIZE * 4.5f;
private static final float DEFAULT_WIDTH = 200.0f;
private static final float DEFAULT_HEIGHT = 150.0f;
private static final float DEFAULT_RIVER_BANK_WIDTH = DEFAULT_PATH_FULL_LINE_SIZE;
private static final long ANIMATION_DURATION = 800;
private static final float DOTTED_LINE_WIDTH_COUNT = (8.5f + 5.5f - 2.0f) * 2.0f * 2.0f;
private static final float DOTTED_LINE_WIDTH_RATE = 1.0f / DOTTED_LINE_WIDTH_COUNT;
private final float[] FISH_MOVE_POINTS = new float[]{
DOTTED_LINE_WIDTH_RATE * 3.0f, DOTTED_LINE_WIDTH_RATE * 6.0f,
DOTTED_LINE_WIDTH_RATE * 15f, DOTTED_LINE_WIDTH_RATE * 18f,
DOTTED_LINE_WIDTH_RATE * 27.0f, DOTTED_LINE_WIDTH_RATE * 30.0f,
DOTTED_LINE_WIDTH_RATE * 39f, DOTTED_LINE_WIDTH_RATE * 42f,
};
private final float FISH_MOVE_POINTS_RATE = 1.0f / FISH_MOVE_POINTS.length;
private static final int DEFAULT_COLOR = Color.parseColor("#fffefed6");
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final float[] mFishHeadPos = new float[2];
private Path mRiverPath;
private PathMeasure mRiverMeasure;
private float mFishRotateDegrees;
private float mRiverBankWidth;
private float mRiverWidth;
private float mRiverHeight;
private float mFishWidth;
private float mFishHeight;
private float mFishEyeSize;
private float mPathFullLineSize;
private float mPathDottedLineSize;
private int mColor;
private FishLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
}
private void init(Context context) {
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mRiverBankWidth = DensityUtil.dip2px(context, DEFAULT_RIVER_BANK_WIDTH);
mPathFullLineSize = DensityUtil.dip2px(context, DEFAULT_PATH_FULL_LINE_SIZE);
mPathDottedLineSize = DensityUtil.dip2px(context, DEFAULT_PATH_DOTTED_LINE_SIZE);
mFishWidth = DensityUtil.dip2px(context, DEFAULT_FISH_WIDTH);
mFishHeight = DensityUtil.dip2px(context, DEFAULT_FISH_HEIGHT);
mFishEyeSize = DensityUtil.dip2px(context, DEFAULT_FISH_EYE_SIZE);
mRiverWidth = DensityUtil.dip2px(context, DEFAULT_RIVER_WIDTH);
mRiverHeight = DensityUtil.dip2px(context, DEFAULT_RIVER_HEIGHT);
mColor = DEFAULT_COLOR;
mDuration = ANIMATION_DURATION;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mRiverBankWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setPathEffect(new DashPathEffect(new float[]{mPathFullLineSize, mPathDottedLineSize}, mPathDottedLineSize));
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
RectF arcBounds = mTempBounds;
arcBounds.set(bounds);
mPaint.setColor(mColor);
//calculate fish clip bounds
//clip the width of the fish need to increase mPathDottedLineSize * 1.2f
RectF fishRectF = new RectF(mFishHeadPos[0] - mFishWidth / 2.0f - mPathDottedLineSize * 1.2f, mFishHeadPos[1] - mFishHeight / 2.0f,
mFishHeadPos[0] + mFishWidth / 2.0f + mPathDottedLineSize * 1.2f, mFishHeadPos[1] + mFishHeight / 2.0f);
Matrix matrix = new Matrix();
matrix.postRotate(mFishRotateDegrees, fishRectF.centerX(), fishRectF.centerY());
matrix.mapRect(fishRectF);
//draw river
int riverSaveCount = canvas.save();
mPaint.setStyle(Paint.Style.STROKE);
canvas.clipRect(fishRectF, Region.Op.DIFFERENCE);
canvas.drawPath(createRiverPath(arcBounds), mPaint);
canvas.restoreToCount(riverSaveCount);
//draw fish
int fishSaveCount = canvas.save();
mPaint.setStyle(Paint.Style.FILL);
canvas.rotate(mFishRotateDegrees, mFishHeadPos[0], mFishHeadPos[1]);
canvas.clipPath(createFishEyePath(mFishHeadPos[0], mFishHeadPos[1] - mFishHeight * 0.06f), Region.Op.DIFFERENCE);
canvas.drawPath(createFishPath(mFishHeadPos[0], mFishHeadPos[1]), mPaint);
canvas.restoreToCount(fishSaveCount);
canvas.restoreToCount(saveCount);
}
private float calculateRotateDegrees(float fishProgress) {
if (fishProgress < FISH_MOVE_POINTS_RATE * 2) {
return 90;
}
if (fishProgress < FISH_MOVE_POINTS_RATE * 4) {
return 180;
}
if (fishProgress < FISH_MOVE_POINTS_RATE * 6) {
return 270;
}
return 0.0f;
}
@Override
protected void computeRender(float renderProgress) {
if (mRiverPath == null) {
return;
}
if (mRiverMeasure == null) {
mRiverMeasure = new PathMeasure(mRiverPath, false);
}
float fishProgress = FISH_INTERPOLATOR.getInterpolation(renderProgress);
mRiverMeasure.getPosTan(mRiverMeasure.getLength() * fishProgress, mFishHeadPos, null);
mFishRotateDegrees = calculateRotateDegrees(fishProgress);
}
@Override
protected void setAlpha(int alpha) {
}
@Override
protected void setColorFilter(ColorFilter cf) {
}
@Override
protected void reset() {
}
private Path createFishEyePath(float fishEyeCenterX, float fishEyeCenterY) {
Path path = new Path();
path.addCircle(fishEyeCenterX, fishEyeCenterY, mFishEyeSize, Path.Direction.CW);
return path;
}
private Path createFishPath(float fishCenterX, float fishCenterY) {
Path path = new Path();
float fishHeadX = fishCenterX;
float fishHeadY = fishCenterY - mFishHeight / 2.0f;
//the head of the fish
path.moveTo(fishHeadX, fishHeadY);
//the left body of the fish
path.quadTo(fishHeadX - mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.222f, fishHeadX - mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.444f);
path.lineTo(fishHeadX - mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.666f);
path.lineTo(fishHeadX - mFishWidth * 0.5f, fishHeadY + mFishHeight * 0.8f);
path.lineTo(fishHeadX - mFishWidth * 0.5f, fishHeadY + mFishHeight);
//the tail of the fish
path.lineTo(fishHeadX, fishHeadY + mFishHeight * 0.9f);
//the right body of the fish
path.lineTo(fishHeadX + mFishWidth * 0.5f, fishHeadY + mFishHeight);
path.lineTo(fishHeadX + mFishWidth * 0.5f, fishHeadY + mFishHeight * 0.8f);
path.lineTo(fishHeadX + mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.666f);
path.lineTo(fishHeadX + mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.444f);
path.quadTo(fishHeadX + mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.222f, fishHeadX, fishHeadY);
path.close();
return path;
}
private Path createRiverPath(RectF arcBounds) {
if (mRiverPath != null) {
return mRiverPath;
}
mRiverPath = new Path();
RectF rectF = new RectF(arcBounds.centerX() - mRiverWidth / 2.0f, arcBounds.centerY() - mRiverHeight / 2.0f,
arcBounds.centerX() + mRiverWidth / 2.0f, arcBounds.centerY() + mRiverHeight / 2.0f);
rectF.inset(mRiverBankWidth / 2.0f, mRiverBankWidth / 2.0f);
mRiverPath.addRect(rectF, Path.Direction.CW);
return mRiverPath;
}
private class FishInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
int index = ((int) (input / FISH_MOVE_POINTS_RATE));
if (index >= FISH_MOVE_POINTS.length) {
index = FISH_MOVE_POINTS.length - 1;
}
return FISH_MOVE_POINTS[index];
}
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public FishLoadingRenderer build() {
FishLoadingRenderer loadingRenderer = new FishLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,261 @@
package app.dinus.com.loadingdrawable.render.animal;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.view.animation.Interpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class GhostsEyeLoadingRenderer extends LoadingRenderer {
private Interpolator EYE_BALL_INTERPOLATOR = new EyeBallInterpolator();
private Interpolator EYE_CIRCLE_INTERPOLATOR = new EyeCircleInterpolator();
private static final float DEFAULT_WIDTH = 200.0f;
private static final float DEFAULT_HEIGHT = 176.0f;
private static final float DEFAULT_EYE_EDGE_WIDTH = 5.0f;
private static final float DEFAULT_EYE_BALL_HEIGHT = 9.0f;
private static final float DEFAULT_EYE_BALL_WIDTH = 11.0f;
private static final float DEFAULT_EYE_CIRCLE_INTERVAL = 8.0f;
private static final float DEFAULT_EYE_BALL_OFFSET_Y = 2.0f;
private static final float DEFAULT_ABOVE_RADIAN_EYE_CIRCLE_OFFSET = 6.0f;
private static final float DEFAULT_EYE_CIRCLE_RADIUS = 21.0f;
private static final float DEFAULT_MAX_EYE_JUMP_DISTANCE = 11.0f;
private static final float LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET = 0.0f;
private static final float RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET = 0.067f;
private static final float LEFT_EYE_BALL_END_JUMP_OFFSET = 0.4f;
private static final float LEFT_EYE_CIRCLE_END_JUMP_OFFSET = 0.533f;
private static final float RIGHT_EYE_BALL_END_JUMP_OFFSET = 0.467f;
private static final float RIGHT_EYE_CIRCLE_END_JUMP_OFFSET = 0.60f;
private static final int DEGREE_180 = 180;
private static final long ANIMATION_DURATION = 2333;
private static final int DEFAULT_COLOR = Color.parseColor("#ff484852");
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private float mEyeInterval;
private float mEyeCircleRadius;
private float mMaxEyeJumptDistance;
private float mAboveRadianEyeOffsetX;
private float mEyeBallOffsetY;
private float mEyeEdgeWidth;
private float mEyeBallWidth;
private float mEyeBallHeight;
private float mLeftEyeCircleOffsetY;
private float mRightEyeCircleOffsetY;
private float mLeftEyeBallOffsetY;
private float mRightEyeBallOffsetY;
private int mColor;
private GhostsEyeLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
}
private void init(Context context) {
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mEyeEdgeWidth = DensityUtil.dip2px(context, DEFAULT_EYE_EDGE_WIDTH);
mEyeInterval = DensityUtil.dip2px(context, DEFAULT_EYE_CIRCLE_INTERVAL);
mEyeBallOffsetY = DensityUtil.dip2px(context, DEFAULT_EYE_BALL_OFFSET_Y);
mEyeCircleRadius = DensityUtil.dip2px(context, DEFAULT_EYE_CIRCLE_RADIUS);
mMaxEyeJumptDistance = DensityUtil.dip2px(context, DEFAULT_MAX_EYE_JUMP_DISTANCE);
mAboveRadianEyeOffsetX = DensityUtil.dip2px(context, DEFAULT_ABOVE_RADIAN_EYE_CIRCLE_OFFSET);
mEyeBallWidth = DensityUtil.dip2px(context, DEFAULT_EYE_BALL_WIDTH);
mEyeBallHeight = DensityUtil.dip2px(context, DEFAULT_EYE_BALL_HEIGHT);
mColor = DEFAULT_COLOR;
mDuration = ANIMATION_DURATION;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mEyeEdgeWidth);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
RectF arcBounds = mTempBounds;
arcBounds.set(bounds);
mPaint.setColor(mColor);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(createLeftEyeCircle(arcBounds, mLeftEyeCircleOffsetY), mPaint);
canvas.drawPath(createRightEyeCircle(arcBounds, mRightEyeCircleOffsetY), mPaint);
mPaint.setStyle(Paint.Style.FILL);
//create left eye ball
canvas.drawOval(createLeftEyeBall(arcBounds, mLeftEyeBallOffsetY), mPaint);
//create right eye ball
canvas.drawOval(createRightEyeBall(arcBounds, mRightEyeBallOffsetY), mPaint);
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
if (renderProgress <= LEFT_EYE_BALL_END_JUMP_OFFSET && renderProgress >= LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) {
float eyeCircle$BallJumpUpProgress = (renderProgress - LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) / (LEFT_EYE_BALL_END_JUMP_OFFSET - LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET);
mLeftEyeBallOffsetY = -mMaxEyeJumptDistance * EYE_BALL_INTERPOLATOR.getInterpolation(eyeCircle$BallJumpUpProgress);
}
if (renderProgress <= LEFT_EYE_CIRCLE_END_JUMP_OFFSET && renderProgress >= LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) {
float eyeCircle$BallJumpUpProgress = (renderProgress - LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) / (LEFT_EYE_CIRCLE_END_JUMP_OFFSET - LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET);
mLeftEyeCircleOffsetY = -mMaxEyeJumptDistance * EYE_CIRCLE_INTERPOLATOR.getInterpolation(eyeCircle$BallJumpUpProgress);
}
if (renderProgress <= RIGHT_EYE_BALL_END_JUMP_OFFSET && renderProgress >= RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) {
float eyeCircle$BallJumpUpProgress = (renderProgress - RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) / (RIGHT_EYE_BALL_END_JUMP_OFFSET - RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET);
mRightEyeBallOffsetY = -mMaxEyeJumptDistance * EYE_BALL_INTERPOLATOR.getInterpolation(eyeCircle$BallJumpUpProgress);
}
if (renderProgress <= RIGHT_EYE_CIRCLE_END_JUMP_OFFSET && renderProgress >= RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) {
float eyeCircle$BallJumpUpProgress = (renderProgress - RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) / (RIGHT_EYE_CIRCLE_END_JUMP_OFFSET - RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET);
mRightEyeCircleOffsetY = -mMaxEyeJumptDistance * EYE_CIRCLE_INTERPOLATOR.getInterpolation(eyeCircle$BallJumpUpProgress);
}
}
@Override
protected void setAlpha(int alpha) {
}
@Override
protected void setColorFilter(ColorFilter cf) {
}
@Override
protected void reset() {
mLeftEyeBallOffsetY = 0.0f;
mRightEyeBallOffsetY = 0.0f;
mLeftEyeCircleOffsetY = 0.0f;
mRightEyeCircleOffsetY = 0.0f;
}
private RectF createLeftEyeBall(RectF arcBounds, float offsetY) {
//the center of the left eye
float leftEyeCenterX = arcBounds.centerX() - mEyeInterval / 2.0f - mEyeCircleRadius;
float leftEyeCenterY = arcBounds.centerY() - mEyeBallOffsetY + offsetY;
RectF rectF = new RectF(leftEyeCenterX - mEyeBallWidth / 2.0f, leftEyeCenterY - mEyeBallHeight / 2.0f,
leftEyeCenterX + mEyeBallWidth / 2.0f, leftEyeCenterY + mEyeBallHeight / 2.0f);
return rectF;
}
private RectF createRightEyeBall(RectF arcBounds, float offsetY) {
//the center of the right eye
float rightEyeCenterX = arcBounds.centerX() + mEyeInterval / 2.0f + mEyeCircleRadius;
float rightEyeCenterY = arcBounds.centerY() - mEyeBallOffsetY + offsetY;
RectF rectF = new RectF(rightEyeCenterX - mEyeBallWidth / 2.0f, rightEyeCenterY - mEyeBallHeight / 2.0f,
rightEyeCenterX + mEyeBallWidth / 2.0f, rightEyeCenterY + mEyeBallHeight / 2.0f);
return rectF;
}
private Path createLeftEyeCircle(RectF arcBounds, float offsetY) {
Path path = new Path();
//the center of the left eye
float leftEyeCenterX = arcBounds.centerX() - mEyeInterval / 2.0f - mEyeCircleRadius;
float leftEyeCenterY = arcBounds.centerY() + offsetY;
//the bounds of left eye
RectF leftEyeBounds = new RectF(leftEyeCenterX - mEyeCircleRadius, leftEyeCenterY - mEyeCircleRadius,
leftEyeCenterX + mEyeCircleRadius, leftEyeCenterY + mEyeCircleRadius);
path.addArc(leftEyeBounds, 0, DEGREE_180 + 15);
//the above radian of of the eye
path.quadTo(leftEyeBounds.left + mAboveRadianEyeOffsetX, leftEyeBounds.top + mEyeCircleRadius * 0.2f,
leftEyeBounds.left + mAboveRadianEyeOffsetX / 4.0f, leftEyeBounds.top - mEyeCircleRadius * 0.15f);
return path;
}
private Path createRightEyeCircle(RectF arcBounds, float offsetY) {
Path path = new Path();
//the center of the right eye
float rightEyeCenterX = arcBounds.centerX() + mEyeInterval / 2.0f + mEyeCircleRadius;
float rightEyeCenterY = arcBounds.centerY() + offsetY;
//the bounds of left eye
RectF leftEyeBounds = new RectF(rightEyeCenterX - mEyeCircleRadius, rightEyeCenterY - mEyeCircleRadius,
rightEyeCenterX + mEyeCircleRadius, rightEyeCenterY + mEyeCircleRadius);
path.addArc(leftEyeBounds, 180, -(DEGREE_180 + 15));
//the above radian of of the eye
path.quadTo(leftEyeBounds.right - mAboveRadianEyeOffsetX, leftEyeBounds.top + mEyeCircleRadius * 0.2f,
leftEyeBounds.right - mAboveRadianEyeOffsetX / 4.0f, leftEyeBounds.top - mEyeCircleRadius * 0.15f);
return path;
}
private class EyeCircleInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
if (input < 0.25f) {
return input * 4.0f;
} else if (input < 0.5f) {
return 1.0f - (input - 0.25f) * 4.0f;
} else if (input < 0.75f) {
return (input - 0.5f) * 2.0f;
} else {
return 0.5f - (input - 0.75f) * 2.0f;
}
}
}
private class EyeBallInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
if (input < 0.333333f) {
return input * 3.0f;
} else {
return 1.0f - (input - 0.333333f) * 1.5f;
}
}
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public GhostsEyeLoadingRenderer build() {
GhostsEyeLoadingRenderer loadingRenderer = new GhostsEyeLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,303 @@
package app.dinus.com.loadingdrawable.render.circle.jump;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import androidx.annotation.Size;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class CollisionLoadingRenderer extends LoadingRenderer {
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final int MAX_ALPHA = 255;
private static final int OVAL_ALPHA = 64;
private static final int DEFAULT_BALL_COUNT = 7;
private static final float DEFAULT_OVAL_HEIGHT = 1.5f;
private static final float DEFAULT_BALL_RADIUS = 7.5f;
private static final float DEFAULT_WIDTH = 15.0f * 11;
private static final float DEFAULT_HEIGHT = 15.0f * 4;
private static final float START_LEFT_DURATION_OFFSET = 0.25f;
private static final float START_RIGHT_DURATION_OFFSET = 0.5f;
private static final float END_RIGHT_DURATION_OFFSET = 0.75f;
private static final float END_LEFT_DURATION_OFFSET = 1.0f;
private static final int[] DEFAULT_COLORS = new int[]{
Color.parseColor("#FF28435D"), Color.parseColor("#FFC32720")
};
private static final float[] DEFAULT_POSITIONS = new float[]{
0.0f, 1.0f
};
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final RectF mOvalRect = new RectF();
@Size(2)
private int[] mColors;
private float[] mPositions;
private float mOvalVerticalRadius;
private float mBallRadius;
private float mBallCenterY;
private float mBallSideOffsets;
private float mBallMoveXOffsets;
private float mBallQuadCoefficient;
private float mLeftBallMoveXOffsets;
private float mLeftBallMoveYOffsets;
private float mRightBallMoveXOffsets;
private float mRightBallMoveYOffsets;
private float mLeftOvalShapeRate;
private float mRightOvalShapeRate;
private int mBallCount;
private CollisionLoadingRenderer(Context context) {
super(context);
init(context);
adjustParams();
setupPaint();
}
private void init(Context context) {
mBallRadius = DensityUtil.dip2px(context, DEFAULT_BALL_RADIUS);
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mOvalVerticalRadius = DensityUtil.dip2px(context, DEFAULT_OVAL_HEIGHT);
mColors = DEFAULT_COLORS;
mPositions = DEFAULT_POSITIONS;
mBallCount = DEFAULT_BALL_COUNT;
//mBallMoveYOffsets = mBallQuadCoefficient * mBallMoveXOffsets ^ 2
// ==> if mBallMoveYOffsets == mBallMoveXOffsets
// ==> mBallQuadCoefficient = 1.0f / mBallMoveXOffsets;
mBallMoveXOffsets = 1.5f * (2 * mBallRadius);
mBallQuadCoefficient = 1.0f / mBallMoveXOffsets;
}
private void adjustParams() {
mBallCenterY = mHeight / 2.0f;
mBallSideOffsets = (mWidth - mBallRadius * 2.0f * (mBallCount - 2)) / 2;
}
private void setupPaint() {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setShader(new LinearGradient(mBallSideOffsets, 0, mWidth - mBallSideOffsets, 0,
mColors, mPositions, Shader.TileMode.CLAMP));
}
@Override
protected void draw(Canvas canvas) {
int saveCount = canvas.save();
for (int i = 1; i < mBallCount - 1; i++) {
mPaint.setAlpha(MAX_ALPHA);
canvas.drawCircle(mBallRadius * (i * 2 - 1) + mBallSideOffsets, mBallCenterY, mBallRadius, mPaint);
mOvalRect.set(mBallRadius * (i * 2 - 2) + mBallSideOffsets, mHeight - mOvalVerticalRadius * 2,
mBallRadius * (i * 2) + mBallSideOffsets, mHeight);
mPaint.setAlpha(OVAL_ALPHA);
canvas.drawOval(mOvalRect, mPaint);
}
//draw the first ball
mPaint.setAlpha(MAX_ALPHA);
canvas.drawCircle(mBallSideOffsets - mBallRadius - mLeftBallMoveXOffsets,
mBallCenterY - mLeftBallMoveYOffsets, mBallRadius, mPaint);
mOvalRect.set(mBallSideOffsets - mBallRadius - mBallRadius * mLeftOvalShapeRate - mLeftBallMoveXOffsets,
mHeight - mOvalVerticalRadius - mOvalVerticalRadius * mLeftOvalShapeRate,
mBallSideOffsets - mBallRadius + mBallRadius * mLeftOvalShapeRate - mLeftBallMoveXOffsets,
mHeight - mOvalVerticalRadius + mOvalVerticalRadius * mLeftOvalShapeRate);
mPaint.setAlpha(OVAL_ALPHA);
canvas.drawOval(mOvalRect, mPaint);
//draw the last ball
mPaint.setAlpha(MAX_ALPHA);
canvas.drawCircle(mBallRadius * (mBallCount * 2 - 3) + mBallSideOffsets + mRightBallMoveXOffsets,
mBallCenterY - mRightBallMoveYOffsets, mBallRadius, mPaint);
mOvalRect.set(mBallRadius * (mBallCount * 2 - 3) - mBallRadius * mRightOvalShapeRate + mBallSideOffsets + mRightBallMoveXOffsets,
mHeight - mOvalVerticalRadius - mOvalVerticalRadius * mRightOvalShapeRate,
mBallRadius * (mBallCount * 2 - 3) + mBallRadius * mRightOvalShapeRate + mBallSideOffsets + mRightBallMoveXOffsets,
mHeight - mOvalVerticalRadius + mOvalVerticalRadius * mRightOvalShapeRate);
mPaint.setAlpha(OVAL_ALPHA);
canvas.drawOval(mOvalRect, mPaint);
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
// Moving the left ball to the left sides only occurs in the first 25% of a jump animation
if (renderProgress <= START_LEFT_DURATION_OFFSET) {
float startLeftOffsetProgress = renderProgress / START_LEFT_DURATION_OFFSET;
computeLeftBallMoveOffsets(DECELERATE_INTERPOLATOR.getInterpolation(startLeftOffsetProgress));
return;
}
// Moving the left ball to the origin location only occurs between 25% and 50% of a jump ring animation
if (renderProgress <= START_RIGHT_DURATION_OFFSET) {
float startRightOffsetProgress = (renderProgress - START_LEFT_DURATION_OFFSET) / (START_RIGHT_DURATION_OFFSET - START_LEFT_DURATION_OFFSET);
computeLeftBallMoveOffsets(ACCELERATE_INTERPOLATOR.getInterpolation(1.0f - startRightOffsetProgress));
return;
}
// Moving the right ball to the right sides only occurs between 50% and 75% of a jump animation
if (renderProgress <= END_RIGHT_DURATION_OFFSET) {
float endRightOffsetProgress = (renderProgress - START_RIGHT_DURATION_OFFSET) / (END_RIGHT_DURATION_OFFSET - START_RIGHT_DURATION_OFFSET);
computeRightBallMoveOffsets(DECELERATE_INTERPOLATOR.getInterpolation(endRightOffsetProgress));
return;
}
// Moving the right ball to the origin location only occurs after 75% of a jump animation
if (renderProgress <= END_LEFT_DURATION_OFFSET) {
float endRightOffsetProgress = (renderProgress - END_RIGHT_DURATION_OFFSET) / (END_LEFT_DURATION_OFFSET - END_RIGHT_DURATION_OFFSET);
computeRightBallMoveOffsets(ACCELERATE_INTERPOLATOR.getInterpolation(1 - endRightOffsetProgress));
return;
}
}
private void computeLeftBallMoveOffsets(float progress) {
mRightBallMoveXOffsets = 0.0f;
mRightBallMoveYOffsets = 0.0f;
mLeftOvalShapeRate = 1.0f - progress;
mLeftBallMoveXOffsets = mBallMoveXOffsets * progress;
mLeftBallMoveYOffsets = (float) (Math.pow(mLeftBallMoveXOffsets, 2) * mBallQuadCoefficient);
}
private void computeRightBallMoveOffsets(float progress) {
mLeftBallMoveXOffsets = 0.0f;
mLeftBallMoveYOffsets = 0.0f;
mRightOvalShapeRate = 1.0f - progress;
mRightBallMoveXOffsets = mBallMoveXOffsets * progress;
mRightBallMoveYOffsets = (float) (Math.pow(mRightBallMoveXOffsets, 2) * mBallQuadCoefficient);
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
}
private void apply(Builder builder) {
this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
this.mOvalVerticalRadius = builder.mOvalVerticalRadius > 0 ? builder.mOvalVerticalRadius : this.mOvalVerticalRadius;
this.mBallRadius = builder.mBallRadius > 0 ? builder.mBallRadius : this.mBallRadius;
this.mBallMoveXOffsets = builder.mBallMoveXOffsets > 0 ? builder.mBallMoveXOffsets : this.mBallMoveXOffsets;
this.mBallQuadCoefficient = builder.mBallQuadCoefficient > 0 ? builder.mBallQuadCoefficient : this.mBallQuadCoefficient;
this.mBallCount = builder.mBallCount > 0 ? builder.mBallCount : this.mBallCount;
this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
this.mColors = builder.mColors != null ? builder.mColors : this.mColors;
adjustParams();
setupPaint();
}
public static class Builder {
private Context mContext;
private int mWidth;
private int mHeight;
private float mOvalVerticalRadius;
private int mBallCount;
private float mBallRadius;
private float mBallMoveXOffsets;
private float mBallQuadCoefficient;
private int mDuration;
@Size(2)
private int[] mColors;
public Builder(Context mContext) {
this.mContext = mContext;
}
public Builder setWidth(int width) {
this.mWidth = width;
return this;
}
public Builder setHeight(int height) {
this.mHeight = height;
return this;
}
public Builder setOvalVerticalRadius(int ovalVerticalRadius) {
this.mOvalVerticalRadius = ovalVerticalRadius;
return this;
}
public Builder setBallRadius(int ballRadius) {
this.mBallRadius = ballRadius;
return this;
}
public Builder setBallMoveXOffsets(int ballMoveXOffsets) {
this.mBallMoveXOffsets = ballMoveXOffsets;
return this;
}
public Builder setBallQuadCoefficient(int ballQuadCoefficient) {
this.mBallQuadCoefficient = ballQuadCoefficient;
return this;
}
public Builder setBallCount(int ballCount) {
this.mBallCount = ballCount;
return this;
}
public Builder setColors(@Size(2) int[] colors) {
this.mColors = colors;
return this;
}
public Builder setDuration(int duration) {
this.mDuration = duration;
return this;
}
public CollisionLoadingRenderer build() {
CollisionLoadingRenderer loadingRenderer = new CollisionLoadingRenderer(mContext);
loadingRenderer.apply(this);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,339 @@
package app.dinus.com.loadingdrawable.render.circle.jump;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class DanceLoadingRenderer extends LoadingRenderer {
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final long ANIMATION_DURATION = 1888;
private static final float DEFAULT_CENTER_RADIUS = 12.5f;
private static final float DEFAULT_STROKE_WIDTH = 1.5f;
private static final float DEFAULT_DANCE_BALL_RADIUS = 2.0f;
private static final int NUM_POINTS = 3;
private static final int DEGREE_360 = 360;
private static final int RING_START_ANGLE = -90;
private static final int DANCE_START_ANGLE = 0;
private static final int DANCE_INTERVAL_ANGLE = 60;
private static final int DEFAULT_COLOR = Color.WHITE;
//the center coordinate of the oval
private static final float[] POINT_X = new float[NUM_POINTS];
private static final float[] POINT_Y = new float[NUM_POINTS];
//1: the coordinate x from small to large; -1: the coordinate x from large to small
private static final int[] DIRECTION = new int[]{1, 1, -1};
private static final float BALL_FORWARD_START_ENTER_DURATION_OFFSET = 0f;
private static final float BALL_FORWARD_END_ENTER_DURATION_OFFSET = 0.125f;
private static final float RING_FORWARD_START_ROTATE_DURATION_OFFSET = 0.125f;
private static final float RING_FORWARD_END_ROTATE_DURATION_OFFSET = 0.375f;
private static final float CENTER_CIRCLE_FORWARD_START_SCALE_DURATION_OFFSET = 0.225f;
private static final float CENTER_CIRCLE_FORWARD_END_SCALE_DURATION_OFFSET = 0.475f;
private static final float BALL_FORWARD_START_EXIT_DURATION_OFFSET = 0.375f;
private static final float BALL_FORWARD_END_EXIT_DURATION_OFFSET = 0.54f;
private static final float RING_REVERSAL_START_ROTATE_DURATION_OFFSET = 0.5f;
private static final float RING_REVERSAL_END_ROTATE_DURATION_OFFSET = 0.75f;
private static final float BALL_REVERSAL_START_ENTER_DURATION_OFFSET = 0.6f;
private static final float BALL_REVERSAL_END_ENTER_DURATION_OFFSET = 0.725f;
private static final float CENTER_CIRCLE_REVERSAL_START_SCALE_DURATION_OFFSET = 0.675f;
private static final float CENTER_CIRCLE_REVERSAL_END_SCALE_DURATION_OFFSET = 0.875f;
private static final float BALL_REVERSAL_START_EXIT_DURATION_OFFSET = 0.875f;
private static final float BALL_REVERSAL_END_EXIT_DURATION_OFFSET = 1.0f;
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final RectF mCurrentBounds = new RectF();
private float mScale;
private float mRotation;
private float mStrokeInset;
private float mCenterRadius;
private float mStrokeWidth;
private float mDanceBallRadius;
private float mShapeChangeWidth;
private float mShapeChangeHeight;
private int mColor;
private int mArcColor;
private DanceLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
}
private void init(Context context) {
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
mDanceBallRadius = DensityUtil.dip2px(context, DEFAULT_DANCE_BALL_RADIUS);
setColor(DEFAULT_COLOR);
setInsets((int) mWidth, (int) mHeight);
mDuration = ANIMATION_DURATION;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
mTempBounds.set(bounds);
mTempBounds.inset(mStrokeInset, mStrokeInset);
mCurrentBounds.set(mTempBounds);
float outerCircleRadius = Math.min(mTempBounds.height(), mTempBounds.width()) / 2.0f;
float interCircleRadius = outerCircleRadius / 2.0f;
float centerRingWidth = interCircleRadius - mStrokeWidth / 2;
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mColor);
mPaint.setStrokeWidth(mStrokeWidth);
canvas.drawCircle(mTempBounds.centerX(), mTempBounds.centerY(), outerCircleRadius, mPaint);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(mTempBounds.centerX(), mTempBounds.centerY(), interCircleRadius * mScale, mPaint);
if (mRotation != 0) {
mPaint.setColor(mArcColor);
mPaint.setStyle(Paint.Style.STROKE);
//strokeWidth / 2.0f + mStrokeWidth / 2.0f is the center of the inter circle width
mTempBounds.inset(centerRingWidth / 2.0f + mStrokeWidth / 2.0f, centerRingWidth / 2.0f + mStrokeWidth / 2.0f);
mPaint.setStrokeWidth(centerRingWidth);
canvas.drawArc(mTempBounds, RING_START_ANGLE, mRotation, false, mPaint);
}
mPaint.setColor(mColor);
mPaint.setStyle(Paint.Style.FILL);
for (int i = 0; i < NUM_POINTS; i++) {
canvas.rotate(i * DANCE_INTERVAL_ANGLE, POINT_X[i], POINT_Y[i]);
RectF rectF = new RectF(POINT_X[i] - mDanceBallRadius - mShapeChangeWidth / 2.0f,
POINT_Y[i] - mDanceBallRadius - mShapeChangeHeight / 2.0f,
POINT_X[i] + mDanceBallRadius + mShapeChangeWidth / 2.0f,
POINT_Y[i] + mDanceBallRadius + mShapeChangeHeight / 2.0f);
canvas.drawOval(rectF, mPaint);
canvas.rotate(-i * DANCE_INTERVAL_ANGLE, POINT_X[i], POINT_Y[i]);
}
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
float radius = Math.min(mCurrentBounds.height(), mCurrentBounds.width()) / 2.0f;
//the origin coordinate is the centerLeft of the field mCurrentBounds
float originCoordinateX = mCurrentBounds.left;
float originCoordinateY = mCurrentBounds.top + radius;
if (renderProgress <= BALL_FORWARD_END_ENTER_DURATION_OFFSET && renderProgress > BALL_FORWARD_START_ENTER_DURATION_OFFSET) {
final float ballForwardEnterProgress = (renderProgress - BALL_FORWARD_START_ENTER_DURATION_OFFSET) / (BALL_FORWARD_END_ENTER_DURATION_OFFSET - BALL_FORWARD_START_ENTER_DURATION_OFFSET);
mShapeChangeHeight = (0.5f - ballForwardEnterProgress) * mDanceBallRadius / 2.0f;
mShapeChangeWidth = -mShapeChangeHeight;
//y = k(x - r)--> k = tan(angle)
//(x - r)^2 + y^2 = r^2
// compute crossover point --> (k(x -r)) ^ 2 + (x - )^2 = r^2
// so x --> [r + r / sqrt(k ^ 2 + 1), r - r / sqrt(k ^ 2 + 1)]
for (int i = 0; i < NUM_POINTS; i++) {
float k = (float) Math.tan((DANCE_START_ANGLE + DANCE_INTERVAL_ANGLE * i) / 360.0f * (2.0f * Math.PI));
// progress[-1, 1]
float progress = (ACCELERATE_INTERPOLATOR.getInterpolation(ballForwardEnterProgress) / 2.0f - 0.5f) * 2.0f * DIRECTION[i];
POINT_X[i] = (float) (radius + progress * (radius / Math.sqrt(Math.pow(k, 2.0f) + 1.0f)));
POINT_Y[i] = k * (POINT_X[i] - radius);
POINT_X[i] += originCoordinateX;
POINT_Y[i] += originCoordinateY;
}
}
if (renderProgress <= RING_FORWARD_END_ROTATE_DURATION_OFFSET && renderProgress > RING_FORWARD_START_ROTATE_DURATION_OFFSET) {
final float forwardRotateProgress = (renderProgress - RING_FORWARD_START_ROTATE_DURATION_OFFSET) / (RING_FORWARD_END_ROTATE_DURATION_OFFSET - RING_FORWARD_START_ROTATE_DURATION_OFFSET);
mRotation = DEGREE_360 * MATERIAL_INTERPOLATOR.getInterpolation(forwardRotateProgress);
}
if (renderProgress <= CENTER_CIRCLE_FORWARD_END_SCALE_DURATION_OFFSET && renderProgress > CENTER_CIRCLE_FORWARD_START_SCALE_DURATION_OFFSET) {
final float centerCircleScaleProgress = (renderProgress - CENTER_CIRCLE_FORWARD_START_SCALE_DURATION_OFFSET) / (CENTER_CIRCLE_FORWARD_END_SCALE_DURATION_OFFSET - CENTER_CIRCLE_FORWARD_START_SCALE_DURATION_OFFSET);
if (centerCircleScaleProgress <= 0.5f) {
mScale = 1.0f + DECELERATE_INTERPOLATOR.getInterpolation(centerCircleScaleProgress * 2.0f) * 0.2f;
} else {
mScale = 1.2f - ACCELERATE_INTERPOLATOR.getInterpolation((centerCircleScaleProgress - 0.5f) * 2.0f) * 0.2f;
}
}
if (renderProgress <= BALL_FORWARD_END_EXIT_DURATION_OFFSET && renderProgress > BALL_FORWARD_START_EXIT_DURATION_OFFSET) {
final float ballForwardExitProgress = (renderProgress - BALL_FORWARD_START_EXIT_DURATION_OFFSET) / (BALL_FORWARD_END_EXIT_DURATION_OFFSET - BALL_FORWARD_START_EXIT_DURATION_OFFSET);
mShapeChangeHeight = (ballForwardExitProgress - 0.5f) * mDanceBallRadius / 2.0f;
mShapeChangeWidth = -mShapeChangeHeight;
for (int i = 0; i < NUM_POINTS; i++) {
float k = (float) Math.tan((DANCE_START_ANGLE + DANCE_INTERVAL_ANGLE * i) / 360.0f * (2.0f * Math.PI));
float progress = (DECELERATE_INTERPOLATOR.getInterpolation(ballForwardExitProgress) / 2.0f) * 2.0f * DIRECTION[i];
POINT_X[i] = (float) (radius + progress * (radius / Math.sqrt(Math.pow(k, 2.0f) + 1.0f)));
POINT_Y[i] = k * (POINT_X[i] - radius);
POINT_X[i] += originCoordinateX;
POINT_Y[i] += originCoordinateY;
}
}
if (renderProgress <= RING_REVERSAL_END_ROTATE_DURATION_OFFSET && renderProgress > RING_REVERSAL_START_ROTATE_DURATION_OFFSET) {
float scaledTime = (renderProgress - RING_REVERSAL_START_ROTATE_DURATION_OFFSET) / (RING_REVERSAL_END_ROTATE_DURATION_OFFSET - RING_REVERSAL_START_ROTATE_DURATION_OFFSET);
mRotation = DEGREE_360 * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime) - 360;
} else if (renderProgress > RING_REVERSAL_END_ROTATE_DURATION_OFFSET) {
mRotation = 0.0f;
}
if (renderProgress <= BALL_REVERSAL_END_ENTER_DURATION_OFFSET && renderProgress > BALL_REVERSAL_START_ENTER_DURATION_OFFSET) {
final float ballReversalEnterProgress = (renderProgress - BALL_REVERSAL_START_ENTER_DURATION_OFFSET) / (BALL_REVERSAL_END_ENTER_DURATION_OFFSET - BALL_REVERSAL_START_ENTER_DURATION_OFFSET);
mShapeChangeHeight = (0.5f - ballReversalEnterProgress) * mDanceBallRadius / 2.0f;
mShapeChangeWidth = -mShapeChangeHeight;
for (int i = 0; i < NUM_POINTS; i++) {
float k = (float) Math.tan((DANCE_START_ANGLE + DANCE_INTERVAL_ANGLE * i) / 360.0f * (2.0f * Math.PI));
float progress = (0.5f - ACCELERATE_INTERPOLATOR.getInterpolation(ballReversalEnterProgress) / 2.0f) * 2.0f * DIRECTION[i];
POINT_X[i] = (float) (radius + progress * (radius / Math.sqrt(Math.pow(k, 2.0f) + 1.0f)));
POINT_Y[i] = k * (POINT_X[i] - radius);
POINT_X[i] += originCoordinateX;
POINT_Y[i] += originCoordinateY;
}
}
if (renderProgress <= CENTER_CIRCLE_REVERSAL_END_SCALE_DURATION_OFFSET && renderProgress > CENTER_CIRCLE_REVERSAL_START_SCALE_DURATION_OFFSET) {
final float centerCircleScaleProgress = (renderProgress - CENTER_CIRCLE_REVERSAL_START_SCALE_DURATION_OFFSET) / (CENTER_CIRCLE_REVERSAL_END_SCALE_DURATION_OFFSET - CENTER_CIRCLE_REVERSAL_START_SCALE_DURATION_OFFSET);
if (centerCircleScaleProgress <= 0.5f) {
mScale = 1.0f + DECELERATE_INTERPOLATOR.getInterpolation(centerCircleScaleProgress * 2.0f) * 0.2f;
} else {
mScale = 1.2f - ACCELERATE_INTERPOLATOR.getInterpolation((centerCircleScaleProgress - 0.5f) * 2.0f) * 0.2f;
}
}
if (renderProgress <= BALL_REVERSAL_END_EXIT_DURATION_OFFSET && renderProgress > BALL_REVERSAL_START_EXIT_DURATION_OFFSET) {
final float ballReversalExitProgress = (renderProgress - BALL_REVERSAL_START_EXIT_DURATION_OFFSET) / (BALL_REVERSAL_END_EXIT_DURATION_OFFSET - BALL_REVERSAL_START_EXIT_DURATION_OFFSET);
mShapeChangeHeight = (ballReversalExitProgress - 0.5f) * mDanceBallRadius / 2.0f;
mShapeChangeWidth = -mShapeChangeHeight;
for (int i = 0; i < NUM_POINTS; i++) {
float k = (float) Math.tan((DANCE_START_ANGLE + DANCE_INTERVAL_ANGLE * i) / 360.0f * (2.0f * Math.PI));
float progress = (0.0f - DECELERATE_INTERPOLATOR.getInterpolation(ballReversalExitProgress) / 2.0f) * 2.0f * DIRECTION[i];
POINT_X[i] = (float) (radius + progress * (radius / Math.sqrt(Math.pow(k, 2.0f) + 1.0f)));
POINT_Y[i] = k * (POINT_X[i] - radius);
POINT_X[i] += originCoordinateX;
POINT_Y[i] += originCoordinateY;
}
}
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
mScale = 1.0f;
mRotation = 0;
}
private void setColor(int color) {
mColor = color;
mArcColor = halfAlphaColor(mColor);
}
private void setRotation(float rotation) {
mRotation = rotation;
}
private void setDanceBallRadius(float danceBallRadius) {
this.mDanceBallRadius = danceBallRadius;
}
private float getDanceBallRadius() {
return mDanceBallRadius;
}
private float getRotation() {
return mRotation;
}
private void setInsets(int width, int height) {
final float minEdge = (float) Math.min(width, height);
float insets;
if (mCenterRadius <= 0 || minEdge < 0) {
insets = (float) Math.ceil(mStrokeWidth / 2.0f);
} else {
insets = minEdge / 2.0f - mCenterRadius;
}
mStrokeInset = insets;
}
private int halfAlphaColor(int colorValue) {
int startA = (colorValue >> 24) & 0xff;
int startR = (colorValue >> 16) & 0xff;
int startG = (colorValue >> 8) & 0xff;
int startB = colorValue & 0xff;
return ((startA / 2) << 24)
| (startR << 16)
| (startG << 8)
| startB;
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public DanceLoadingRenderer build() {
DanceLoadingRenderer loadingRenderer = new DanceLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,260 @@
package app.dinus.com.loadingdrawable.render.circle.jump;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class GuardLoadingRenderer extends LoadingRenderer {
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final long ANIMATION_DURATION = 5000;
private static final float DEFAULT_STROKE_WIDTH = 1.0f;
private static final float DEFAULT_CENTER_RADIUS = 12.5f;
private static final float DEFAULT_SKIP_BALL_RADIUS = 1.0f;
private static final float START_TRIM_INIT_ROTATION = -0.5f;
private static final float START_TRIM_MAX_ROTATION = -0.25f;
private static final float END_TRIM_INIT_ROTATION = 0.25f;
private static final float END_TRIM_MAX_ROTATION = 0.75f;
private static final float START_TRIM_DURATION_OFFSET = 0.23f;
private static final float WAVE_DURATION_OFFSET = 0.36f;
private static final float BALL_SKIP_DURATION_OFFSET = 0.74f;
private static final float BALL_SCALE_DURATION_OFFSET = 0.82f;
private static final float END_TRIM_DURATION_OFFSET = 1.0f;
private static final int DEFAULT_COLOR = Color.WHITE;
private static final int DEFAULT_BALL_COLOR = Color.RED;
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final RectF mCurrentBounds = new RectF();
private final float[] mCurrentPosition = new float[2];
private float mStrokeInset;
private float mSkipBallSize;
private float mScale;
private float mEndTrim;
private float mRotation;
private float mStartTrim;
private float mWaveProgress;
private float mStrokeWidth;
private float mCenterRadius;
private int mColor;
private int mBallColor;
private PathMeasure mPathMeasure;
private GuardLoadingRenderer(Context context) {
super(context);
mDuration = ANIMATION_DURATION;
init(context);
setupPaint();
}
private void init(Context context) {
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
mSkipBallSize = DensityUtil.dip2px(context, DEFAULT_SKIP_BALL_RADIUS);
mColor = DEFAULT_COLOR;
mBallColor = DEFAULT_BALL_COLOR;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
setInsets((int) mWidth, (int) mHeight);
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
RectF arcBounds = mTempBounds;
arcBounds.set(bounds);
arcBounds.inset(mStrokeInset, mStrokeInset);
mCurrentBounds.set(arcBounds);
int saveCount = canvas.save();
//draw circle trim
float startAngle = (mStartTrim + mRotation) * 360;
float endAngle = (mEndTrim + mRotation) * 360;
float sweepAngle = endAngle - startAngle;
if (sweepAngle != 0) {
mPaint.setColor(mColor);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
}
//draw water wave
if (mWaveProgress < 1.0f) {
mPaint.setColor(Color.argb((int) (Color.alpha(mColor) * (1.0f - mWaveProgress)),
Color.red(mColor), Color.green(mColor), Color.blue(mColor)));
mPaint.setStyle(Paint.Style.STROKE);
float radius = Math.min(arcBounds.width(), arcBounds.height()) / 2.0f;
canvas.drawCircle(arcBounds.centerX(), arcBounds.centerY(), radius * (1.0f + mWaveProgress), mPaint);
}
//draw ball bounce
if (mPathMeasure != null) {
mPaint.setColor(mBallColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(mCurrentPosition[0], mCurrentPosition[1], mSkipBallSize * mScale, mPaint);
}
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
if (renderProgress <= START_TRIM_DURATION_OFFSET) {
final float startTrimProgress = (renderProgress) / START_TRIM_DURATION_OFFSET;
mEndTrim = -MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
mRotation = START_TRIM_INIT_ROTATION + START_TRIM_MAX_ROTATION
* MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
}
if (renderProgress <= WAVE_DURATION_OFFSET && renderProgress > START_TRIM_DURATION_OFFSET) {
final float waveProgress = (renderProgress - START_TRIM_DURATION_OFFSET)
/ (WAVE_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
mWaveProgress = ACCELERATE_INTERPOLATOR.getInterpolation(waveProgress);
}
if (renderProgress <= BALL_SKIP_DURATION_OFFSET && renderProgress > WAVE_DURATION_OFFSET) {
if (mPathMeasure == null) {
mPathMeasure = new PathMeasure(createSkipBallPath(), false);
}
final float ballSkipProgress = (renderProgress - WAVE_DURATION_OFFSET)
/ (BALL_SKIP_DURATION_OFFSET - WAVE_DURATION_OFFSET);
mPathMeasure.getPosTan(ballSkipProgress * mPathMeasure.getLength(), mCurrentPosition, null);
mWaveProgress = 1.0f;
}
if (renderProgress <= BALL_SCALE_DURATION_OFFSET && renderProgress > BALL_SKIP_DURATION_OFFSET) {
final float ballScaleProgress =
(renderProgress - BALL_SKIP_DURATION_OFFSET)
/ (BALL_SCALE_DURATION_OFFSET - BALL_SKIP_DURATION_OFFSET);
if (ballScaleProgress < 0.5f) {
mScale = 1.0f + DECELERATE_INTERPOLATOR.getInterpolation(ballScaleProgress * 2.0f);
} else {
mScale = 2.0f - ACCELERATE_INTERPOLATOR.getInterpolation((ballScaleProgress - 0.5f) * 2.0f) * 2.0f;
}
}
if (renderProgress >= BALL_SCALE_DURATION_OFFSET) {
final float endTrimProgress =
(renderProgress - BALL_SKIP_DURATION_OFFSET)
/ (END_TRIM_DURATION_OFFSET - BALL_SKIP_DURATION_OFFSET);
mEndTrim = -1 + MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
mRotation = END_TRIM_INIT_ROTATION + END_TRIM_MAX_ROTATION
* MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
mScale = 1.0f;
mPathMeasure = null;
}
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
mScale = 1.0f;
mEndTrim = 0.0f;
mRotation = 0.0f;
mStartTrim = 0.0f;
mWaveProgress = 1.0f;
}
private Path createSkipBallPath() {
float radius = Math.min(mCurrentBounds.width(), mCurrentBounds.height()) / 2.0f;
float radiusPow2 = (float) Math.pow(radius, 2.0f);
float originCoordinateX = mCurrentBounds.centerX();
float originCoordinateY = mCurrentBounds.centerY();
float[] coordinateX = new float[]{0.0f, 0.0f, -0.8f * radius, 0.75f * radius,
-0.45f * radius, 0.9f * radius, -0.5f * radius};
float[] sign = new float[]{1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f};
Path path = new Path();
for (int i = 0; i < coordinateX.length; i++) {
// x^2 + y^2 = radius^2 --> y = sqrt(radius^2 - x^2)
if (i == 0) {
path.moveTo(
originCoordinateX + coordinateX[i],
originCoordinateY + sign[i]
* (float) Math.sqrt(radiusPow2 - Math.pow(coordinateX[i], 2.0f)));
continue;
}
path.lineTo(
originCoordinateX + coordinateX[i],
originCoordinateY + sign[i]
* (float) Math.sqrt(radiusPow2 - Math.pow(coordinateX[i], 2.0f)));
if (i == coordinateX.length - 1) {
path.lineTo(originCoordinateX, originCoordinateY);
}
}
return path;
}
private void setInsets(int width, int height) {
final float minEdge = (float) Math.min(width, height);
float insets;
if (mCenterRadius <= 0 || minEdge < 0) {
insets = (float) Math.ceil(mStrokeWidth / 2.0f);
} else {
insets = minEdge / 2.0f - mCenterRadius;
}
mStrokeInset = insets;
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public GuardLoadingRenderer build() {
GuardLoadingRenderer loadingRenderer = new GuardLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,227 @@
package app.dinus.com.loadingdrawable.render.circle.jump;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class SwapLoadingRenderer extends LoadingRenderer {
private static final Interpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
private static final long ANIMATION_DURATION = 2500;
private static final int DEFAULT_CIRCLE_COUNT = 5;
private static final float DEFAULT_BALL_RADIUS = 7.5f;
private static final float DEFAULT_WIDTH = 15.0f * 11;
private static final float DEFAULT_HEIGHT = 15.0f * 5;
private static final float DEFAULT_STROKE_WIDTH = 1.5f;
private static final int DEFAULT_COLOR = Color.WHITE;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int mColor;
private int mSwapIndex;
private int mBallCount;
private float mBallSideOffsets;
private float mBallCenterY;
private float mBallRadius;
private float mBallInterval;
private float mSwapBallOffsetX;
private float mSwapBallOffsetY;
private float mASwapThreshold;
private float mStrokeWidth;
private SwapLoadingRenderer(Context context) {
super(context);
init(context);
adjustParams();
setupPaint();
}
private void init(Context context) {
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mBallRadius = DensityUtil.dip2px(context, DEFAULT_BALL_RADIUS);
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mColor = DEFAULT_COLOR;
mDuration = ANIMATION_DURATION;
mBallCount = DEFAULT_CIRCLE_COUNT;
mBallInterval = mBallRadius;
}
private void adjustParams() {
mBallCenterY = mHeight / 2.0f;
mBallSideOffsets = (mWidth - mBallRadius * 2 * mBallCount - mBallInterval * (mBallCount - 1)) / 2.0f;
mASwapThreshold = 1.0f / mBallCount;
}
private void setupPaint() {
mPaint.setColor(mColor);
mPaint.setStrokeWidth(mStrokeWidth);
}
@Override
protected void draw(Canvas canvas) {
int saveCount = canvas.save();
for (int i = 0; i < mBallCount; i++) {
if (i == mSwapIndex) {
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(mBallSideOffsets + mBallRadius * (i * 2 + 1) + i * mBallInterval + mSwapBallOffsetX
, mBallCenterY - mSwapBallOffsetY, mBallRadius, mPaint);
} else if (i == (mSwapIndex + 1) % mBallCount) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(mBallSideOffsets + mBallRadius * (i * 2 + 1) + i * mBallInterval - mSwapBallOffsetX
, mBallCenterY + mSwapBallOffsetY, mBallRadius - mStrokeWidth / 2, mPaint);
} else {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(mBallSideOffsets + mBallRadius * (i * 2 + 1) + i * mBallInterval, mBallCenterY
, mBallRadius - mStrokeWidth / 2, mPaint);
}
}
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
mSwapIndex = (int) (renderProgress / mASwapThreshold);
// Swap trace : x^2 + y^2 = r ^ 2
float swapTraceProgress = ACCELERATE_DECELERATE_INTERPOLATOR.getInterpolation(
(renderProgress - mSwapIndex * mASwapThreshold) / mASwapThreshold);
float swapTraceRadius = mSwapIndex == mBallCount - 1
? (mBallRadius * 2 * (mBallCount - 1) + mBallInterval * (mBallCount - 1)) / 2
: (mBallRadius * 2 + mBallInterval) / 2;
// Calculate the X offset of the swap ball
mSwapBallOffsetX = mSwapIndex == mBallCount - 1
? -swapTraceProgress * swapTraceRadius * 2
: swapTraceProgress * swapTraceRadius * 2;
// if mSwapIndex == mBallCount - 1 then (swapTraceRadius, swapTraceRadius) as the origin of coordinates
// else (-swapTraceRadius, -swapTraceRadius) as the origin of coordinates
float xCoordinate = mSwapIndex == mBallCount - 1
? mSwapBallOffsetX + swapTraceRadius
: mSwapBallOffsetX - swapTraceRadius;
// Calculate the Y offset of the swap ball
mSwapBallOffsetY = (float) (mSwapIndex % 2 == 0 && mSwapIndex != mBallCount - 1
? Math.sqrt(Math.pow(swapTraceRadius, 2.0f) - Math.pow(xCoordinate, 2.0f))
: -Math.sqrt(Math.pow(swapTraceRadius, 2.0f) - Math.pow(xCoordinate, 2.0f)));
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
}
private void apply(Builder builder) {
this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
this.mBallRadius = builder.mBallRadius > 0 ? builder.mBallRadius : this.mBallRadius;
this.mBallInterval = builder.mBallInterval > 0 ? builder.mBallInterval : this.mBallInterval;
this.mBallCount = builder.mBallCount > 0 ? builder.mBallCount : this.mBallCount;
this.mColor = builder.mColor != 0 ? builder.mColor : this.mColor;
this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
adjustParams();
setupPaint();
}
public static class Builder {
private Context mContext;
private int mWidth;
private int mHeight;
private int mStrokeWidth;
private int mBallCount;
private int mBallRadius;
private int mBallInterval;
private int mDuration;
private int mColor;
public Builder(Context mContext) {
this.mContext = mContext;
}
public Builder setWidth(int width) {
this.mWidth = width;
return this;
}
public Builder setHeight(int height) {
this.mHeight = height;
return this;
}
public Builder setStrokeWidth(int strokeWidth) {
this.mStrokeWidth = strokeWidth;
return this;
}
public Builder setBallRadius(int ballRadius) {
this.mBallRadius = ballRadius;
return this;
}
public Builder setBallInterval(int ballInterval) {
this.mBallInterval = ballInterval;
return this;
}
public Builder setBallCount(int ballCount) {
this.mBallCount = ballCount;
return this;
}
public Builder setColor(int color) {
this.mColor = color;
return this;
}
public Builder setDuration(int duration) {
this.mDuration = duration;
return this;
}
public SwapLoadingRenderer build() {
SwapLoadingRenderer loadingRenderer = new SwapLoadingRenderer(mContext);
loadingRenderer.apply(this);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,288 @@
package app.dinus.com.loadingdrawable.render.circle.rotate;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import androidx.annotation.IntRange;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class GearLoadingRenderer extends LoadingRenderer {
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final int GEAR_COUNT = 4;
private static final int NUM_POINTS = 3;
private static final int MAX_ALPHA = 255;
private static final int DEGREE_360 = 360;
private static final int DEFAULT_GEAR_SWIPE_DEGREES = 60;
private static final float FULL_GROUP_ROTATION = 3.0f * DEGREE_360;
private static final float START_SCALE_DURATION_OFFSET = 0.3f;
private static final float START_TRIM_DURATION_OFFSET = 0.5f;
private static final float END_TRIM_DURATION_OFFSET = 0.7f;
private static final float END_SCALE_DURATION_OFFSET = 1.0f;
private static final float DEFAULT_CENTER_RADIUS = 12.5f;
private static final float DEFAULT_STROKE_WIDTH = 2.5f;
private static final int DEFAULT_COLOR = Color.WHITE;
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animator) {
super.onAnimationRepeat(animator);
storeOriginals();
mStartDegrees = mEndDegrees;
mRotationCount = (mRotationCount + 1) % NUM_POINTS;
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mRotationCount = 0;
}
};
private int mColor;
private int mGearCount;
private int mGearSwipeDegrees;
private float mStrokeInset;
private float mRotationCount;
private float mGroupRotation;
private float mScale;
private float mEndDegrees;
private float mStartDegrees;
private float mSwipeDegrees;
private float mOriginEndDegrees;
private float mOriginStartDegrees;
private float mStrokeWidth;
private float mCenterRadius;
private GearLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
addRenderListener(mAnimatorListener);
}
private void init(Context context) {
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
mColor = DEFAULT_COLOR;
mGearCount = GEAR_COUNT;
mGearSwipeDegrees = DEFAULT_GEAR_SWIPE_DEGREES;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
initStrokeInset(mWidth, mHeight);
}
@Override
protected void draw(Canvas canvas) {
int saveCount = canvas.save();
mTempBounds.set(mBounds);
mTempBounds.inset(mStrokeInset, mStrokeInset);
mTempBounds.inset(mTempBounds.width() * (1.0f - mScale) / 2.0f, mTempBounds.width() * (1.0f - mScale) / 2.0f);
canvas.rotate(mGroupRotation, mTempBounds.centerX(), mTempBounds.centerY());
mPaint.setColor(mColor);
mPaint.setAlpha((int) (MAX_ALPHA * mScale));
mPaint.setStrokeWidth(mStrokeWidth * mScale);
if (mSwipeDegrees != 0) {
for (int i = 0; i < mGearCount; i++) {
canvas.drawArc(mTempBounds, mStartDegrees + DEGREE_360 / mGearCount * i, mSwipeDegrees, false, mPaint);
}
}
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
// Scaling up the start size only occurs in the first 20% of a single ring animation
if (renderProgress <= START_SCALE_DURATION_OFFSET) {
float startScaleProgress = (renderProgress) / START_SCALE_DURATION_OFFSET;
mScale = DECELERATE_INTERPOLATOR.getInterpolation(startScaleProgress);
}
// Moving the start trim only occurs between 20% to 50% of a single ring animation
if (renderProgress <= START_TRIM_DURATION_OFFSET && renderProgress > START_SCALE_DURATION_OFFSET) {
float startTrimProgress = (renderProgress - START_SCALE_DURATION_OFFSET) / (START_TRIM_DURATION_OFFSET - START_SCALE_DURATION_OFFSET);
mStartDegrees = mOriginStartDegrees + mGearSwipeDegrees * startTrimProgress;
}
// Moving the end trim starts between 50% to 80% of a single ring animation
if (renderProgress <= END_TRIM_DURATION_OFFSET && renderProgress > START_TRIM_DURATION_OFFSET) {
float endTrimProgress = (renderProgress - START_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
mEndDegrees = mOriginEndDegrees + mGearSwipeDegrees * endTrimProgress;
}
// Scaling down the end size starts after 80% of a single ring animation
if (renderProgress > END_TRIM_DURATION_OFFSET) {
float endScaleProgress = (renderProgress - END_TRIM_DURATION_OFFSET) / (END_SCALE_DURATION_OFFSET - END_TRIM_DURATION_OFFSET);
mScale = 1.0f - ACCELERATE_INTERPOLATOR.getInterpolation(endScaleProgress);
}
if (renderProgress <= END_TRIM_DURATION_OFFSET && renderProgress > START_SCALE_DURATION_OFFSET) {
float rotateProgress = (renderProgress - START_SCALE_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - START_SCALE_DURATION_OFFSET);
mGroupRotation = ((FULL_GROUP_ROTATION / NUM_POINTS) * rotateProgress) + (FULL_GROUP_ROTATION * (mRotationCount / NUM_POINTS));
}
if (Math.abs(mEndDegrees - mStartDegrees) > 0) {
mSwipeDegrees = mEndDegrees - mStartDegrees;
}
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
resetOriginals();
}
private void initStrokeInset(float width, float height) {
float minSize = Math.min(width, height);
float strokeInset = minSize / 2.0f - mCenterRadius;
float minStrokeInset = (float) Math.ceil(mStrokeWidth / 2.0f);
mStrokeInset = strokeInset < minStrokeInset ? minStrokeInset : strokeInset;
}
private void storeOriginals() {
mOriginEndDegrees = mEndDegrees;
mOriginStartDegrees = mEndDegrees;
}
private void resetOriginals() {
mOriginEndDegrees = 0;
mOriginStartDegrees = 0;
mEndDegrees = 0;
mStartDegrees = 0;
mSwipeDegrees = 1;
}
private void apply(Builder builder) {
this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
this.mCenterRadius = builder.mCenterRadius > 0 ? builder.mCenterRadius : this.mCenterRadius;
this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
this.mColor = builder.mColor != 0 ? builder.mColor : this.mColor;
this.mGearCount = builder.mGearCount > 0 ? builder.mGearCount : this.mGearCount;
this.mGearSwipeDegrees = builder.mGearSwipeDegrees > 0 ? builder.mGearSwipeDegrees : this.mGearSwipeDegrees;
setupPaint();
initStrokeInset(this.mWidth, this.mHeight);
}
public static class Builder {
private Context mContext;
private int mWidth;
private int mHeight;
private int mStrokeWidth;
private int mCenterRadius;
private int mDuration;
private int mColor;
private int mGearCount;
private int mGearSwipeDegrees;
public Builder(Context mContext) {
this.mContext = mContext;
}
public Builder setWidth(int width) {
this.mWidth = width;
return this;
}
public Builder setHeight(int height) {
this.mHeight = height;
return this;
}
public Builder setStrokeWidth(int strokeWidth) {
this.mStrokeWidth = strokeWidth;
return this;
}
public Builder setCenterRadius(int centerRadius) {
this.mCenterRadius = centerRadius;
return this;
}
public Builder setDuration(int duration) {
this.mDuration = duration;
return this;
}
public Builder setColor(int color) {
this.mColor = color;
return this;
}
public Builder setGearCount(int gearCount) {
this.mGearCount = gearCount;
return this;
}
public Builder setGearSwipeDegrees(@IntRange(from = 0, to = 360) int gearSwipeDegrees) {
this.mGearSwipeDegrees = gearSwipeDegrees;
return this;
}
public GearLoadingRenderer build() {
GearLoadingRenderer loadingRenderer = new GearLoadingRenderer(mContext);
loadingRenderer.apply(this);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,300 @@
package app.dinus.com.loadingdrawable.render.circle.rotate;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import androidx.annotation.Size;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class LevelLoadingRenderer extends LoadingRenderer {
private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final int NUM_POINTS = 5;
private static final int DEGREE_360 = 360;
private static final float MAX_SWIPE_DEGREES = 0.8f * DEGREE_360;
private static final float FULL_GROUP_ROTATION = 3.0f * DEGREE_360;
private static final float[] LEVEL_SWEEP_ANGLE_OFFSETS = new float[]{1.0f, 7.0f / 8.0f, 5.0f / 8.0f};
private static final float START_TRIM_DURATION_OFFSET = 0.5f;
private static final float END_TRIM_DURATION_OFFSET = 1.0f;
private static final float DEFAULT_CENTER_RADIUS = 12.5f;
private static final float DEFAULT_STROKE_WIDTH = 2.5f;
private static final int[] DEFAULT_LEVEL_COLORS = new int[]{Color.parseColor("#55ffffff"),
Color.parseColor("#b1ffffff"), Color.parseColor("#ffffffff")};
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animator) {
super.onAnimationRepeat(animator);
storeOriginals();
mStartDegrees = mEndDegrees;
mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mRotationCount = 0;
}
};
@Size(3)
private int[] mLevelColors;
@Size(3)
private float[] mLevelSwipeDegrees;
private float mStrokeInset;
private float mRotationCount;
private float mGroupRotation;
private float mEndDegrees;
private float mStartDegrees;
private float mOriginEndDegrees;
private float mOriginStartDegrees;
private float mStrokeWidth;
private float mCenterRadius;
private LevelLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
addRenderListener(mAnimatorListener);
}
private void init(Context context) {
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
mLevelSwipeDegrees = new float[3];
mLevelColors = DEFAULT_LEVEL_COLORS;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
initStrokeInset((int) mWidth, (int) mHeight);
}
@Override
protected void draw(Canvas canvas) {
int saveCount = canvas.save();
mTempBounds.set(mBounds);
mTempBounds.inset(mStrokeInset, mStrokeInset);
canvas.rotate(mGroupRotation, mTempBounds.centerX(), mTempBounds.centerY());
for (int i = 0; i < 3; i++) {
if (mLevelSwipeDegrees[i] != 0) {
mPaint.setColor(mLevelColors[i]);
canvas.drawArc(mTempBounds, mEndDegrees, mLevelSwipeDegrees[i], false, mPaint);
}
}
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
// Moving the start trim only occurs in the first 50% of a single ring animation
if (renderProgress <= START_TRIM_DURATION_OFFSET) {
float startTrimProgress = (renderProgress) / START_TRIM_DURATION_OFFSET;
mStartDegrees = mOriginStartDegrees + MAX_SWIPE_DEGREES * MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
float mSwipeDegrees = mEndDegrees - mStartDegrees;
float levelSwipeDegreesProgress = Math.abs(mSwipeDegrees) / MAX_SWIPE_DEGREES;
float level1Increment = DECELERATE_INTERPOLATOR.getInterpolation(levelSwipeDegreesProgress) - LINEAR_INTERPOLATOR.getInterpolation(levelSwipeDegreesProgress);
float level3Increment = ACCELERATE_INTERPOLATOR.getInterpolation(levelSwipeDegreesProgress) - LINEAR_INTERPOLATOR.getInterpolation(levelSwipeDegreesProgress);
mLevelSwipeDegrees[0] = -mSwipeDegrees * LEVEL_SWEEP_ANGLE_OFFSETS[0] * (1.0f + level1Increment);
mLevelSwipeDegrees[1] = -mSwipeDegrees * LEVEL_SWEEP_ANGLE_OFFSETS[1] * 1.0f;
mLevelSwipeDegrees[2] = -mSwipeDegrees * LEVEL_SWEEP_ANGLE_OFFSETS[2] * (1.0f + level3Increment);
}
// Moving the end trim starts after 50% of a single ring animation
if (renderProgress > START_TRIM_DURATION_OFFSET) {
float endTrimProgress = (renderProgress - START_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
mEndDegrees = mOriginEndDegrees + MAX_SWIPE_DEGREES * MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
float mSwipeDegrees = mEndDegrees - mStartDegrees;
float levelSwipeDegreesProgress = Math.abs(mSwipeDegrees) / MAX_SWIPE_DEGREES;
if (levelSwipeDegreesProgress > LEVEL_SWEEP_ANGLE_OFFSETS[1]) {
mLevelSwipeDegrees[0] = -mSwipeDegrees;
mLevelSwipeDegrees[1] = MAX_SWIPE_DEGREES * LEVEL_SWEEP_ANGLE_OFFSETS[1];
mLevelSwipeDegrees[2] = MAX_SWIPE_DEGREES * LEVEL_SWEEP_ANGLE_OFFSETS[2];
} else if (levelSwipeDegreesProgress > LEVEL_SWEEP_ANGLE_OFFSETS[2]) {
mLevelSwipeDegrees[0] = 0;
mLevelSwipeDegrees[1] = -mSwipeDegrees;
mLevelSwipeDegrees[2] = MAX_SWIPE_DEGREES * LEVEL_SWEEP_ANGLE_OFFSETS[2];
} else {
mLevelSwipeDegrees[0] = 0;
mLevelSwipeDegrees[1] = 0;
mLevelSwipeDegrees[2] = -mSwipeDegrees;
}
}
mGroupRotation = ((FULL_GROUP_ROTATION / NUM_POINTS) * renderProgress) + (FULL_GROUP_ROTATION * (mRotationCount / NUM_POINTS));
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
resetOriginals();
}
private void initStrokeInset(float width, float height) {
float minSize = Math.min(width, height);
float strokeInset = minSize / 2.0f - mCenterRadius;
float minStrokeInset = (float) Math.ceil(mStrokeWidth / 2.0f);
mStrokeInset = strokeInset < minStrokeInset ? minStrokeInset : strokeInset;
}
private void storeOriginals() {
mOriginEndDegrees = mEndDegrees;
mOriginStartDegrees = mEndDegrees;
}
private void resetOriginals() {
mOriginEndDegrees = 0;
mOriginStartDegrees = 0;
mEndDegrees = 0;
mStartDegrees = 0;
mLevelSwipeDegrees[0] = 0;
mLevelSwipeDegrees[1] = 0;
mLevelSwipeDegrees[2] = 0;
}
private void apply(Builder builder) {
this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
this.mCenterRadius = builder.mCenterRadius > 0 ? builder.mCenterRadius : this.mCenterRadius;
this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
this.mLevelColors = builder.mLevelColors != null ? builder.mLevelColors : this.mLevelColors;
setupPaint();
initStrokeInset(this.mWidth, this.mHeight);
}
public static class Builder {
private Context mContext;
private int mWidth;
private int mHeight;
private int mStrokeWidth;
private int mCenterRadius;
private int mDuration;
@Size(3)
private int[] mLevelColors;
public Builder(Context mContext) {
this.mContext = mContext;
}
public Builder setWidth(int width) {
this.mWidth = width;
return this;
}
public Builder setHeight(int height) {
this.mHeight = height;
return this;
}
public Builder setStrokeWidth(int strokeWidth) {
this.mStrokeWidth = strokeWidth;
return this;
}
public Builder setCenterRadius(int centerRadius) {
this.mCenterRadius = centerRadius;
return this;
}
public Builder setDuration(int duration) {
this.mDuration = duration;
return this;
}
public Builder setLevelColors(@Size(3) int[] colors) {
this.mLevelColors = colors;
return this;
}
public Builder setLevelColor(int color) {
return setLevelColors(new int[]{oneThirdAlphaColor(color), twoThirdAlphaColor(color), color});
}
public LevelLoadingRenderer build() {
LevelLoadingRenderer loadingRenderer = new LevelLoadingRenderer(mContext);
loadingRenderer.apply(this);
return loadingRenderer;
}
private int oneThirdAlphaColor(int colorValue) {
int startA = (colorValue >> 24) & 0xff;
int startR = (colorValue >> 16) & 0xff;
int startG = (colorValue >> 8) & 0xff;
int startB = colorValue & 0xff;
return (startA / 3 << 24) | (startR << 16) | (startG << 8) | startB;
}
private int twoThirdAlphaColor(int colorValue) {
int startA = (colorValue >> 24) & 0xff;
int startR = (colorValue >> 16) & 0xff;
int startG = (colorValue >> 8) & 0xff;
int startB = colorValue & 0xff;
return (startA * 2 / 3 << 24) | (startR << 16) | (startG << 8) | startB;
}
}
}

View File

@@ -0,0 +1,292 @@
package app.dinus.com.loadingdrawable.render.circle.rotate;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.animation.Interpolator;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class MaterialLoadingRenderer extends LoadingRenderer {
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
private static final int DEGREE_360 = 360;
private static final int NUM_POINTS = 5;
private static final float MAX_SWIPE_DEGREES = 0.8f * DEGREE_360;
private static final float FULL_GROUP_ROTATION = 3.0f * DEGREE_360;
private static final float COLOR_START_DELAY_OFFSET = 0.8f;
private static final float END_TRIM_DURATION_OFFSET = 1.0f;
private static final float START_TRIM_DURATION_OFFSET = 0.5f;
private static final float DEFAULT_CENTER_RADIUS = 12.5f;
private static final float DEFAULT_STROKE_WIDTH = 2.5f;
private static final int[] DEFAULT_COLORS = new int[]{
Color.RED, Color.GREEN, Color.BLUE
};
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animator) {
super.onAnimationRepeat(animator);
storeOriginals();
goToNextColor();
mStartDegrees = mEndDegrees;
mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mRotationCount = 0;
}
};
private int[] mColors;
private int mColorIndex;
private int mCurrentColor;
private float mStrokeInset;
private float mRotationCount;
private float mGroupRotation;
private float mEndDegrees;
private float mStartDegrees;
private float mSwipeDegrees;
private float mOriginEndDegrees;
private float mOriginStartDegrees;
private float mStrokeWidth;
private float mCenterRadius;
private MaterialLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
addRenderListener(mAnimatorListener);
}
private void init(Context context) {
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
mColors = DEFAULT_COLORS;
setColorIndex(0);
initStrokeInset(mWidth, mHeight);
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void draw(Canvas canvas) {
int saveCount = canvas.save();
mTempBounds.set(mBounds);
mTempBounds.inset(mStrokeInset, mStrokeInset);
canvas.rotate(mGroupRotation, mTempBounds.centerX(), mTempBounds.centerY());
if (mSwipeDegrees != 0) {
mPaint.setColor(mCurrentColor);
canvas.drawArc(mTempBounds, mStartDegrees, mSwipeDegrees, false, mPaint);
}
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
updateRingColor(renderProgress);
// Moving the start trim only occurs in the first 50% of a single ring animation
if (renderProgress <= START_TRIM_DURATION_OFFSET) {
float startTrimProgress = renderProgress / START_TRIM_DURATION_OFFSET;
mStartDegrees = mOriginStartDegrees + MAX_SWIPE_DEGREES
* MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
}
// Moving the end trim starts after 50% of a single ring animation completes
if (renderProgress > START_TRIM_DURATION_OFFSET) {
float endTrimProgress = (renderProgress - START_TRIM_DURATION_OFFSET)
/ (END_TRIM_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
mEndDegrees = mOriginEndDegrees + MAX_SWIPE_DEGREES
* MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
}
if (Math.abs(mEndDegrees - mStartDegrees) > 0) {
mSwipeDegrees = mEndDegrees - mStartDegrees;
}
mGroupRotation = ((FULL_GROUP_ROTATION / NUM_POINTS) * renderProgress)
+ (FULL_GROUP_ROTATION * (mRotationCount / NUM_POINTS));
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
resetOriginals();
}
private void setColorIndex(int index) {
mColorIndex = index;
mCurrentColor = mColors[mColorIndex];
}
private int getNextColor() {
return mColors[getNextColorIndex()];
}
private int getNextColorIndex() {
return (mColorIndex + 1) % (mColors.length);
}
private void goToNextColor() {
setColorIndex(getNextColorIndex());
}
private void initStrokeInset(float width, float height) {
float minSize = Math.min(width, height);
float strokeInset = minSize / 2.0f - mCenterRadius;
float minStrokeInset = (float) Math.ceil(mStrokeWidth / 2.0f);
mStrokeInset = strokeInset < minStrokeInset ? minStrokeInset : strokeInset;
}
private void storeOriginals() {
mOriginEndDegrees = mEndDegrees;
mOriginStartDegrees = mEndDegrees;
}
private void resetOriginals() {
mOriginEndDegrees = 0;
mOriginStartDegrees = 0;
mEndDegrees = 0;
mStartDegrees = 0;
}
private int getStartingColor() {
return mColors[mColorIndex];
}
private void updateRingColor(float interpolatedTime) {
if (interpolatedTime > COLOR_START_DELAY_OFFSET) {
mCurrentColor = evaluateColorChange((interpolatedTime - COLOR_START_DELAY_OFFSET)
/ (1.0f - COLOR_START_DELAY_OFFSET), getStartingColor(), getNextColor());
}
}
private int evaluateColorChange(float fraction, int startValue, int endValue) {
int startA = (startValue >> 24) & 0xff;
int startR = (startValue >> 16) & 0xff;
int startG = (startValue >> 8) & 0xff;
int startB = startValue & 0xff;
int endA = (endValue >> 24) & 0xff;
int endR = (endValue >> 16) & 0xff;
int endG = (endValue >> 8) & 0xff;
int endB = endValue & 0xff;
return ((startA + (int) (fraction * (endA - startA))) << 24)
| ((startR + (int) (fraction * (endR - startR))) << 16)
| ((startG + (int) (fraction * (endG - startG))) << 8)
| ((startB + (int) (fraction * (endB - startB))));
}
private void apply(Builder builder) {
this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
this.mCenterRadius = builder.mCenterRadius > 0 ? builder.mCenterRadius : this.mCenterRadius;
this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
this.mColors = builder.mColors != null && builder.mColors.length > 0 ? builder.mColors : this.mColors;
setColorIndex(0);
setupPaint();
initStrokeInset(this.mWidth, this.mHeight);
}
public static class Builder {
private Context mContext;
private int mWidth;
private int mHeight;
private int mStrokeWidth;
private int mCenterRadius;
private int mDuration;
private int[] mColors;
public Builder(Context mContext) {
this.mContext = mContext;
}
public Builder setWidth(int width) {
this.mWidth = width;
return this;
}
public Builder setHeight(int height) {
this.mHeight = height;
return this;
}
public Builder setStrokeWidth(int strokeWidth) {
this.mStrokeWidth = strokeWidth;
return this;
}
public Builder setCenterRadius(int centerRadius) {
this.mCenterRadius = centerRadius;
return this;
}
public Builder setDuration(int duration) {
this.mDuration = duration;
return this;
}
public Builder setColors(int[] colors) {
this.mColors = colors;
return this;
}
public MaterialLoadingRenderer build() {
MaterialLoadingRenderer loadingRenderer = new MaterialLoadingRenderer(mContext);
loadingRenderer.apply(this);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,261 @@
package app.dinus.com.loadingdrawable.render.circle.rotate;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.animation.Interpolator;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class WhorlLoadingRenderer extends LoadingRenderer {
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
private static final int DEGREE_180 = 180;
private static final int DEGREE_360 = 360;
private static final int NUM_POINTS = 5;
private static final float MAX_SWIPE_DEGREES = 0.6f * DEGREE_360;
private static final float FULL_GROUP_ROTATION = 3.0f * DEGREE_360;
private static final float START_TRIM_DURATION_OFFSET = 0.5f;
private static final float END_TRIM_DURATION_OFFSET = 1.0f;
private static final float DEFAULT_CENTER_RADIUS = 12.5f;
private static final float DEFAULT_STROKE_WIDTH = 2.5f;
private static final int[] DEFAULT_COLORS = new int[]{
Color.RED, Color.GREEN, Color.BLUE
};
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final RectF mTempArcBounds = new RectF();
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animator) {
super.onAnimationRepeat(animator);
storeOriginals();
mStartDegrees = mEndDegrees;
mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mRotationCount = 0;
}
};
private int[] mColors;
private float mStrokeInset;
private float mRotationCount;
private float mGroupRotation;
private float mEndDegrees;
private float mStartDegrees;
private float mSwipeDegrees;
private float mOriginEndDegrees;
private float mOriginStartDegrees;
private float mStrokeWidth;
private float mCenterRadius;
private WhorlLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
addRenderListener(mAnimatorListener);
}
private void init(Context context) {
mColors = DEFAULT_COLORS;
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
initStrokeInset(mWidth, mHeight);
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void draw(Canvas canvas) {
int saveCount = canvas.save();
mTempBounds.set(mBounds);
mTempBounds.inset(mStrokeInset, mStrokeInset);
canvas.rotate(mGroupRotation, mTempBounds.centerX(), mTempBounds.centerY());
if (mSwipeDegrees != 0) {
for (int i = 0; i < mColors.length; i++) {
mPaint.setStrokeWidth(mStrokeWidth / (i + 1));
mPaint.setColor(mColors[i]);
canvas.drawArc(createArcBounds(mTempBounds, i), mStartDegrees + DEGREE_180 * (i % 2),
mSwipeDegrees, false, mPaint);
}
}
canvas.restoreToCount(saveCount);
}
private RectF createArcBounds(RectF sourceArcBounds, int index) {
int intervalWidth = 0;
for (int i = 0; i < index; i++) {
intervalWidth += mStrokeWidth / (i + 1.0f) * 1.5f;
}
int arcBoundsLeft = (int) (sourceArcBounds.left + intervalWidth);
int arcBoundsTop = (int) (sourceArcBounds.top + intervalWidth);
int arcBoundsRight = (int) (sourceArcBounds.right - intervalWidth);
int arcBoundsBottom = (int) (sourceArcBounds.bottom - intervalWidth);
mTempArcBounds.set(arcBoundsLeft, arcBoundsTop, arcBoundsRight, arcBoundsBottom);
return mTempArcBounds;
}
@Override
protected void computeRender(float renderProgress) {
// Moving the start trim only occurs in the first 50% of a single ring animation
if (renderProgress <= START_TRIM_DURATION_OFFSET) {
float startTrimProgress = (renderProgress) / (1.0f - START_TRIM_DURATION_OFFSET);
mStartDegrees = mOriginStartDegrees + MAX_SWIPE_DEGREES * MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
}
// Moving the end trim starts after 50% of a single ring animation
if (renderProgress > START_TRIM_DURATION_OFFSET) {
float endTrimProgress = (renderProgress - START_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
mEndDegrees = mOriginEndDegrees + MAX_SWIPE_DEGREES * MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
}
if (Math.abs(mEndDegrees - mStartDegrees) > 0) {
mSwipeDegrees = mEndDegrees - mStartDegrees;
}
mGroupRotation = ((FULL_GROUP_ROTATION / NUM_POINTS) * renderProgress) + (FULL_GROUP_ROTATION * (mRotationCount / NUM_POINTS));
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
resetOriginals();
}
private void initStrokeInset(float width, float height) {
float minSize = Math.min(width, height);
float strokeInset = minSize / 2.0f - mCenterRadius;
float minStrokeInset = (float) Math.ceil(mStrokeWidth / 2.0f);
mStrokeInset = strokeInset < minStrokeInset ? minStrokeInset : strokeInset;
}
private void storeOriginals() {
mOriginEndDegrees = mEndDegrees;
mOriginStartDegrees = mEndDegrees;
}
private void resetOriginals() {
mOriginEndDegrees = 0;
mOriginStartDegrees = 0;
mEndDegrees = 0;
mStartDegrees = 0;
mSwipeDegrees = 0;
}
private void apply(Builder builder) {
this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
this.mCenterRadius = builder.mCenterRadius > 0 ? builder.mCenterRadius : this.mCenterRadius;
this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
this.mColors = builder.mColors != null && builder.mColors.length > 0 ? builder.mColors : this.mColors;
setupPaint();
initStrokeInset(this.mWidth, this.mHeight);
}
public static class Builder {
private Context mContext;
private int mWidth;
private int mHeight;
private int mStrokeWidth;
private int mCenterRadius;
private int mDuration;
private int[] mColors;
public Builder(Context mContext) {
this.mContext = mContext;
}
public Builder setWidth(int width) {
this.mWidth = width;
return this;
}
public Builder setHeight(int height) {
this.mHeight = height;
return this;
}
public Builder setStrokeWidth(int strokeWidth) {
this.mStrokeWidth = strokeWidth;
return this;
}
public Builder setCenterRadius(int centerRadius) {
this.mCenterRadius = centerRadius;
return this;
}
public Builder setDuration(int duration) {
this.mDuration = duration;
return this;
}
public Builder setColors(int[] colors) {
this.mColors = colors;
return this;
}
public WhorlLoadingRenderer build() {
WhorlLoadingRenderer loadingRenderer = new WhorlLoadingRenderer(mContext);
loadingRenderer.apply(this);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,311 @@
package app.dinus.com.loadingdrawable.render.goods;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class BalloonLoadingRenderer extends LoadingRenderer {
private static final String PERCENT_SIGN = "%";
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final float START_INHALE_DURATION_OFFSET = 0.4f;
private static final float DEFAULT_WIDTH = 200.0f;
private static final float DEFAULT_HEIGHT = 150.0f;
private static final float DEFAULT_STROKE_WIDTH = 2.0f;
private static final float DEFAULT_GAS_TUBE_WIDTH = 48;
private static final float DEFAULT_GAS_TUBE_HEIGHT = 20;
private static final float DEFAULT_CANNULA_WIDTH = 13;
private static final float DEFAULT_CANNULA_HEIGHT = 37;
private static final float DEFAULT_CANNULA_OFFSET_Y = 3;
private static final float DEFAULT_CANNULA_MAX_OFFSET_Y = 15;
private static final float DEFAULT_PIPE_BODY_WIDTH = 16;
private static final float DEFAULT_PIPE_BODY_HEIGHT = 36;
private static final float DEFAULT_BALLOON_WIDTH = 38;
private static final float DEFAULT_BALLOON_HEIGHT = 48;
private static final float DEFAULT_RECT_CORNER_RADIUS = 2;
private static final int DEFAULT_BALLOON_COLOR = Color.parseColor("#ffF3C211");
private static final int DEFAULT_GAS_TUBE_COLOR = Color.parseColor("#ff174469");
private static final int DEFAULT_PIPE_BODY_COLOR = Color.parseColor("#aa2369B1");
private static final int DEFAULT_CANNULA_COLOR = Color.parseColor("#ff174469");
private static final float DEFAULT_TEXT_SIZE = 7.0f;
private static final long ANIMATION_DURATION = 3333;
private final Paint mPaint = new Paint();
private final RectF mCurrentBounds = new RectF();
private final RectF mGasTubeBounds = new RectF();
private final RectF mPipeBodyBounds = new RectF();
private final RectF mCannulaBounds = new RectF();
private final RectF mBalloonBounds = new RectF();
private final Rect mProgressBounds = new Rect();
private float mTextSize;
private float mProgress;
private String mProgressText;
private float mGasTubeWidth;
private float mGasTubeHeight;
private float mCannulaWidth;
private float mCannulaHeight;
private float mCannulaMaxOffsetY;
private float mCannulaOffsetY;
private float mPipeBodyWidth;
private float mPipeBodyHeight;
private float mBalloonWidth;
private float mBalloonHeight;
private float mRectCornerRadius;
private float mStrokeWidth;
private int mBalloonColor;
private int mGasTubeColor;
private int mCannulaColor;
private int mPipeBodyColor;
private BalloonLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
}
private void init(Context context) {
mTextSize = DensityUtil.dip2px(context, DEFAULT_TEXT_SIZE);
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mGasTubeWidth = DensityUtil.dip2px(context, DEFAULT_GAS_TUBE_WIDTH);
mGasTubeHeight = DensityUtil.dip2px(context, DEFAULT_GAS_TUBE_HEIGHT);
mCannulaWidth = DensityUtil.dip2px(context, DEFAULT_CANNULA_WIDTH);
mCannulaHeight = DensityUtil.dip2px(context, DEFAULT_CANNULA_HEIGHT);
mCannulaOffsetY = DensityUtil.dip2px(context, DEFAULT_CANNULA_OFFSET_Y);
mCannulaMaxOffsetY = DensityUtil.dip2px(context, DEFAULT_CANNULA_MAX_OFFSET_Y);
mPipeBodyWidth = DensityUtil.dip2px(context, DEFAULT_PIPE_BODY_WIDTH);
mPipeBodyHeight = DensityUtil.dip2px(context, DEFAULT_PIPE_BODY_HEIGHT);
mBalloonWidth = DensityUtil.dip2px(context, DEFAULT_BALLOON_WIDTH);
mBalloonHeight = DensityUtil.dip2px(context, DEFAULT_BALLOON_HEIGHT);
mRectCornerRadius = DensityUtil.dip2px(context, DEFAULT_RECT_CORNER_RADIUS);
mBalloonColor = DEFAULT_BALLOON_COLOR;
mGasTubeColor = DEFAULT_GAS_TUBE_COLOR;
mCannulaColor = DEFAULT_CANNULA_COLOR;
mPipeBodyColor = DEFAULT_PIPE_BODY_COLOR;
mProgressText = 10 + PERCENT_SIGN;
mDuration = ANIMATION_DURATION;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
RectF arcBounds = mCurrentBounds;
arcBounds.set(bounds);
//draw draw gas tube
mPaint.setColor(mGasTubeColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
canvas.drawPath(createGasTubePath(mGasTubeBounds), mPaint);
//draw balloon
mPaint.setColor(mBalloonColor);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawPath(createBalloonPath(mBalloonBounds, mProgress), mPaint);
//draw progress
mPaint.setColor(mGasTubeColor);
mPaint.setTextSize(mTextSize);
mPaint.setStrokeWidth(mStrokeWidth / 5.0f);
canvas.drawText(mProgressText, arcBounds.centerX() - mProgressBounds.width() / 2.0f,
mGasTubeBounds.centerY() + mProgressBounds.height() / 2.0f, mPaint);
//draw cannula
mPaint.setColor(mCannulaColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
canvas.drawPath(createCannulaHeadPath(mCannulaBounds), mPaint);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(createCannulaBottomPath(mCannulaBounds), mPaint);
//draw pipe body
mPaint.setColor(mPipeBodyColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRoundRect(mPipeBodyBounds, mRectCornerRadius, mRectCornerRadius, mPaint);
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
RectF arcBounds = mCurrentBounds;
//compute gas tube bounds
mGasTubeBounds.set(arcBounds.centerX() - mGasTubeWidth / 2.0f, arcBounds.centerY(),
arcBounds.centerX() + mGasTubeWidth / 2.0f, arcBounds.centerY() + mGasTubeHeight);
//compute pipe body bounds
mPipeBodyBounds.set(arcBounds.centerX() + mGasTubeWidth / 2.0f - mPipeBodyWidth / 2.0f, arcBounds.centerY() - mPipeBodyHeight,
arcBounds.centerX() + mGasTubeWidth / 2.0f + mPipeBodyWidth / 2.0f, arcBounds.centerY());
//compute cannula bounds
mCannulaBounds.set(arcBounds.centerX() + mGasTubeWidth / 2.0f - mCannulaWidth / 2.0f, arcBounds.centerY() - mCannulaHeight - mCannulaOffsetY,
arcBounds.centerX() + mGasTubeWidth / 2.0f + mCannulaWidth / 2.0f, arcBounds.centerY() - mCannulaOffsetY);
//compute balloon bounds
float insetX = mBalloonWidth * 0.333f * (1 - mProgress);
float insetY = mBalloonHeight * 0.667f * (1 - mProgress);
mBalloonBounds.set(arcBounds.centerX() - mGasTubeWidth / 2.0f - mBalloonWidth / 2.0f + insetX, arcBounds.centerY() - mBalloonHeight + insetY,
arcBounds.centerX() - mGasTubeWidth / 2.0f + mBalloonWidth / 2.0f - insetX, arcBounds.centerY());
if (renderProgress <= START_INHALE_DURATION_OFFSET) {
mCannulaBounds.offset(0, -mCannulaMaxOffsetY * renderProgress / START_INHALE_DURATION_OFFSET);
mProgress = 0.0f;
mProgressText = 10 + PERCENT_SIGN;
mPaint.setTextSize(mTextSize);
mPaint.getTextBounds(mProgressText, 0, mProgressText.length(), mProgressBounds);
} else {
float exhaleProgress = ACCELERATE_INTERPOLATOR.getInterpolation(1.0f - (renderProgress - START_INHALE_DURATION_OFFSET) / (1.0f - START_INHALE_DURATION_OFFSET));
mCannulaBounds.offset(0, -mCannulaMaxOffsetY * exhaleProgress);
mProgress = 1.0f - exhaleProgress;
mProgressText = adjustProgress((int) (exhaleProgress * 100.0f)) + PERCENT_SIGN;
mPaint.setTextSize(mTextSize);
mPaint.getTextBounds(mProgressText, 0, mProgressText.length(), mProgressBounds);
}
}
private int adjustProgress(int progress) {
progress = progress / 10 * 10;
progress = 100 - progress + 10;
if (progress > 100) {
progress = 100;
}
return progress;
}
private Path createGasTubePath(RectF gasTubeRect) {
Path path = new Path();
path.moveTo(gasTubeRect.left, gasTubeRect.top);
path.lineTo(gasTubeRect.left, gasTubeRect.bottom);
path.lineTo(gasTubeRect.right, gasTubeRect.bottom);
path.lineTo(gasTubeRect.right, gasTubeRect.top);
return path;
}
private Path createCannulaHeadPath(RectF cannulaRect) {
Path path = new Path();
path.moveTo(cannulaRect.left, cannulaRect.top);
path.lineTo(cannulaRect.right, cannulaRect.top);
path.moveTo(cannulaRect.centerX(), cannulaRect.top);
path.lineTo(cannulaRect.centerX(), cannulaRect.bottom - 0.833f * cannulaRect.width());
return path;
}
private Path createCannulaBottomPath(RectF cannulaRect) {
RectF cannulaHeadRect = new RectF(cannulaRect.left, cannulaRect.bottom - 0.833f * cannulaRect.width(),
cannulaRect.right, cannulaRect.bottom);
Path path = new Path();
path.addRoundRect(cannulaHeadRect, mRectCornerRadius, mRectCornerRadius, Path.Direction.CCW);
return path;
}
/**
* Coordinates are approximate, you have better cooperate with the designer's design draft
*/
private Path createBalloonPath(RectF balloonRect, float progress) {
Path path = new Path();
path.moveTo(balloonRect.centerX(), balloonRect.bottom);
float progressWidth = balloonRect.width() * progress;
float progressHeight = balloonRect.height() * progress;
//draw left half
float leftIncrementX1 = progressWidth * -0.48f;
float leftIncrementY1 = progressHeight * 0.75f;
float leftIncrementX2 = progressWidth * -0.03f;
float leftIncrementY2 = progressHeight * -1.6f;
float leftIncrementX3 = progressWidth * 0.9f;
float leftIncrementY3 = progressHeight * -1.0f;
path.cubicTo(balloonRect.left + balloonRect.width() * 0.25f + leftIncrementX1, balloonRect.centerY() - balloonRect.height() * 0.4f + leftIncrementY1,
balloonRect.left - balloonRect.width() * 0.20f + leftIncrementX2, balloonRect.centerY() + balloonRect.height() * 1.15f + leftIncrementY2,
balloonRect.left - balloonRect.width() * 0.4f + leftIncrementX3, balloonRect.bottom + leftIncrementY3);
// the results of the left final transformation
// path.cubicTo(balloonRect.left - balloonRect.width() * 0.13f, balloonRect.centerY() + balloonRect.height() * 0.35f,
// balloonRect.left - balloonRect.width() * 0.23f, balloonRect.centerY() - balloonRect.height() * 0.45f,
// balloonRect.left + balloonRect.width() * 0.5f, balloonRect.bottom balloonRect.height());
//draw right half
float rightIncrementX1 = progressWidth * 1.51f;
float rightIncrementY1 = progressHeight * -0.05f;
float rightIncrementX2 = progressWidth * 0.03f;
float rightIncrementY2 = progressHeight * 0.5f;
float rightIncrementX3 = 0.0f;
float rightIncrementY3 = 0.0f;
path.cubicTo(balloonRect.left - balloonRect.width() * 0.38f + rightIncrementX1, balloonRect.centerY() - balloonRect.height() * 0.4f + rightIncrementY1,
balloonRect.left + balloonRect.width() * 1.1f + rightIncrementX2, balloonRect.centerY() - balloonRect.height() * 0.15f + rightIncrementY2,
balloonRect.left + balloonRect.width() * 0.5f + rightIncrementX3, balloonRect.bottom + rightIncrementY3);
// the results of the right final transformation
// path.cubicTo(balloonRect.left + balloonRect.width() * 1.23f, balloonRect.centerY() - balloonRect.height() * 0.45f,
// balloonRect.left + balloonRect.width() * 1.13f, balloonRect.centerY() + balloonRect.height() * 0.35f,
// balloonRect.left + balloonRect.width() * 0.5f, balloonRect.bottom);
return path;
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public BalloonLoadingRenderer build() {
BalloonLoadingRenderer loadingRenderer = new BalloonLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,323 @@
package app.dinus.com.loadingdrawable.render.goods;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.view.animation.Interpolator;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class WaterBottleLoadingRenderer extends LoadingRenderer {
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
private static final float DEFAULT_WIDTH = 200.0f;
private static final float DEFAULT_HEIGHT = 150.0f;
private static final float DEFAULT_STROKE_WIDTH = 1.5f;
private static final float DEFAULT_BOTTLE_WIDTH = 30;
private static final float DEFAULT_BOTTLE_HEIGHT = 43;
private static final float WATER_LOWEST_POINT_TO_BOTTLENECK_DISTANCE = 30;
private static final int DEFAULT_WAVE_COUNT = 5;
private static final int DEFAULT_WATER_DROP_COUNT = 25;
private static final int MAX_WATER_DROP_RADIUS = 5;
private static final int MIN_WATER_DROP_RADIUS = 1;
private static final int DEFAULT_BOTTLE_COLOR = Color.parseColor("#FFDAEBEB");
private static final int DEFAULT_WATER_COLOR = Color.parseColor("#FF29E3F2");
private static final float DEFAULT_TEXT_SIZE = 7.0f;
private static final String LOADING_TEXT = "loading";
private static final long ANIMATION_DURATION = 11111;
private final Random mRandom = new Random();
private final Paint mPaint = new Paint();
private final RectF mCurrentBounds = new RectF();
private final RectF mBottleBounds = new RectF();
private final RectF mWaterBounds = new RectF();
private final Rect mLoadingBounds = new Rect();
private final List<WaterDropHolder> mWaterDropHolders = new ArrayList<>();
private float mTextSize;
private float mProgress;
private float mBottleWidth;
private float mBottleHeight;
private float mStrokeWidth;
private float mWaterLowestPointToBottleneckDistance;
private int mBottleColor;
private int mWaterColor;
private int mWaveCount;
private WaterBottleLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
}
private void init(Context context) {
mTextSize = DensityUtil.dip2px(context, DEFAULT_TEXT_SIZE);
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mBottleWidth = DensityUtil.dip2px(context, DEFAULT_BOTTLE_WIDTH);
mBottleHeight = DensityUtil.dip2px(context, DEFAULT_BOTTLE_HEIGHT);
mWaterLowestPointToBottleneckDistance = DensityUtil.dip2px(context, WATER_LOWEST_POINT_TO_BOTTLENECK_DISTANCE);
mBottleColor = DEFAULT_BOTTLE_COLOR;
mWaterColor = DEFAULT_WATER_COLOR;
mWaveCount = DEFAULT_WAVE_COUNT;
mDuration = ANIMATION_DURATION;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStrokeJoin(Paint.Join.ROUND);
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
RectF arcBounds = mCurrentBounds;
arcBounds.set(bounds);
//draw bottle
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mBottleColor);
canvas.drawPath(createBottlePath(mBottleBounds), mPaint);
//draw water
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(mWaterColor);
canvas.drawPath(createWaterPath(mWaterBounds, mProgress), mPaint);
//draw water drop
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mWaterColor);
for (WaterDropHolder waterDropHolder : mWaterDropHolders) {
if (waterDropHolder.mNeedDraw) {
canvas.drawCircle(waterDropHolder.mInitX, waterDropHolder.mCurrentY, waterDropHolder.mRadius, mPaint);
}
}
//draw loading text
mPaint.setColor(mBottleColor);
canvas.drawText(LOADING_TEXT, mBottleBounds.centerX() - mLoadingBounds.width() / 2.0f,
mBottleBounds.bottom + mBottleBounds.height() * 0.2f, mPaint);
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
if (mCurrentBounds.width() <= 0) {
return;
}
RectF arcBounds = mCurrentBounds;
//compute gas tube bounds
mBottleBounds.set(arcBounds.centerX() - mBottleWidth / 2.0f, arcBounds.centerY() - mBottleHeight / 2.0f,
arcBounds.centerX() + mBottleWidth / 2.0f, arcBounds.centerY() + mBottleHeight / 2.0f);
//compute pipe body bounds
mWaterBounds.set(mBottleBounds.left + mStrokeWidth * 1.5f, mBottleBounds.top + mWaterLowestPointToBottleneckDistance,
mBottleBounds.right - mStrokeWidth * 1.5f, mBottleBounds.bottom - mStrokeWidth * 1.5f);
//compute wave progress
float totalWaveProgress = renderProgress * mWaveCount;
float currentWaveProgress = totalWaveProgress - ((int) totalWaveProgress);
if (currentWaveProgress > 0.5f) {
mProgress = 1.0f - MATERIAL_INTERPOLATOR.getInterpolation((currentWaveProgress - 0.5f) * 2.0f);
} else {
mProgress = MATERIAL_INTERPOLATOR.getInterpolation(currentWaveProgress * 2.0f);
}
//init water drop holders
if (mWaterDropHolders.isEmpty()) {
initWaterDropHolders(mBottleBounds, mWaterBounds);
}
//compute the location of these water drops
for (WaterDropHolder waterDropHolder : mWaterDropHolders) {
if (waterDropHolder.mDelayDuration < renderProgress
&& waterDropHolder.mDelayDuration + waterDropHolder.mDuration > renderProgress) {
float riseProgress = (renderProgress - waterDropHolder.mDelayDuration) / waterDropHolder.mDuration;
riseProgress = riseProgress < 0.5f ? riseProgress * 2.0f : 1.0f - (riseProgress - 0.5f) * 2.0f;
waterDropHolder.mCurrentY = waterDropHolder.mInitY -
MATERIAL_INTERPOLATOR.getInterpolation(riseProgress) * waterDropHolder.mRiseHeight;
waterDropHolder.mNeedDraw = true;
} else {
waterDropHolder.mNeedDraw = false;
}
}
//measure loading text
mPaint.setTextSize(mTextSize);
mPaint.getTextBounds(LOADING_TEXT, 0, LOADING_TEXT.length(), mLoadingBounds);
}
private Path createBottlePath(RectF bottleRect) {
float bottleneckWidth = bottleRect.width() * 0.3f;
float bottleneckHeight = bottleRect.height() * 0.415f;
float bottleneckDecorationWidth = bottleneckWidth * 1.1f;
float bottleneckDecorationHeight = bottleneckHeight * 0.167f;
Path path = new Path();
//draw the left side of the bottleneck decoration
path.moveTo(bottleRect.centerX() - bottleneckDecorationWidth * 0.5f, bottleRect.top);
path.quadTo(bottleRect.centerX() - bottleneckDecorationWidth * 0.5f - bottleneckWidth * 0.15f, bottleRect.top + bottleneckDecorationHeight * 0.5f,
bottleRect.centerX() - bottleneckWidth * 0.5f, bottleRect.top + bottleneckDecorationHeight);
path.lineTo(bottleRect.centerX() - bottleneckWidth * 0.5f, bottleRect.top + bottleneckHeight);
//draw the left side of the bottle's body
float radius = (bottleRect.width() - mStrokeWidth) / 2.0f;
float centerY = bottleRect.bottom - 0.86f * radius;
RectF bodyRect = new RectF(bottleRect.left, centerY - radius, bottleRect.right, centerY + radius);
path.addArc(bodyRect, 255, -135);
//draw the bottom of the bottle
float bottleBottomWidth = bottleRect.width() / 2.0f;
path.lineTo(bottleRect.centerX() - bottleBottomWidth / 2.0f, bottleRect.bottom);
path.lineTo(bottleRect.centerX() + bottleBottomWidth / 2.0f, bottleRect.bottom);
//draw the right side of the bottle's body
path.addArc(bodyRect, 60, -135);
//draw the right side of the bottleneck decoration
path.lineTo(bottleRect.centerX() + bottleneckWidth * 0.5f, bottleRect.top + bottleneckDecorationHeight);
path.quadTo(bottleRect.centerX() + bottleneckDecorationWidth * 0.5f + bottleneckWidth * 0.15f, bottleRect.top + bottleneckDecorationHeight * 0.5f,
bottleRect.centerX() + bottleneckDecorationWidth * 0.5f, bottleRect.top);
return path;
}
private Path createWaterPath(RectF waterRect, float progress) {
Path path = new Path();
path.moveTo(waterRect.left, waterRect.top);
//Similar to the way draw the bottle's bottom sides
float radius = (waterRect.width() - mStrokeWidth) / 2.0f;
float centerY = waterRect.bottom - 0.86f * radius;
float bottleBottomWidth = waterRect.width() / 2.0f;
RectF bodyRect = new RectF(waterRect.left, centerY - radius, waterRect.right, centerY + radius);
path.addArc(bodyRect, 187.5f, -67.5f);
path.lineTo(waterRect.centerX() - bottleBottomWidth / 2.0f, waterRect.bottom);
path.lineTo(waterRect.centerX() + bottleBottomWidth / 2.0f, waterRect.bottom);
path.addArc(bodyRect, 60, -67.5f);
//draw the water waves
float cubicXChangeSize = waterRect.width() * 0.35f * progress;
float cubicYChangeSize = waterRect.height() * 1.2f * progress;
path.cubicTo(waterRect.left + waterRect.width() * 0.80f - cubicXChangeSize, waterRect.top - waterRect.height() * 1.2f + cubicYChangeSize,
waterRect.left + waterRect.width() * 0.55f - cubicXChangeSize, waterRect.top - cubicYChangeSize,
waterRect.left, waterRect.top - mStrokeWidth / 2.0f);
path.lineTo(waterRect.left, waterRect.top);
return path;
}
private void initWaterDropHolders(RectF bottleRect, RectF waterRect) {
float bottleRadius = bottleRect.width() / 2.0f;
float lowestWaterPointY = waterRect.top;
float twoSidesInterval = 0.2f * bottleRect.width();
float atLeastDelayDuration = 0.1f;
float unitDuration = 0.1f;
float delayDurationRange = 0.6f;
int radiusRandomRange = MAX_WATER_DROP_RADIUS - MIN_WATER_DROP_RADIUS;
float currentXRandomRange = bottleRect.width() * 0.6f;
for (int i = 0; i < DEFAULT_WATER_DROP_COUNT; i++) {
WaterDropHolder waterDropHolder = new WaterDropHolder();
waterDropHolder.mRadius = MIN_WATER_DROP_RADIUS + mRandom.nextInt(radiusRandomRange);
waterDropHolder.mInitX = bottleRect.left + twoSidesInterval + mRandom.nextFloat() * currentXRandomRange;
waterDropHolder.mInitY = lowestWaterPointY + waterDropHolder.mRadius / 2.0f;
waterDropHolder.mRiseHeight = getMaxRiseHeight(bottleRadius, waterDropHolder.mRadius, waterDropHolder.mInitX - bottleRect.left)
* (0.2f + 0.8f * mRandom.nextFloat());
waterDropHolder.mDelayDuration = atLeastDelayDuration + mRandom.nextFloat() * delayDurationRange;
waterDropHolder.mDuration = waterDropHolder.mRiseHeight / bottleRadius * unitDuration;
mWaterDropHolders.add(waterDropHolder);
}
}
private float getMaxRiseHeight(float bottleRadius, float waterDropRadius, float currentX) {
float coordinateX = currentX - bottleRadius;
float bottleneckRadius = bottleRadius * 0.3f;
if (coordinateX - waterDropRadius > -bottleneckRadius
&& coordinateX + waterDropRadius < bottleneckRadius) {
return bottleRadius * 2.0f;
}
return (float) (Math.sqrt(Math.pow(bottleRadius, 2.0f) - Math.pow(coordinateX, 2.0f)) - waterDropRadius);
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
}
private class WaterDropHolder {
public float mCurrentY;
public float mInitX;
public float mInitY;
public float mDelayDuration;
public float mRiseHeight;
public float mRadius;
public float mDuration;
public boolean mNeedDraw;
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public WaterBottleLoadingRenderer build() {
WaterBottleLoadingRenderer loadingRenderer = new WaterBottleLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,361 @@
package app.dinus.com.loadingdrawable.render.scenery;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class DayNightLoadingRenderer extends LoadingRenderer {
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final Interpolator FASTOUTLINEARIN_INTERPOLATOR = new FastOutLinearInInterpolator();
private static final Interpolator[] INTERPOLATORS = new Interpolator[]{LINEAR_INTERPOLATOR,
DECELERATE_INTERPOLATOR, ACCELERATE_INTERPOLATOR, FASTOUTLINEARIN_INTERPOLATOR, MATERIAL_INTERPOLATOR};
private static final int MAX_ALPHA = 255;
private static final int DEGREE_360 = 360;
private static final int MAX_SUN_RAY_COUNT = 12;
private static final float DEFAULT_WIDTH = 200.0f;
private static final float DEFAULT_HEIGHT = 150.0f;
private static final float DEFAULT_STROKE_WIDTH = 2.5f;
private static final float DEFAULT_SUN$MOON_RADIUS = 12.0f;
private static final float DEFAULT_STAR_RADIUS = 2.5f;
private static final float DEFAULT_SUN_RAY_LENGTH = 10.0f;
private static final float DEFAULT_SUN_RAY_OFFSET = 3.0f;
public static final float STAR_RISE_PROGRESS_OFFSET = 0.2f;
public static final float STAR_DECREASE_PROGRESS_OFFSET = 0.8f;
public static final float STAR_FLASH_PROGRESS_PERCENTAGE = 0.2f;
private static final float MAX_SUN_ROTATE_DEGREE = DEGREE_360 / 3.0f;
private static final float MAX_MOON_ROTATE_DEGREE = DEGREE_360 / 6.0f;
private static final float SUN_RAY_INTERVAL_DEGREE = DEGREE_360 / 3.0f / 55;
private static final float SUN_RISE_DURATION_OFFSET = 0.143f;
private static final float SUN_ROTATE_DURATION_OFFSET = 0.492f;
private static final float SUN_DECREASE_DURATION_OFFSET = 0.570f;
private static final float MOON_RISE_DURATION_OFFSET = 0.713f;
private static final float MOON_DECREASE_START_DURATION_OFFSET = 0.935f;
private static final float MOON_DECREASE_END_DURATION_OFFSET = 1.0f;
private static final float STAR_RISE_START_DURATION_OFFSET = 0.684f;
private static final float STAR_DECREASE_START_DURATION_OFFSET = 1.0f;
private static final int DEFAULT_COLOR = Color.parseColor("#ff21fd8e");
private static final long ANIMATION_DURATION = 5111;
private final Random mRandom = new Random();
private final List<StarHolder> mStarHolders = new ArrayList<>();
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animator) {
super.onAnimationRepeat(animator);
}
};
private int mCurrentColor;
private float mMaxStarOffsets;
private float mStrokeWidth;
private float mStarRadius;
private float mSun$MoonRadius;
private float mSunCoordinateY;
private float mMoonCoordinateY;
//the y-coordinate of the end point of the sun ray
private float mSunRayEndCoordinateY;
//the y-coordinate of the start point of the sun ray
private float mSunRayStartCoordinateY;
//the y-coordinate of the start point of the sun
private float mInitSun$MoonCoordinateY;
//the distance from the outside to the center of the drawable
private float mMaxSun$MoonRiseDistance;
private float mSunRayRotation;
private float mMoonRotation;
//the number of sun's rays is increasing
private boolean mIsExpandSunRay;
private boolean mShowStar;
private int mSunRayCount;
private DayNightLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
addRenderListener(mAnimatorListener);
}
private void init(Context context) {
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mStarRadius = DensityUtil.dip2px(context, DEFAULT_STAR_RADIUS);
mSun$MoonRadius = DensityUtil.dip2px(context, DEFAULT_SUN$MOON_RADIUS);
mInitSun$MoonCoordinateY = mHeight + mSun$MoonRadius + mStrokeWidth * 2.0f;
mMaxSun$MoonRiseDistance = mHeight / 2.0f + mSun$MoonRadius;
mSunRayStartCoordinateY = mInitSun$MoonCoordinateY - mMaxSun$MoonRiseDistance //the center
- mSun$MoonRadius //sub the radius
- mStrokeWidth // sub the with the sun circle
- DensityUtil.dip2px(context, DEFAULT_SUN_RAY_OFFSET); //sub the interval between the sun and the sun ray
//add strokeWidth * 2.0f because the stroke cap is Paint.Cap.ROUND
mSunRayEndCoordinateY = mSunRayStartCoordinateY - DensityUtil.dip2px(context, DEFAULT_SUN_RAY_LENGTH)
+ mStrokeWidth;
mSunCoordinateY = mInitSun$MoonCoordinateY;
mMoonCoordinateY = mInitSun$MoonCoordinateY;
mCurrentColor = DEFAULT_COLOR;
mDuration = ANIMATION_DURATION;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
RectF arcBounds = mTempBounds;
arcBounds.set(bounds);
mPaint.setAlpha(MAX_ALPHA);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mCurrentColor);
if (mSunCoordinateY < mInitSun$MoonCoordinateY) {
canvas.drawCircle(arcBounds.centerX(), mSunCoordinateY, mSun$MoonRadius, mPaint);
}
if (mMoonCoordinateY < mInitSun$MoonCoordinateY) {
int moonSaveCount = canvas.save();
canvas.rotate(mMoonRotation, arcBounds.centerX(), mMoonCoordinateY);
canvas.drawPath(createMoonPath(arcBounds.centerX(), mMoonCoordinateY), mPaint);
canvas.restoreToCount(moonSaveCount);
}
for (int i = 0; i < mSunRayCount; i++) {
int sunRaySaveCount = canvas.save();
//rotate 45 degrees can change the direction of 0 degrees to 1:30 clock
//-mSunRayRotation means reverse rotation
canvas.rotate(45 - mSunRayRotation
+ (mIsExpandSunRay ? i : MAX_SUN_RAY_COUNT - i) * DEGREE_360 / MAX_SUN_RAY_COUNT,
arcBounds.centerX(), mSunCoordinateY);
canvas.drawLine(arcBounds.centerX(), mSunRayStartCoordinateY, arcBounds.centerX(), mSunRayEndCoordinateY, mPaint);
canvas.restoreToCount(sunRaySaveCount);
}
if (mShowStar) {
if (mStarHolders.isEmpty()) {
initStarHolders(arcBounds);
}
for (int i = 0; i < mStarHolders.size(); i++) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAlpha(mStarHolders.get(i).mAlpha);
canvas.drawCircle(mStarHolders.get(i).mCurrentPoint.x, mStarHolders.get(i).mCurrentPoint.y, mStarRadius, mPaint);
}
}
canvas.restoreToCount(saveCount);
}
@Override
protected void computeRender(float renderProgress) {
if (renderProgress <= SUN_RISE_DURATION_OFFSET) {
float sunRiseProgress = renderProgress / SUN_RISE_DURATION_OFFSET;
mSunCoordinateY = mInitSun$MoonCoordinateY - mMaxSun$MoonRiseDistance * MATERIAL_INTERPOLATOR.getInterpolation(sunRiseProgress);
mMoonCoordinateY = mInitSun$MoonCoordinateY;
mShowStar = false;
}
if (renderProgress <= SUN_ROTATE_DURATION_OFFSET && renderProgress > SUN_RISE_DURATION_OFFSET) {
float sunRotateProgress = (renderProgress - SUN_RISE_DURATION_OFFSET) / (SUN_ROTATE_DURATION_OFFSET - SUN_RISE_DURATION_OFFSET);
mSunRayRotation = sunRotateProgress * MAX_SUN_ROTATE_DEGREE;
if ((int) (mSunRayRotation / SUN_RAY_INTERVAL_DEGREE) <= MAX_SUN_RAY_COUNT) {
mIsExpandSunRay = true;
mSunRayCount = (int) (mSunRayRotation / SUN_RAY_INTERVAL_DEGREE);
}
if ((int) ((MAX_SUN_ROTATE_DEGREE - mSunRayRotation) / SUN_RAY_INTERVAL_DEGREE) <= MAX_SUN_RAY_COUNT) {
mIsExpandSunRay = false;
mSunRayCount = (int) ((MAX_SUN_ROTATE_DEGREE - mSunRayRotation) / SUN_RAY_INTERVAL_DEGREE);
}
}
if (renderProgress <= SUN_DECREASE_DURATION_OFFSET && renderProgress > SUN_ROTATE_DURATION_OFFSET) {
float sunDecreaseProgress = (renderProgress - SUN_ROTATE_DURATION_OFFSET) / (SUN_DECREASE_DURATION_OFFSET - SUN_ROTATE_DURATION_OFFSET);
mSunCoordinateY = mInitSun$MoonCoordinateY - mMaxSun$MoonRiseDistance * (1.0f - ACCELERATE_INTERPOLATOR.getInterpolation(sunDecreaseProgress));
}
if (renderProgress <= MOON_RISE_DURATION_OFFSET && renderProgress > SUN_DECREASE_DURATION_OFFSET) {
float moonRiseProgress = (renderProgress - SUN_DECREASE_DURATION_OFFSET) / (MOON_RISE_DURATION_OFFSET - SUN_DECREASE_DURATION_OFFSET);
mMoonRotation = MATERIAL_INTERPOLATOR.getInterpolation(moonRiseProgress) * MAX_MOON_ROTATE_DEGREE;
mSunCoordinateY = mInitSun$MoonCoordinateY;
mMoonCoordinateY = mInitSun$MoonCoordinateY - mMaxSun$MoonRiseDistance * MATERIAL_INTERPOLATOR.getInterpolation(moonRiseProgress);
}
if (renderProgress <= STAR_DECREASE_START_DURATION_OFFSET && renderProgress > STAR_RISE_START_DURATION_OFFSET) {
float starProgress = (renderProgress - STAR_RISE_START_DURATION_OFFSET) / (STAR_DECREASE_START_DURATION_OFFSET - STAR_RISE_START_DURATION_OFFSET);
if (starProgress <= STAR_RISE_PROGRESS_OFFSET) {
for (int i = 0; i < mStarHolders.size(); i++) {
StarHolder starHolder = mStarHolders.get(i);
starHolder.mCurrentPoint.y = starHolder.mPoint.y - (1.0f - starHolder.mInterpolator.getInterpolation(starProgress * 5.0f)) * (mMaxStarOffsets * 0.65f);
starHolder.mCurrentPoint.x = starHolder.mPoint.x;
}
}
if (starProgress > STAR_RISE_PROGRESS_OFFSET && starProgress < STAR_DECREASE_PROGRESS_OFFSET) {
for (int i = 0; i < mStarHolders.size(); i++) {
StarHolder starHolder = mStarHolders.get(i);
if (starHolder.mFlashOffset < starProgress && starProgress < starHolder.mFlashOffset + STAR_FLASH_PROGRESS_PERCENTAGE) {
starHolder.mAlpha = (int) (MAX_ALPHA * MATERIAL_INTERPOLATOR.getInterpolation(
Math.abs(starProgress - (starHolder.mFlashOffset + STAR_FLASH_PROGRESS_PERCENTAGE / 2.0f)) / (STAR_FLASH_PROGRESS_PERCENTAGE / 2.0f)));
}
}
}
if (starProgress >= STAR_DECREASE_PROGRESS_OFFSET) {
for (int i = 0; i < mStarHolders.size(); i++) {
StarHolder starHolder = mStarHolders.get(i);
starHolder.mCurrentPoint.y = starHolder.mPoint.y + starHolder.mInterpolator.getInterpolation((starProgress - STAR_DECREASE_PROGRESS_OFFSET) * 5.0f) * mMaxStarOffsets;
starHolder.mCurrentPoint.x = starHolder.mPoint.x;
}
}
mShowStar = true;
}
if (renderProgress <= MOON_DECREASE_END_DURATION_OFFSET && renderProgress > MOON_DECREASE_START_DURATION_OFFSET) {
float moonDecreaseProgress = (renderProgress - MOON_DECREASE_START_DURATION_OFFSET) / (MOON_DECREASE_END_DURATION_OFFSET - MOON_DECREASE_START_DURATION_OFFSET);
mMoonCoordinateY = mInitSun$MoonCoordinateY - mMaxSun$MoonRiseDistance * (1.0f - ACCELERATE_INTERPOLATOR.getInterpolation(moonDecreaseProgress));
}
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
}
private void initStarHolders(RectF currentBounds) {
mStarHolders.add(new StarHolder(0.3f, new PointF(currentBounds.left + currentBounds.width() * 0.175f,
currentBounds.top + currentBounds.height() * 0.0934f)));
mStarHolders.add(new StarHolder(0.2f, new PointF(currentBounds.left + currentBounds.width() * 0.175f,
currentBounds.top + currentBounds.height() * 0.62f)));
mStarHolders.add(new StarHolder(0.2f, new PointF(currentBounds.left + currentBounds.width() * 0.2525f,
currentBounds.top + currentBounds.height() * 0.43f)));
mStarHolders.add(new StarHolder(0.5f, new PointF(currentBounds.left + currentBounds.width() * 0.4075f,
currentBounds.top + currentBounds.height() * 0.0934f)));
mStarHolders.add(new StarHolder(new PointF(currentBounds.left + currentBounds.width() * 0.825f,
currentBounds.top + currentBounds.height() * 0.04f)));
mStarHolders.add(new StarHolder(new PointF(currentBounds.left + currentBounds.width() * 0.7075f,
currentBounds.top + currentBounds.height() * 0.147f)));
mStarHolders.add(new StarHolder(new PointF(currentBounds.left + currentBounds.width() * 0.3475f,
currentBounds.top + currentBounds.height() * 0.2567f)));
mStarHolders.add(new StarHolder(0.6f, new PointF(currentBounds.left + currentBounds.width() * 0.5825f,
currentBounds.top + currentBounds.height() * 0.277f)));
mStarHolders.add(new StarHolder(new PointF(currentBounds.left + currentBounds.width() * 0.84f,
currentBounds.top + currentBounds.height() * 0.32f)));
mStarHolders.add(new StarHolder(new PointF(currentBounds.left + currentBounds.width() * 0.8f,
currentBounds.top + currentBounds.height() / 0.502f)));
mStarHolders.add(new StarHolder(0.6f, new PointF(currentBounds.left + currentBounds.width() * 0.7f,
currentBounds.top + currentBounds.height() * 0.473f)));
mMaxStarOffsets = currentBounds.height();
}
private Path createMoonPath(float moonCenterX, float moonCenterY) {
RectF moonRectF = new RectF(moonCenterX - mSun$MoonRadius, moonCenterY - mSun$MoonRadius,
moonCenterX + mSun$MoonRadius, moonCenterY + mSun$MoonRadius);
Path path = new Path();
path.addArc(moonRectF, -90, 180);
path.quadTo(moonCenterX + mSun$MoonRadius / 2.0f, moonCenterY, moonCenterX, moonCenterY - mSun$MoonRadius);
return path;
}
private class StarHolder {
public int mAlpha;
public PointF mCurrentPoint;
public final PointF mPoint;
public final float mFlashOffset;
public final Interpolator mInterpolator;
public StarHolder(PointF point) {
this(1.0f, point);
}
public StarHolder(float flashOffset, PointF mPoint) {
this.mAlpha = MAX_ALPHA;
this.mCurrentPoint = new PointF();
this.mPoint = mPoint;
this.mFlashOffset = flashOffset;
this.mInterpolator = INTERPOLATORS[mRandom.nextInt(INTERPOLATORS.length)];
}
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public DayNightLoadingRenderer build() {
DayNightLoadingRenderer loadingRenderer = new DayNightLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,474 @@
package app.dinus.com.loadingdrawable.render.scenery;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import androidx.annotation.IntDef;
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.R;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class ElectricFanLoadingRenderer extends LoadingRenderer {
private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final Interpolator FASTOUTLINEARIN_INTERPOLATOR = new FastOutLinearInInterpolator();
private static final Interpolator[] INTERPOLATORS = new Interpolator[]{LINEAR_INTERPOLATOR,
DECELERATE_INTERPOLATOR, ACCELERATE_INTERPOLATOR, FASTOUTLINEARIN_INTERPOLATOR, MATERIAL_INTERPOLATOR};
private static final List<LeafHolder> mLeafHolders = new ArrayList<>();
private static final Random mRandom = new Random();
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animator) {
super.onAnimationRepeat(animator);
reset();
}
};
public static final int MODE_NORMAL = 0;
public static final int MODE_LEAF_COUNT = 1;
@IntDef({MODE_NORMAL, MODE_LEAF_COUNT})
@Retention(RetentionPolicy.SOURCE)
public @interface MODE {
}
private static final String PERCENTAGE_100 = "100%";
private static final long ANIMATION_DURATION = 7333;
private static final int LEAF_COUNT = 28;
private static final int DEGREE_180 = 180;
private static final int DEGREE_360 = 360;
private static final int FULL_GROUP_ROTATION = (int) (5.25f * DEGREE_360);
private static final int DEFAULT_PROGRESS_COLOR = 0xfffca72e;
private static final int DEFAULT_PROGRESS_BGCOLOR = 0xfffcd49f;
private static final int DEFAULT_ELECTRIC_FAN_BGCOLOR = 0xfffccc59;
private static final int DEFAULT_ELECTRIC_FAN_OUTLINE_COLOR = Color.WHITE;
private static final float DEFAULT_WIDTH = 182.0f;
private static final float DEFAULT_HEIGHT = 65.0f;
private static final float DEFAULT_TEXT_SIZE = 11.0f;
private static final float DEFAULT_STROKE_WIDTH = 2.0f;
private static final float DEFAULT_STROKE_INTERVAL = .2f;
private static final float DEFAULT_CENTER_RADIUS = 16.0f;
private static final float DEFAULT_PROGRESS_CENTER_RADIUS = 11.0f;
private static final float DEFAULT_LEAF_FLY_DURATION_FACTOR = 0.1f;
private static final float LEAF_CREATE_DURATION_INTERVAL = 1.0f / LEAF_COUNT;
private static final float DECELERATE_DURATION_PERCENTAGE = 0.4f;
private static final float ACCELERATE_DURATION_PERCENTAGE = 0.6f;
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final RectF mCurrentProgressBounds = new RectF();
private float mTextSize;
private float mStrokeXInset;
private float mStrokeYInset;
private float mProgressCenterRadius;
private float mScale;
private float mRotation;
private float mProgress;
private float mNextLeafCreateThreshold;
private int mProgressColor;
private int mProgressBgColor;
private int mElectricFanBgColor;
private int mElectricFanOutlineColor;
private float mStrokeWidth;
private float mCenterRadius;
@MODE
private int mMode;
private int mCurrentLeafCount;
private Drawable mLeafDrawable;
private Drawable mLoadingDrawable;
private Drawable mElectricFanDrawable;
private ElectricFanLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
addRenderListener(mAnimatorListener);
}
private void init(Context context) {
mMode = MODE_NORMAL;
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mTextSize = DensityUtil.dip2px(context, DEFAULT_TEXT_SIZE);
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
mProgressCenterRadius = DensityUtil.dip2px(context, DEFAULT_PROGRESS_CENTER_RADIUS);
mProgressColor = DEFAULT_PROGRESS_COLOR;
mProgressBgColor = DEFAULT_PROGRESS_BGCOLOR;
mElectricFanBgColor = DEFAULT_ELECTRIC_FAN_BGCOLOR;
mElectricFanOutlineColor = DEFAULT_ELECTRIC_FAN_OUTLINE_COLOR;
mLeafDrawable = context.getResources().getDrawable(R.drawable.ic_leaf);
mLoadingDrawable = context.getResources().getDrawable(R.drawable.ic_loading);
mElectricFanDrawable = context.getResources().getDrawable(R.drawable.ic_eletric_fan);
mDuration = ANIMATION_DURATION;
setInsets((int) mWidth, (int) mHeight);
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
RectF arcBounds = mTempBounds;
arcBounds.set(bounds);
arcBounds.inset(mStrokeXInset, mStrokeYInset);
mCurrentProgressBounds.set(arcBounds.left, arcBounds.bottom - 2 * mCenterRadius,
arcBounds.right, arcBounds.bottom);
//draw loading drawable
mLoadingDrawable.setBounds((int) arcBounds.centerX() - mLoadingDrawable.getIntrinsicWidth() / 2,
0,
(int) arcBounds.centerX() + mLoadingDrawable.getIntrinsicWidth() / 2,
mLoadingDrawable.getIntrinsicHeight());
mLoadingDrawable.draw(canvas);
//draw progress background
float progressInset = mCenterRadius - mProgressCenterRadius;
RectF progressRect = new RectF(mCurrentProgressBounds);
//sub DEFAULT_STROKE_INTERVAL, otherwise will have a interval between progress background and progress outline
progressRect.inset(progressInset - DEFAULT_STROKE_INTERVAL, progressInset - DEFAULT_STROKE_INTERVAL);
mPaint.setColor(mProgressBgColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRoundRect(progressRect, mProgressCenterRadius, mProgressCenterRadius, mPaint);
//draw progress
mPaint.setColor(mProgressColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(createProgressPath(mProgress, mProgressCenterRadius, progressRect), mPaint);
//draw leaves
for (int i = 0; i < mLeafHolders.size(); i++) {
int leafSaveCount = canvas.save();
LeafHolder leafHolder = mLeafHolders.get(i);
Rect leafBounds = leafHolder.mLeafRect;
canvas.rotate(leafHolder.mLeafRotation, leafBounds.centerX(), leafBounds.centerY());
mLeafDrawable.setBounds(leafBounds);
mLeafDrawable.draw(canvas);
canvas.restoreToCount(leafSaveCount);
}
//draw progress background outline,
//after drawing the leaves and then draw the outline of the progress background can
//prevent the leaves from flying to the outside
RectF progressOutlineRect = new RectF(mCurrentProgressBounds);
float progressOutlineStrokeInset = (mCenterRadius - mProgressCenterRadius) / 2.0f;
progressOutlineRect.inset(progressOutlineStrokeInset, progressOutlineStrokeInset);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mProgressBgColor);
mPaint.setStrokeWidth(mCenterRadius - mProgressCenterRadius);
canvas.drawRoundRect(progressOutlineRect, mCenterRadius, mCenterRadius, mPaint);
//draw electric fan outline
float electricFanCenterX = arcBounds.right - mCenterRadius;
float electricFanCenterY = arcBounds.bottom - mCenterRadius;
mPaint.setColor(mElectricFanOutlineColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
canvas.drawCircle(arcBounds.right - mCenterRadius, arcBounds.bottom - mCenterRadius,
mCenterRadius - mStrokeWidth / 2.0f, mPaint);
//draw electric background
mPaint.setColor(mElectricFanBgColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(arcBounds.right - mCenterRadius, arcBounds.bottom - mCenterRadius,
mCenterRadius - mStrokeWidth + DEFAULT_STROKE_INTERVAL, mPaint);
//draw electric fan
int rotateSaveCount = canvas.save();
canvas.rotate(mRotation, electricFanCenterX, electricFanCenterY);
mElectricFanDrawable.setBounds((int) (electricFanCenterX - mElectricFanDrawable.getIntrinsicWidth() / 2 * mScale),
(int) (electricFanCenterY - mElectricFanDrawable.getIntrinsicHeight() / 2 * mScale),
(int) (electricFanCenterX + mElectricFanDrawable.getIntrinsicWidth() / 2 * mScale),
(int) (electricFanCenterY + mElectricFanDrawable.getIntrinsicHeight() / 2 * mScale));
mElectricFanDrawable.draw(canvas);
canvas.restoreToCount(rotateSaveCount);
//draw 100% text
if (mScale < 1.0f) {
mPaint.setTextSize(mTextSize * (1 - mScale));
mPaint.setColor(mElectricFanOutlineColor);
Rect textRect = new Rect();
mPaint.getTextBounds(PERCENTAGE_100, 0, PERCENTAGE_100.length(), textRect);
canvas.drawText(PERCENTAGE_100, electricFanCenterX - textRect.width() / 2.0f,
electricFanCenterY + textRect.height() / 2.0f, mPaint);
}
canvas.restoreToCount(saveCount);
}
private Path createProgressPath(float progress, float circleRadius, RectF progressRect) {
RectF arcProgressRect = new RectF(progressRect.left, progressRect.top, progressRect.left + circleRadius * 2, progressRect.bottom);
RectF rectProgressRect = null;
float progressWidth = progress * progressRect.width();
float progressModeWidth = mMode == MODE_LEAF_COUNT ?
(float) mCurrentLeafCount / (float) LEAF_COUNT * progressRect.width() : progress * progressRect.width();
float swipeAngle = DEGREE_180;
//the left half circle of the progressbar
if (progressModeWidth < circleRadius) {
swipeAngle = progressModeWidth / circleRadius * DEGREE_180;
}
//the center rect of the progressbar
if (progressModeWidth < progressRect.width() - circleRadius && progressModeWidth >= circleRadius) {
rectProgressRect = new RectF(progressRect.left + circleRadius, progressRect.top, progressRect.left + progressModeWidth, progressRect.bottom);
}
//the right half circle of the progressbar
if (progressWidth >= progressRect.width() - circleRadius) {
rectProgressRect = new RectF(progressRect.left + circleRadius, progressRect.top, progressRect.right - circleRadius, progressRect.bottom);
mScale = (progressRect.width() - progressWidth) / circleRadius;
}
//the left of the right half circle
if (progressWidth < progressRect.width() - circleRadius) {
mRotation = (progressWidth / (progressRect.width() - circleRadius)) * FULL_GROUP_ROTATION % DEGREE_360;
RectF leafRect = new RectF(progressRect.left + progressWidth, progressRect.top, progressRect.right - circleRadius, progressRect.bottom);
addLeaf(progress, leafRect);
}
Path path = new Path();
path.addArc(arcProgressRect, DEGREE_180 - swipeAngle / 2, swipeAngle);
if (rectProgressRect != null) {
path.addRect(rectProgressRect, Path.Direction.CW);
}
return path;
}
@Override
protected void computeRender(float renderProgress) {
if (renderProgress < DECELERATE_DURATION_PERCENTAGE) {
mProgress = DECELERATE_INTERPOLATOR.getInterpolation(renderProgress / DECELERATE_DURATION_PERCENTAGE) * DECELERATE_DURATION_PERCENTAGE;
} else {
mProgress = ACCELERATE_INTERPOLATOR.getInterpolation((renderProgress - DECELERATE_DURATION_PERCENTAGE) / ACCELERATE_DURATION_PERCENTAGE) * ACCELERATE_DURATION_PERCENTAGE + DECELERATE_DURATION_PERCENTAGE;
}
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
mScale = 1.0f;
mCurrentLeafCount = 0;
mNextLeafCreateThreshold = 0.0f;
mLeafHolders.clear();
}
protected void setInsets(int width, int height) {
final float minEdge = (float) Math.min(width, height);
float insetXs;
if (mCenterRadius <= 0 || minEdge < 0) {
insetXs = (float) Math.ceil(mCenterRadius / 2.0f);
} else {
insetXs = mCenterRadius;
}
mStrokeYInset = (float) Math.ceil(mCenterRadius / 2.0f);
mStrokeXInset = insetXs;
}
private void addLeaf(float progress, RectF leafFlyRect) {
if (progress < mNextLeafCreateThreshold) {
return;
}
mNextLeafCreateThreshold += LEAF_CREATE_DURATION_INTERVAL;
LeafHolder leafHolder = new LeafHolder();
mLeafHolders.add(leafHolder);
Animator leafAnimator = getAnimator(leafHolder, leafFlyRect, progress);
leafAnimator.addListener(new AnimEndListener(leafHolder));
leafAnimator.start();
}
private Animator getAnimator(LeafHolder target, RectF leafFlyRect, float progress) {
ValueAnimator bezierValueAnimator = getBezierValueAnimator(target, leafFlyRect, progress);
AnimatorSet finalSet = new AnimatorSet();
finalSet.playSequentially(bezierValueAnimator);
finalSet.setInterpolator(INTERPOLATORS[mRandom.nextInt(INTERPOLATORS.length)]);
finalSet.setTarget(target);
return finalSet;
}
private ValueAnimator getBezierValueAnimator(LeafHolder target, RectF leafFlyRect, float progress) {
BezierEvaluator evaluator = new BezierEvaluator(getPoint1(leafFlyRect), getPoint2(leafFlyRect));
int leafFlyStartY = (int) (mCurrentProgressBounds.bottom - mLeafDrawable.getIntrinsicHeight());
int leafFlyRange = (int) (mCurrentProgressBounds.height() - mLeafDrawable.getIntrinsicHeight());
int startPointY = leafFlyStartY - mRandom.nextInt(leafFlyRange);
int endPointY = leafFlyStartY - mRandom.nextInt(leafFlyRange);
ValueAnimator animator = ValueAnimator.ofObject(evaluator,
new PointF((int) (leafFlyRect.right - mLeafDrawable.getIntrinsicWidth()), startPointY),
new PointF(leafFlyRect.left, endPointY));
animator.addUpdateListener(new BezierListener(target));
animator.setTarget(target);
animator.setDuration((long) ((mRandom.nextInt(300) + mDuration * DEFAULT_LEAF_FLY_DURATION_FACTOR) * (1.0f - progress)));
return animator;
}
//get the pointF which belong to the right half side
private PointF getPoint1(RectF leafFlyRect) {
PointF point = new PointF();
point.x = leafFlyRect.right - mRandom.nextInt((int) (leafFlyRect.width() / 2));
point.y = (int) (leafFlyRect.bottom - mRandom.nextInt((int) leafFlyRect.height()));
return point;
}
//get the pointF which belong to the left half side
private PointF getPoint2(RectF leafFlyRect) {
PointF point = new PointF();
point.x = leafFlyRect.left + mRandom.nextInt((int) (leafFlyRect.width() / 2));
point.y = (int) (leafFlyRect.bottom - mRandom.nextInt((int) leafFlyRect.height()));
return point;
}
private class BezierEvaluator implements TypeEvaluator<PointF> {
private PointF point1;
private PointF point2;
public BezierEvaluator(PointF point1, PointF point2) {
this.point1 = point1;
this.point2 = point2;
}
//Third-order Bezier curve formula: B(t) = point0 * (1-t)^3 + 3 * point1 * t * (1-t)^2 + 3 * point2 * t^2 * (1-t) + point3 * t^3
@Override
public PointF evaluate(float fraction, PointF point0, PointF point3) {
float t = fraction;
float tLeft = 1.0f - t;
float x = (float) (point0.x * Math.pow(tLeft, 3) + 3 * point1.x * t * Math.pow(tLeft, 2) + 3 * point2.x * Math.pow(t, 2) * tLeft + point3.x * Math.pow(t, 3));
float y = (float) (point0.y * Math.pow(tLeft, 3) + 3 * point1.y * t * Math.pow(tLeft, 2) + 3 * point2.y * Math.pow(t, 2) * tLeft + point3.y * Math.pow(t, 3));
return new PointF(x, y);
}
}
private class BezierListener implements ValueAnimator.AnimatorUpdateListener {
private LeafHolder target;
public BezierListener(LeafHolder target) {
this.target = target;
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF point = (PointF) animation.getAnimatedValue();
target.mLeafRect.set((int) point.x, (int) point.y,
(int) (point.x + mLeafDrawable.getIntrinsicWidth()), (int) (point.y + mLeafDrawable.getIntrinsicHeight()));
target.mLeafRotation = target.mMaxRotation * animation.getAnimatedFraction();
}
}
private class AnimEndListener extends AnimatorListenerAdapter {
private LeafHolder target;
public AnimEndListener(LeafHolder target) {
this.target = target;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mLeafHolders.remove(target);
mCurrentLeafCount++;
}
}
private class LeafHolder {
public Rect mLeafRect = new Rect();
public float mLeafRotation = 0.0f;
public float mMaxRotation = mRandom.nextInt(120);
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public ElectricFanLoadingRenderer build() {
ElectricFanLoadingRenderer loadingRenderer = new ElectricFanLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,676 @@
package app.dinus.com.loadingdrawable.render.shapechange;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class CircleBroodLoadingRenderer extends LoadingRenderer {
private final Interpolator MOTHER_MOVE_INTERPOLATOR = new MotherMoveInterpolator();
private final Interpolator CHILD_MOVE_INTERPOLATOR = new ChildMoveInterpolator();
private final Interpolator ACCELERATE_INTERPOLATOR03 = new AccelerateInterpolator(0.3f);
private final Interpolator ACCELERATE_INTERPOLATOR05 = new AccelerateInterpolator(0.5f);
private final Interpolator ACCELERATE_INTERPOLATOR08 = new AccelerateInterpolator(0.8f);
private final Interpolator ACCELERATE_INTERPOLATOR10 = new AccelerateInterpolator(1.0f);
private final Interpolator DECELERATE_INTERPOLATOR03 = new DecelerateInterpolator(0.3f);
private final Interpolator DECELERATE_INTERPOLATOR05 = new DecelerateInterpolator(0.5f);
private final Interpolator DECELERATE_INTERPOLATOR08 = new DecelerateInterpolator(0.8f);
private final Interpolator DECELERATE_INTERPOLATOR10 = new DecelerateInterpolator(1.0f);
private float STAGE_MOTHER_FORWARD_TOP_LEFT = 0.34f;
private float STAGE_MOTHER_BACKWARD_TOP_LEFT = 0.5f;
private float STAGE_MOTHER_FORWARD_BOTTOM_LEFT = 0.65f;
private float STAGE_MOTHER_BACKWARD_BOTTOM_LEFT = 0.833f;
private float STAGE_CHILD_DELAY = 0.1f;
private float STAGE_CHILD_PRE_FORWARD_TOP_LEFT = 0.26f;
private float STAGE_CHILD_FORWARD_TOP_LEFT = 0.34f;
private float STAGE_CHILD_PRE_BACKWARD_TOP_LEFT = 0.42f;
private float STAGE_CHILD_BACKWARD_TOP_LEFT = 0.5f;
private float STAGE_CHILD_FORWARD_BOTTOM_LEFT = 0.7f;
private float STAGE_CHILD_BACKWARD_BOTTOM_LEFT = 0.9f;
private final float OVAL_BEZIER_FACTOR = 0.55152f;
private final float DEFAULT_WIDTH = 40.0f;
private final float DEFAULT_HEIGHT = 20.0f;
private final float MAX_MATHER_OVAL_SIZE = 12;//19;
private final float MIN_CHILD_OVAL_RADIUS = 1;//5;
private final float MAX_MATHER_SHAPE_CHANGE_FACTOR = 0.8452f;
private final int DEFAULT_OVAL_COLOR = Color.parseColor("#FFBE1C23");
private final int DEFAULT_OVAL_DEEP_COLOR = Color.parseColor("#FFB21721");
private final int DEFAULT_BACKGROUND_COLOR = Color.parseColor("#FFE3C172");
private final int DEFAULT_BACKGROUND_DEEP_COLOR = Color.parseColor("#FFE2B552");
private final long ANIMATION_DURATION = 4111;
private final Paint mPaint = new Paint();
private final RectF mCurrentBounds = new RectF();
private final Path mMotherOvalPath = new Path();
private final Path mMotherMovePath = new Path();
private final Path mChildMovePath = new Path();
private final float[] mMotherPosition = new float[2];
private final float[] mChildPosition = new float[2];
private final PathMeasure mMotherMovePathMeasure = new PathMeasure();
private final PathMeasure mChildMovePathMeasure = new PathMeasure();
private float mChildOvalRadius;
private float mBasicChildOvalRadius;
private float mMaxMotherOvalSize;
private float mMotherOvalHalfWidth;
private float mMotherOvalHalfHeight;
private float mChildLeftXOffset;
private float mChildLeftYOffset;
private float mChildRightXOffset;
private float mChildRightYOffset;
private int mOvalColor;
private int mOvalDeepColor;
private int mBackgroundColor;
private int mBackgroundDeepColor;
private int mCurrentOvalColor;
private int mCurrentBackgroundColor;
private int mRevealCircleRadius;
private int mMaxRevealCircleRadius;
private int mRotateDegrees;
private float mStageMotherForwardTopLeftLength;
private float mStageMotherBackwardTopLeftLength;
private float mStageMotherForwardBottomLeftLength;
private float mStageMotherBackwardBottomLeftLength;
private float mStageChildPreForwardTopLeftLength;
private float mStageChildForwardTopLeftLength;
private float mStageChildPreBackwardTopLeftLength;
private float mStageChildBackwardTopLeftLength;
private float mStageChildForwardBottomLeftLength;
private float mStageChildBackwardBottomLeftLength;
private CircleBroodLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
}
private void init(Context context) {
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mMaxMotherOvalSize = DensityUtil.dip2px(context, MAX_MATHER_OVAL_SIZE);
mBasicChildOvalRadius = DensityUtil.dip2px(context, MIN_CHILD_OVAL_RADIUS);
mOvalColor = DEFAULT_OVAL_COLOR;
mOvalDeepColor = DEFAULT_OVAL_DEEP_COLOR;
mBackgroundColor = DEFAULT_BACKGROUND_COLOR;
mBackgroundDeepColor = DEFAULT_BACKGROUND_DEEP_COLOR;
mMotherOvalHalfWidth = mMaxMotherOvalSize;
mMotherOvalHalfHeight = mMaxMotherOvalSize;
mMaxRevealCircleRadius = (int) (Math.sqrt(mWidth * mWidth + mHeight * mHeight) / 2 + 1);
mDuration = ANIMATION_DURATION;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(1.0f);
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
RectF arcBounds = mCurrentBounds;
arcBounds.set(bounds);
//draw background
canvas.drawColor(mCurrentBackgroundColor);
//draw reveal circle
if (mRevealCircleRadius > 0) {
mPaint.setColor(mCurrentBackgroundColor == mBackgroundColor ? mBackgroundDeepColor : mBackgroundColor);
canvas.drawCircle(arcBounds.centerX(), arcBounds.centerY(), mRevealCircleRadius, mPaint);
}
//draw mother oval
mPaint.setColor(mCurrentOvalColor);
int motherSaveCount = canvas.save();
canvas.rotate(mRotateDegrees, mMotherPosition[0], mMotherPosition[1]);
canvas.drawPath(createMotherPath(), mPaint);
canvas.drawPath(createLinkPath(), mPaint);
canvas.restoreToCount(motherSaveCount);
int childSaveCount = canvas.save();
canvas.rotate(mRotateDegrees, mChildPosition[0], mChildPosition[1]);
canvas.drawPath(createChildPath(), mPaint);
canvas.restoreToCount(childSaveCount);
canvas.restoreToCount(saveCount);
// canvas.drawPath(mMotherMovePath, mPaint);
// canvas.drawPath(mChildMovePath, mPaint);
// canvas.drawLine(mMotherPosition[0], mMotherPosition[1], mChildPosition[0], mChildPosition[1], mPaint);
}
private Path createMotherPath() {
mMotherOvalPath.reset();
mMotherOvalPath.addOval(new RectF(mMotherPosition[0] - mMotherOvalHalfWidth, mMotherPosition[1] - mMotherOvalHalfHeight,
mMotherPosition[0] + mMotherOvalHalfWidth, mMotherPosition[1] + mMotherOvalHalfHeight), Path.Direction.CW);
return mMotherOvalPath;
}
private Path createChildPath() {
float bezierOffset = mChildOvalRadius * OVAL_BEZIER_FACTOR;
Path path = new Path();
path.moveTo(mChildPosition[0], mChildPosition[1] - mChildOvalRadius);
//left_top arc
path.cubicTo(mChildPosition[0] - bezierOffset - mChildLeftXOffset, mChildPosition[1] - mChildOvalRadius,
mChildPosition[0] - mChildOvalRadius - mChildLeftXOffset, mChildPosition[1] - bezierOffset + mChildLeftYOffset,
mChildPosition[0] - mChildOvalRadius - mChildLeftXOffset, mChildPosition[1]);
//left_bottom arc
path.cubicTo(mChildPosition[0] - mChildOvalRadius - mChildLeftXOffset, mChildPosition[1] + bezierOffset - mChildLeftYOffset,
mChildPosition[0] - bezierOffset - mChildLeftXOffset, mChildPosition[1] + mChildOvalRadius,
mChildPosition[0], mChildPosition[1] + mChildOvalRadius);
//right_bottom arc
path.cubicTo(mChildPosition[0] + bezierOffset + mChildRightXOffset, mChildPosition[1] + mChildOvalRadius,
mChildPosition[0] + mChildOvalRadius + mChildRightXOffset, mChildPosition[1] + bezierOffset - mChildRightYOffset,
mChildPosition[0] + mChildOvalRadius + mChildRightXOffset, mChildPosition[1]);
//right_top arc
path.cubicTo(mChildPosition[0] + mChildOvalRadius + mChildRightXOffset, mChildPosition[1] - bezierOffset + mChildRightYOffset,
mChildPosition[0] + bezierOffset + mChildRightXOffset, mChildPosition[1] - mChildOvalRadius,
mChildPosition[0], mChildPosition[1] - mChildOvalRadius);
return path;
}
private Path createLinkPath() {
Path path = new Path();
float bezierOffset = mMotherOvalHalfWidth * OVAL_BEZIER_FACTOR;
float distance = (float) Math.sqrt(Math.pow(mMotherPosition[0] - mChildPosition[0], 2.0f) + Math.pow(mMotherPosition[1] - mChildPosition[1], 2.0f));
if (distance <= mMotherOvalHalfWidth + mChildOvalRadius * 1.2f
&& distance >= mMotherOvalHalfWidth - mChildOvalRadius * 1.2f) {
float maxOffsetY = 2 * mChildOvalRadius * 1.2f;
float offsetRate = (distance - (mMotherOvalHalfWidth - mChildOvalRadius * 1.2f)) / maxOffsetY;
float mMotherOvalOffsetY = mMotherOvalHalfHeight - offsetRate * (mMotherOvalHalfHeight - mChildOvalRadius) * 0.85f;
mMotherOvalPath.addOval(new RectF(mMotherPosition[0] - mMotherOvalHalfWidth, mMotherPosition[1] - mMotherOvalOffsetY,
mMotherPosition[0] + mMotherOvalHalfWidth, mMotherPosition[1] + mMotherOvalOffsetY), Path.Direction.CW);
float mMotherXOffset = distance - mMotherOvalHalfWidth + mChildOvalRadius;
float distanceUltraLeft = (float) Math.sqrt(Math.pow(mMotherPosition[0] - mMotherOvalHalfWidth - mChildPosition[0], 2.0f)
+ Math.pow(mMotherPosition[1] - mChildPosition[1], 2.0f));
float distanceUltraRight = (float) Math.sqrt(Math.pow(mMotherPosition[0] + mMotherOvalHalfWidth - mChildPosition[0], 2.0f)
+ Math.pow(mMotherPosition[1] - mChildPosition[1], 2.0f));
path.moveTo(mMotherPosition[0], mMotherPosition[1] + mMotherOvalOffsetY);
if (distanceUltraRight < distanceUltraLeft) {
//right_bottom arc
path.cubicTo(mMotherPosition[0] + bezierOffset + mMotherXOffset, mMotherPosition[1] + mMotherOvalOffsetY,
mMotherPosition[0] + distance + mChildOvalRadius, mMotherPosition[1] + mChildOvalRadius * 1.5f,
mMotherPosition[0] + distance + mChildOvalRadius, mMotherPosition[1]);
//right_top arc
path.cubicTo(mMotherPosition[0] + distance + mChildOvalRadius, mMotherPosition[1] - mChildOvalRadius * 1.5f,
mMotherPosition[0] + bezierOffset + mMotherXOffset, mMotherPosition[1] - mMotherOvalOffsetY,
mMotherPosition[0], mMotherPosition[1] - mMotherOvalOffsetY);
} else {
//left_bottom arc
path.cubicTo(mMotherPosition[0] - bezierOffset - mMotherXOffset, mMotherPosition[1] + mMotherOvalOffsetY,
mMotherPosition[0] - distance - mChildOvalRadius, mMotherPosition[1] + mChildOvalRadius * 1.5f,
mMotherPosition[0] - distance - mChildOvalRadius, mMotherPosition[1]);
//left_top arc
path.cubicTo(mMotherPosition[0] - distance - mChildOvalRadius, mMotherPosition[1] - mChildOvalRadius * 1.5f,
mMotherPosition[0] - bezierOffset - mMotherXOffset, mMotherPosition[1] - mMotherOvalOffsetY,
mMotherPosition[0], mMotherPosition[1] - mMotherOvalOffsetY);
}
path.lineTo(mMotherPosition[0], mMotherPosition[1] + mMotherOvalOffsetY);
}
return path;
}
@Override
protected void computeRender(float renderProgress) {
if (mCurrentBounds.isEmpty()) {
return;
}
if (mMotherMovePath.isEmpty()) {
mMotherMovePath.set(createMotherMovePath());
mMotherMovePathMeasure.setPath(mMotherMovePath, false);
mChildMovePath.set(createChildMovePath());
mChildMovePathMeasure.setPath(mChildMovePath, false);
}
//mother oval
float motherMoveProgress = MOTHER_MOVE_INTERPOLATOR.getInterpolation(renderProgress);
mMotherMovePathMeasure.getPosTan(getCurrentMotherMoveLength(motherMoveProgress), mMotherPosition, null);
mMotherOvalHalfWidth = mMaxMotherOvalSize;
mMotherOvalHalfHeight = mMaxMotherOvalSize * getMotherShapeFactor(motherMoveProgress);
//child Oval
float childMoveProgress = CHILD_MOVE_INTERPOLATOR.getInterpolation(renderProgress);
mChildMovePathMeasure.getPosTan(getCurrentChildMoveLength(childMoveProgress), mChildPosition, null);
setupChildParams(childMoveProgress);
mRotateDegrees = (int) (Math.toDegrees(Math.atan((mMotherPosition[1] - mChildPosition[1]) /
(mMotherPosition[0] - mChildPosition[0]))));
mRevealCircleRadius = getCurrentRevealCircleRadius(renderProgress);
mCurrentOvalColor = getCurrentOvalColor(renderProgress);
mCurrentBackgroundColor = getCurrentBackgroundColor(renderProgress);
}
private void setupChildParams(float input) {
mChildOvalRadius = mBasicChildOvalRadius;
mChildRightXOffset = 0.0f;
mChildLeftXOffset = 0.0f;
if (input <= STAGE_CHILD_PRE_FORWARD_TOP_LEFT) {
if (input >= 0.25) {
float shapeProgress = (input - 0.25f) / 0.01f;
mChildLeftXOffset = (1.0f - shapeProgress) * mChildOvalRadius * 0.25f;
} else {
mChildLeftXOffset = mChildOvalRadius * 0.25f;
}
} else if (input <= STAGE_CHILD_FORWARD_TOP_LEFT) {
if (input > 0.275f && input < 0.285f) {
float shapeProgress = (input - 0.275f) / 0.01f;
mChildLeftXOffset = shapeProgress * mChildOvalRadius * 0.25f;
} else if (input > 0.285f) {
mChildLeftXOffset = mChildOvalRadius * 0.25f;
}
} else if (input <= STAGE_CHILD_PRE_BACKWARD_TOP_LEFT) {
if (input > 0.38f) {
float radiusProgress = (input - 0.38f) / 0.04f;
mChildOvalRadius = mBasicChildOvalRadius * (1.0f + radiusProgress);
}
} else if (input <= STAGE_CHILD_BACKWARD_TOP_LEFT) {
if (input < 0.46f) {
float radiusProgress = (input - 0.42f) / 0.04f;
mChildOvalRadius = mBasicChildOvalRadius * (2.0f - radiusProgress);
}
} else if (input <= STAGE_CHILD_FORWARD_BOTTOM_LEFT) {
if (input > 0.65f) {
float radiusProgress = (input - 0.65f) / 0.05f;
mChildOvalRadius = mBasicChildOvalRadius * (1.0f + radiusProgress);
}
} else if (input <= STAGE_CHILD_BACKWARD_BOTTOM_LEFT) {
if (input < 0.71f) {
mChildOvalRadius = mBasicChildOvalRadius * 2.0f;
} else if (input < 0.76f) {
float radiusProgress = (input - 0.71f) / 0.05f;
mChildOvalRadius = mBasicChildOvalRadius * (2.0f - radiusProgress);
}
} else {
}
mChildRightYOffset = mChildRightXOffset / 2.5f;
mChildLeftYOffset = mChildLeftXOffset / 2.5f;
}
private float getMotherShapeFactor(float input) {
float shapeProgress;
if (input <= STAGE_MOTHER_FORWARD_TOP_LEFT) {
shapeProgress = input / STAGE_MOTHER_FORWARD_TOP_LEFT;
} else if (input <= STAGE_MOTHER_BACKWARD_TOP_LEFT) {
shapeProgress = (input - STAGE_MOTHER_FORWARD_TOP_LEFT) / (STAGE_MOTHER_BACKWARD_TOP_LEFT - STAGE_MOTHER_FORWARD_TOP_LEFT);
} else if (input <= STAGE_MOTHER_FORWARD_BOTTOM_LEFT) {
shapeProgress = (input - STAGE_MOTHER_BACKWARD_TOP_LEFT) / (STAGE_MOTHER_FORWARD_BOTTOM_LEFT - STAGE_MOTHER_BACKWARD_TOP_LEFT);
} else if (input <= STAGE_MOTHER_BACKWARD_BOTTOM_LEFT) {
shapeProgress = (input - STAGE_MOTHER_FORWARD_BOTTOM_LEFT) / (STAGE_MOTHER_BACKWARD_BOTTOM_LEFT - STAGE_MOTHER_FORWARD_BOTTOM_LEFT);
} else {
shapeProgress = 1.0f;
}
return shapeProgress < 0.5f ?
1.0f - (1.0f - MAX_MATHER_SHAPE_CHANGE_FACTOR) * shapeProgress * 2.0f :
MAX_MATHER_SHAPE_CHANGE_FACTOR + (1.0f - MAX_MATHER_SHAPE_CHANGE_FACTOR) * (shapeProgress - 0.5f) * 2.0f;
}
private float getCurrentMotherMoveLength(float input) {
float currentStartDistance = 0.0f;
float currentStageDistance = 0.0f;
float currentStateStartProgress = 0.0f;
float currentStateEndProgress = 0.0f;
if (input > 0.0f) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageMotherForwardTopLeftLength;
currentStateStartProgress = 0.0f;
currentStateEndProgress = STAGE_MOTHER_FORWARD_TOP_LEFT;
}
if (input > STAGE_MOTHER_FORWARD_TOP_LEFT) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageMotherBackwardTopLeftLength;
currentStateStartProgress = STAGE_MOTHER_FORWARD_TOP_LEFT;
currentStateEndProgress = STAGE_MOTHER_BACKWARD_TOP_LEFT;
}
if (input > STAGE_MOTHER_BACKWARD_TOP_LEFT) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageMotherForwardBottomLeftLength;
currentStateStartProgress = STAGE_MOTHER_BACKWARD_TOP_LEFT;
currentStateEndProgress = STAGE_MOTHER_FORWARD_BOTTOM_LEFT;
}
if (input > STAGE_MOTHER_FORWARD_BOTTOM_LEFT) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageMotherBackwardBottomLeftLength;
currentStateStartProgress = STAGE_MOTHER_FORWARD_BOTTOM_LEFT;
currentStateEndProgress = STAGE_MOTHER_BACKWARD_BOTTOM_LEFT;
}
if (input > STAGE_MOTHER_BACKWARD_BOTTOM_LEFT) {
return currentStartDistance + currentStageDistance;
}
return currentStartDistance + (input - currentStateStartProgress) /
(currentStateEndProgress - currentStateStartProgress) * currentStageDistance;
}
private float getCurrentChildMoveLength(float input) {
float currentStartDistance = 0.0f;
float currentStageDistance = 0.0f;
float currentStateStartProgress = 0.0f;
float currentStateEndProgress = 0.0f;
if (input > 0.0f) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageChildPreForwardTopLeftLength;
currentStateStartProgress = 0.0f;
currentStateEndProgress = STAGE_CHILD_PRE_FORWARD_TOP_LEFT;
}
if (input > STAGE_CHILD_PRE_FORWARD_TOP_LEFT) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageChildForwardTopLeftLength;
currentStateStartProgress = STAGE_CHILD_PRE_FORWARD_TOP_LEFT;
currentStateEndProgress = STAGE_CHILD_FORWARD_TOP_LEFT;
}
if (input > STAGE_CHILD_FORWARD_TOP_LEFT) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageChildPreBackwardTopLeftLength;
currentStateStartProgress = STAGE_CHILD_FORWARD_TOP_LEFT;
currentStateEndProgress = STAGE_CHILD_PRE_BACKWARD_TOP_LEFT;
}
if (input > STAGE_CHILD_PRE_BACKWARD_TOP_LEFT) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageChildBackwardTopLeftLength;
currentStateStartProgress = STAGE_CHILD_PRE_BACKWARD_TOP_LEFT;
currentStateEndProgress = STAGE_CHILD_BACKWARD_TOP_LEFT;
}
if (input > STAGE_CHILD_BACKWARD_TOP_LEFT) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageChildForwardBottomLeftLength;
currentStateStartProgress = STAGE_CHILD_BACKWARD_TOP_LEFT;
currentStateEndProgress = STAGE_CHILD_FORWARD_BOTTOM_LEFT;
}
if (input > STAGE_CHILD_FORWARD_BOTTOM_LEFT) {
currentStartDistance += currentStageDistance;
currentStageDistance = mStageChildBackwardBottomLeftLength;
currentStateStartProgress = STAGE_CHILD_FORWARD_BOTTOM_LEFT;
currentStateEndProgress = STAGE_CHILD_BACKWARD_BOTTOM_LEFT;
}
if (input > STAGE_CHILD_BACKWARD_BOTTOM_LEFT) {
return currentStartDistance + currentStageDistance;
}
return currentStartDistance + (input - currentStateStartProgress) /
(currentStateEndProgress - currentStateStartProgress) * currentStageDistance;
}
private Path createMotherMovePath() {
Path path = new Path();
float centerX = mCurrentBounds.centerX();
float centerY = mCurrentBounds.centerY();
float currentPathLength = 0.0f;
path.moveTo(centerX, centerY);
//forward top left
path.quadTo(centerX - mMotherOvalHalfWidth * 2.0f, centerY,
centerX - mMotherOvalHalfWidth * 2.0f, centerY - mMotherOvalHalfHeight);
mStageMotherForwardTopLeftLength = getRestLength(path, currentPathLength);
currentPathLength += mStageMotherForwardTopLeftLength;
//backward top left
path.quadTo(centerX - mMotherOvalHalfWidth * 1.0f, centerY - mMotherOvalHalfHeight,
centerX, centerY);
mStageMotherBackwardTopLeftLength = getRestLength(path, currentPathLength);
currentPathLength += mStageMotherBackwardTopLeftLength;
//forward bottom left
path.quadTo(centerX, centerY + mMotherOvalHalfHeight,
centerX - mMotherOvalHalfWidth / 2, centerY + mMotherOvalHalfHeight * 1.1f);
mStageMotherForwardBottomLeftLength = getRestLength(path, currentPathLength);
currentPathLength += mStageMotherForwardBottomLeftLength;
//backward bottom left
path.quadTo(centerX - mMotherOvalHalfWidth / 2, centerY + mMotherOvalHalfHeight * 0.6f,
centerX, centerY);
mStageMotherBackwardBottomLeftLength = getRestLength(path, currentPathLength);
return path;
}
private Path createChildMovePath() {
Path path = new Path();
float centerX = mCurrentBounds.centerX();
float centerY = mCurrentBounds.centerY();
float currentPathLength = 0.0f;
//start
path.moveTo(centerX, centerY);
//pre forward top left
path.lineTo(centerX + mMotherOvalHalfWidth * 0.75f, centerY);
mStageChildPreForwardTopLeftLength = getRestLength(path, currentPathLength);
currentPathLength += mStageChildPreForwardTopLeftLength;
//forward top left
path.quadTo(centerX - mMotherOvalHalfWidth * 0.5f, centerY,
centerX - mMotherOvalHalfWidth * 2.0f, centerY - mMotherOvalHalfHeight);
mStageChildForwardTopLeftLength = getRestLength(path, currentPathLength);
currentPathLength += mStageChildForwardTopLeftLength;
//pre backward top left
path.lineTo(centerX - mMotherOvalHalfWidth * 2.0f + mMotherOvalHalfWidth * 0.2f, centerY - mMotherOvalHalfHeight);
path.quadTo(centerX - mMotherOvalHalfWidth * 2.5f, centerY - mMotherOvalHalfHeight * 2,
centerX - mMotherOvalHalfWidth * 1.5f, centerY - mMotherOvalHalfHeight * 2.25f);
mStageChildPreBackwardTopLeftLength = getRestLength(path, currentPathLength);
currentPathLength += mStageChildPreBackwardTopLeftLength;
//backward top left
path.quadTo(centerX - mMotherOvalHalfWidth * 0.2f, centerY - mMotherOvalHalfHeight * 2.25f,
centerX, centerY);
mStageChildBackwardTopLeftLength = getRestLength(path, currentPathLength);
currentPathLength += mStageChildBackwardTopLeftLength;
//forward bottom left
path.cubicTo(centerX, centerY + mMotherOvalHalfHeight,
centerX - mMotherOvalHalfWidth, centerY + mMotherOvalHalfHeight * 2.5f,
centerX - mMotherOvalHalfWidth * 1.5f, centerY + mMotherOvalHalfHeight * 2.5f);
mStageChildForwardBottomLeftLength = getRestLength(path, currentPathLength);
currentPathLength += mStageChildForwardBottomLeftLength;
//backward bottom left
path.cubicTo(
centerX - mMotherOvalHalfWidth * 2.0f, centerY + mMotherOvalHalfHeight * 2.5f,
centerX - mMotherOvalHalfWidth * 3.0f, centerY + mMotherOvalHalfHeight * 0.8f,
centerX, centerY);
mStageChildBackwardBottomLeftLength = getRestLength(path, currentPathLength);
return path;
}
private int getCurrentRevealCircleRadius(float input) {
int result = 0;
if (input > 0.44f && input < 0.48f) {
result = (int) ((input - 0.44f) / 0.04f * mMaxRevealCircleRadius);
}
if (input > 0.81f && input < 0.85f) {
result = (int) ((input - 0.81f) / 0.04f * mMaxRevealCircleRadius);
}
return result;
}
private int getCurrentBackgroundColor(float input) {
return input < 0.48f || input > 0.85f ? mBackgroundColor : mBackgroundDeepColor;
}
private int getCurrentOvalColor(float input) {
int result;
if (input < 0.5f) {
result = mOvalColor;
} else if (input < 0.75f) {
float colorProgress = (input - 0.5f) / 0.2f;
result = evaluateColorChange(colorProgress, mOvalColor, mOvalDeepColor);
} else if (input < 0.85f) {
result = mOvalDeepColor;
} else {
float colorProgress = (input - 0.9f) / 0.1f;
result = evaluateColorChange(colorProgress, mOvalDeepColor, mOvalColor);
}
return result;
}
private int evaluateColorChange(float fraction, int startValue, int endValue) {
int startA = (startValue >> 24) & 0xff;
int startR = (startValue >> 16) & 0xff;
int startG = (startValue >> 8) & 0xff;
int startB = startValue & 0xff;
int endA = (endValue >> 24) & 0xff;
int endR = (endValue >> 16) & 0xff;
int endG = (endValue >> 8) & 0xff;
int endB = endValue & 0xff;
return ((startA + (int) (fraction * (endA - startA))) << 24) |
((startR + (int) (fraction * (endR - startR))) << 16) |
((startG + (int) (fraction * (endG - startG))) << 8) |
((startB + (int) (fraction * (endB - startB))));
}
private float getRestLength(Path path, float startD) {
Path tempPath = new Path();
PathMeasure pathMeasure = new PathMeasure(path, false);
pathMeasure.getSegment(startD, pathMeasure.getLength(), tempPath, true);
pathMeasure.setPath(tempPath, false);
return pathMeasure.getLength();
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
}
private class MotherMoveInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
float result;
if (input <= STAGE_MOTHER_FORWARD_TOP_LEFT) {
result = ACCELERATE_INTERPOLATOR10.getInterpolation(input * 2.941f) / 2.941f;
} else if (input <= STAGE_MOTHER_BACKWARD_TOP_LEFT) {
result = 0.34f + DECELERATE_INTERPOLATOR10.getInterpolation((input - 0.34f) * 6.25f) / 6.25f;
} else if (input <= STAGE_MOTHER_FORWARD_BOTTOM_LEFT) {
result = 0.5f + ACCELERATE_INTERPOLATOR03.getInterpolation((input - 0.5f) * 6.666f) / 4.0f;
} else if (input <= STAGE_MOTHER_BACKWARD_BOTTOM_LEFT) {
result = 0.75f + DECELERATE_INTERPOLATOR03.getInterpolation((input - 0.65f) * 5.46f) / 4.0f;
} else {
result = 1.0f;
}
return result;
}
}
private class ChildMoveInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
float result;
if (input < STAGE_CHILD_DELAY) {
return 0.0f;
} else if (input <= STAGE_CHILD_PRE_FORWARD_TOP_LEFT) {
result = DECELERATE_INTERPOLATOR10.getInterpolation((input - 0.1f) * 6.25f) / 3.846f;
} else if (input <= STAGE_CHILD_FORWARD_TOP_LEFT) {
result = 0.26f + ACCELERATE_INTERPOLATOR10.getInterpolation((input - 0.26f) * 12.5f) / 12.5f;
} else if (input <= STAGE_CHILD_PRE_BACKWARD_TOP_LEFT) {
result = 0.34f + DECELERATE_INTERPOLATOR08.getInterpolation((input - 0.34f) * 12.5f) / 12.5f;
} else if (input <= STAGE_CHILD_BACKWARD_TOP_LEFT) {
result = 0.42f + ACCELERATE_INTERPOLATOR08.getInterpolation((input - 0.42f) * 12.5f) / 12.5f;
} else if (input <= STAGE_CHILD_FORWARD_BOTTOM_LEFT) {
result = 0.5f + DECELERATE_INTERPOLATOR05.getInterpolation((input - 0.5f) * 5.0f) / 5.0f;
} else if (input <= STAGE_CHILD_BACKWARD_BOTTOM_LEFT) {
result = 0.7f + ACCELERATE_INTERPOLATOR05.getInterpolation((input - 0.7f) * 5.0f) / 3.33f;
} else {
result = 1.0f;
}
return result;
}
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public CircleBroodLoadingRenderer build() {
CircleBroodLoadingRenderer loadingRenderer = new CircleBroodLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

View File

@@ -0,0 +1,270 @@
package app.dinus.com.loadingdrawable.render.shapechange;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import app.dinus.com.loadingdrawable.DensityUtil;
import app.dinus.com.loadingdrawable.render.LoadingRenderer;
public class CoolWaitLoadingRenderer extends LoadingRenderer {
private final Interpolator ACCELERATE_INTERPOLATOR08 = new AccelerateInterpolator(0.8f);
private final Interpolator ACCELERATE_INTERPOLATOR10 = new AccelerateInterpolator(1.0f);
private final Interpolator ACCELERATE_INTERPOLATOR15 = new AccelerateInterpolator(1.5f);
private final Interpolator DECELERATE_INTERPOLATOR03 = new DecelerateInterpolator(0.3f);
private final Interpolator DECELERATE_INTERPOLATOR05 = new DecelerateInterpolator(0.5f);
private final Interpolator DECELERATE_INTERPOLATOR08 = new DecelerateInterpolator(0.8f);
private final Interpolator DECELERATE_INTERPOLATOR10 = new DecelerateInterpolator(1.0f);
private static final Interpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
private final float DEFAULT_WIDTH = 200.0f;
private final float DEFAULT_HEIGHT = 150.0f;
private final float DEFAULT_STROKE_WIDTH = 8.0f;
private final float WAIT_CIRCLE_RADIUS = 50.0f;
private static final float WAIT_TRIM_DURATION_OFFSET = 0.5f;
private static final float END_TRIM_DURATION_OFFSET = 1.0f;
private final long ANIMATION_DURATION = 2222;
private final Paint mPaint = new Paint();
private final Path mWaitPath = new Path();
private final Path mCurrentTopWaitPath = new Path();
private final Path mCurrentMiddleWaitPath = new Path();
private final Path mCurrentBottomWaitPath = new Path();
private final PathMeasure mWaitPathMeasure = new PathMeasure();
private final RectF mCurrentBounds = new RectF();
private float mStrokeWidth;
private float mWaitCircleRadius;
private float mOriginEndDistance;
private float mOriginStartDistance;
private float mWaitPathLength;
private int mTopColor;
private int mMiddleColor;
private int mBottomColor;
private CoolWaitLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
}
private void init(Context context) {
mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
mWaitCircleRadius = DensityUtil.dip2px(context, WAIT_CIRCLE_RADIUS);
mTopColor = Color.WHITE;
mMiddleColor = Color.parseColor("#FFF3C742");
mBottomColor = Color.parseColor("#FF89CC59");
mDuration = ANIMATION_DURATION;
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
RectF arcBounds = mCurrentBounds;
arcBounds.set(bounds);
mPaint.setColor(mBottomColor);
canvas.drawPath(mCurrentBottomWaitPath, mPaint);
mPaint.setColor(mMiddleColor);
canvas.drawPath(mCurrentMiddleWaitPath, mPaint);
mPaint.setColor(mTopColor);
canvas.drawPath(mCurrentTopWaitPath, mPaint);
canvas.restoreToCount(saveCount);
}
private Path createWaitPath(RectF bounds) {
Path path = new Path();
//create circle
path.moveTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY());
//create w
path.cubicTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius * 0.5f,
bounds.centerX() + mWaitCircleRadius * 0.3f, bounds.centerY() - mWaitCircleRadius,
bounds.centerX() - mWaitCircleRadius * 0.35f, bounds.centerY() + mWaitCircleRadius * 0.5f);
path.quadTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
bounds.centerX() + mWaitCircleRadius * 0.05f, bounds.centerY() + mWaitCircleRadius * 0.5f);
path.lineTo(bounds.centerX() + mWaitCircleRadius * 0.75f, bounds.centerY() - mWaitCircleRadius * 0.2f);
path.cubicTo(bounds.centerX(), bounds.centerY() + mWaitCircleRadius * 1f,
bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius * 0.4f,
bounds.centerX() + mWaitCircleRadius, bounds.centerY());
//create arc
path.arcTo(new RectF(bounds.centerX() - mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius), 0, -359);
path.arcTo(new RectF(bounds.centerX() - mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius), 1, -359);
path.arcTo(new RectF(bounds.centerX() - mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius), 2, -2);
//create w
path.cubicTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius * 0.5f,
bounds.centerX() + mWaitCircleRadius * 0.3f, bounds.centerY() - mWaitCircleRadius,
bounds.centerX() - mWaitCircleRadius * 0.35f, bounds.centerY() + mWaitCircleRadius * 0.5f);
path.quadTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
bounds.centerX() + mWaitCircleRadius * 0.05f, bounds.centerY() + mWaitCircleRadius * 0.5f);
path.lineTo(bounds.centerX() + mWaitCircleRadius * 0.75f, bounds.centerY() - mWaitCircleRadius * 0.2f);
path.cubicTo(bounds.centerX(), bounds.centerY() + mWaitCircleRadius * 1f,
bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius * 0.4f,
bounds.centerX() + mWaitCircleRadius, bounds.centerY());
return path;
}
@Override
protected void computeRender(float renderProgress) {
if (mCurrentBounds.isEmpty()) {
return;
}
if (mWaitPath.isEmpty()) {
mWaitPath.set(createWaitPath(mCurrentBounds));
mWaitPathMeasure.setPath(mWaitPath, false);
mWaitPathLength = mWaitPathMeasure.getLength();
mOriginEndDistance = mWaitPathLength * 0.255f;
mOriginStartDistance = mWaitPathLength * 0.045f;
}
mCurrentTopWaitPath.reset();
mCurrentMiddleWaitPath.reset();
mCurrentBottomWaitPath.reset();
//draw the first half : top
if (renderProgress <= WAIT_TRIM_DURATION_OFFSET) {
float topTrimProgress = ACCELERATE_DECELERATE_INTERPOLATOR.getInterpolation(renderProgress / WAIT_TRIM_DURATION_OFFSET);
float topEndDistance = mOriginEndDistance + mWaitPathLength * 0.3f * topTrimProgress;
float topStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f * topTrimProgress;
mWaitPathMeasure.getSegment(topStartDistance, topEndDistance, mCurrentTopWaitPath, true);
}
//draw the first half : middle
if (renderProgress > 0.02f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET * 0.75f) {
float middleStartTrimProgress = ACCELERATE_INTERPOLATOR10.getInterpolation((renderProgress - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.73f));
float middleEndTrimProgress = DECELERATE_INTERPOLATOR08.getInterpolation((renderProgress - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.73f));
float middleEndDistance = mOriginStartDistance + mWaitPathLength * 0.42f * middleEndTrimProgress;
float middleStartDistance = mOriginStartDistance + mWaitPathLength * 0.42f * middleStartTrimProgress;
mWaitPathMeasure.getSegment(middleStartDistance, middleEndDistance, mCurrentMiddleWaitPath, true);
}
//draw the first half : bottom
if (renderProgress > 0.04f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET * 0.75f) {
float bottomStartTrimProgress = ACCELERATE_INTERPOLATOR15.getInterpolation((renderProgress - 0.04f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.71f));
float bottomEndTrimProgress = DECELERATE_INTERPOLATOR05.getInterpolation((renderProgress - 0.04f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.71f));
float bottomEndDistance = mOriginStartDistance + mWaitPathLength * 0.42f * bottomEndTrimProgress;
float bottomStartDistance = mOriginStartDistance + mWaitPathLength * 0.42f * bottomStartTrimProgress;
mWaitPathMeasure.getSegment(bottomStartDistance, bottomEndDistance, mCurrentBottomWaitPath, true);
}
//draw the last half : top
if (renderProgress <= END_TRIM_DURATION_OFFSET && renderProgress > WAIT_TRIM_DURATION_OFFSET) {
float trimProgress = ACCELERATE_DECELERATE_INTERPOLATOR.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - WAIT_TRIM_DURATION_OFFSET));
float topEndDistance = mOriginEndDistance + mWaitPathLength * 0.3f + mWaitPathLength * 0.45f * trimProgress;
float topStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.27f * trimProgress;
mWaitPathMeasure.getSegment(topStartDistance, topEndDistance, mCurrentTopWaitPath, true);
}
//draw the last half : middle
if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.02f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET + WAIT_TRIM_DURATION_OFFSET * 0.62f) {
float middleStartTrimProgress = ACCELERATE_INTERPOLATOR08.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));
float middleEndTrimProgress = DECELERATE_INTERPOLATOR03.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));
float middleEndDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.20f * middleEndTrimProgress;
float middleStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.10f * middleStartTrimProgress;
mWaitPathMeasure.getSegment(middleStartDistance, middleEndDistance, mCurrentMiddleWaitPath, true);
}
if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.62f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= END_TRIM_DURATION_OFFSET) {
float middleStartTrimProgress = DECELERATE_INTERPOLATOR10.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.62f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.38f));
float middleEndTrimProgress = DECELERATE_INTERPOLATOR03.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.62f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.38f));
float middleEndDistance = mOriginStartDistance + mWaitPathLength * 0.68f + mWaitPathLength * 0.325f * middleEndTrimProgress;
float middleStartDistance = mOriginStartDistance + mWaitPathLength * 0.58f + mWaitPathLength * 0.17f * middleStartTrimProgress;
mWaitPathMeasure.getSegment(middleStartDistance, middleEndDistance, mCurrentMiddleWaitPath, true);
}
//draw the last half : bottom
if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.10f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET + WAIT_TRIM_DURATION_OFFSET * 0.70f) {
float bottomStartTrimProgress = ACCELERATE_INTERPOLATOR15.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.10f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));
float bottomEndTrimProgress = DECELERATE_INTERPOLATOR03.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.10f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));
float bottomEndDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.20f * bottomEndTrimProgress;
float bottomStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.10f * bottomStartTrimProgress;
mWaitPathMeasure.getSegment(bottomStartDistance, bottomEndDistance, mCurrentBottomWaitPath, true);
}
if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.70f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= END_TRIM_DURATION_OFFSET) {
float bottomStartTrimProgress = DECELERATE_INTERPOLATOR05.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.70f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.30f));
float bottomEndTrimProgress = DECELERATE_INTERPOLATOR03.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.70f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.30f));
float bottomEndDistance = mOriginStartDistance + mWaitPathLength * 0.68f + mWaitPathLength * 0.325f * bottomEndTrimProgress;
float bottomStartDistance = mOriginStartDistance + mWaitPathLength * 0.58f + mWaitPathLength * 0.17f * bottomStartTrimProgress;
mWaitPathMeasure.getSegment(bottomStartDistance, bottomEndDistance, mCurrentBottomWaitPath, true);
}
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public CoolWaitLoadingRenderer build() {
CoolWaitLoadingRenderer loadingRenderer = new CoolWaitLoadingRenderer(mContext);
return loadingRenderer;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1018 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LoadingView">
<attr name="loading_renderer">
<!--circle rotate-->
<enum name="MaterialLoadingRenderer" value="0"/>
<enum name="LevelLoadingRenderer" value="1"/>
<enum name="WhorlLoadingRenderer" value="2"/>
<enum name="GearLoadingRenderer" value="3"/>
<!--circle jump-->
<enum name="SwapLoadingRenderer" value="4"/>
<enum name="GuardLoadingRenderer" value="5"/>
<enum name="DanceLoadingRenderer" value="6"/>
<enum name="CollisionLoadingRenderer" value="7"/>
<!--Scenery-->
<enum name="DayNightLoadingRenderer" value="8"/>
<enum name="ElectricFanLoadingRenderer" value="9"/>
<!--Animal-->
<enum name="FishLoadingRenderer" value="10"/>
<enum name="GhostsEyeLoadingRenderer" value="11"/>
<!--Goods-->
<enum name="BalloonLoadingRenderer" value="12"/>
<enum name="WaterBottleLoadingRenderer" value="13"/>
<!--ShapeChange-->
<enum name="CircleBroodLoadingRenderer" value="14"/>
<enum name="CoolWaitLoadingRenderer" value="15"/>
</attr>
</declare-styleable>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Library</string>
</resources>

View File

@@ -945,3 +945,9 @@ public static java.lang.String TABLENAME;
-keep class **.R$string { *; }
-keep class com.qcloud.cos.** { *; }
-dontwarn com.qcloud.cos.**
-keep class com.qcloud.cos.model.** { *; }
-keep class * implements com.qcloud.cos.** { *; }
-keepattributes Signature, *Annotation*
-keep class com.qcloud.cos.**$* { *; }

View File

@@ -11,9 +11,9 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 40,
"versionName": "1.0.5.2",
"outputFile": "羽声_1.0.5.2_40.apk"
"versionCode": 43,
"versionName": "1.0.5.3",
"outputFile": "羽声_1.0.5.3_43.apk"
}
],
"elementType": "File",
@@ -22,14 +22,14 @@
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/羽声_1.0.5.2_40.dm"
"baselineProfiles/1/羽声_1.0.5.3_43.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/羽声_1.0.5.2_40.dm"
"baselineProfiles/0/羽声_1.0.5.3_43.dm"
]
}
],

Binary file not shown.

View File

@@ -28,8 +28,8 @@ isBuildModule=false
#org.gradle.deamon=false
android.injected.testOnly=false
APP_VERSION_NAME=1.0.5.2
APP_VERSION_CODE=40
APP_VERSION_NAME=1.0.5.3
APP_VERSION_CODE=43
org.gradle.jvm.toolchain.useLegacyAdapters=false
#org.gradle.java.home=C\:\\Users\\qx\\.jdks\\ms-17.0.15

View File

@@ -78,6 +78,7 @@ appcompatVersion = "1.3.1"
legacySupportV4 = "1.0.0"
fragmentKtx = "1.5.6"
cosAndroidLite = "5.9.46"
interpolator = "1.0.0"
[libraries]
alipay-alipaysdk-android = { module = "com.alipay.sdk:alipaysdk-android", version.ref = "alipayAlipaysdkAndroid" }
@@ -162,6 +163,7 @@ zcw-togglebutton-library = { module = "com.zcw:togglebutton-library", version.re
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompatVersion" }
androidx-legacy-support-v4 = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacySupportV4" }
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
androidx-interpolator = { group = "androidx.interpolator", name = "interpolator", version.ref = "interpolator" }
[plugins]

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import com.hjq.toast.ToastUtils;
import com.xscm.moduleutil.base.CommonAppContext;
import com.xscm.moduleutil.bean.UserBean;
import com.xscm.moduleutil.http.BaseObserver;
import com.xscm.moduleutil.presenter.BasePresenter;
@@ -26,7 +27,7 @@ public class ImproveInfoPresenter extends BasePresenter<ImproveInfoContacts.View
public void uploadFile(File file, int type) {
MvpRef.get().showLoadings("上传中...");
String url = OSSOperUtils.getPath(file, type);
CosUploadManager.getInstance().upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
CosUploadManager.getInstance(CommonAppContext.getInstance()).upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
@Override
public void onSuccess(String url) {
if (MvpRef==null){
@@ -42,6 +43,12 @@ public class ImproveInfoPresenter extends BasePresenter<ImproveInfoContacts.View
ToastUtils.show("上传失败");
MvpRef.get().disLoadings();
}
@Override
public void onFailure1(IllegalStateException e) {
ToastUtils.show("上传失败");
MvpRef.get().disLoadings();
}
});
// OSSOperUtils.newInstance().putObjectMethod(url, file.getPath(), new OSSOperUtils.OssCallback() {

View File

@@ -221,6 +221,8 @@ dependencies {
// 腾讯cos云储存
api 'com.qcloud.cos:cos-android:5.9.+'
api project(':Loadinglibrary')
// room数据库
// def room_version = "2.5.0"
// implementation "androidx.room:room-runtime:$room_version"

View File

@@ -1157,7 +1157,7 @@ public abstract class BaseAppCompatActivity<VDB extends ViewDataBinding> extends
// 这里可以根据实际需求实现跳转逻辑
// 例如:跳转到礼物详情页面、用户主页等
// 使用缓存数据进入房间
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), xlhBean.getRoom_id(), "");
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), xlhBean.getRoom_id(), "",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("from", "我的界面").withString("roomId", xlhBean.getRoom_id()).navigation();
}
@@ -1166,7 +1166,7 @@ public abstract class BaseAppCompatActivity<VDB extends ViewDataBinding> extends
// 这里可以根据实际需求实现跳转逻辑
// 例如:跳转到礼物详情页面、用户主页等
// 使用缓存数据进入房间
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), redBean.getRoom_id(), "");
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), redBean.getRoom_id(), "",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("from", "我的界面").withString("roomId", xlhBean.getRoom_id()).navigation();
}

View File

@@ -390,7 +390,7 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
@JavascriptInterface
public void jumpRoomPage(String room_id) {
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), room_id,"");
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), room_id,"",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页").withString("roomId", room_id).navigation();
}

View File

@@ -78,7 +78,7 @@ public class OfficialNoticeActivity extends BaseMvpActivity<NewsPresenter, Activ
@Override
public void onClick(View view) {
if (item.getRoom_id()>0){
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id()+"","");
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id()+"","",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("roomId", item.getRoom_id() + "").navigation();
}else if (item.getRoom_id() == 0 && item.getUrl() != null && !item.getUrl().isEmpty()){
Intent intent = new Intent(OfficialNoticeActivity.this, WebViewActivity.class);

View File

@@ -459,7 +459,7 @@ public class CommonAppContext extends MultiDexApplication implements Applicatio
SpUtil.getInstance().setBooleanValue("youth_model_shown", false);
startInitSdk();
// 初始化通常在Application或Activity的onCreate中
CosUploadManager.getInstance().init(this);
CosUploadManager.getInstance(CommonAppContext.getInstance());
// 启动IM连接服务
// IMServiceManager.getInstance().startIMService(this);
}

View File

@@ -35,6 +35,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import io.reactivex.disposables.Disposable;
import org.jetbrains.annotations.NotNull;
/**
* 房间管理器
@@ -102,10 +103,10 @@ public class RoomManager {
if (roomInfo != null) {
// 使用缓存数据直接进入房间
navigateToRoom(context, roomId, password, roomInfo, false);
navigateToRoom(context, roomId, password, roomInfo, false,null);
} else {
// 获取房间数据后进入房间
fetchRoomDataAndEnter(context, roomId, password);
fetchRoomDataAndEnter(context, roomId, password,null);
}
}
@@ -116,19 +117,20 @@ public class RoomManager {
* @param roomId 房间ID
* @param password 房间密码
*/
public void fetchRoomDataAndEnter(Context context, String roomId, String password) {
public void fetchRoomDataAndEnter(Context context, String roomId, String password,String taskId) {
// 显示加载提示
// 这里可以根据需要添加加载对话框
if (CommonAppContext.getInstance().isRoomJoininj){
return;
}
CommonAppContext.getInstance().isRoomJoininj=true;
// 检查是否有有效的缓存数据
RoomInfoResp roomInfo = getCachedRoomData(roomId);
// 检查是否是当前房间且用户在线
// boolean isCurrentRoom = isCurrentRoom(roomId);
if (CommonAppContext.getInstance().playId == null) {
fetchAndJoinRoom(context, roomId, password);
fetchAndJoinRoom(context, roomId, password,taskId);
} else {
if (!CommonAppContext.getInstance().playId.equals(roomId)) {
MessageListenerSingleton.getInstance().joinGroup(roomId);
@@ -137,11 +139,13 @@ public class RoomManager {
CommonAppContext.getInstance().isPlaying = false;
CommonAppContext.getInstance().isRoomJoininj=false;
EventBus.getDefault().post(new RoomOutEvent());
// fetchAndJoinRoom(context, roomId, password);
// return;
} else if (CommonAppContext.getInstance().lable_id.equals("6")) {
upInfo(context, roomId, password, true, roomInfo, true);
upInfo(context, roomId, password, true, roomInfo, true,taskId);
return;
}
isUserOnline(context, roomId, password, roomInfo);
isUserOnline(context, roomId, password, roomInfo,taskId);
}
@@ -195,58 +199,26 @@ public class RoomManager {
// navigateToRoom(context, roomId, password, null);
}
private void upInfo(Context context, String roomId, String password, boolean isOnline, RoomInfoResp roomInfo, boolean isCurrentRoom) {
private void upInfo(Context context, String roomId, String password, boolean isOnline, RoomInfoResp roomInfo, boolean isCurrentRoom,String taskId) {
if (isOnline) {
navigateToRoom(context, roomId, password, roomInfo, isOnline);
} else {
// CommonAppContext.getInstance().isShow = false;
// CommonAppContext.getInstance().isPlaying = false;
// EventBus.getDefault().post(new RoomOutEvent());
// try {
// Thread.sleep(300);
// } catch (InterruptedException e) {
// Thread.currentThread().interrupt();
// }
fetchAndJoinRoom(context, roomId, password);
RetrofitClient.getInstance().postRoomInfo(roomId, new BaseObserver<RoomInfoResp>() {
@Override
public void onSubscribe(@NotNull Disposable disposable) {
}
// if (isCurrentRoom&& isOnline) {
// if (roomInfo != null) {
// navigateToRoom(context, roomId, password, roomInfo);
// } else {
// // 即使在线,如果没有缓存数据,也需要获取数据
// fetchAndJoinRoom(context, roomId, password);
// }
// return;
// }
// 如果有缓存数据且用户在线,使用缓存数据进入房间
// if (roomInfo != null && isOnline) {
// RetrofitClient.getInstance().postRoomInfo(roomId, new BaseObserver<RoomInfoResp>() {
//
// @Override
// public void onSubscribe(Disposable d) {
//
// }
//
// @Override
// public void onNext(RoomInfoResp roomInfoResp) {
//// cacheRoomData(roomId, roomInfo);
// navigateToRoom(context, roomId, password, roomInfoResp);
// }
// });
// cacheRoomData(roomId, roomInfo);
// navigateToRoom(context, roomId, password, roomInfo);
return;
// }
// 其他情况,获取新的房间数据并加入房间
// fetchAndJoinRoom(context, roomId, password);
@Override
public void onNext(@NotNull RoomInfoResp roomInfoResp) {
navigateToRoom(context, roomId, password, roomInfoResp, false,taskId);
}
});
// navigateToRoom(context, roomId, password, roomInfo, isOnline);
} else {
fetchAndJoinRoom(context, roomId, password,taskId);
}
}
/**
@@ -256,7 +228,7 @@ public class RoomManager {
* @param roomId 房间ID
* @param password 房间密码
*/
private void fetchAndJoinRoom(Context context, String roomId, String password) {
private void fetchAndJoinRoom(Context context, String roomId, String password,String taskId) {
// 获取房间数据
// 等待一段时间确保退出完成
@@ -266,7 +238,6 @@ public class RoomManager {
// Thread.currentThread().interrupt();
// }
// navigateToRoom(context, roomId, password, null, false);
LogUtils.dTag("RoomActivity", "fetchAndJoinRoom:"+roomId.toString());
RetrofitClient.getInstance().roomGetIn(roomId, password, new BaseObserver<RoomInfoResp>() {
@Override
@@ -292,7 +263,12 @@ public class RoomManager {
AgoraManager.getInstance(context)
.joinRoom(token, roomId, uid, enableMic, enableJs);
cacheRoomData(roomId, resp);
navigateToRoom(context, roomId, password, resp, false);
navigateToRoom(context, roomId, password, resp, false,taskId);
}
@Override
public void onError(Throwable e) {
super.onError(e);
}
});
}
@@ -328,8 +304,7 @@ public class RoomManager {
* @param password 房间密码
* @param roomInfo 房间信息
*/
private void navigateToRoom(Context context, String roomId, String password, RoomInfoResp roomInfo, boolean isOnline) {
LogUtils.dTag("RoomActivity", "navigateToRoom"+roomInfo.toString());
private void navigateToRoom(Context context, String roomId, String password, RoomInfoResp roomInfo, boolean isOnline,String taskId) {
try {
// 构建跳转参数
@@ -340,28 +315,23 @@ public class RoomManager {
if (!TextUtils.isEmpty(password)) {
bundle.putString("password", password);
}
if (taskId != null){
bundle.putString("taskId", taskId);
}
if (roomInfo == null){
LogUtils.dTag("RoomActivity", "navigateToRoom:房间信息获取存在问题");
return;
}
if (isOnline){
ARouter.getInstance()
.build(ARouteConstants.ROOM_DETAILS)
.navigation(context);
}else {
// 使用ARouter跳转到房间页面
ARouter.getInstance()
.build(ARouteConstants.ROOM_DETAILS)
.with(bundle)
.navigation(context);
}
} catch (Exception e) {
Logger.e(TAG, "跳转房间页面失败: " + e.getMessage());
}finally {
}
}
@@ -416,7 +386,7 @@ public class RoomManager {
* @param roomId 房间ID
* @return true表示用户在线false表示不在线
*/
private boolean isUserOnline(Context context, String roomId, String password, RoomInfoResp roomInfo) {
private boolean isUserOnline(Context context, String roomId, String password, RoomInfoResp roomInfo,String taskId) {
// 这里应该实现检查用户是否在线的逻辑
// 可以通过检查Agora是否还在房间中或者通过服务端接口查询用户状态等方式实现
// 目前返回false需要根据实际需求实现具体逻辑
@@ -426,6 +396,9 @@ public class RoomManager {
// } catch (InterruptedException e) {
// Thread.currentThread().interrupt();
// }
final boolean[] isOnline = {false};
RetrofitClient.getInstance().getRoomOnline(roomId, "1", "50", new BaseObserver<RoomOnline>() {
@Override
@@ -453,7 +426,8 @@ public class RoomManager {
}
}
}
upInfo(context, roomId, password, isOnline[0], roomInfo, true);
upInfo(context, roomId, password, isOnline[0], roomInfo, true, taskId);
} else {
isOnline[0] = false;
}
@@ -462,11 +436,11 @@ public class RoomManager {
e.printStackTrace();
isOnline[0] = false;
// 即使出现异常也继续执行
upInfo(context, roomId, password, isOnline[0], roomInfo, true);
upInfo(context, roomId, password, isOnline[0], roomInfo, true, taskId);
}
}
});
return isOnline[0];
return false;
}
/**

View File

@@ -0,0 +1,49 @@
package com.xscm.moduleutil.dialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.xscm.moduleutil.R;
/**
* com.xscm.moduleutil.dialog
* qx
* 2025/10/27
*/
public class LoadingDialog extends Dialog {
public LoadingDialog(@NonNull Context context) {
super(context);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置布局这里假设你有一个R.layout.loading_dialog布局文件
setContentView(R.layout.loading_dialog);
// 设置不可取消
setCancelable(false);
}
/**
* 显示加载对话框
*/
public void showLoading() {
if (!isShowing()) {
show();
}
}
/**
* 隐藏加载对话框
*/
public void hideLoading() {
if (isShowing()) {
dismiss();
}
}
}

View File

@@ -178,7 +178,7 @@ public class RoomAuctionWebViewDialog extends BaseDialog<DialogRoomAuctionWebvie
@JavascriptInterface
public void jumpRoomPage(String room_id) {
RoomManager.getInstance().fetchRoomDataAndEnter(getContext(), room_id,"");
RoomManager.getInstance().fetchRoomDataAndEnter(getContext(), room_id,"",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页").withString("roomId", room_id).navigation();
}

View File

@@ -193,7 +193,7 @@ public class WebViewDialog extends BaseDialog<WebViewDialogBinding> {
@JavascriptInterface
public void jumpRoomPage(String room_id) {
RoomManager.getInstance().fetchRoomDataAndEnter(getContext(), room_id,"");
RoomManager.getInstance().fetchRoomDataAndEnter(getContext(), room_id,"",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页").withString("roomId", room_id).navigation();
}

View File

@@ -1938,6 +1938,7 @@ public class RetrofitClient {
public void onFailure(Call<BaseModel<RoomInfoResp>> call, Throwable t) {
MessageListenerSingleton.getInstance().quitGroup(roomId);
CommonAppContext.getInstance().isRoomJoininj = false;
observer.onError( t);
}
});
}

View File

@@ -445,6 +445,7 @@ public class AgoraManager {
@Override
public void onLyricResult(String requestId, long songCode, String lyricUrl, int reason) {
if (lyricUrl != null) {
LogUtils.e("roomInfoEvent",lyricUrl);
getLyricsInstance(lyricUrl);
}
}
@@ -1449,6 +1450,7 @@ public class AgoraManager {
.build();
if (StatusUtil.isCompleted(task)) {
MusicFileBean musicFileBean = new MusicFileBean();
LogUtils.e("roomInfoEvent",convertFileToByteArray(task.getFile()));
musicFileBean.setFileData(convertFileToByteArray(task.getFile()));
EventBus.getDefault().post(musicFileBean);
} else {
@@ -1477,6 +1479,7 @@ public class AgoraManager {
@Override
public void taskEnd(@NonNull DownloadTask task, @NonNull EndCause cause, @Nullable Exception realCause, @NonNull Listener1Assist.Listener1Model model) {
Logger.e("taskEnd", model);
LogUtils.e("roomInfoEvent",convertFileToByteArray(task.getFile()));
MusicFileBean musicFileBean = new MusicFileBean();
musicFileBean.setFileData(convertFileToByteArray(task.getFile()));
EventBus.getDefault().post(musicFileBean);

View File

@@ -26,6 +26,7 @@ public class ARouteConstants {
public static final String H5 ="/moduleUtil/WebViewActivity"; //网页
// public static final String GIFT_WALL ="/moduleroom/UserGiftWallFragment"; //实名认证
public static final String RECHARGE_ACTIVITY ="/modulevocal/RechargeActivity"; //实名认证
public static final String DailyTasksActivity ="/modulevocal/DailyTasksActivity"; //每日任务

View File

@@ -5,6 +5,7 @@ import android.os.Handler;
import android.os.Looper;
import androidx.annotation.Nullable;
import com.blankj.utilcode.util.LogUtils;
import com.hjq.toast.ToastUtils;
import com.tencent.cos.xml.CosXmlService;
import com.tencent.cos.xml.CosXmlServiceConfig;
import com.tencent.cos.xml.exception.CosXmlClientException;
@@ -31,33 +32,33 @@ import org.jetbrains.annotations.NotNull;
*/
public class CosUploadManager {
private static volatile CosUploadManager instance;
private Context context;
private Context mContext;
private Handler mainHandler;
// 私有构造函数,防止外部实例化
private CosUploadManager() {
private CosUploadManager(Context context)
{
mContext = context;
mainHandler = new Handler(Looper.getMainLooper());
}
// 双重检查锁定获取单例实例
public static CosUploadManager getInstance() {
public static CosUploadManager getInstance(Context context) {
if (instance == null) {
synchronized (CosUploadManager.class) {
if (instance == null) {
instance = new CosUploadManager();
instance = new CosUploadManager(context);
}
}
}
return instance;
}
public void init(Context context) {
this.context = context.getApplicationContext();
}
public void upParameters( String objectKey, String localPath, UploadCallback callback) {
// 确保已初始化
if (context == null) {
callback.onFailure(new IllegalStateException("CosUploadManager not initialized with context"));
return;
}
// if (context == null) {
// callback.onFailure1(new IllegalStateException("CosUploadManager not initialized with context"));
// return;
// }
RetrofitClient.getInstance().getTempKey(new BaseObserver<TempKeyBean>() {
@Override
public void onSubscribe(@NotNull Disposable disposable) {
@@ -91,7 +92,7 @@ public class CosUploadManager {
.setRegion(region)
.isHttps(true) // 使用 HTTPS 请求, 默认为 HTTP 请求
.builder();
CosXmlService cosXmlService = new CosXmlService(context, serviceConfig);
CosXmlService cosXmlService = new CosXmlService(mContext, serviceConfig);
// 任何 CosXmlRequest 都支持这种方式,例如上传 PutObjectRequest、下载 GetObjectRequest、删除 DeleteObjectRequest 等
// 以下用上传进行示例
@@ -142,5 +143,7 @@ public class CosUploadManager {
public interface UploadCallback {
void onSuccess(String url); // 上传成功返回访问URL
void onFailure(Exception e); // 上传失败
void onFailure1(IllegalStateException e);
}
}

View File

@@ -0,0 +1,288 @@
package com.xscm.moduleutil.widget;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.blankj.utilcode.util.ScreenUtils;
import com.xscm.moduleutil.utils.BarUtils;
/**
* com.xscm.moduleutil.widget
* qx
* 2025/10/27
*/
public class DropDayTaskView extends LinearLayout {
private int rightMargin = 0;
private float lastX, lastY;
private int screenWidth;
private int screenHeight; // 添加屏幕高度变量
public DropDayTaskView(Context context) {
super(context);
init();
}
public DropDayTaskView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DropDayTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
void init() {
// 初始化屏幕尺寸
screenWidth = ScreenUtils.getScreenWidth();
screenHeight = ScreenUtils.getScreenHeight();
post(new Runnable() {
@Override
public void run() {
//设置初始位置
int sh = ScreenUtils.getScreenHeight();
int sw = ScreenUtils.getScreenWidth();
// setBackgroundResource(R.drawable.bg_home_drop_view);
int y = (int) (0.7f * sh) - getHeight();
// 确保Y坐标不会超出屏幕范围
y = Math.max(0, Math.min(y, sh - getHeight()));
int x = sw - getWidth();
setTranslationX(x);
setTranslationY(y);
}
});
updateSize();
mStatusBarHeight = BarUtils.getStatusBarHeight();
}
/**
* 更新屏幕尺寸信息
*/
protected void updateSize() {
ViewGroup viewGroup = (ViewGroup) getParent();
if (viewGroup != null) {
mScreenWidth = viewGroup.getWidth();
mScreenHeight = viewGroup.getHeight();
} else {
// 如果父视图为空,使用屏幕的实际宽度和高度
mScreenWidth = getResources().getDisplayMetrics().widthPixels;
mScreenHeight = getResources().getDisplayMetrics().heightPixels;
}
}
boolean starDrap = false;
float X1;
float X2;
float Y1;
float Y2;
// 记录视图初始位置
private float originalX;
private float originalY;
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (starDrap) return true;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
X1 = event.getRawX();
Y1 = event.getRawY();
// 记录视图当前位置
originalX = getTranslationX();
originalY = getTranslationY();
break;
case MotionEvent.ACTION_MOVE:
X2 = event.getRawX();
Y2 = event.getRawY();
Action(X1, X2, Y1, Y2);
break;
}
return starDrap;
}
String TAG = "DropHourlView";
public boolean Action(float X1, float X2, float Y1, float Y2) {
float ComparedX = X2 - X1;//第二次的X坐标的位置减去第一次X坐标的位置代表X坐标上的变化情况
float ComparedY = Y2 - Y1;//同理
//当X坐标的变化量的绝对值大于Y坐标的变化量的绝对值以X坐标的变化情况作为判断依据
//上下左右的判断,都在一条直线上,但手指的操作不可能划直线,所有选择变化量大的方向上的量
//作为判断依据
if (Math.abs(ComparedX) > 30 || Math.abs(ComparedY) > 30) {
Log.i(TAG, "Action: 拖动");
starDrap = true;
// setBackgroundResource(R.drawable.bg_home_drop_view);
return true;
} else {
starDrap = false;
return false;
}
}
private float mOriginalRawX;
private float mOriginalRawY;
private float mOriginalX;
private float mOriginalY;
protected int mScreenWidth;
private int mScreenHeight;
private int mStatusBarHeight;
private void updateViewPosition(MotionEvent event) {
// 计算新的Y位置
float desY = mOriginalY + event.getRawY() - mOriginalRawY;
// 限制Y位置不超出屏幕边界
if (desY < mStatusBarHeight) {
desY = mStatusBarHeight;
}
if (desY > mScreenHeight - getHeight()) {
desY = mScreenHeight - getHeight();
}
// 计算新的X位置
float desX = mOriginalX + event.getRawX() - mOriginalRawX;
// 限制X位置不超出屏幕边界
if (desX < 0) {
desX = 0;
}
if (desX > mScreenWidth - getWidth()) {
desX = mScreenWidth - getWidth();
}
// 设置视图的新位置
setX(desX);
setY(desY);
}
private void changeOriginalTouchParams(MotionEvent event) {
mOriginalX = getX();//getX()相对于控件X坐标的距离
mOriginalY = getY();
mOriginalRawX = event.getRawX();//getRawX()指控件在屏幕上的X坐标
mOriginalRawY = event.getRawY();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event == null) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
changeOriginalTouchParams(event);
updateSize(); // 添加这行确保尺寸是最新的
// ... 其他现有代码 ...
break;
case MotionEvent.ACTION_MOVE:
updateViewPosition(event); // 使用更新后的带边界检查的方法
// setBackgroundResource(R.drawable.bg_home_drop_view);
// 使用屏幕绝对坐标计算新位置
// float newX = originalX + (event.getRawX() - X1);
// float newY = originalY + (event.getRawY() - Y1);
//
// // 限制X和Y坐标在屏幕范围内
// newX = Math.max(0, Math.min(newX, screenWidth - getWidth()));
// newY = Math.max(0, Math.min(newY, screenHeight - getHeight()));
//
// setTranslationX(newX);
// setTranslationY(newY);
// X2 = event.getRawX();
break;
case MotionEvent.ACTION_UP:
starDrap = false;
int sw = ScreenUtils.getScreenWidth();
Log.i(TAG, "onTouchEvent: " + sw + "," + X2);
boolean isR = getTranslationX() + getWidth() / 2 >= sw / 2;//贴边方向
// 获取当前Y坐标
float currentY = getTranslationY();
// 创建X轴和Y轴的动画
ObjectAnimator animX = ObjectAnimator.ofFloat(this, "translationX", isR ? sw - getWidth() : 0f).setDuration(200);
// Y轴保持当前位置但确保在屏幕范围内
currentY = Math.max(0, Math.min(currentY, screenHeight - getHeight()));
ObjectAnimator animY = ObjectAnimator.ofFloat(this, "translationY", currentY).setDuration(200);
animX.start();
animY.start();
break;
}
return true;
}
public void doRevealAnimation(View mPuppet, boolean flag) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int[] vLocation = new int[2];
getLocationInWindow(vLocation);
int centerX = vLocation[0] + getMeasuredWidth() / 2;
int centerY = vLocation[1] + getMeasuredHeight() / 2;
int height = ScreenUtils.getScreenHeight();
int width = ScreenUtils.getScreenWidth();
int maxRradius = (int) Math.hypot(height, width);
Log.e("hei", maxRradius + "");
if (flag) {
mPuppet.setVisibility(VISIBLE);
Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, maxRradius, 0);
animator.setDuration(600);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mPuppet.setVisibility(View.GONE);
}
});
animator.start();
flag = false;
} else {
Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, 0, maxRradius);
animator.setDuration(1000);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mPuppet.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
flag = true;
}
}
}
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_8" />
<solid android:color="@color/transparent" />
<solid android:color="#4d000000" />
</shape>

View File

@@ -4,8 +4,8 @@
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#4d000000"/>
<corners
android:topLeftRadius="8dp"
android:topRightRadius="8dp"
android:bottomLeftRadius="8dp"
android:bottomRightRadius="8dp"/>
android:topLeftRadius="12dp"
android:topRightRadius="12dp"
android:bottomLeftRadius="12dp"
android:bottomRightRadius="12dp"/>
</shape>

View File

@@ -2,12 +2,12 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:left="8dp"
android:top="8dp"
android:top="2dp"
android:right="8dp"
android:bottom="8dp">
android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#4d000000" />
<corners android:topLeftRadius="8dp" android:topRightRadius="8dp" android:bottomLeftRadius="8dp" android:bottomRightRadius="8dp" />
<corners android:topLeftRadius="12dp" android:topRightRadius="12dp" android:bottomLeftRadius="12dp" android:bottomRightRadius="12dp" />
</shape>
</item>
</layer-list>

View File

@@ -6,75 +6,16 @@
android:id="@+id/bubble1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="@dimen/dp_256"
android:maxWidth="@dimen/dp_256"
android:padding="@dimen/dp_8"
android:background="@drawable/ease_row_pubilc_user_bg"
android:layout_marginTop="@dimen/dp_3"
android:layout_marginStart="@dimen/dp_8"
android:paddingStart="@dimen/dp_8"
android:paddingTop="@dimen/dp_2"
android:paddingBottom="@dimen/dp_2"
android:background="@drawable/ease_row_pubilc_sys_bg"
>
<!-- 网络背景图片 -->
<!-- <ImageView-->
<!-- android:id="@+id/network_background"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="0dp"-->
<!-- tools:src="@mipmap/a1img_9"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintEnd_toEndOf="parent"/>-->
<!-- 用户头像 -->
<com.xscm.moduleutil.widget.GifAvatarOvalView
android:id="@+id/avatar"
android:layout_width="@dimen/dp_28"
android:layout_height="@dimen/dp_28"
android:layout_marginEnd="@dimen/dp_3"
android:layout_marginTop="@dimen/dp_3"
android:layout_marginBottom="@dimen/dp_3"
android:layout_marginStart="@dimen/dp_8"
app:riv_oval="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<!-- 用户名称 -->
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#CCA882"
android:textSize="@dimen/sp_12"
android:layout_marginStart="@dimen/dp_2"
tools:text="饶利"
app:layout_constraintStart_toEndOf="@+id/avatar"
app:layout_constraintTop_toTopOf="@+id/avatar"
android:visibility="gone"/>
<!-- 用户标签容器 -->
<LinearLayout
android:id="@+id/line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_2"
android:layout_marginTop="@dimen/dp_5"
android:orientation="horizontal"
app:layout_constraintStart_toEndOf="@+id/avatar"
app:layout_constraintTop_toBottomOf="@+id/tv_name"
android:visibility="gone"/>
<!-- 消息内容 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="left|center_vertical"
android:layout_marginStart="4dp"
android:layout_marginTop="2dp"
android:padding="@dimen/dp_3"
android:paddingEnd="@dimen/dp_10"
app:layout_constraintStart_toEndOf="@+id/avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/line">
<TextView
android:id="@+id/tv_content"
@@ -82,24 +23,14 @@
android:layout_height="wrap_content"
android:lineHeight="@dimen/dp_20"
android:lineSpacingExtra="@dimen/dp_2"
android:paddingStart="@dimen/dp_8"
android:paddingEnd="@dimen/dp_8"
android:paddingTop="@dimen/dp_2"
android:paddingBottom="@dimen/dp_2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:textColor="@color/white"
android:textSize="@dimen/sp_14"
android:textSize="@dimen/sp_12"
tools:text="饶利: 潇洒亼◇生2.0"
tools:visibility="gone"/>
<ImageView
android:id="@+id/im_emj"
android:layout_width="@dimen/dp_50"
android:layout_height="@dimen/dp_50"
android:layout_marginStart="@dimen/dp_2"
android:paddingStart="@dimen/dp_8"
android:paddingEnd="@dimen/dp_8"
android:scaleType="fitCenter"
tools:src="@mipmap/ic_launcher"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -4,9 +4,10 @@
android:layout_height="wrap_content"
android:id="@+id/bubble"
android:layout_marginTop="@dimen/dp_3"
android:layout_marginBottom="@dimen/dp_3"
android:padding="@dimen/dp_8"
android:layout_marginStart="@dimen/dp_8"
android:paddingStart="@dimen/dp_8"
android:paddingTop="@dimen/dp_2"
android:paddingBottom="@dimen/dp_2"
android:background="@drawable/ease_row_pubilc_sys_bg"
>
@@ -21,11 +22,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:paddingTop="@dimen/dp_5"
android:paddingBottom="@dimen/dp_5"
android:paddingTop="@dimen/dp_2"
android:paddingBottom="@dimen/dp_2"
android:paddingEnd="@dimen/dp_8"
android:text="我是房间公告"
android:textSize="@dimen/sp_14"/>
android:textSize="@dimen/sp_11"/>
</LinearLayout>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@color/transparent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<app.dinus.com.loadingdrawable.LoadingView
android:id="@+id/cool_wait_view"
android:layout_width="@dimen/dp_100"
android:layout_height="@dimen/dp_100"
android:visibility="visible"
app:loading_renderer="CoolWaitLoadingRenderer"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -129,7 +129,7 @@ public class DynamicDetailActivity extends BaseMvpActivity<CirclePresenter, Acti
@Override
public void onGensui(CircleListBean item) {
if (item.getRoom_id()!=null && !item.getRoom_id().equals("0")) {
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id(),"");
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id(),"",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页热门列表").withString("roomId", item.getRoom_id()).navigation();
}else {

View File

@@ -114,7 +114,7 @@ public class DynamicListActivity extends BaseMvpActivity<CirclePresenter, Activi
@Override
public void onGensui(CircleListBean item) {
if (item.getRoom_id()!=null && !item.getRoom_id().equals("0")) {
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id(),"");
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id(),"",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页热门列表").withString("roomId", item.getRoom_id()).navigation();
}else {

View File

@@ -163,7 +163,7 @@ public class CircleCategoryFragment extends BaseMvpFragment<CirclePresenter, Fra
@Override
public void onGensui(CircleListBean item) {
if (item.getRoom_id()!=null && !item.getRoom_id().equals("0")) {
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id(),"");
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id(),"",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页热门列表").withString("roomId", item.getRoom_id()).navigation();
}else {

View File

@@ -131,7 +131,7 @@ public class ExpandColumnFragment extends BaseMvpFragment<CirclePresenter, Fragm
@Override
public void onGnsClick(ExpandColumnBean item) {
if ( item.getRoom_id()!=0){
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id()+"","");
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id()+"","",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页热门列表").withString("roomId", item.getRoom_id()+"").navigation();
}else {

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import com.example.modulecircle.contacts.ReleaseContract;
import com.hjq.toast.ToastUtils;
import com.xscm.moduleutil.base.CommonAppContext;
import com.xscm.moduleutil.bean.HeatedBean;
import com.xscm.moduleutil.http.BaseObserver;
import com.xscm.moduleutil.presenter.BasePresenter;
@@ -53,7 +54,7 @@ public class ReleasePresenter extends BasePresenter<ReleaseContract.View> implem
String url = OSSOperUtils.getPath(file, type);
CosUploadManager.getInstance().upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
CosUploadManager.getInstance(CommonAppContext.getInstance()).upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
@Override
public void onSuccess(String url) {
if (MvpRef==null){
@@ -67,6 +68,13 @@ public class ReleasePresenter extends BasePresenter<ReleaseContract.View> implem
public void onFailure(Exception e) {
ToastUtils.show("上传失败");
}
@Override
public void onFailure1(IllegalStateException e) {
ToastUtils.show("上传失败");
}
});
// OSSOperUtils.newInstance().putObjectMethod(url, file.getPath(), new OSSOperUtils.OssCallback() {
// @Override

View File

@@ -9,7 +9,6 @@
<activity
android:name=".activity.MainActivity"
android:configChanges="fontScale"
android:launchMode="singleTask"
android:screenOrientation="portrait">
</activity>
</application>

View File

@@ -379,9 +379,10 @@ public class MainActivity extends BaseMvpActivity<HomePresenter, ActivityMainBin
CommonAppContext.getInstance().isRoomJoininj = false;
EventBus.getDefault().post(new RoomOutEvent());
} else if (id == R.id.riv) {
mBinding.coolWaitView.setVisibility(View.VISIBLE);
String roomId = CommonAppContext.getInstance().playId;
if (!TextUtils.isEmpty(roomId)) {
RoomManager.getInstance().fetchRoomDataAndEnter(this, CommonAppContext.getInstance().playId, "");
RoomManager.getInstance().fetchRoomDataAndEnter(this, CommonAppContext.getInstance().playId, "",null);
// MvpPre.getRoomOnline(roomId, "1", "100");
// AppStateManager stateManager = AppStateManager.getInstance();
@@ -441,6 +442,7 @@ public class MainActivity extends BaseMvpActivity<HomePresenter, ActivityMainBin
// }
}
@Override
public void quitSuccess(String roomId) {
// RtcManager.getInstance().leaveChannel(roomId);
@@ -790,12 +792,12 @@ public class MainActivity extends BaseMvpActivity<HomePresenter, ActivityMainBin
}
if (!isPlaying) {
RoomManager.getInstance().fetchRoomDataAndEnter(this, CommonAppContext.getInstance().playId, "");
RoomManager.getInstance().fetchRoomDataAndEnter(this, CommonAppContext.getInstance().playId, "",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).withString("form", "首页").withString("roomId", CommonAppContext.getInstance().playId).navigation();
} else {
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页").withString("roomId", CommonAppContext.getInstance().playId).navigation();
RoomManager.getInstance().fetchRoomDataAndEnter(this, CommonAppContext.getInstance().playId, "");
RoomManager.getInstance().fetchRoomDataAndEnter(this, CommonAppContext.getInstance().playId, "",null);
}
}
@@ -1065,11 +1067,12 @@ public class MainActivity extends BaseMvpActivity<HomePresenter, ActivityMainBin
}
// @Override
// protected void onPause() {
// super.onPause();
// moveTaskToBack(true);
// }
@Override
protected void onPause() {
super.onPause();
mBinding.coolWaitView.setVisibility(View.GONE);
}
private SystemLocationProvider locationProvider;
private void initLocation() {

View File

@@ -250,6 +250,15 @@
android:clickable="true"
android:focusable="true"
/>
<app.dinus.com.loadingdrawable.LoadingView
android:id="@+id/cool_wait_view"
android:layout_width="@dimen/dp_100"
android:layout_height="@dimen/dp_100"
android:layout_centerInParent="true"
android:visibility="gone"
android:background="@drawable/ps_ic_shadow_bg"
app:loading_renderer="CoolWaitLoadingRenderer"/>
</RelativeLayout>
</layout>

View File

@@ -17,9 +17,10 @@
android:name=".activity.RoomActivity"
android:screenOrientation="portrait"
android:launchMode="singleTask"
android:excludeFromRecents="true"
android:exported="true"
android:excludeFromRecents="false"
android:exported="false"
android:windowSoftInputMode="adjustPan"
android:alwaysRetainTaskState="true"
android:enableOnBackInvokedCallback="false"
android:theme="@style/TransparentActivityTheme"
android:configChanges="orientation|screenSize|keyboardHidden"

View File

@@ -125,7 +125,8 @@ class RoomActivity : BaseMvpActivity<RoomPresenter?, ActivityRoomBinding?>(),
var roomId: String? = null
var mRoomInfoResp: RoomInfoResp? = null
@JvmField
@Autowired
var taskId: String? = null
var likeUserAdapter: LikeUserAdapter<RoomOnlineBean>? = null
@@ -179,6 +180,7 @@ class RoomActivity : BaseMvpActivity<RoomPresenter?, ActivityRoomBinding?>(),
roomId = intent.getStringExtra("roomId")
mRoomInfoResp = intent.getSerializableExtra("roomInfo") as RoomInfoResp?
LogUtils.dTag("RoomActivity", "doDone" + mRoomInfoResp.toString())
taskId = intent.getStringExtra("taskId")
}
@@ -482,6 +484,20 @@ class RoomActivity : BaseMvpActivity<RoomPresenter?, ActivityRoomBinding?>(),
private val testHandler = Handler()
private var testRunnable: Runnable? = null
// override fun onNewIntent(intent: Intent?) {
// super.onNewIntent(intent)
// setIntent( intent)
// Log.d("TAG", "=== onNewIntent called ===");
// if (intent != null) {
// val roomId = intent.getStringExtra("roomId")
// redPacketInfo = intent.getSerializableExtra("redPacketInfo") as RedPacketInfo?
// }
// }
override fun onDestroy() {
super.onDestroy()
Log.d("TAG", "=== onDestroy called ===");
}
override fun onCreate(savedInstanceState: Bundle?) {
// 在super.onCreate之前设置主题以避免闪白屏
@@ -489,7 +505,6 @@ class RoomActivity : BaseMvpActivity<RoomPresenter?, ActivityRoomBinding?>(),
super.onCreate(savedInstanceState)
// // 进入房间10s后检查是否显示提示上麦对话框
LogUtils.e("RoomActivity", "onCreate")
isSave = false
sDestroied = false
isMinimized = false
@@ -862,6 +877,10 @@ class RoomActivity : BaseMvpActivity<RoomPresenter?, ActivityRoomBinding?>(),
val fragment = HourlyChartDialog.newInstance()
fragment.show(supportFragmentManager, "HourlyChartDialog")
}
mBinding!!.clDayTask.visibility = View.VISIBLE
mBinding!!.imDayTask.setOnClickListener {
ARouter.getInstance().build(ARouteConstants.DailyTasksActivity).navigation()
}
mBinding!!.drvRed.visibility = View.GONE
mBinding!!.redBj.setOnClickListener {
@@ -3508,8 +3527,7 @@ class RoomActivity : BaseMvpActivity<RoomPresenter?, ActivityRoomBinding?>(),
mBinding!!.ivQuanC.setOnClickListener { v: View? ->
if (mRoomInfoResp!!.room_info.head_line.room_id != null && mRoomInfoResp!!.room_info.head_line.room_id.isNotEmpty()) {
if (mRoomInfoResp!!.room_info.head_line.room_id != roomId) {
RoomManager.getInstance()
.fetchRoomDataAndEnter(applicationContext, mRoomInfoResp!!.room_info.head_line.room_id, "")
RoomManager.getInstance().fetchRoomDataAndEnter(applicationContext, mRoomInfoResp!!.room_info.head_line.room_id, "",null)
} else {
com.blankj.utilcode.util.ToastUtils.showLong("您就在当前房间")
}

View File

@@ -75,7 +75,7 @@ public class EaseChatAdapter extends BaseMultiItemQuickAdapter<EMMessageInfo, Ba
super(null);
addItemType(1, com.xscm.moduleutil.R.layout.ease_row_received_message_system);//系统消息、官方公告
addItemType(2, com.xscm.moduleutil.R.layout.ease_row_received_message_user_send);//用户发送的消息
addItemType(3, com.xscm.moduleutil.R.layout.ease_row_received_message_giftr_send);//加入房间 、赠送礼物、上下麦、禁言 2025年6月12日15:42:08让和聊天显示的是同一个额布局后面需要修改
addItemType(3, com.xscm.moduleutil.R.layout.ease_row_received_message_system);//加入房间 、赠送礼物、上下麦、禁言 2025年6月12日15:42:08让和聊天显示的是同一个额布局后面需要修改
// addItemType(3, com.qxcm.moduleutil.R.layout.ease_row_received_message_join_room);//加入房间 、赠送礼物、上下麦、禁言
addItemType(4, com.xscm.moduleutil.R.layout.ease_row_received_message_new_user);//新用户注册
addItemType(5, com.xscm.moduleutil.R.layout.ease_row_received_message_wagging);//摇签
@@ -350,14 +350,15 @@ public class EaseChatAdapter extends BaseMultiItemQuickAdapter<EMMessageInfo, Ba
// }
// });
// } else {
helper.getView(com.xscm.moduleutil.R.id.bubble1).setBackgroundResource(com.xscm.moduleutil.R.drawable.ease_row_pubilc_user_bg);
// helper.getView(com.xscm.moduleutil.R.id.bubble1).setBackgroundResource(com.xscm.moduleutil.R.drawable.ease_row_pubilc_user_bg);
// }
// if (emMessage.getText().getFromUserInfo() != null && emMessage.getText().getFromUserInfo().getAvatar() != null) {
// ImageUtils.loadHeadCC(emMessage.getText().getFromUserInfo().getAvatar(), helper.getView(com.xscm.moduleutil.R.id.avatar));
// }
// helper.setText(com.xscm.moduleutil.R.id.tv_name, emMessage.getText().getFromUserInfo().getNickname());
// helper.setText(com.xscm.moduleutil.R.id.tv_content, emMessage.getText().getText());
helper.setText(com.xscm.moduleutil.R.id.tv_content, getSpannable(emMessage));
helper.getView(R.id.tv_content).setVisibility(View.VISIBLE);
helper.setText(R.id.tv_content, getSpannable(emMessage));
// List<String> images1 = emMessage.getText().getFromUserInfo().getIcon();
// LinearLayout ll_images1 = helper.getView(com.xscm.moduleutil.R.id.line);
// ll_images1.removeAllViews();

View File

@@ -131,7 +131,7 @@ public class HourlyChartDialog extends BaseMvpDialogFragment<HourlyChartPresente
@Override
public void onItemClick(RoomHourBean.RoomListBean item, int position) {
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id(),"");
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id(),"",null);
dismiss();
}
});

View File

@@ -635,6 +635,7 @@ public class RoomKtvFragment extends BaseMvpFragment<RoomPresenter, FragmentRoom
@Subscribe(threadMode = ThreadMode.MAIN)
public void roomInfoEvent(MusicFileBean messageEvent) {
LogUtils.e("roomInfoEvent", messageEvent.getFileData());
LyricsModel mLyricsModel = KaraokeView.parseLyricsData(messageEvent.getFileData());
if (mLyricsModel != null) {
mKaraokeView.setLyricsData(mLyricsModel);

View File

@@ -33,6 +33,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.blankj.utilcode.util.ActivityUtils;
import com.blankj.utilcode.util.ObjectUtils;
import com.example.moduleroom.R;
import com.example.moduleroom.activity.RoomActivity;
@@ -143,12 +144,12 @@ public class SingSongFragment extends BaseRoomFragment<SingSongPresenter, Fragme
@Override
protected void initData() {
Observable.timer(20, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> {
upDtaView(true);
});
Observable.timer(500, TimeUnit.MILLISECONDS)
// Observable.timer(20, TimeUnit.MILLISECONDS)
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(aLong -> {
// upDtaView(true);
// });
Observable.timer(10, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> {
upDtaView(false);
@@ -165,7 +166,7 @@ public class SingSongFragment extends BaseRoomFragment<SingSongPresenter, Fragme
((RoomActivity) getActivity()).setvisibTop(true);
}
// 初始化 PopupWindow
initPopupWindow();
isWhether2(isFirst);
parentFragment = (RoomFragment) getParentFragment();
@@ -183,7 +184,8 @@ public class SingSongFragment extends BaseRoomFragment<SingSongPresenter, Fragme
// roomInfoUpdate(data);
// }
// });
roomId = roomInfoResp.getRoom_info().getRoom_id();
roomId = roomInfoResp.getRoom_info().getRoom_id(); initPopupWindow();
return super.onCreateView(inflater, container, savedInstanceState);
}
@@ -229,9 +231,8 @@ public class SingSongFragment extends BaseRoomFragment<SingSongPresenter, Fragme
/// 抱麦、锁麦
private void initPopupWindow() {
if (requireContext() == null) return;
// 加载弹出视图布局
View popupView = LayoutInflater.from(requireContext()).inflate(R.layout.popup_menu, null, false);
View popupView = LayoutInflater.from(getContext()).inflate(R.layout.popup_menu, null, false);
// 创建 PopupWindow
// popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
@@ -410,7 +411,7 @@ public class SingSongFragment extends BaseRoomFragment<SingSongPresenter, Fragme
FlexboxLayout flexboxLayout = mBinding.flexboxLayout;
wheatLayoutSingManager = new WheatLayoutSingManager(requireContext(), flexboxLayout);
wheatLayoutSingManager = new WheatLayoutSingManager(CommonAppContext.getInstance(), flexboxLayout);
if (!isFirst) {
wheatLayoutSingManager.setOnWheatClickListener(new WheatLayoutSingManager.OnWheatClickListener() {
@Override

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import com.example.moduleroom.contacts.RoomBackgroundContacts;
import com.hjq.toast.ToastUtils;
import com.xscm.moduleutil.base.CommonAppContext;
import com.xscm.moduleutil.bean.RoomBgBean;
import com.xscm.moduleutil.http.BaseObserver;
import com.xscm.moduleutil.presenter.BasePresenter;
@@ -38,7 +39,7 @@ public class RoomBackgroundPresenter extends BasePresenter<RoomBackgroundContact
@Override
public void uploadFile(File file, int type, int index, int size) {
String url = OSSOperUtils.getPath(file, type);
CosUploadManager.getInstance().upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
CosUploadManager.getInstance(CommonAppContext.getInstance()).upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
@Override
public void onSuccess(String url) {
if (isViewAttach()) {
@@ -51,6 +52,12 @@ public class RoomBackgroundPresenter extends BasePresenter<RoomBackgroundContact
public void onFailure(Exception e) {
ToastUtils.show("上传失败");
}
@Override
public void onFailure1(IllegalStateException e) {
ToastUtils.show("上传失败");
MvpRef.get().disLoadings();
}
});
// OSSOperUtils.newInstance().putObjectMethod(url, file.getPath(), new OSSOperUtils.OssCallback() {
// @Override

View File

@@ -84,6 +84,29 @@
</com.xscm.moduleutil.widget.DropRedView>
<com.xscm.moduleutil.widget.DropDayTaskView
android:id="@+id/cl_day_task"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_10"
android:gravity="right"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible"
android:elevation="4dp"
android:visibility="invisible">
<!-- android:background="@mipmap/room_xsb"-->
<ImageView
android:id="@+id/im_day_task"
android:layout_width="@dimen/dp_80"
android:layout_height="@dimen/dp_60"
android:src="@mipmap/day_task"
android:scaleType="fitCenter"/>
</com.xscm.moduleutil.widget.DropDayTaskView>
<ImageView
android:id="@+id/iv_bg"
@@ -146,7 +169,7 @@
android:id="@+id/vp_room_pager"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/dp_5"
android:layout_marginBottom="@dimen/dp_10"
android:clipChildren="false"
android:clipToPadding="false"
app:layout_constraintEnd_toEndOf="parent"
@@ -157,7 +180,7 @@
android:id="@+id/ease_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="@dimen/dp_5"
android:layout_marginTop="@dimen/dp_12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -496,8 +519,8 @@
<ImageView
android:id="@+id/iv_sound_effects"
android:layout_width="@dimen/dp_46"
android:layout_height="@dimen/dp_78"
android:layout_width="@dimen/dp_56"
android:layout_height="@dimen/dp_27"
android:layout_marginEnd="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_14"
android:src="@mipmap/room_sound_effects"

View File

@@ -32,7 +32,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.3"
android:background="#40000000"
android:background="#4d000000"
android:scaleType="fitXY"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View File

@@ -4,9 +4,11 @@ package com.example.modulevocal.activity;
import android.content.Intent;
import android.graphics.Color;
import android.view.View;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.launcher.ARouter;
import com.example.modulevocal.R;
import com.example.modulevocal.adapter.TaskBoxAdapter;
@@ -19,6 +21,7 @@ import com.hjq.toast.ToastUtils;
import com.xscm.moduleutil.activity.BaseMvpActivity;
import com.xscm.moduleutil.activity.WebViewActivity;
import com.xscm.moduleutil.base.CommonAppContext;
import com.xscm.moduleutil.base.RoomManager;
import com.xscm.moduleutil.bean.GiftBoxBean;
import com.xscm.moduleutil.bean.GiftName;
import com.xscm.moduleutil.bean.TaskItem;
@@ -33,6 +36,7 @@ import java.util.List;
* @data 2025/5/27
* @description: 每日任务
*/
@Route(path = ARouteConstants.DailyTasksActivity)
public class DailyTasksActivity extends BaseMvpActivity<DailyTasksPresenter, ActivityDailyTasksBinding> implements DailyTasksConacts.View {
private TaskBoxAdapter mTaskBoxAdapter;
@@ -142,11 +146,16 @@ public class DailyTasksActivity extends BaseMvpActivity<DailyTasksPresenter, Act
if (item.getTask_status() == 2){
MvpPre.dailyTasksReceive(item.getTask_id() + "");
}else if (item.getTask_status() == 1) {
if (item.getTask_id() == 9) {
ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("roomId", item.getFrom_id()).withString("taskId", item.getTask_id() + "").navigation();
} else {
ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("roomId", item.getFrom_id()).withString("taskId", item.getTask_id() + "").navigation();
}
mBinding.coolWaitView.setVisibility(View.VISIBLE);
// if (item.getTask_id() == 9) {
RoomManager.getInstance().fetchRoomDataAndEnter(DailyTasksActivity.this, item.getFrom_id(), "", item.getTask_id() + "");
finish();
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("roomId", item.getFrom_id()).withString("taskId", item.getTask_id() + "").navigation();
// } else {
// RoomManager.getInstance().fetchRoomDataAndEnter(DailyTasksActivity.this, item.getFrom_id(), "", item.getTask_id() + "");
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("roomId", item.getFrom_id()).withString("taskId", item.getTask_id() + "").navigation();
// }
}
}
}
@@ -155,6 +164,12 @@ public class DailyTasksActivity extends BaseMvpActivity<DailyTasksPresenter, Act
}
@Override
protected void onPause() {
super.onPause();
mBinding.coolWaitView.setVisibility(View.GONE);
}
@Override
public void dailyTasksOpenBox(GiftName giftName) {
ToastUtils.show(giftName.getGift_name());

View File

@@ -191,7 +191,8 @@ public class MyRoomActivity extends BaseMvpActivity<MyRoomPresenter, RoomActivit
mBinding.topBar.setImgPaddingRight(35);
mBinding.topBar.setRightImgVIsible(true);
mBinding.riv.setOnClickListener(v -> {
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), CommonAppContext.getInstance().playId, "");
isShowLoading(true);
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), CommonAppContext.getInstance().playId, "",null);
});
mBinding.ivGuanbi.setOnClickListener(v -> {
mBinding.ll.setVisibility(View.INVISIBLE);
@@ -202,6 +203,20 @@ public class MyRoomActivity extends BaseMvpActivity<MyRoomPresenter, RoomActivit
initListener();
}
public void isShowLoading(boolean b) {
if (b) {
mBinding.coolWaitView.setVisibility(View.VISIBLE);
} else {
mBinding.coolWaitView.setVisibility(View.GONE);
}
}
@Override
protected void onPause() {
super.onPause();
isShowLoading(false);
}
@Override
protected void onResume() {
super.onResume();

View File

@@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.alibaba.android.arouter.launcher.ARouter;
import com.example.modulevocal.R;
import com.example.modulevocal.activity.MyRoomActivity;
import com.makeramen.roundedimageview.RoundedImageView;
import com.xscm.moduleutil.RoomAutionTimeBean;
import com.xscm.moduleutil.base.CommonAppContext;
@@ -33,6 +34,7 @@ import com.xscm.moduleutil.bean.RoomData;
import com.xscm.moduleutil.bean.RoomRelationBean;
import com.xscm.moduleutil.bean.RoonGiftModel;
import com.xscm.moduleutil.bean.ViewItem;
import com.xscm.moduleutil.bean.room.PkRoomInfo;
import com.xscm.moduleutil.color.ThemeableDrawableUtils;
import com.xscm.moduleutil.dialog.ConfirmDialog;
import com.xscm.moduleutil.utils.ARouteConstants;
@@ -50,11 +52,16 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private int selectedTimePosition = -1;
private int selectedGiftPosition = -1;
// public void submitList(List<ViewItem> items) {
private MyRoomActivity myRoomActivity;
// public void submitList(List<ViewItem> items) {
// viewItems.clear();
// viewItems.addAll(items);
// notifyDataSetChanged();
// }
public void setMyRoomActivity(MyRoomActivity myRoomActivity) {
this.myRoomActivity = myRoomActivity;
}
public void submitList(List<ViewItem> items) {
if (items == null || items.isEmpty()) {
@@ -217,7 +224,7 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
static class TextViewHolder extends RecyclerView.ViewHolder {
TextView textView;
ImageView imageView1,imageView2;
ImageView imageView1, imageView2;
TextViewHolder(View itemView) {
super(itemView);
@@ -228,9 +235,9 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
void bind(RoomData roomData) {
textView.setText(roomData.getTitle());
if (roomData.getTitleType()==0){
if (roomData.getTitleType() == 0) {
imageView2.setVisibility(View.INVISIBLE);
}else {
} else {
imageView2.setVisibility(View.VISIBLE);
imageView2.setImageResource(roomData.getTitleType());
}
@@ -249,7 +256,7 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
}
static class RelationViewHolder extends RecyclerView.ViewHolder {
class RelationViewHolder extends RecyclerView.ViewHolder {
RoundedImageView riv;
ImageView iv_play, im_sh;
TextView tv_name;
@@ -276,7 +283,6 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
void bind(MyRoomBean item) {
ImageUtils.loadImageView(item.getRoom_cover(), riv);
if (item.getLabel_icon() != null) {
ImageUtils.loadImageView(item.getLabel_icon(), iv_play);
@@ -308,10 +314,11 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@Override
public void onClick(View v) {
// TODO: 跳转到房间详情页面
if (item.getApply_status().equals("1")){
if (item.getApply_status().equals("1")) {
queren(itemView.getContext());
return;
}
// startActivity(new Intent(getContext(), RoomDetailsActivity.class));
ARouter.getInstance().build(ARouteConstants.MY_ROOM_DETAILS).withString("roomId", item.getRoom_id() + "").navigation();
}
@@ -319,11 +326,13 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
cl_my_room_list.setOnClickListener(new View.OnClickListener() {//跳转房间
@Override
public void onClick(View v) {
if (item.getApply_status().equals("1")){
if (item.getApply_status().equals("1")) {
queren(itemView.getContext());
return;
}
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() +"","");
if (myRoomActivity != null)
myRoomActivity.isShowLoading(true);
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() + "", "",null);
// TODO: 跳转到房间
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("from", "我的界面").withString("roomId", item.getRoom_id() + "").navigation();
@@ -333,28 +342,29 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@Override
public void onClick(View v) {//房间补贴
if (item.getApply_status().equals("1")){
if (item.getApply_status().equals("1")) {
queren(itemView.getContext());
return;
}
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() +"","");
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() + "", "",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_ALLOWANCE).withString("from", "我的界面").withString("roomId", item.getRoom_id() + "").navigation();
}
});
if (item.getApply_status().equals("2")){
if (item.getApply_status().equals("2")) {
im_sh.setVisibility(View.GONE);
}else if (item.getApply_status().equals("1")){
} else if (item.getApply_status().equals("1")) {
im_sh.setVisibility(View.VISIBLE);
}
ThemeableDrawableUtils.setThemeableRoundedBackground(rl_mx, itemView.getContext().getColor(com.xscm.moduleutil.R.color.color6c49e25) , 53);
ThemeableDrawableUtils.setThemeableRoundedBackground(rl_mx, itemView.getContext().getColor(com.xscm.moduleutil.R.color.color6c49e25), 53);
rl_mx.setTextColor(ColorManager.getInstance().getPrimaryColorInt());
ThemeableDrawableUtils.setThemeableRoundedBackground(tv_room_bt, itemView.getContext().getColor(com.xscm.moduleutil.R.color.colorCDEB52E), 53);
tv_room_bt.setTextColor(ContextCompat.getColor(itemView.getContext(), com.xscm.moduleutil.R.color.colorC58600) );
tv_room_bt.setTextColor(ContextCompat.getColor(itemView.getContext(), com.xscm.moduleutil.R.color.colorC58600));
}
}
private static void queren(Context context) {
// 创建并显示确认对话框
new ConfirmDialog(context,
@@ -406,7 +416,7 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@Override
public void onClick(View v) {
// TODO: 跳转到房间详情页面
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() +"","");
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() + "", "",null);
// startActivity(new Intent(getContext(), RoomDetailsActivity.class));
// ARouter.getInstance().build(ARouteConstants.MY_ROOM_DETAILS).withString("roomId", item.getRoom_id() + "").navigation();
@@ -415,17 +425,18 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
cl_my_room_list.setOnClickListener(new View.OnClickListener() {//跳转房间
@Override
public void onClick(View v) {
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() +"","");
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() + "", "",null);
// TODO: 跳转到房间
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("from", "我的界面").withString("roomId", item.getRoom_id() + "").navigation();
}
});
ThemeableDrawableUtils.setThemeableRoundedBackground(rl_mx, itemView.getContext().getColor(com.xscm.moduleutil.R.color.color6c49e25) , 53);
ThemeableDrawableUtils.setThemeableRoundedBackground(rl_mx, itemView.getContext().getColor(com.xscm.moduleutil.R.color.color6c49e25), 53);
rl_mx.setTextColor(ColorManager.getInstance().getPrimaryColorInt());
}
}
public static String formatTimestampToDaysHours(long timestamp) {
// 获取当前时间戳(秒)
long currentTimestamp = System.currentTimeMillis() / 1000;
@@ -480,7 +491,7 @@ public class MyCreateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
rl_root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() +"","");
RoomManager.getInstance().fetchRoomDataAndEnter(itemView.getContext(), item.getRoom_id() + "", "",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("from", "我的界面").withString("roomId", item.getRoom_id() + "").navigation();

View File

@@ -131,7 +131,7 @@ public class CirleListFragment extends BaseMvpFragment<UserHomepagePresenter, Fr
@Override
public void onGensui(CircleListBean item) {
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id() +"","");
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id() +"","",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form","首页热门列表").withString("roomId", item.getRoom_id()).navigation();

View File

@@ -8,6 +8,7 @@ import androidx.recyclerview.widget.GridLayoutManager;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.example.modulevocal.R;
import com.example.modulevocal.activity.MyRoomActivity;
import com.example.modulevocal.adapter.ChatRoomMyFootAdapter;
import com.example.modulevocal.adapter.MyCreateAdapter;
import com.example.modulevocal.conacts.MyRoomListContacts;
@@ -99,7 +100,9 @@ public class MyCreateFragment extends BaseMvpFragment<MyRoomPresenter, RoomFragm
GridLayoutManager layoutManager = new GridLayoutManager(requireContext(), 2); // 最大支持 4 列
mBinding.rvMyRoomList.setLayoutManager(layoutManager);
mBinding.rvMyRoomList.setAdapter(adapter = new MyCreateAdapter());
if (getActivity() instanceof MyRoomActivity){
adapter.setMyRoomActivity((MyRoomActivity)getActivity());
}
// 设置 SpanSizeLookup 控制不同 item 占据的列数
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override

View File

@@ -10,6 +10,7 @@ import androidx.recyclerview.widget.GridLayoutManager;
import com.alibaba.android.arouter.launcher.ARouter;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.example.modulevocal.R;
import com.example.modulevocal.activity.MyRoomActivity;
import com.example.modulevocal.adapter.ChatRoomMyFollowAdapter;
import com.example.modulevocal.conacts.MyRoomListContacts;
import com.example.modulevocal.databinding.RoomFragmentMyFollowBinding;
@@ -77,7 +78,10 @@ public class MyFollowFragment extends BaseMvpFragment<MyRoomPresenter, RoomFragm
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
MyRoomBean item = followAdapter.getItem(position);
if (item != null) {
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id() +"","");
if (getActivity() instanceof MyRoomActivity){
((MyRoomActivity)getActivity()).isShowLoading( true);
}
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id() +"","",null);
// ARouter.getInstance().build(ARouteConstants.LIVE_ROOM).withString("form", "历史记录列表").withString("roomId", item.getRoom_id()).navigation();
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("from", "我的界面").withString("roomId", item.getRoom_id() + "").navigation();

View File

@@ -9,6 +9,7 @@ import androidx.recyclerview.widget.GridLayoutManager;
import com.alibaba.android.arouter.launcher.ARouter;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.example.modulevocal.R;
import com.example.modulevocal.activity.MyRoomActivity;
import com.example.modulevocal.adapter.ChatRoomMyManageAdapter;
import com.example.modulevocal.conacts.MyRoomListContacts;
import com.example.modulevocal.databinding.RoomFragmentMyManageBinding;
@@ -79,7 +80,10 @@ public class MyManageFragment extends BaseMvpFragment<MyRoomPresenter, RoomFragm
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
MyRoomBean item = manageAdapter.getItem(position);
if (item != null) {
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id() +"","");
if (getActivity() instanceof MyRoomActivity){
((MyRoomActivity)getActivity()).isShowLoading( true);
}
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id() +"","",null);
// ARouter.getInstance().build(ARouteConstants.LIVE_ROOM).withString("form", "历史记录列表").withString("roomId", item.getRoom_id()).navigation();
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("from", "我的界面").withString("roomId", item.getRoom_id() + "").navigation();

View File

@@ -16,6 +16,7 @@ import com.alibaba.android.arouter.launcher.ARouter;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.example.modulevocal.R;
import com.example.modulevocal.activity.MyRoomActivity;
import com.example.modulevocal.conacts.MyRoomListContacts;
import com.example.modulevocal.databinding.RoomFragmentMyRoomListListBinding;
import com.example.modulevocal.presenter.MyRoomPresenter;
@@ -148,7 +149,10 @@ public class MyRoomListFragment extends BaseMvpFragment<MyRoomPresenter, RoomFra
helper.getView(R.id.cl_my_room_list).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id() +"","");
if (getActivity() instanceof MyRoomActivity){
((MyRoomActivity)getActivity()).isShowLoading( true);
}
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id() +"","",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("from", "我的界面").withString("roomId", item.getRoom_id() + "").navigation();

View File

@@ -221,7 +221,7 @@ public class UserHomepageFragment extends BaseMvpFragment<UserHomepagePresenter,
intent.putExtra(TUIConstants.TUIChat.CHAT_TYPE, V2TIMConversation.V2TIM_C2C);
startActivity(intent);
} else {
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), userInfo.getRoom_id(),"");
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), userInfo.getRoom_id(),"",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("roomId", userInfo.getRoom_id()).navigation();
}

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import com.example.modulevocal.conacts.AlbumDetailConacts;
import com.hjq.toast.ToastUtils;
import com.xscm.moduleutil.base.CommonAppContext;
import com.xscm.moduleutil.bean.AlbumBean;
import com.xscm.moduleutil.http.BaseObserver;
import com.xscm.moduleutil.presenter.BasePresenter;
@@ -49,7 +50,7 @@ public class AlbumDetailPresenter extends BasePresenter<AlbumDetailConacts.View>
public void uploadFile(File file, int type, int index, int size) {
MvpRef.get().showLoadings("上传中...");
String url = OSSOperUtils.getPath(file, type);
CosUploadManager.getInstance().upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
CosUploadManager.getInstance(CommonAppContext.getInstance()).upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
@Override
public void onSuccess(String url) {
if (isViewAttach()) {
@@ -64,6 +65,12 @@ public class AlbumDetailPresenter extends BasePresenter<AlbumDetailConacts.View>
ToastUtils.show("上传失败");
MvpRef.get().disLoadings();
}
@Override
public void onFailure1(IllegalStateException e) {
ToastUtils.show("上传失败");
MvpRef.get().disLoadings();
}
});
// OSSOperUtils.newInstance().putObjectMethod(url, file.getPath(), new OSSOperUtils.OssCallback() {

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import com.example.modulevocal.conacts.CreatedRoomConactos;
import com.hjq.toast.ToastUtils;
import com.xscm.moduleutil.base.CommonAppContext;
import com.xscm.moduleutil.http.BaseObserver;
import com.xscm.moduleutil.presenter.BasePresenter;
import com.xscm.moduleutil.utils.cos.CosUploadManager;
@@ -81,7 +82,7 @@ public class CreatedRoomPresenter extends BasePresenter<CreatedRoomConactos.View
public void uploadFile(File file, int type) {
// MvpRef.get().showLoadings("上传中...");
String url = OSSOperUtils.getPath(file, type);
CosUploadManager.getInstance().upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
CosUploadManager.getInstance(CommonAppContext.getInstance()).upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
@Override
public void onSuccess(String url) {
if (isViewAttach()) {
@@ -95,6 +96,11 @@ public class CreatedRoomPresenter extends BasePresenter<CreatedRoomConactos.View
ToastUtils.show("上传失败");
// MvpRef.get().disLoadings();
}
@Override
public void onFailure1(IllegalStateException e) {
ToastUtils.show("上传失败");
}
});
// OSSOperUtils.newInstance().putObjectMethod(url, file.getPath(), new OSSOperUtils.OssCallback() {

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import com.example.modulevocal.conacts.EditUserConactos;
import com.hjq.toast.ToastUtils;
import com.xscm.moduleutil.base.CommonAppContext;
import com.xscm.moduleutil.bean.UserInfo;
import com.xscm.moduleutil.http.BaseObserver;
import com.xscm.moduleutil.presenter.BasePresenter;
@@ -29,7 +30,7 @@ public class EditUserPresenter extends BasePresenter<EditUserConactos.View> impl
public void uploadFile(File file, int type, int index, int size) {
MvpRef.get().showLoadings("上传中...");
String url = OSSOperUtils.getPath(file, type);
CosUploadManager.getInstance().upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
CosUploadManager.getInstance(CommonAppContext.getInstance()).upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
@Override
public void onSuccess(String url) {
if (isViewAttach()) {
@@ -41,7 +42,13 @@ public class EditUserPresenter extends BasePresenter<EditUserConactos.View> impl
@Override
public void onFailure(Exception e) {
ToastUtils.show("上传失败");
ToastUtils.show("上传失败",e);
MvpRef.get().disLoadings();
}
@Override
public void onFailure1(IllegalStateException e) {
ToastUtils.show("上传失败",e);
MvpRef.get().disLoadings();
}
});

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import com.example.modulevocal.conacts.MyAlbumConacts;
import com.hjq.toast.ToastUtils;
import com.xscm.moduleutil.base.CommonAppContext;
import com.xscm.moduleutil.bean.AlbumBean;
import com.xscm.moduleutil.http.BaseObserver;
import com.xscm.moduleutil.presenter.BasePresenter;
@@ -55,7 +56,7 @@ public class MyAlbumPresenter extends BasePresenter<MyAlbumConacts.View> impleme
public void uploadFile(File file, int type) {
MvpRef.get().showLoadings("上传中...");
String url = OSSOperUtils.getPath(file, type);
CosUploadManager.getInstance().upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
CosUploadManager.getInstance(CommonAppContext.getInstance()).upParameters(url,file.getPath(), new CosUploadManager.UploadCallback() {
@Override
public void onSuccess(String url) {
if (isViewAttach()) {
@@ -70,6 +71,12 @@ public class MyAlbumPresenter extends BasePresenter<MyAlbumConacts.View> impleme
ToastUtils.show("上传失败");
MvpRef.get().disLoadings();
}
@Override
public void onFailure1(IllegalStateException e) {
ToastUtils.show("上传失败");
MvpRef.get().disLoadings();
}
});
// OSSOperUtils.newInstance().putObjectMethod(url, file.getPath(), new OSSOperUtils.OssCallback() {
// @Override

View File

@@ -10,7 +10,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:background="@color/color_F8E3C8">
<com.xscm.moduleutil.widget.CustomTopBar
@@ -95,7 +95,18 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<app.dinus.com.loadingdrawable.LoadingView
android:id="@+id/cool_wait_view"
android:layout_width="@dimen/dp_100"
android:layout_height="@dimen/dp_100"
android:layout_centerInParent="true"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="@drawable/ps_ic_shadow_bg"
app:loading_renderer="CoolWaitLoadingRenderer"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -242,5 +242,18 @@
android:layout_marginRight="@dimen/dp_14"
android:src="@mipmap/icon_guanbi" />
</com.xscm.moduleutil.widget.DropView>
<app.dinus.com.loadingdrawable.LoadingView
android:id="@+id/cool_wait_view"
android:layout_width="@dimen/dp_100"
android:layout_height="@dimen/dp_100"
android:layout_centerInParent="true"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="@drawable/ps_ic_shadow_bg"
app:loading_renderer="CoolWaitLoadingRenderer"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -94,14 +94,21 @@ public class PopularRoomActivity extends BaseMvpActivity<VoiceCategoryPresenter,
}
TopRoom item = mAdapter.getItem(position);
if (item != null ) {
mBinding.coolWaitView.setVisibility(View.VISIBLE);
MessageListenerSingleton.getInstance().joinGroup(item.getRoom_id());
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id(), "");
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id(), "",null);
}
}
});
}
@Override
protected void onPause() {
super.onPause();
mBinding.coolWaitView.setVisibility(View.GONE);
}
@Override
protected void onResume() {
super.onResume();

View File

@@ -31,6 +31,7 @@ public class RankingListActivity extends BaseAppCompatActivity<ActivityRankingLi
@Override
protected void initView() {
mBinding.topBar.setTitle("排行榜");
mBinding.topBar.setColor(getResources().getColor(R.color.white));
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
mBinding.viewPager.setAdapter(sectionsPagerAdapter);
mBinding.tabs.setupWithViewPager( mBinding.viewPager);

View File

@@ -154,7 +154,7 @@ public class SearchActivity extends BaseMvpActivity<SearchPresenter, ActivitySea
public void onGnsClick(UserResultResp item) {
if (item.getRoom_id() != null && !item.getRoom_id().isEmpty() && !"0".equals(item.getRoom_id())) {
RoomManager.getInstance().fetchRoomDataAndEnter(SearchActivity.this, item.getRoom_id() + "", "");
RoomManager.getInstance().fetchRoomDataAndEnter(SearchActivity.this, item.getRoom_id() + "", "",null);
} else {
ChatLauncher.getInstance().launchC2CChat(SearchActivity.this, item.getUser_id() + "");
@@ -173,7 +173,7 @@ public class SearchActivity extends BaseMvpActivity<SearchPresenter, ActivitySea
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
RoomSearchResp item = mSearchRoomResultAdapter.getItem(position);
if (item != null) {
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id(), "");
RoomManager.getInstance().fetchRoomDataAndEnter(getApplicationContext(), item.getRoom_id(), "",null);
// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "搜索界面").withString("roomId", item.getId()).navigation();
}

View File

@@ -8,6 +8,7 @@ import androidx.recyclerview.widget.GridLayoutManager;
import com.alibaba.android.arouter.launcher.ARouter;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.example.moduleroom.activity.RoomActivity;
import com.example.modulevoice.R;
import com.example.modulevoice.adapter.HotAdapter;
import com.example.modulevoice.contacts.HotListContacts;
@@ -34,12 +35,16 @@ public class HotListFragment extends BaseMvpFragment<HotListPresenter, FragmentH
private String tag;
private int page;
public static HotListFragment newInstance(String type, String tag) {
private VoiceCategoryFragment voiceCategoryFragment;
public HotListFragment(){}
public HotListFragment(VoiceCategoryFragment voiceCategoryFragment){
this.voiceCategoryFragment = voiceCategoryFragment;
}
public static HotListFragment newInstance(VoiceCategoryFragment voiceCategoryFragment,String type, String tag) {
Bundle args = new Bundle();
args.putString("label_id", type);
args.putString("tag", tag);
HotListFragment fragment = new HotListFragment();
HotListFragment fragment = new HotListFragment(voiceCategoryFragment);
fragment.setArguments(args);
return fragment;
}
@@ -117,7 +122,7 @@ public class HotListFragment extends BaseMvpFragment<HotListPresenter, FragmentH
if ( ClickUtils.isFastDoubleClick()){
return;
}
voiceCategoryFragment.showLoading();
// 添加索引有效性检查
if (position < 0 || position >= mAdapter.getData().size()) {
return;
@@ -134,7 +139,7 @@ public class HotListFragment extends BaseMvpFragment<HotListPresenter, FragmentH
// } catch (InterruptedException e) {
// Thread.currentThread().interrupt();
// }
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id(), "");
RoomManager.getInstance().fetchRoomDataAndEnter(getActivity(), item.getRoom_id(), "",null);
// MvpPre.getRoomIn(item.getRoom_id(), "");
}
}

Some files were not shown because too many files have changed in this diff Show More