新增搜索历史删除粒子爆炸动效

master
fengyuecanzhu 3 years ago
parent 525b59acda
commit 8171e64a30
  1. 1
      app/src/main/assets/updatelog.fy
  2. 4
      app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java
  3. 18
      app/src/main/java/xyz/fycz/myreader/widget/TagGroup.java
  4. 150
      app/src/main/java/xyz/fycz/myreader/widget/explosion_field/ExplosionAnimator.java
  5. 200
      app/src/main/java/xyz/fycz/myreader/widget/explosion_field/ExplosionField.java
  6. 8
      app/src/main/java/xyz/fycz/myreader/widget/explosion_field/OnAnimatorListener.java
  7. 72
      app/src/main/java/xyz/fycz/myreader/widget/explosion_field/Utils.java

@ -2,6 +2,7 @@
风月读书v2.3.2 风月读书v2.3.2
更新内容: 更新内容:
1、修复MIUI13阅读界面切换日夜间闪退的问题 1、修复MIUI13阅读界面切换日夜间闪退的问题
2、
2021.12.11 2021.12.11
风月读书v2.2.8 风月读书v2.2.8

@ -318,9 +318,8 @@ public class SearchBookActivity extends BaseActivity {
public void onDelete(TagGroup tagGroup, String tag, int pos) { public void onDelete(TagGroup tagGroup, String tag, int pos) {
if (mCurHistories.get(pos) != null) { if (mCurHistories.get(pos) != null) {
mSearchHistoryService.deleteHistory(mCurHistories.get(pos)); mSearchHistoryService.deleteHistory(mCurHistories.get(pos));
mSearchHistories.remove(mCurHistories.get(pos));
} }
needReGetHistory = true;
initHistoryList();
} }
}); });
@ -587,7 +586,6 @@ public class SearchBookActivity extends BaseActivity {
hisDis.dispose(); hisDis.dispose();
} }
Single.create((SingleOnSubscribe<Boolean>) emitter -> { Single.create((SingleOnSubscribe<Boolean>) emitter -> {
if (needReGetHistory)
mSearchHistories = mSearchHistoryService.findAllSearchHistory(); mSearchHistories = mSearchHistoryService.findAllSearchHistory();
mHistoryNames.clear(); mHistoryNames.clear();
mCurHistories.clear(); mCurHistories.clear();

@ -1,5 +1,6 @@
package xyz.fycz.myreader.widget; package xyz.fycz.myreader.widget;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
@ -32,6 +33,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import xyz.fycz.myreader.R; import xyz.fycz.myreader.R;
import xyz.fycz.myreader.widget.explosion_field.ExplosionField;
/** /**
* A <code>TagGroup</code> is a special layout with a set of tags. * A <code>TagGroup</code> is a special layout with a set of tags.
@ -139,6 +141,8 @@ public class TagGroup extends ViewGroup {
/** Listener used to handle tag click event. */ /** Listener used to handle tag click event. */
private InternalTagClickListener mInternalTagClickListener = new InternalTagClickListener(); private InternalTagClickListener mInternalTagClickListener = new InternalTagClickListener();
private ExplosionField mExplosionField;
public TagGroup(Context context) { public TagGroup(Context context) {
this(context, null); this(context, null);
} }
@ -197,6 +201,18 @@ public class TagGroup extends ViewGroup {
} }
} }
@Override
protected void onAttachedToWindow() {
mExplosionField = ExplosionField.attach2Window((Activity) getContext());
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
mExplosionField.clear();
super.onDetachedFromWindow();
}
/** /**
* Call this to submit the INPUT tag. * Call this to submit the INPUT tag.
*/ */
@ -559,6 +575,7 @@ public class TagGroup extends ViewGroup {
} }
protected void deleteTag(TagView tagView) { protected void deleteTag(TagView tagView) {
mExplosionField.explode(tagView);
removeView(tagView); removeView(tagView);
if (mOnTagChangeListener != null) { if (mOnTagChangeListener != null) {
mOnTagChangeListener.onDelete(TagGroup.this, tagView.getText().toString(), tagView.pos); mOnTagChangeListener.onDelete(TagGroup.this, tagView.getText().toString(), tagView.pos);
@ -1034,6 +1051,7 @@ public class TagGroup extends ViewGroup {
} }
break; break;
} }
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: { case MotionEvent.ACTION_UP: {
isPressed = false; isPressed = false;
invalidatePaint(); invalidatePaint();

@ -0,0 +1,150 @@
/*
* Copyright (C) 2015 tyrantgit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.fycz.myreader.widget.explosion_field;
import android.animation.ValueAnimator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import java.util.Random;
public class ExplosionAnimator extends ValueAnimator {
static long DEFAULT_DURATION = 0x400;
private static final Interpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(0.6f);
private static final float END_VALUE = 1.4f;
private static final float X = Utils.dp2Px(5);
private static final float Y = Utils.dp2Px(20);
private static final float V = Utils.dp2Px(2);
private static final float W = Utils.dp2Px(1);
private Paint mPaint;
private Particle[] mParticles;
private Rect mBound;
private View mContainer;
public ExplosionAnimator(View container, Bitmap bitmap, Rect bound) {
mPaint = new Paint();
mBound = new Rect(bound);
int partLen = 15;
mParticles = new Particle[partLen * partLen];
Random random = new Random(System.currentTimeMillis());
int w = bitmap.getWidth() / (partLen + 2);
int h = bitmap.getHeight() / (partLen + 2);
for (int i = 0; i < partLen; i++) {
for (int j = 0; j < partLen; j++) {
mParticles[(i * partLen) + j] = generateParticle(bitmap.getPixel((j + 1) * w, (i + 1) * h), random);
}
}
mContainer = container;
setFloatValues(0f, END_VALUE);
setInterpolator(DEFAULT_INTERPOLATOR);
setDuration(DEFAULT_DURATION);
}
private Particle generateParticle(int color, Random random) {
Particle particle = new Particle();
particle.color = color;
particle.radius = V;
if (random.nextFloat() < 0.2f) {
particle.baseRadius = V + ((X - V) * random.nextFloat());
} else {
particle.baseRadius = W + ((V - W) * random.nextFloat());
}
float nextFloat = random.nextFloat();
particle.top = mBound.height() * ((0.18f * random.nextFloat()) + 0.2f);
particle.top = nextFloat < 0.2f ? particle.top : particle.top + ((particle.top * 0.2f) * random.nextFloat());
particle.bottom = (mBound.height() * (random.nextFloat() - 0.5f)) * 1.8f;
float f = nextFloat < 0.2f ? particle.bottom : nextFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f;
particle.bottom = f;
particle.mag = 4.0f * particle.top / particle.bottom;
particle.neg = (-particle.mag) / particle.bottom;
f = mBound.centerX() + (Y * (random.nextFloat() - 0.5f));
particle.baseCx = f;
particle.cx = f;
f = mBound.centerY() + (Y * (random.nextFloat() - 0.5f));
particle.baseCy = f;
particle.cy = f;
particle.life = END_VALUE / 10 * random.nextFloat();
particle.overflow = 0.4f * random.nextFloat();
particle.alpha = 1f;
return particle;
}
public boolean draw(Canvas canvas) {
if (!isStarted()) {
return false;
}
for (Particle particle : mParticles) {
particle.advance((float) getAnimatedValue());
if (particle.alpha > 0f) {
mPaint.setColor(particle.color);
mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha));
canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint);
}
}
mContainer.invalidate();
return true;
}
@Override
public void start() {
super.start();
mContainer.invalidate(mBound);
}
private class Particle {
float alpha;
int color;
float cx;
float cy;
float radius;
float baseCx;
float baseCy;
float baseRadius;
float top;
float bottom;
float mag;
float neg;
float life;
float overflow;
public void advance(float factor) {
float f = 0f;
float normalization = factor / END_VALUE;
if (normalization < life || normalization > 1f - overflow) {
alpha = 0f;
return;
}
normalization = (normalization - life) / (1f - life - overflow);
float f2 = normalization * END_VALUE;
if (normalization >= 0.7f) {
f = (normalization - 0.7f) / 0.3f;
}
alpha = 1f - f;
f = bottom * f2;
cx = baseCx + f;
cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag;
radius = V + (baseRadius - V) * f2;
}
}
}

@ -0,0 +1,200 @@
/*
* Copyright (C) 2015 tyrantgit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.fycz.myreader.widget.explosion_field;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.media.MediaPlayer;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class ExplosionField extends View {
private long customDuration = ExplosionAnimator.DEFAULT_DURATION;
private int idPlayAnimationEffect = 0;
private OnAnimatorListener mZAnimatorListener;
private OnClickListener mOnClickListener;
private List<ExplosionAnimator> mExplosions = new ArrayList<>();
private int[] mExpandInset = new int[2];
public ExplosionField(Context context) {
super(context);
init();
}
public ExplosionField(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ExplosionField(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
Arrays.fill(mExpandInset, Utils.dp2Px(32));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (ExplosionAnimator explosion : mExplosions) {
explosion.draw(canvas);
}
}
public void playSoundAnimationEffect(int id) {
this.idPlayAnimationEffect = id;
}
public void setCustomDuration(long customDuration) {
this.customDuration = customDuration;
}
public void addActionEvent(OnAnimatorListener ievents) {
this.mZAnimatorListener = ievents;
}
public void expandExplosionBound(int dx, int dy) {
mExpandInset[0] = dx;
mExpandInset[1] = dy;
}
public void explode(Bitmap bitmap, Rect bound, long startDelay) {
explode(bitmap, bound, startDelay, null);
}
public void explode(Bitmap bitmap, Rect bound, long startDelay, final View view) {
long currentDuration = customDuration;
final ExplosionAnimator explosion = new ExplosionAnimator(this, bitmap, bound);
explosion.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mExplosions.remove(animation);
if (view != null) {
view.setScaleX(1);
view.setScaleY(1);
view.setAlpha(1);
view.setOnClickListener(mOnClickListener);//set event
}
}
});
explosion.setStartDelay(startDelay);
explosion.setDuration(currentDuration);
mExplosions.add(explosion);
explosion.start();
}
public void explode(View view) {
explode(view, false);
}
public void explode(final View view, Boolean restartState) {
Rect r = new Rect();
view.getGlobalVisibleRect(r);
int[] location = new int[2];
getLocationOnScreen(location);
// getLocationInWindow(location);
// view.getLocationInWindow(location);
r.offset(-location[0], -location[1]);
r.inset(-mExpandInset[0], -mExpandInset[1]);
int startDelay = 100;
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
Random random = new Random();
@Override
public void onAnimationUpdate(ValueAnimator animation) {
view.setTranslationX((random.nextFloat() - 0.5f) * view.getWidth() * 0.05f);
view.setTranslationY((random.nextFloat() - 0.5f) * view.getHeight() * 0.05f);
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
if (idPlayAnimationEffect != 0)
MediaPlayer.create(getContext(), idPlayAnimationEffect).start();
}
@Override
public void onAnimationEnd(Animator animator) {
if (mZAnimatorListener != null) {
mZAnimatorListener.onAnimationEnd(animator, ExplosionField.this);
}
}
@Override
public void onAnimationCancel(Animator animator) {
Log.i("PRUEBA", "CANCEL");
}
@Override
public void onAnimationRepeat(Animator animator) {
Log.i("PRUEBA", "REPEAT");
}
});
animator.start();
view.animate().setDuration(150).setStartDelay(startDelay).scaleX(0f).scaleY(0f).alpha(0f).start();
if (restartState)
explode(Utils.createBitmapFromView(view), r, startDelay, view);
else
explode(Utils.createBitmapFromView(view), r, startDelay);
}
public void clear() {
mExplosions.clear();
invalidate();
}
public static ExplosionField attach2Window(Activity activity) {
ViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
ExplosionField explosionField = new ExplosionField(activity);
rootView.addView(explosionField, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return explosionField;
}
public void setOnClickListener(OnClickListener mOnClickListener) {
this.mOnClickListener = mOnClickListener;
}
}

@ -0,0 +1,8 @@
package xyz.fycz.myreader.widget.explosion_field;
import android.animation.Animator;
import android.view.View;
public interface OnAnimatorListener {
void onAnimationEnd(Animator animator, View view);
}

@ -0,0 +1,72 @@
/*
* Copyright (C) 2015 tyrantgit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.fycz.myreader.widget.explosion_field;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
public class Utils {
private Utils() {
}
private static final float DENSITY = Resources.getSystem().getDisplayMetrics().density;
private static final Canvas sCanvas = new Canvas();
public static int dp2Px(int dp) {
return Math.round(dp * DENSITY);
}
public static Bitmap createBitmapFromView(View view) {
if (view instanceof ImageView) {
Drawable drawable = ((ImageView) view).getDrawable();
if (drawable != null && drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
}
view.clearFocus();
Bitmap bitmap = createBitmapSafely(view.getWidth(),
view.getHeight(), Bitmap.Config.ARGB_8888, 1);
if (bitmap != null) {
synchronized (sCanvas) {
Canvas canvas = sCanvas;
canvas.setBitmap(bitmap);
view.draw(canvas);
canvas.setBitmap(null);
}
}
return bitmap;
}
public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) {
try {
return Bitmap.createBitmap(width, height, config);
} catch (OutOfMemoryError e) {
e.printStackTrace();
if (retryCount > 0) {
System.gc();
return createBitmapSafely(width, height, config, retryCount - 1);
}
return null;
}
}
}
Loading…
Cancel
Save