diff --git a/Android/app/src/main/java/pub/doric/demo/MainActivity.java b/Android/app/src/main/java/pub/doric/demo/MainActivity.java index 7d3ece4f..eb9886f9 100644 --- a/Android/app/src/main/java/pub/doric/demo/MainActivity.java +++ b/Android/app/src/main/java/pub/doric/demo/MainActivity.java @@ -54,7 +54,7 @@ public class MainActivity extends AppCompatActivity { }); swipeLayout.setBackgroundColor(Color.YELLOW); swipeLayout.getRefreshView().setBackgroundColor(Color.RED); - swipeLayout.setPullDownHeight(100); + swipeLayout.setPullDownHeight(300); RecyclerView recyclerView = findViewById(R.id.root); recyclerView.setBackgroundColor(Color.WHITE); recyclerView.setLayoutManager(new LinearLayoutManager(this)); diff --git a/Android/doric/src/main/java/pub/doric/pullable/CircleImageView.java b/Android/doric/src/main/java/pub/doric/pullable/CircleImageView.java deleted file mode 100644 index b003051b..00000000 --- a/Android/doric/src/main/java/pub/doric/pullable/CircleImageView.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * 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 pub.doric.pullable; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.RadialGradient; -import android.graphics.Shader; -import android.graphics.drawable.ShapeDrawable; -import android.graphics.drawable.shapes.OvalShape; -import android.view.View; -import android.view.animation.Animation; - -import androidx.appcompat.widget.AppCompatImageView; -import androidx.core.content.ContextCompat; -import androidx.core.view.ViewCompat; - -/** - * Private class created to work around issues with AnimationListeners being - * called before the animation is actually complete and support shadows on older - * platforms. - */ -class CircleImageView extends AppCompatImageView { - - private static final int KEY_SHADOW_COLOR = 0x1E000000; - private static final int FILL_SHADOW_COLOR = 0x3D000000; - // PX - private static final float X_OFFSET = 0f; - private static final float Y_OFFSET = 1.75f; - private static final float SHADOW_RADIUS = 3.5f; - private static final int SHADOW_ELEVATION = 4; - - private Animation.AnimationListener mListener; - int mShadowRadius; - - CircleImageView(Context context, int color) { - super(context); - final float density = getContext().getResources().getDisplayMetrics().density; - final int shadowYOffset = (int) (density * Y_OFFSET); - final int shadowXOffset = (int) (density * X_OFFSET); - - mShadowRadius = (int) (density * SHADOW_RADIUS); - - ShapeDrawable circle; - if (elevationSupported()) { - circle = new ShapeDrawable(new OvalShape()); - ViewCompat.setElevation(this, SHADOW_ELEVATION * density); - } else { - OvalShape oval = new OvalShadow(mShadowRadius); - circle = new ShapeDrawable(oval); - setLayerType(View.LAYER_TYPE_SOFTWARE, circle.getPaint()); - circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, - KEY_SHADOW_COLOR); - final int padding = mShadowRadius; - // set padding so the inner image sits correctly within the shadow. - setPadding(padding, padding, padding, padding); - } - circle.getPaint().setColor(color); - ViewCompat.setBackground(this, circle); - } - - private boolean elevationSupported() { - return android.os.Build.VERSION.SDK_INT >= 21; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (!elevationSupported()) { - setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight() - + mShadowRadius * 2); - } - } - - public void setAnimationListener(Animation.AnimationListener listener) { - mListener = listener; - } - - @Override - public void onAnimationStart() { - super.onAnimationStart(); - if (mListener != null) { - mListener.onAnimationStart(getAnimation()); - } - } - - @Override - public void onAnimationEnd() { - super.onAnimationEnd(); - if (mListener != null) { - mListener.onAnimationEnd(getAnimation()); - } - } - - /** - * Update the background color of the circle image view. - * - * @param colorRes Id of a color resource. - */ - public void setBackgroundColorRes(int colorRes) { - setBackgroundColor(ContextCompat.getColor(getContext(), colorRes)); - } - - @Override - public void setBackgroundColor(int color) { - if (getBackground() instanceof ShapeDrawable) { - ((ShapeDrawable) getBackground()).getPaint().setColor(color); - } - } - - private class OvalShadow extends OvalShape { - private RadialGradient mRadialGradient; - private Paint mShadowPaint; - - OvalShadow(int shadowRadius) { - super(); - mShadowPaint = new Paint(); - mShadowRadius = shadowRadius; - updateRadialGradient((int) rect().width()); - } - - @Override - protected void onResize(float width, float height) { - super.onResize(width, height); - updateRadialGradient((int) width); - } - - @Override - public void draw(Canvas canvas, Paint paint) { - final int viewWidth = CircleImageView.this.getWidth(); - final int viewHeight = CircleImageView.this.getHeight(); - canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2, mShadowPaint); - canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2 - mShadowRadius, paint); - } - - private void updateRadialGradient(int diameter) { - mRadialGradient = new RadialGradient(diameter / 2, diameter / 2, - mShadowRadius, new int[]{FILL_SHADOW_COLOR, Color.TRANSPARENT}, - null, Shader.TileMode.CLAMP); - mShadowPaint.setShader(mRadialGradient); - } - } -} diff --git a/Android/doric/src/main/java/pub/doric/pullable/DoricSwipeLayout.java b/Android/doric/src/main/java/pub/doric/pullable/DoricSwipeLayout.java index f83c3431..b0dfd6be 100644 --- a/Android/doric/src/main/java/pub/doric/pullable/DoricSwipeLayout.java +++ b/Android/doric/src/main/java/pub/doric/pullable/DoricSwipeLayout.java @@ -1,5 +1,7 @@ package pub.doric.pullable; +import android.animation.Animator; +import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; @@ -17,7 +19,6 @@ import android.widget.ListView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.Px; import androidx.annotation.VisibleForTesting; import androidx.core.view.NestedScrollingChild; import androidx.core.view.NestedScrollingChildHelper; @@ -69,8 +70,6 @@ public class DoricSwipeLayout extends ViewGroup implements NestedScrollingParent private static final int ANIMATE_TO_START_DURATION = 200; - // Default background for the progress spinner - private static final int CIRCLE_BG_LIGHT = 0xFFFAFAFA; // Default offset in dips from the top of the view to where the progress spinner should stop private static final int DEFAULT_CIRCLE_TARGET = 64; @@ -159,17 +158,55 @@ public class DoricSwipeLayout extends ViewGroup implements NestedScrollingParent } }; private int mPullDownHeight = 0; + private ValueAnimator headerViewAnimator; void reset() { - mRefreshView.stopAnimation(); - mRefreshView.setVisibility(View.GONE); - // Return the circle to its start position - if (mScale) { - setAnimationProgress(0 /* animation complete and view is hidden */); - } else { - setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop); + if (headerViewAnimator != null && headerViewAnimator.isRunning()) { + headerViewAnimator.cancel(); } - mCurrentTargetOffsetTop = mRefreshView.getTop(); + headerViewAnimator = ValueAnimator.ofInt(mRefreshView.getBottom(), 0); + headerViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mCurrentTargetOffsetTop = (int) animation.getAnimatedValue() + - mRefreshView.getMeasuredHeight(); + mRefreshView.requestLayout(); + } + }); + headerViewAnimator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + mRefreshView.stopAnimation(); + mRefreshView.setVisibility(View.GONE); + // Return the circle to its start position + + if (mScale) { + setAnimationProgress(0 /* animation complete and view is hidden */); + } else { + setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop); + } + mCurrentTargetOffsetTop = mRefreshView.getTop(); + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + headerViewAnimator.setDuration(SCALE_DOWN_DURATION); + headerViewAnimator.start(); + + } @Override @@ -186,80 +223,6 @@ public class DoricSwipeLayout extends ViewGroup implements NestedScrollingParent reset(); } - /** - * The refresh indicator starting and resting position is always positioned - * near the top of the refreshing content. This position is a consistent - * location, but can be adjusted in either direction based on whether or not - * there is a toolbar or actionbar present. - *
- * Note: Calling this will reset the position of the refresh indicator to
- * start
.
- *