Drop edgeEffect

* This md2 but not md3

* We have no reason to reinvent the wheel
This commit is contained in:
tarsin committed 2022-06-23 08:16:40 +08:00
1 parent 2e74222244
commit 0ced054631
8 files changed
+7 -774

No files matched your search

@@ -353,7 +353,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
.setStartPosition(Settings.getStartPosition())
.setStartPage(startPage)
.setBackgroundColor(ResourcesKt.resolveColor(getTheme(), android.R.attr.colorBackground))
.setEdgeColor(ResourcesKt.resolveColor(getTheme(), R.attr.colorEdgeEffect) & 0xffffff | 0x33000000)
.setPagerInterval(Settings.getShowPageInterval() ? resources.getDimensionPixelOffset(R.dimen.gallery_pager_interval) : 0)
.setScrollInterval(Settings.getShowPageInterval() ? resources.getDimensionPixelOffset(R.dimen.gallery_scroll_interval) : 0)
.setPageMinHeight(resources.getDimensionPixelOffset(R.dimen.gallery_page_min_height))
@@ -36,7 +36,6 @@ import com.hippo.glview.util.GalleryUtils;
import com.hippo.glview.view.AnimationTime;
import com.hippo.glview.view.GLRoot;
import com.hippo.glview.view.GLView;
import com.hippo.glview.widget.GLEdgeView;
import com.hippo.glview.widget.GLProgressView;
import com.hippo.glview.widget.GLTextureView;
import com.hippo.yorozuya.MathUtils;
@@ -94,7 +93,6 @@ public final class GalleryView extends GLView implements GestureRecognizer.Liste
private final GestureRecognizer mGestureRecognizer;
@Nullable
private final Listener mListener;
private final GLEdgeView mEdgeView;
private final Pool<GalleryPageView> mGalleryPageViewPool = new Pool<>(5);
private final int mBackgroundColor;
private final int mPageMinHeight;
@@ -144,7 +142,6 @@ public final class GalleryView extends GLView implements GestureRecognizer.Liste
mAdapter.setGalleryView(this);
mListener = build.mListener;
mGestureRecognizer = new GestureRecognizer(mContext, this);
mEdgeView = new GLEdgeView(build.mEdgeColor);
mLayoutMode = build.mLayoutMode;
mScaleMode = build.mScaleMode;
@@ -291,7 +288,6 @@ public final class GalleryView extends GLView implements GestureRecognizer.Liste
@Override
public void onAttachToRoot(GLRoot root) {
super.onAttachToRoot(root);
mEdgeView.onAttachToRoot(root);
postMethod(METHOD_ON_ATTACH_TO_ROOT);
}
@@ -305,7 +301,6 @@ public final class GalleryView extends GLView implements GestureRecognizer.Liste
}
super.onDetachFromRoot();
mEdgeView.onDetachFromRoot();
}
public int getLayoutMode() {
@@ -342,10 +337,6 @@ public final class GalleryView extends GLView implements GestureRecognizer.Liste
return true;
}
GLEdgeView getEdgeView() {
return mEdgeView;
}
String getDefaultErrorStr() {
return mDefaultErrorString;
}
@@ -482,10 +473,7 @@ public final class GalleryView extends GLView implements GestureRecognizer.Liste
@Override
protected void onLayout(boolean changeSize, int left, int top, int right, int bottom) {
mEdgeView.layout(left, top, right, bottom);
fill();
if (changeSize) {
int width = right - left;
int height = bottom - top;
@@ -861,7 +849,6 @@ public final class GalleryView extends GLView implements GestureRecognizer.Liste
mWillFill = false;
super.render(canvas);
mEdgeView.render(canvas);
int newCurrentIndex;
if (mLayoutManager != null) {
@@ -1054,11 +1041,6 @@ public final class GalleryView extends GLView implements GestureRecognizer.Liste
return this;
}
public Builder setEdgeColor(int edgeColor) {
mEdgeColor = edgeColor;
return this;
}
public Builder setPagerInterval(int pagerInterval) {
mPagerInterval = pagerInterval;
return this;
@@ -25,9 +25,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import com.hippo.glview.anim.Animation;
import com.hippo.glview.anim.FloatAnimation;
import com.hippo.glview.view.GLView;
import com.hippo.glview.widget.GLEdgeView;
import com.hippo.glview.widget.GLProgressView;
import com.hippo.glview.widget.GLTextureView;
import com.hippo.yorozuya.AnimationUtils;
@@ -54,7 +52,6 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
private final SmoothScroller mSmoothScroller;
private final PageFling mPageFling;
private final SmoothScaler mSmoothScaler;
private final OverScroller mOverScroller;
private final Rect mTempRect = new Rect();
private GalleryView.Adapter mAdapter;
private GLProgressView mProgress;
@@ -89,7 +86,6 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
mSmoothScroller = new SmoothScroller();
mPageFling = new PageFling(context);
mSmoothScaler = new SmoothScaler();
mOverScroller = new OverScroller();
}
public void setInterval(int interval) {
@@ -120,12 +116,10 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
private boolean cancelAllAnimations() {
boolean running = mSmoothScroller.isRunning() ||
mPageFling.isRunning() ||
mSmoothScaler.isRunning() ||
mOverScroller.isRunning();
mSmoothScaler.isRunning();
mSmoothScroller.cancel();
mPageFling.cancel();
mSmoothScaler.cancel();
mOverScroller.cancel();
return running;
}
@@ -417,8 +411,6 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
@Override
public void onUp() {
mGalleryView.getEdgeView().onRelease();
if (mCurrent == null) {
return;
}
@@ -580,39 +572,6 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
return remain;
}
public void overScrollEdge(int dx, int dy, float x, float y) {
GLEdgeView edgeView = mGalleryView.getEdgeView();
mDeltaX += dx;
mDeltaY += dy;
if (mDeltaX < 0) {
edgeView.onPull(-mDeltaX, y, GLEdgeView.LEFT);
if (!edgeView.isFinished(GLEdgeView.RIGHT)) {
edgeView.onRelease(GLEdgeView.RIGHT);
}
} else if (mDeltaX > 0) {
edgeView.onPull(mDeltaX, y, GLEdgeView.RIGHT);
if (!edgeView.isFinished(GLEdgeView.LEFT)) {
edgeView.onRelease(GLEdgeView.LEFT);
}
}
if (mCurrent.getImageView().canFlingVertically()) {
if (mDeltaY < 0) {
edgeView.onPull(-mDeltaY, x, GLEdgeView.TOP);
if (!edgeView.isFinished(GLEdgeView.BOTTOM)) {
edgeView.onRelease(GLEdgeView.BOTTOM);
}
} else if (mDeltaY > 0) {
edgeView.onPull(mDeltaY, x, GLEdgeView.BOTTOM);
if (!edgeView.isFinished(GLEdgeView.TOP)) {
edgeView.onRelease(GLEdgeView.TOP);
}
}
}
}
public void scrollInternal(float dx, float dy, float x, float y) {
if (mCurrent == null) {
return;
@@ -640,7 +599,6 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
(getLeftPage() == null && mOffset == 0 && remainX < 0) ||
(getRightPage() == null && mOffset == 0 && remainX > 0)) {
// On edge
overScrollEdge(remainX, remainY, x, y);
remainX = 0;
remainY = 0;
} else if (mCanScrollBetweenPages) {
@@ -699,7 +657,6 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
boolean invalidate = mSmoothScroller.calculate(time);
invalidate |= mPageFling.calculate(time);
invalidate |= mSmoothScaler.calculate(time);
invalidate |= mOverScroller.calculate(time);
return invalidate;
}
@@ -726,15 +683,11 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
}
if (mMode == MODE_LEFT_TO_RIGHT) {
if (mIndex == 0) {
mOverScroller.overScroll(GLEdgeView.LEFT);
} else {
if (mIndex != 0) {
setCurrentIndex(mIndex - 1);
}
} else {
if (mIndex >= size - 1) {
mOverScroller.overScroll(GLEdgeView.LEFT);
} else {
if (mIndex < size - 1) {
setCurrentIndex(mIndex + 1);
}
}
@@ -748,15 +701,11 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
}
if (mMode == MODE_LEFT_TO_RIGHT) {
if (mIndex >= size - 1) {
mOverScroller.overScroll(GLEdgeView.RIGHT);
} else {
if (mIndex < size - 1) {
setCurrentIndex(mIndex + 1);
}
} else {
if (mIndex == 0) {
mOverScroller.overScroll(GLEdgeView.RIGHT);
} else {
if (mIndex != 0) {
setCurrentIndex(mIndex - 1);
}
}
@@ -973,34 +922,6 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
mLastX = x;
mLastY = y;
}
@Override
protected void onFinish() {
if (mCurrent == null) {
return;
}
GLEdgeView edgeView = mGalleryView.getEdgeView();
ImageView imageView = mCurrent.getImageView();
if (imageView.canFlingHorizontally()) {
if (mVelocityX > 0 && getLeftPage() == null && imageView.getMaxDx() == 0 &&
edgeView.isFinished(GLEdgeView.LEFT)) {
edgeView.onAbsorb(mVelocityX, GLEdgeView.LEFT);
} else if (mVelocityX < 0 && getRightPage() == null && imageView.getMinDx() == 0 &&
edgeView.isFinished(GLEdgeView.RIGHT)) {
edgeView.onAbsorb(-mVelocityX, GLEdgeView.RIGHT);
}
}
if (imageView.canFlingVertically()) {
if (mVelocityY > 0 && imageView.getMaxDy() == 0 &&
edgeView.isFinished(GLEdgeView.TOP)) {
edgeView.onAbsorb(mVelocityY, GLEdgeView.TOP);
} else if (mVelocityY < 0 && imageView.getMinDy() == 0 &&
edgeView.isFinished(GLEdgeView.BOTTOM)) {
edgeView.onAbsorb(-mVelocityY, GLEdgeView.BOTTOM);
}
}
}
}
private class SmoothScaler extends Animation {
@@ -1039,47 +960,4 @@ class PagerLayoutManager extends GalleryView.LayoutManager {
mScaleValue = scale;
}
}
private class OverScroller extends FloatAnimation {
private int mDirection;
private int mPosition;
public OverScroller() {
setDuration(300L);
}
public void overScroll(int direction) {
mDirection = direction;
int range;
switch (mDirection) {
case GLEdgeView.LEFT:
case GLEdgeView.RIGHT:
range = mGalleryView.getWidth() / 7;
mPosition = mGalleryView.getHeight() / 2;
break;
case GLEdgeView.TOP:
case GLEdgeView.BOTTOM:
range = mGalleryView.getHeight() / 7;
mPosition = mGalleryView.getWidth() / 2;
break;
default:
return;
}
setRange(0, range);
start();
mGalleryView.invalidate();
}
@Override
protected void onCalculate(float progress) {
super.onCalculate(progress);
mGalleryView.getEdgeView().onPull(get(), mPosition, mDirection);
}
@Override
protected void onFinish() {
mGalleryView.getEdgeView().onRelease(mDirection);
}
}
}
@@ -23,9 +23,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
import com.hippo.glview.anim.Animation;
import com.hippo.glview.anim.FloatAnimation;
import com.hippo.glview.view.GLView;
import com.hippo.glview.widget.GLEdgeView;
import com.hippo.glview.widget.GLProgressView;
import com.hippo.glview.widget.GLTextureView;
import com.hippo.yorozuya.AnimationUtils;
@@ -51,7 +49,6 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
private final LinkedList<GalleryPageView> mTempPages = new LinkedList<>();
private final PageFling mPageFling;
private final SmoothScaler mSmoothScaler;
private final OverScroller mOverScroller;
private GalleryView.Adapter mAdapter;
private GLProgressView mProgress;
private String mErrorStr;
@@ -80,7 +77,6 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
mInterval = interval;
mPageFling = new PageFling(context);
mSmoothScaler = new SmoothScaler();
mOverScroller = new OverScroller();
}
public void setInterval(int interval) {
@@ -117,11 +113,9 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
// Return true for animations are running
private boolean cancelAllAnimations() {
boolean running = mPageFling.isRunning() ||
mSmoothScaler.isRunning() ||
mOverScroller.isRunning();
mSmoothScaler.isRunning();
mPageFling.cancel();
mSmoothScaler.cancel();
mOverScroller.cancel();
return running;
}
@@ -536,7 +530,6 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
@Override
public void onUp() {
mScrollUp = false;
mGalleryView.getEdgeView().onRelease();
}
@Override
@@ -561,37 +554,6 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
}
public void overScrollEdge(int dx, int dy, float x, float y) {
GLEdgeView edgeView = mGalleryView.getEdgeView();
mDeltaX += dx;
mDeltaY += dy;
if (mDeltaX < 0) {
edgeView.onPull(-mDeltaX, y, GLEdgeView.LEFT);
if (!edgeView.isFinished(GLEdgeView.RIGHT)) {
edgeView.onRelease(GLEdgeView.RIGHT);
}
} else if (mDeltaX > 0) {
edgeView.onPull(mDeltaX, y, GLEdgeView.RIGHT);
if (!edgeView.isFinished(GLEdgeView.LEFT)) {
edgeView.onRelease(GLEdgeView.LEFT);
}
}
if (mDeltaY < 0) {
edgeView.onPull(-mDeltaY, x, GLEdgeView.TOP);
if (!edgeView.isFinished(GLEdgeView.BOTTOM)) {
edgeView.onRelease(GLEdgeView.BOTTOM);
}
} else if (mDeltaY > 0) {
edgeView.onPull(mDeltaY, x, GLEdgeView.BOTTOM);
if (!edgeView.isFinished(GLEdgeView.TOP)) {
edgeView.onRelease(GLEdgeView.TOP);
}
}
}
private void getBottomState() {
List<GalleryPageView> pages = mPages;
int bottom = mOffsetY;
@@ -673,9 +635,6 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
mOffsetY = limit;
requestFill = true;
}
if (!fling) {
overScrollEdge(0, remainY + limit - mOffsetY, x, y);
}
remainY = 0;
result = true;
}
@@ -716,9 +675,6 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
mOffsetY -= bottom - limit;
requestFill = true;
}
if (!fling) {
overScrollEdge(0, remainY + limit - bottom, x, y);
}
remainY = 0;
result = true;
}
@@ -841,7 +797,6 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
public boolean onUpdateAnimation(long time) {
boolean invalidate = mPageFling.calculate(time);
invalidate |= mSmoothScaler.calculate(time);
invalidate |= mOverScroller.calculate(time);
return invalidate;
}
@@ -872,7 +827,6 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
///////
GalleryView galleryView = mGalleryView;
if (mIndex == 0 && mOffsetY >= 0) {
mOverScroller.overScroll(GLEdgeView.TOP);
} else {
// Cancel all animations
cancelAllAnimations();
@@ -926,9 +880,7 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
getBottomState();
int bottom = mBottomStateBottom;
boolean hasNext = mBottomStateHasNext;
if (!hasNext && bottom <= galleryView.getHeight()) {
mOverScroller.overScroll(GLEdgeView.BOTTOM);
} else {
if (hasNext || bottom > galleryView.getHeight()) {
// Cancel all animations
cancelAllAnimations();
@@ -1141,17 +1093,6 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
int bottom = mBottomStateBottom;
boolean hasNext = mBottomStateHasNext;
boolean bottomEdge = !hasNext && bottom <= mGalleryView.getHeight();
if (topEdge && bottomEdge) {
return;
}
GLEdgeView edgeView = mGalleryView.getEdgeView();
if (topEdge && edgeView.isFinished(GLEdgeView.TOP)) {
edgeView.onAbsorb(mVelocityY, GLEdgeView.TOP);
} else if (bottomEdge && edgeView.isFinished(GLEdgeView.BOTTOM)) {
edgeView.onAbsorb(-mVelocityY, GLEdgeView.BOTTOM);
}
}
}
@@ -1190,47 +1131,4 @@ class ScrollLayoutManager extends GalleryView.LayoutManager {
mLastScale = scale;
}
}
private class OverScroller extends FloatAnimation {
private int mDirection;
private int mPosition;
public OverScroller() {
setDuration(300L);
}
public void overScroll(int direction) {
mDirection = direction;
int range;
switch (mDirection) {
case GLEdgeView.LEFT:
case GLEdgeView.RIGHT:
range = mGalleryView.getWidth() / 7;
mPosition = mGalleryView.getHeight() / 2;
break;
case GLEdgeView.TOP:
case GLEdgeView.BOTTOM:
range = mGalleryView.getHeight() / 7;
mPosition = mGalleryView.getWidth() / 2;
break;
default:
return;
}
setRange(0, range);
start();
mGalleryView.invalidate();
}
@Override
protected void onCalculate(float progress) {
super.onCalculate(progress);
mGalleryView.getEdgeView().onPull(get(), mPosition, mDirection);
}
@Override
protected void onFinish() {
mGalleryView.getEdgeView().onRelease(mDirection);
}
}
}
@@ -1,360 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* 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 com.hippo.glview.widget;
import android.graphics.Rect;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.hippo.glview.glrenderer.GLCanvas;
/**
* Material design edge effect
*/
public class GLEdgeEffect {
// Time it will take the effect to fully recede in ms
private static final int RECEDE_TIME = 600;
// Time it will take before a pulled glow begins receding in ms
private static final int PULL_TIME = 167;
// Time it will take in ms for a pulled glow to decay to partial strength before release
private static final int PULL_DECAY_TIME = 2000;
private static final float MAX_ALPHA = 0.5f;
private static final float MAX_GLOW_SCALE = 2.f;
private static final float PULL_GLOW_BEGIN = 0.f;
// Minimum velocity that will be absorbed
private static final int MIN_VELOCITY = 100;
// Maximum velocity, clamps at this value
private static final int MAX_VELOCITY = 10000;
private static final float EPSILON = 0.001f;
private static final double ANGLE = Math.PI / 6;
private static final float SIN = (float) Math.sin(ANGLE);
private static final float COS = (float) Math.cos(ANGLE);
private static final int STATE_IDLE = 0;
private static final int STATE_PULL = 1;
private static final int STATE_ABSORB = 2;
private static final int STATE_RECEDE = 3;
private static final int STATE_PULL_DECAY = 4;
private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f;
private static final int VELOCITY_GLOW_FACTOR = 6;
private final Interpolator mInterpolator;
private final Rect mBounds = new Rect();
private float mGlowAlpha;
private float mGlowScaleY;
private float mGlowAlphaStart;
private float mGlowAlphaFinish;
private float mGlowScaleYStart;
private float mGlowScaleYFinish;
private long mStartTime;
private float mDuration;
private int mState = STATE_IDLE;
private float mPullDistance;
private int mColor;
private float mRadius;
private float mBaseGlowScale;
private float mDisplacement = 0.5f;
private float mTargetDisplacement = 0.5f;
/**
* Construct a new EdgeEffect with a theme appropriate for the provided context.
*/
public GLEdgeEffect(int color) {
mColor = color;
mInterpolator = new DecelerateInterpolator();
}
/**
* Set the size of this edge effect in pixels.
*
* @param width Effect width in pixels
* @param height Effect height in pixels
*/
public void setSize(int width, int height) {
final float r = width * 0.75f / SIN;
final float y = COS * r;
final float h = r - y;
final float or = height * 0.75f / SIN;
final float oy = COS * or;
final float oh = or - oy;
mRadius = r;
mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f;
mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
}
/**
* Reports if this EdgeEffect's animation is finished. If this method returns false
* after a call to {@link #draw(GLCanvas)} the host widget should schedule another
* drawing pass to continue the animation.
*
* @return true if animation is finished, false if drawing should continue on the next frame.
*/
public boolean isFinished() {
return mState == STATE_IDLE;
}
/**
* Immediately finish the current animation.
* After this call {@link #isFinished()} will return true.
*/
public void finish() {
mState = STATE_IDLE;
}
/**
* A view should call this when content is pulled away from an edge by the user.
* This will update the state of the current visual effect and its associated animation.
* The host view should always {@link android.view.View#invalidate()} after this
* and draw the results accordingly.
*
* <p>Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement
* of the pull point is known.</p>
*
* @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
* 1.f (full length of the view) or negative values to express change
* back toward the edge reached to initiate the effect.
*/
public void onPull(float deltaDistance) {
onPull(deltaDistance, 0.5f);
}
/**
* A view should call this when content is pulled away from an edge by the user.
* This will update the state of the current visual effect and its associated animation.
* The host view should always {@link android.view.View#invalidate()} after this
* and draw the results accordingly.
*
* @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
* 1.f (full length of the view) or negative values to express change
* back toward the edge reached to initiate the effect.
* @param displacement The displacement from the starting side of the effect of the point
* initiating the pull. In the case of touch this is the finger position.
* Values may be from 0-1.
*/
public void onPull(float deltaDistance, float displacement) {
final long now = AnimationUtils.currentAnimationTimeMillis();
mTargetDisplacement = displacement;
if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
return;
}
if (mState != STATE_PULL) {
mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
}
mState = STATE_PULL;
mStartTime = now;
mDuration = PULL_TIME;
mPullDistance += deltaDistance;
final float absdd = Math.abs(deltaDistance);
mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
if (mPullDistance == 0) {
mGlowScaleY = mGlowScaleYStart = 0;
} else {
final float scale = Math.max(0, 1 - 1 /
(float) Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3f) / 0.7f;
mGlowScaleY = mGlowScaleYStart = scale;
}
mGlowAlphaFinish = mGlowAlpha;
mGlowScaleYFinish = mGlowScaleY;
}
/**
* Call when the object is released after being pulled.
* This will begin the "decay" phase of the effect. After calling this method
* the host view should {@link android.view.View#invalidate()} and thereby
* draw the results accordingly.
*/
public void onRelease() {
mPullDistance = 0;
if (mState != STATE_PULL && mState != STATE_PULL_DECAY) {
return;
}
mState = STATE_RECEDE;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = RECEDE_TIME;
}
/**
* Call when the effect absorbs an impact at the given velocity.
* Used when a fling reaches the scroll boundary.
*
* <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
* the method <code>getCurrVelocity</code> will provide a reasonable approximation
* to use here.</p>
*
* @param velocity Velocity at impact in pixels per second.
*/
public void onAbsorb(int velocity) {
mState = STATE_ABSORB;
velocity = Math.min(Math.max(MIN_VELOCITY, Math.abs(velocity)), MAX_VELOCITY);
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = 0.15f + (velocity * 0.02f);
// The glow depends more on the velocity, and therefore starts out
// nearly invisible.
mGlowAlphaStart = 0.3f;
mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
// Growth for the size of the glow should be quadratic to properly
// respond
// to a user's scrolling speed. The faster the scrolling speed, the more
// intense the effect should be for both the size and the saturation.
mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f);
// Alpha should change for the glow as well as size.
mGlowAlphaFinish = Math.max(
mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
mTargetDisplacement = 0.5f;
}
/**
* Return the color of this edge effect in argb.
*
* @return The color of this edge effect in argb
*/
public int getColor() {
return mColor;
}
/**
* Set the color of this edge effect in argb.
*
* @param color Color in argb
*/
public void setColor(int color) {
mColor = color;
}
/**
* Draw into the provided canvas. Assumes that the canvas has been rotated
* accordingly and the size has been set. The effect will be drawn the full
* width of X=0 to X=width, beginning from Y=0 and extending to some factor <
* 1.f of height.
*
* @param canvas Canvas to draw into
* @return true if drawing should continue beyond this frame to continue the
* animation
*/
public boolean draw(GLCanvas canvas) {
update();
canvas.save();
final float centerX = mBounds.centerX();
final float centerY = mBounds.height() - mRadius;
canvas.translate(-centerX, 0f);
canvas.scale(1.f, Math.min(mGlowScaleY, 1.f) * mBaseGlowScale, 0);
canvas.translate(centerX, 0f);
final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
float translateX = mBounds.width() * displacement / 2;
canvas.translate(translateX, 0);
canvas.fillOval(centerX, centerY, mRadius, mRadius, mColor);
canvas.restore();
boolean oneLastFrame = false;
if (mState == STATE_RECEDE && mGlowScaleY == 0) {
mState = STATE_IDLE;
oneLastFrame = true;
}
return mState != STATE_IDLE || oneLastFrame;
}
/**
* Return the maximum height that the edge effect will be drawn at given the original
* {@link #setSize(int, int) input size}.
*
* @return The maximum height of the edge effect
*/
public int getMaxHeight() {
return (int) (mBounds.height() * MAX_GLOW_SCALE + 0.5f);
}
private void update() {
final long time = AnimationUtils.currentAnimationTimeMillis();
final float t = Math.min((time - mStartTime) / mDuration, 1.f);
final float interp = mInterpolator.getInterpolation(t);
mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
if (t >= 1.f - EPSILON) {
switch (mState) {
case STATE_ABSORB:
mState = STATE_RECEDE;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = RECEDE_TIME;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
// After absorb, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
break;
case STATE_PULL:
mState = STATE_PULL_DECAY;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = PULL_DECAY_TIME;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
// After pull, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
break;
case STATE_PULL_DECAY:
mState = STATE_RECEDE;
break;
case STATE_RECEDE:
mState = STATE_IDLE;
break;
}
}
}
}
@@ -1,162 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* 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 com.hippo.glview.widget;
import android.opengl.Matrix;
import androidx.annotation.IntDef;
import com.hippo.glview.glrenderer.GLCanvas;
import com.hippo.glview.view.GLView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public final class GLEdgeView extends GLView {
public static final int TOP = 0;
public static final int LEFT = 1;
public static final int BOTTOM = 2;
public static final int RIGHT = 3;
// Each edge effect has a transform matrix, and each matrix has 16 elements.
// We put all the elements in one array. These constants specify the
// starting index of each matrix.
private static final int TOP_M = TOP * 16;
private static final int LEFT_M = LEFT * 16;
private static final int BOTTOM_M = BOTTOM * 16;
private static final int RIGHT_M = RIGHT * 16;
private final GLEdgeEffect[] mEffect = new GLEdgeEffect[4];
private final float[] mMatrix = new float[4 * 16];
public GLEdgeView(int color) {
for (int i = 0; i < 4; i++) {
mEffect[i] = new GLEdgeEffect(color);
}
}
public void setColor(int color) {
for (int i = 0; i < 4; i++) {
mEffect[i].setColor(color);
}
}
@Override
protected void onLayout(
boolean changeSize, int left, int top, int right, int bottom) {
if (!changeSize) return;
int w = right - left;
int h = bottom - top;
for (int i = 0; i < 4; i++) {
if ((i & 1) == 0) { // top or bottom
mEffect[i].setSize(w, h);
} else { // left or right
mEffect[i].setSize(h, w);
}
}
// Set up transforms for the four edges. Without transforms an
// EdgeEffect draws the TOP edge from (0, 0) to (w, Y * h) where Y
// is some factor < 1. For other edges we need to move, rotate, and
// flip the effects into proper places.
Matrix.setIdentityM(mMatrix, TOP_M);
Matrix.setIdentityM(mMatrix, LEFT_M);
Matrix.setIdentityM(mMatrix, BOTTOM_M);
Matrix.setIdentityM(mMatrix, RIGHT_M);
Matrix.rotateM(mMatrix, LEFT_M, 90, 0, 0, 1);
Matrix.scaleM(mMatrix, LEFT_M, 1, -1, 1);
Matrix.translateM(mMatrix, BOTTOM_M, 0, h, 0);
Matrix.scaleM(mMatrix, BOTTOM_M, 1, -1, 1);
Matrix.translateM(mMatrix, RIGHT_M, w, 0, 0);
Matrix.rotateM(mMatrix, RIGHT_M, 90, 0, 0, 1);
}
@Override
public void render(GLCanvas canvas) {
boolean more = false;
for (int i = 0; i < 4; i++) {
canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
canvas.multiplyMatrix(mMatrix, i * 16);
more |= mEffect[i].draw(canvas);
canvas.restore();
}
if (more) {
invalidate();
}
}
// Called when the content is pulled away from the edge.
// offset is in pixels. direction is one of {TOP, LEFT, BOTTOM, RIGHT}.
public void onPull(float offset, @Direction int direction) {
int fullLength = ((direction & 1) == 0) ? getWidth() : getHeight();
mEffect[direction].onPull(offset / fullLength);
if (!mEffect[direction].isFinished()) {
invalidate();
}
}
public void onPull(float offset, float position, @Direction int direction) {
int fullLength = ((direction & 1) == 0) ? getHeight() : getWidth();
int fullPosition = ((direction & 1) == 0) ? getWidth() : getHeight();
mEffect[direction].onPull(offset / fullLength, position / fullPosition);
if (!mEffect[direction].isFinished()) {
invalidate();
}
}
// Call when the object is released after being pulled.
public void onRelease() {
boolean more = false;
for (int i = 0; i < 4; i++) {
mEffect[i].onRelease();
more |= !mEffect[i].isFinished();
}
if (more) {
invalidate();
}
}
public void onRelease(@Direction int direction) {
GLEdgeEffect edgeEffect = mEffect[direction];
edgeEffect.onRelease();
if (!edgeEffect.isFinished()) {
invalidate();
}
}
// Call when the effect absorbs an impact at the given velocity.
// Used when a fling reaches the scroll boundary. velocity is in pixels
// per second. direction is one of {TOP, LEFT, BOTTOM, RIGHT}.
public void onAbsorb(int velocity, int direction) {
mEffect[direction].onAbsorb(velocity);
if (!mEffect[direction].isFinished()) {
invalidate();
}
}
public boolean isFinished(int direction) {
return mEffect[direction].isFinished();
}
@IntDef({TOP, LEFT, BOTTOM, RIGHT})
@Retention(RetentionPolicy.SOURCE)
private @interface Direction {
}
}
-1
View File
@@ -17,7 +17,6 @@
<resources>
<declare-styleable name="Theme">
<attr name="colorEdgeEffect" format="color" />
<attr name="dividerColor" format="color" />
<attr name="drawableColorSecondary" format="color" />
<attr name="drawableColorThemePrimary" format="color" />
-1
View File
@@ -21,7 +21,6 @@
<!-- ========== -->
<style name="Base.AppTheme" parent="Theme.Material3.DynamicColors.DayNight">
<item name="colorEdgeEffect">@color/grey_500</item>
<item name="dividerColor">@color/divider</item>
<item name="drawableColorSecondary">?attr/colorOnSurface</item>
<item name="drawableColorThemePrimary">?attr/colorPrimary</item>