1:修改K歌房
2:修改房间展示每日任务 3:修改页面跳转 4:遗留问题在进入首页的时候出现首页刷新
25
Loadinglibrary/build.gradle
Normal 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
@@ -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 *;
|
||||
#}
|
||||
8
Loadinglibrary/src/main/AndroidManifest.xml
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Loadinglibrary/src/main/res/drawable-xhdpi/ic_eletric_fan.png
Normal file
|
After Width: | Height: | Size: 1018 B |
BIN
Loadinglibrary/src/main/res/drawable-xhdpi/ic_leaf.png
Normal file
|
After Width: | Height: | Size: 268 B |
BIN
Loadinglibrary/src/main/res/drawable-xhdpi/ic_loading.png
Normal file
|
After Width: | Height: | Size: 277 B |
29
Loadinglibrary/src/main/res/values/attrs.xml
Normal 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>
|
||||
3
Loadinglibrary/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Library</string>
|
||||
</resources>
|
||||
6
app/proguard-rules.pro
vendored
@@ -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.**$* { *; }
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"; //每日任务
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
20
moduleUtil/src/main/res/layout/loading_dialog.xml
Normal 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>
|
||||
BIN
moduleUtil/src/main/res/mipmap-hdpi/day_task.webp
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
moduleUtil/src/main/res/mipmap-hdpi/room_sound_effects.webp
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
moduleUtil/src/main/res/mipmap-xhdpi/day_task.webp
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
moduleUtil/src/main/res/mipmap-xhdpi/room_sound_effects.webp
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
moduleUtil/src/main/res/mipmap-xxhdpi/day_task.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
moduleUtil/src/main/res/mipmap-xxhdpi/room_sound_effects.webp
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:configChanges="fontScale"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait">
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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("您就在当前房间")
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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(), "");
|
||||
}
|
||||
}
|
||||
|
||||