From 82e21b7b9b688fd55ac9d38c0502a5ff26789a09 Mon Sep 17 00:00:00 2001 From: Teodor G Date: Sun, 4 Apr 2021 17:23:44 +0300 Subject: [PATCH 1/2] added shimmer package --- material-elements/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/material-elements/build.gradle b/material-elements/build.gradle index e2ae2a8..700de03 100644 --- a/material-elements/build.gradle +++ b/material-elements/build.gradle @@ -112,6 +112,7 @@ def srcDirs = [ 'com/zeoflow/material/elements/ripple', 'com/zeoflow/material/elements/slidingpanel', 'com/zeoflow/material/elements/shape', + 'com/zeoflow/material/elements/shimmer', 'com/zeoflow/material/elements/shadow', 'com/zeoflow/material/elements/slider', 'com/zeoflow/material/elements/snackbar', From 16e83ca4d51d71bfae47e2bbcf430caefb2a7860 Mon Sep 17 00:00:00 2001 From: Teodor G Date: Sun, 4 Apr 2021 17:24:01 +0300 Subject: [PATCH 2/2] added shimmer files --- .../elements/shimmer/ResourceExtension.java | 30 + .../material/elements/shimmer/Shimmer.java | 598 ++++++++++++++++++ .../elements/shimmer/ShimmerDrawable.java | 268 ++++++++ .../elements/shimmer/ShimmerFrameLayout.java | 243 +++++++ .../elements/shimmer/ShimmerLayout.java | 341 ++++++++++ .../elements/shimmer/ShimmerParams.java | 50 ++ .../elements/shimmer/ViewExtensions.java | 44 ++ .../material/elements/shimmer/build.gradle | 34 + .../elements/shimmer/res/values/attrs.xml | 65 ++ 9 files changed, 1673 insertions(+) create mode 100644 material-elements/java/com/zeoflow/material/elements/shimmer/ResourceExtension.java create mode 100644 material-elements/java/com/zeoflow/material/elements/shimmer/Shimmer.java create mode 100644 material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerDrawable.java create mode 100644 material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerFrameLayout.java create mode 100644 material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerLayout.java create mode 100644 material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerParams.java create mode 100644 material-elements/java/com/zeoflow/material/elements/shimmer/ViewExtensions.java create mode 100644 material-elements/java/com/zeoflow/material/elements/shimmer/build.gradle create mode 100644 material-elements/java/com/zeoflow/material/elements/shimmer/res/values/attrs.xml diff --git a/material-elements/java/com/zeoflow/material/elements/shimmer/ResourceExtension.java b/material-elements/java/com/zeoflow/material/elements/shimmer/ResourceExtension.java new file mode 100644 index 0000000..840b6c3 --- /dev/null +++ b/material-elements/java/com/zeoflow/material/elements/shimmer/ResourceExtension.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 ZeoFlow SRL + * + * 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.zeoflow.material.elements.shimmer; + +import android.view.View; + +@SuppressWarnings({"unused", "RedundantSuppression"}) +class ResourceExtension +{ + + public static float dp2px(float dp, View view) + { + return dp * view.getResources().getDisplayMetrics().density * 0.5f; + } + +} diff --git a/material-elements/java/com/zeoflow/material/elements/shimmer/Shimmer.java b/material-elements/java/com/zeoflow/material/elements/shimmer/Shimmer.java new file mode 100644 index 0000000..abfb9d8 --- /dev/null +++ b/material-elements/java/com/zeoflow/material/elements/shimmer/Shimmer.java @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2021 ZeoFlow SRL + * + * 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.zeoflow.material.elements.shimmer; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.RectF; +import android.util.AttributeSet; + +import androidx.annotation.ColorInt; +import androidx.annotation.FloatRange; +import androidx.annotation.IntDef; +import androidx.annotation.Px; + +import com.zeoflow.material.elements.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A Shimmer is an object detailing all of the configuration options available for {@link + * ShimmerFrameLayout} + */ +@SuppressWarnings({"unused", "RedundantSuppression"}) +public class Shimmer +{ + + private static final int COMPONENT_COUNT = 4; + final float[] positions = new float[COMPONENT_COUNT]; + final int[] colors = new int[COMPONENT_COUNT]; + final RectF bounds = new RectF(); + @Direction + int direction = Direction.LEFT_TO_RIGHT; + @ColorInt + int highlightColor = Color.WHITE; + @ColorInt + int baseColor = 0x4cffffff; + @Shape + int shape = Shape.LINEAR; + int fixedWidth = 0; + int fixedHeight = 0; + float widthRatio = 1f; + float heightRatio = 1f; + float intensity = 0f; + float dropoff = 0.5f; + float tilt = 20f; + boolean clipToChildren = true; + boolean autoStart = true; + boolean alphaShimmer = true; + int repeatCount = ValueAnimator.INFINITE; + int repeatMode = ValueAnimator.RESTART; + long animationDuration = 1000L; + long repeatDelay; + long startDelay; + Shimmer() + { + } + int width(int width) + { + return fixedWidth > 0 ? fixedWidth : Math.round(widthRatio * width); + } + int height(int height) + { + return fixedHeight > 0 ? fixedHeight : Math.round(heightRatio * height); + } + void updateColors() + { + switch (shape) + { + default: + case Shape.LINEAR: + colors[0] = baseColor; + colors[1] = highlightColor; + colors[2] = highlightColor; + colors[3] = baseColor; + break; + case Shape.RADIAL: + colors[0] = highlightColor; + colors[1] = highlightColor; + colors[2] = baseColor; + colors[3] = baseColor; + break; + } + } + void updatePositions() + { + switch (shape) + { + default: + case Shape.LINEAR: + positions[0] = Math.max((1f - intensity - dropoff) / 2f, 0f); + positions[1] = Math.max((1f - intensity - 0.001f) / 2f, 0f); + positions[2] = Math.min((1f + intensity + 0.001f) / 2f, 1f); + positions[3] = Math.min((1f + intensity + dropoff) / 2f, 1f); + break; + case Shape.RADIAL: + positions[0] = 0f; + positions[1] = Math.min(intensity, 1f); + positions[2] = Math.min(intensity + dropoff, 1f); + positions[3] = 1f; + break; + } + } + void updateBounds(int viewWidth, int viewHeight) + { + int magnitude = Math.max(viewWidth, viewHeight); + double rad = Math.PI / 2f - Math.toRadians(tilt % 90f); + double hyp = magnitude / Math.sin(rad); + int padding = 3 * Math.round((float) (hyp - magnitude) / 2f); + bounds.set(-padding, -padding, width(viewWidth) + padding, height(viewHeight) + padding); + } + + /** + * The shape of the shimmer's highlight. By default LINEAR is used. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({Shape.LINEAR, Shape.RADIAL}) + public @interface Shape + { + + /** + * Linear gives a ray reflection effect. + */ + int LINEAR = 0; + /** + * Radial gives a spotlight effect. + */ + int RADIAL = 1; + + } + + /** + * Direction of the shimmer's sweep. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + Direction.LEFT_TO_RIGHT, + Direction.TOP_TO_BOTTOM, + Direction.RIGHT_TO_LEFT, + Direction.BOTTOM_TO_TOP + }) + public @interface Direction + { + + int LEFT_TO_RIGHT = 0; + int TOP_TO_BOTTOM = 1; + int RIGHT_TO_LEFT = 2; + int BOTTOM_TO_TOP = 3; + + } + + @SuppressWarnings("UnusedReturnValue") + public abstract static class Builder> + { + + final Shimmer mShimmer = new Shimmer(); + private static float clamp(float min, float max, float value) + { + return Math.min(max, Math.max(min, value)); + } + // Gets around unchecked cast + protected abstract T getThis(); + /** + * Applies all specified options from the {@link AttributeSet}. + */ + public T consumeAttributes(Context context, AttributeSet attrs) + { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShimmerFrameLayout, 0, 0); + return consumeAttributes(a); + } + T consumeAttributes(TypedArray a) + { + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_clip_to_children)) + { + setClipToChildren( + a.getBoolean( + R.styleable.ShimmerFrameLayout_shimmer_clip_to_children, mShimmer.clipToChildren)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_auto_start)) + { + setAutoStart( + a.getBoolean(R.styleable.ShimmerFrameLayout_shimmer_auto_start, mShimmer.autoStart)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_base_alpha)) + { + setBaseAlpha(a.getFloat(R.styleable.ShimmerFrameLayout_shimmer_base_alpha, 0.3f)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_highlight_alpha)) + { + setHighlightAlpha(a.getFloat(R.styleable.ShimmerFrameLayout_shimmer_highlight_alpha, 1f)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_duration)) + { + setDuration( + a.getInt( + R.styleable.ShimmerFrameLayout_shimmer_duration, (int) mShimmer.animationDuration)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_repeat_count)) + { + setRepeatCount( + a.getInt(R.styleable.ShimmerFrameLayout_shimmer_repeat_count, mShimmer.repeatCount)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_repeat_delay)) + { + setRepeatDelay( + a.getInt( + R.styleable.ShimmerFrameLayout_shimmer_repeat_delay, (int) mShimmer.repeatDelay)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_repeat_mode)) + { + setRepeatMode( + a.getInt(R.styleable.ShimmerFrameLayout_shimmer_repeat_mode, mShimmer.repeatMode)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_start_delay)) + { + setStartDelay( + a.getInt( + R.styleable.ShimmerFrameLayout_shimmer_start_delay, (int) mShimmer.startDelay)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_direction)) + { + int direction = + a.getInt(R.styleable.ShimmerFrameLayout_shimmer_direction, mShimmer.direction); + switch (direction) + { + default: + case Direction.LEFT_TO_RIGHT: + setDirection(Direction.LEFT_TO_RIGHT); + break; + case Direction.TOP_TO_BOTTOM: + setDirection(Direction.TOP_TO_BOTTOM); + break; + case Direction.RIGHT_TO_LEFT: + setDirection(Direction.RIGHT_TO_LEFT); + break; + case Direction.BOTTOM_TO_TOP: + setDirection(Direction.BOTTOM_TO_TOP); + break; + } + } + + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_shape)) + { + int shape = a.getInt(R.styleable.ShimmerFrameLayout_shimmer_shape, mShimmer.shape); + switch (shape) + { + default: + case Shape.LINEAR: + setShape(Shape.LINEAR); + break; + case Shape.RADIAL: + setShape(Shape.RADIAL); + break; + } + } + + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_dropoff)) + { + setDropoff(a.getFloat(R.styleable.ShimmerFrameLayout_shimmer_dropoff, mShimmer.dropoff)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_fixed_width)) + { + setFixedWidth( + a.getDimensionPixelSize( + R.styleable.ShimmerFrameLayout_shimmer_fixed_width, mShimmer.fixedWidth)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_fixed_height)) + { + setFixedHeight( + a.getDimensionPixelSize( + R.styleable.ShimmerFrameLayout_shimmer_fixed_height, mShimmer.fixedHeight)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_intensity)) + { + setIntensity( + a.getFloat(R.styleable.ShimmerFrameLayout_shimmer_intensity, mShimmer.intensity)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_width_ratio)) + { + setWidthRatio( + a.getFloat(R.styleable.ShimmerFrameLayout_shimmer_width_ratio, mShimmer.widthRatio)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_height_ratio)) + { + setHeightRatio( + a.getFloat(R.styleable.ShimmerFrameLayout_shimmer_height_ratio, mShimmer.heightRatio)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_tilt)) + { + setTilt(a.getFloat(R.styleable.ShimmerFrameLayout_shimmer_tilt, mShimmer.tilt)); + } + return getThis(); + } + /** + * Copies the configuration of an already built Shimmer to this builder + */ + public T copyFrom(Shimmer other) + { + setDirection(other.direction); + setShape(other.shape); + setFixedWidth(other.fixedWidth); + setFixedHeight(other.fixedHeight); + setWidthRatio(other.widthRatio); + setHeightRatio(other.heightRatio); + setIntensity(other.intensity); + setDropoff(other.dropoff); + setTilt(other.tilt); + setClipToChildren(other.clipToChildren); + setAutoStart(other.autoStart); + setRepeatCount(other.repeatCount); + setRepeatMode(other.repeatMode); + setRepeatDelay(other.repeatDelay); + setStartDelay(other.startDelay); + setDuration(other.animationDuration); + mShimmer.baseColor = other.baseColor; + mShimmer.highlightColor = other.highlightColor; + return getThis(); + } + /** + * Sets the direction of the shimmer's sweep. See {@link Direction}. + */ + public T setDirection(@Direction int direction) + { + mShimmer.direction = direction; + return getThis(); + } + /** + * Sets the shape of the shimmer. See {@link Shape}. + */ + public T setShape(@Shape int shape) + { + mShimmer.shape = shape; + return getThis(); + } + /** + * Sets the fixed width of the shimmer, in pixels. + */ + public T setFixedWidth(@Px int fixedWidth) + { + if (fixedWidth < 0) + { + throw new IllegalArgumentException("Given invalid width: " + fixedWidth); + } + mShimmer.fixedWidth = fixedWidth; + return getThis(); + } + /** + * Sets the fixed height of the shimmer, in pixels. + */ + public T setFixedHeight(@Px int fixedHeight) + { + if (fixedHeight < 0) + { + throw new IllegalArgumentException("Given invalid height: " + fixedHeight); + } + mShimmer.fixedHeight = fixedHeight; + return getThis(); + } + /** + * Sets the width ratio of the shimmer, multiplied against the total width of the layout. + */ + public T setWidthRatio(float widthRatio) + { + if (widthRatio < 0f) + { + throw new IllegalArgumentException("Given invalid width ratio: " + widthRatio); + } + mShimmer.widthRatio = widthRatio; + return getThis(); + } + /** + * Sets the height ratio of the shimmer, multiplied against the total height of the layout. + */ + public T setHeightRatio(float heightRatio) + { + if (heightRatio < 0f) + { + throw new IllegalArgumentException("Given invalid height ratio: " + heightRatio); + } + mShimmer.heightRatio = heightRatio; + return getThis(); + } + /** + * Sets the intensity of the shimmer. A larger value causes the shimmer to be larger. + */ + public T setIntensity(float intensity) + { + if (intensity < 0f) + { + throw new IllegalArgumentException("Given invalid intensity value: " + intensity); + } + mShimmer.intensity = intensity; + return getThis(); + } + /** + * Sets how quickly the shimmer's gradient drops-off. A larger value causes a sharper drop-off. + */ + public T setDropoff(float dropoff) + { + if (dropoff < 0f) + { + throw new IllegalArgumentException("Given invalid dropoff value: " + dropoff); + } + mShimmer.dropoff = dropoff; + return getThis(); + } + /** + * Sets the tilt angle of the shimmer in degrees. + */ + public T setTilt(float tilt) + { + mShimmer.tilt = tilt; + return getThis(); + } + /** + * Sets the base alpha, which is the alpha of the underlying children, amount in the range [0, + * 1]. + */ + public T setBaseAlpha(@FloatRange(from = 0, to = 1) float alpha) + { + int intAlpha = (int) (clamp(0f, 1f, alpha) * 255f); + mShimmer.baseColor = intAlpha << 24 | (mShimmer.baseColor & 0x00FFFFFF); + return getThis(); + } + /** + * Sets the shimmer alpha amount in the range [0, 1]. + */ + public T setHighlightAlpha(@FloatRange(from = 0, to = 1) float alpha) + { + int intAlpha = (int) (clamp(0f, 1f, alpha) * 255f); + mShimmer.highlightColor = intAlpha << 24 | (mShimmer.highlightColor & 0x00FFFFFF); + return getThis(); + } + /** + * Sets whether the shimmer will clip to the childrens' contents, or if it will opaquely draw on + * top of the children. + */ + public T setClipToChildren(boolean status) + { + mShimmer.clipToChildren = status; + return getThis(); + } + /** + * Sets whether the shimmering animation will start automatically. + */ + public T setAutoStart(boolean status) + { + mShimmer.autoStart = status; + return getThis(); + } + /** + * Sets how often the shimmering animation will repeat. See {@link + * ValueAnimator#setRepeatCount(int)}. + */ + public T setRepeatCount(int repeatCount) + { + mShimmer.repeatCount = repeatCount; + return getThis(); + } + /** + * Sets how the shimmering animation will repeat. See {@link + * ValueAnimator#setRepeatMode(int)}. + */ + public T setRepeatMode(int mode) + { + mShimmer.repeatMode = mode; + return getThis(); + } + /** + * Sets how long to wait in between repeats of the shimmering animation. + */ + public T setRepeatDelay(long millis) + { + if (millis < 0) + { + throw new IllegalArgumentException("Given a negative repeat delay: " + millis); + } + mShimmer.repeatDelay = millis; + return getThis(); + } + /** + * Sets how long to wait for starting the shimmering animation. + */ + public T setStartDelay(long millis) + { + if (millis < 0) + { + throw new IllegalArgumentException("Given a negative start delay: " + millis); + } + mShimmer.startDelay = millis; + return getThis(); + } + /** + * Sets how long the shimmering animation takes to do one full sweep. + */ + public T setDuration(long millis) + { + if (millis < 0) + { + throw new IllegalArgumentException("Given a negative duration: " + millis); + } + mShimmer.animationDuration = millis; + return getThis(); + } + public Shimmer build() + { + mShimmer.updateColors(); + mShimmer.updatePositions(); + return mShimmer; + } + + } + + public static class AlphaHighlightBuilder extends Builder + { + + public AlphaHighlightBuilder() + { + mShimmer.alphaShimmer = true; + } + + @Override + protected AlphaHighlightBuilder getThis() + { + return this; + } + + } + + public static class ColorHighlightBuilder extends Builder + { + + public ColorHighlightBuilder() + { + mShimmer.alphaShimmer = false; + } + + /** + * Sets the highlight color for the shimmer. + */ + public ColorHighlightBuilder setHighlightColor(@ColorInt int color) + { + mShimmer.highlightColor = color; + return getThis(); + } + + /** + * Sets the base color for the shimmer. + */ + public ColorHighlightBuilder setBaseColor(@ColorInt int color) + { + mShimmer.baseColor = (mShimmer.baseColor & 0xFF000000) | (color & 0x00FFFFFF); + return getThis(); + } + + @Override + ColorHighlightBuilder consumeAttributes(TypedArray a) + { + super.consumeAttributes(a); + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_base_color)) + { + setBaseColor( + a.getColor(R.styleable.ShimmerFrameLayout_shimmer_base_color, mShimmer.baseColor)); + } + if (a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_highlight_color)) + { + setHighlightColor( + a.getColor( + R.styleable.ShimmerFrameLayout_shimmer_highlight_color, mShimmer.highlightColor)); + } + return getThis(); + } + + @Override + protected ColorHighlightBuilder getThis() + { + return this; + } + + } + +} diff --git a/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerDrawable.java b/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerDrawable.java new file mode 100644 index 0000000..fc69adb --- /dev/null +++ b/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerDrawable.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2021 ZeoFlow SRL + * + * 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.zeoflow.material.elements.shimmer; + +import android.animation.ValueAnimator; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RadialGradient; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.view.animation.LinearInterpolator; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +@SuppressWarnings({"unused", "RedundantSuppression"}) +public final class ShimmerDrawable extends Drawable +{ + + private final ValueAnimator.AnimatorUpdateListener mUpdateListener = + animation -> invalidateSelf(); + + private final Paint mShimmerPaint = new Paint(); + private final Rect mDrawRect = new Rect(); + private final Matrix mShaderMatrix = new Matrix(); + + private @Nullable + ValueAnimator mValueAnimator; + + private @Nullable + Shimmer mShimmer; + + public ShimmerDrawable() + { + mShimmerPaint.setAntiAlias(true); + } + public @Nullable + Shimmer getShimmer() + { + return mShimmer; + } + public void setShimmer(@Nullable Shimmer shimmer) + { + mShimmer = shimmer; + if (mShimmer != null) + { + mShimmerPaint.setXfermode( + new PorterDuffXfermode( + mShimmer.alphaShimmer ? PorterDuff.Mode.DST_IN : PorterDuff.Mode.SRC_IN)); + } + updateShader(); + updateValueAnimator(); + invalidateSelf(); + } + /** + * Starts the shimmer animation. + */ + public void startShimmer() + { + if (mValueAnimator != null && !isShimmerStarted() && getCallback() != null) + { + mValueAnimator.start(); + } + } + + /** + * Stops the shimmer animation. + */ + public void stopShimmer() + { + if (mValueAnimator != null && isShimmerStarted()) + { + mValueAnimator.cancel(); + } + } + + /** + * Return whether the shimmer animation has been started. + */ + public boolean isShimmerStarted() + { + return mValueAnimator != null && mValueAnimator.isStarted(); + } + + @Override + public void onBoundsChange(Rect bounds) + { + super.onBoundsChange(bounds); + mDrawRect.set(bounds); + updateShader(); + maybeStartShimmer(); + } + + @Override + public void draw(@NonNull Canvas canvas) + { + if (mShimmer == null || mShimmerPaint.getShader() == null) + { + return; + } + + final float tiltTan = (float) Math.tan(Math.toRadians(mShimmer.tilt)); + final float translateHeight = mDrawRect.height() + tiltTan * mDrawRect.width(); + final float translateWidth = mDrawRect.width() + tiltTan * mDrawRect.height(); + final float dx; + final float dy; + final float animatedValue = + mValueAnimator != null ? (float) mValueAnimator.getAnimatedValue() : 0f; + switch (mShimmer.direction) + { + default: + case Shimmer.Direction.LEFT_TO_RIGHT: + dx = offset(-translateWidth, translateWidth, animatedValue); + dy = 0; + break; + case Shimmer.Direction.RIGHT_TO_LEFT: + dx = offset(translateWidth, -translateWidth, animatedValue); + dy = 0f; + break; + case Shimmer.Direction.TOP_TO_BOTTOM: + dx = 0f; + dy = offset(-translateHeight, translateHeight, animatedValue); + break; + case Shimmer.Direction.BOTTOM_TO_TOP: + dx = 0f; + dy = offset(translateHeight, -translateHeight, animatedValue); + break; + } + + mShaderMatrix.reset(); + mShaderMatrix.setRotate(mShimmer.tilt, mDrawRect.width() / 2f, mDrawRect.height() / 2f); + mShaderMatrix.postTranslate(dx, dy); + mShimmerPaint.getShader().setLocalMatrix(mShaderMatrix); + canvas.drawRect(mDrawRect, mShimmerPaint); + } + + @Override + public void setAlpha(int alpha) + { + // No-op, modify the Shimmer object you pass in instead + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) + { + // No-op, modify the Shimmer object you pass in instead + } + + @Override + public int getOpacity() + { + return mShimmer != null && (mShimmer.clipToChildren || mShimmer.alphaShimmer) + ? PixelFormat.TRANSLUCENT + : PixelFormat.OPAQUE; + } + + private float offset(float start, float end, float percent) + { + return start + (end - start) * percent; + } + + private void updateValueAnimator() + { + if (mShimmer == null) + { + return; + } + + final boolean started; + if (mValueAnimator != null) + { + started = mValueAnimator.isStarted(); + mValueAnimator.cancel(); + mValueAnimator.removeAllUpdateListeners(); + } else + { + started = false; + } + + mValueAnimator = + ValueAnimator.ofFloat(0f, 1f + (float) (mShimmer.repeatDelay / mShimmer.animationDuration)); + mValueAnimator.setInterpolator(new LinearInterpolator()); + mValueAnimator.setRepeatMode(mShimmer.repeatMode); + mValueAnimator.setStartDelay(mShimmer.startDelay); + mValueAnimator.setRepeatCount(mShimmer.repeatCount); + mValueAnimator.setDuration(mShimmer.animationDuration + mShimmer.repeatDelay); + mValueAnimator.addUpdateListener(mUpdateListener); + if (started) + { + mValueAnimator.start(); + } + } + + void maybeStartShimmer() + { + if (mValueAnimator != null + && !mValueAnimator.isStarted() + && mShimmer != null + && mShimmer.autoStart + && getCallback() != null) + { + mValueAnimator.start(); + } + } + + private void updateShader() + { + final Rect bounds = getBounds(); + final int boundsWidth = bounds.width(); + final int boundsHeight = bounds.height(); + if (boundsWidth == 0 || boundsHeight == 0 || mShimmer == null) + { + return; + } + final int width = mShimmer.width(boundsWidth); + final int height = mShimmer.height(boundsHeight); + + final Shader shader; + switch (mShimmer.shape) + { + default: + case Shimmer.Shape.LINEAR: + boolean vertical = + mShimmer.direction == Shimmer.Direction.TOP_TO_BOTTOM + || mShimmer.direction == Shimmer.Direction.BOTTOM_TO_TOP; + int endX = vertical ? 0 : width; + int endY = vertical ? height : 0; + shader = + new LinearGradient( + 0, 0, endX, endY, mShimmer.colors, mShimmer.positions, Shader.TileMode.CLAMP); + break; + case Shimmer.Shape.RADIAL: + shader = + new RadialGradient( + width / 2f, + height / 2f, + (float) (Math.max(width, height) / Math.sqrt(2)), + mShimmer.colors, + mShimmer.positions, + Shader.TileMode.CLAMP); + break; + } + + mShimmerPaint.setShader(shader); + } + +} diff --git a/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerFrameLayout.java b/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerFrameLayout.java new file mode 100644 index 0000000..1514a06 --- /dev/null +++ b/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerFrameLayout.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2021 ZeoFlow SRL + * + * 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.zeoflow.material.elements.shimmer; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.zeoflow.material.elements.R; + +/** + * Shimmer is an Android library that provides an easy way to add a shimmer effect to any {@link + * View}. It is useful as an unobtrusive loading indicator, and was originally + * developed for Facebook Home. + * + *

Find more examples and usage instructions over at: facebook.github.io/shimmer-android + */ +@SuppressWarnings({"unused", "RedundantSuppression", "UnusedReturnValue"}) +public class ShimmerFrameLayout extends FrameLayout +{ + + private final Paint mContentPaint = new Paint(); + private final ShimmerDrawable mShimmerDrawable = new ShimmerDrawable(); + + private boolean mShowShimmer = true; + private boolean mStoppedShimmerBecauseVisibility = false; + + public ShimmerFrameLayout(Context context) + { + super(context); + init(context, null); + } + + public ShimmerFrameLayout(Context context, AttributeSet attrs) + { + super(context, attrs); + init(context, attrs); + } + + public ShimmerFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) + { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public ShimmerFrameLayout( + Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) + { + super(context, attrs, defStyleAttr, defStyleRes); + init(context, attrs); + } + + private void init(Context context, @Nullable AttributeSet attrs) + { + setWillNotDraw(false); + mShimmerDrawable.setCallback(this); + + if (attrs == null) + { + setShimmer(new Shimmer.AlphaHighlightBuilder().build()); + return; + } + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShimmerFrameLayout, 0, 0); + try + { + Shimmer.Builder shimmerBuilder = + a.hasValue(R.styleable.ShimmerFrameLayout_shimmer_colored) + && a.getBoolean(R.styleable.ShimmerFrameLayout_shimmer_colored, false) + ? new Shimmer.ColorHighlightBuilder() + : new Shimmer.AlphaHighlightBuilder(); + setShimmer(shimmerBuilder.consumeAttributes(a).build()); + } finally + { + a.recycle(); + } + } + public @Nullable + Shimmer getShimmer() + { + return mShimmerDrawable.getShimmer(); + } + public ShimmerFrameLayout setShimmer(@Nullable Shimmer shimmer) + { + mShimmerDrawable.setShimmer(shimmer); + if (shimmer != null && shimmer.clipToChildren) + { + setLayerType(LAYER_TYPE_HARDWARE, mContentPaint); + } else + { + setLayerType(LAYER_TYPE_NONE, null); + } + + return this; + } + /** + * Starts the shimmer animation. + */ + public void startShimmer() + { + mShimmerDrawable.startShimmer(); + } + + /** + * Stops the shimmer animation. + */ + public void stopShimmer() + { + mStoppedShimmerBecauseVisibility = false; + mShimmerDrawable.stopShimmer(); + } + + /** + * Return whether the shimmer animation has been started. + */ + public boolean isShimmerStarted() + { + return mShimmerDrawable.isShimmerStarted(); + } + + /** + * Sets the ShimmerDrawable to be visible. + * + * @param startShimmer Whether to start the shimmer again. + */ + public void showShimmer(boolean startShimmer) + { + mShowShimmer = true; + if (startShimmer) + { + startShimmer(); + } + invalidate(); + } + + /** + * Sets the ShimmerDrawable to be invisible, stopping it in the process. + */ + public void hideShimmer() + { + stopShimmer(); + mShowShimmer = false; + invalidate(); + } + + /** + * Return whether the shimmer drawable is visible. + */ + public boolean isShimmerVisible() + { + return mShowShimmer; + } + + @Override + public void onLayout(boolean changed, int left, int top, int right, int bottom) + { + super.onLayout(changed, left, top, right, bottom); + final int width = getWidth(); + final int height = getHeight(); + mShimmerDrawable.setBounds(0, 0, width, height); + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) + { + super.onVisibilityChanged(changedView, visibility); + // View's constructor directly invokes this method, in which case no fields on + // this class have been fully initialized yet. + if (mShimmerDrawable == null) + { + return; + } + if (visibility != View.VISIBLE) + { + // GONE or INVISIBLE + if (isShimmerStarted()) + { + stopShimmer(); + mStoppedShimmerBecauseVisibility = true; + } + } else if (mStoppedShimmerBecauseVisibility) + { + mShimmerDrawable.maybeStartShimmer(); + mStoppedShimmerBecauseVisibility = false; + } + } + + @Override + public void onAttachedToWindow() + { + super.onAttachedToWindow(); + mShimmerDrawable.maybeStartShimmer(); + } + + @Override + public void onDetachedFromWindow() + { + super.onDetachedFromWindow(); + stopShimmer(); + } + + @Override + public void dispatchDraw(Canvas canvas) + { + super.dispatchDraw(canvas); + if (mShowShimmer) + { + mShimmerDrawable.draw(canvas); + } + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) + { + return super.verifyDrawable(who) || who == mShimmerDrawable; + } + +} diff --git a/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerLayout.java b/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerLayout.java new file mode 100644 index 0000000..d937a0e --- /dev/null +++ b/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerLayout.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2021 ZeoFlow SRL + * + * 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.zeoflow.material.elements.shimmer; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.widget.FrameLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.FloatRange; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.Px; + +import com.zeoflow.material.elements.R; +import com.zeoflow.material.elements.shimmer.Shimmer.ColorHighlightBuilder; + +import java.util.ArrayList; +import java.util.List; + +import static com.zeoflow.material.elements.shimmer.ResourceExtension.dp2px; +import static com.zeoflow.material.elements.shimmer.ViewExtensions.invisible; +import static com.zeoflow.material.elements.shimmer.ViewExtensions.visibility; +import static com.zeoflow.material.elements.shimmer.ViewExtensions.visible; + +@SuppressWarnings({"unused", "RedundantSuppression"}) +public class ShimmerLayout extends FrameLayout +{ + + private final List maskElements = new ArrayList<>(); + @Px + public float radius = dp2px(8f, this); + public Drawable drawable; + public boolean isVeiled = false; + public ShimmerFrameLayout shimmerContainer = new ShimmerFrameLayout(getContext()); + public Shimmer nonShimmer = new Shimmer.AlphaHighlightBuilder().setBaseAlpha(1.0f).setDropoff(1.0f).build(); + public Shimmer shimmer = new Shimmer.AlphaHighlightBuilder().build(); + public boolean shimmerEnable = true; + public boolean defaultChildVisible = false; + @ColorInt + private int baseColor = Color.parseColor("#D4D4D4"); + @ColorInt + private int highlightColor = Color.parseColor("#BDBDBD"); + @FloatRange(from = 0.0, to = 1.0) + private float baseAlpha = 1.0f; + @FloatRange(from = 0.0, to = 1.0) + private float highlightAlpha = 1.0f; + @FloatRange(from = 0.0, to = 1.0) + private float dropOff = 0.5f; + @LayoutRes + private int layout = -1; + + public ShimmerLayout(@NonNull Context context) + { + this(context, null); + } + public ShimmerLayout(@NonNull Context context, @Nullable AttributeSet attrs) + { + this(context, attrs, 0); + } + public ShimmerLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) + { + this(context, attrs, defStyleAttr, 0); + } + public ShimmerLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) + { + super(context, attrs, defStyleAttr, defStyleRes); + getAttrs(attrs); + onCreate(); + } + public int getLayout() + { + return layout; + } + public void setLayout(@LayoutRes int layout) + { + this.layout = layout; + invalidateLayout(layout); + } + /** + * Remove previous views and inflate a new layout using an inflated view. + */ + public void setLayout(View layout) + { + removeAllViews(); + addView(layout); + onFinishInflate(); + } + public void setShimmerContainer(Shimmer shimmer) + { + this.shimmer = shimmer; + shimmerContainer.setShimmer(shimmer); + } + public void setEnabled(boolean enabled) + { + this.shimmerEnable = enabled; + if (enabled) + { + shimmerContainer.setShimmer(shimmer); + } else + { + shimmerContainer.setShimmer(nonShimmer); + } + } + private void getAttrs(AttributeSet attrs) + { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ShimmerLayout); + try + { + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_veiled)) + isVeiled = a.getBoolean(R.styleable.ShimmerLayout_shimmerLayout_veiled, isVeiled); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_layout)) + layout = a.getResourceId(R.styleable.ShimmerLayout_shimmerLayout_layout, -1); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_drawable)) + drawable = a.getDrawable(R.styleable.ShimmerLayout_shimmerLayout_drawable); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_radius)) + radius = a.getDimension(R.styleable.ShimmerLayout_shimmerLayout_radius, radius); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_shimmerEnable)) + shimmerEnable = a.getBoolean(R.styleable.ShimmerLayout_shimmerLayout_shimmerEnable, shimmerEnable); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_baseColor)) + baseColor = a.getColor(R.styleable.ShimmerLayout_shimmerLayout_baseColor, baseColor); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_highlightColor)) + highlightColor = + a.getColor(R.styleable.ShimmerLayout_shimmerLayout_highlightColor, highlightColor); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_baseAlpha)) + baseAlpha = a.getFloat(R.styleable.ShimmerLayout_shimmerLayout_baseAlpha, baseAlpha); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_highlightAlpha)) + highlightAlpha = + a.getFloat(R.styleable.ShimmerLayout_shimmerLayout_highlightAlpha, highlightAlpha); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_dropOff)) + dropOff = a.getFloat(R.styleable.ShimmerLayout_shimmerLayout_dropOff, dropOff); + if (a.hasValue(R.styleable.ShimmerLayout_shimmerLayout_defaultChildVisible)) + defaultChildVisible = + a.getBoolean(R.styleable.ShimmerLayout_shimmerLayout_defaultChildVisible, defaultChildVisible); + } finally + { + a.recycle(); + } + } + private void onCreate() + { + invisible(this.shimmerContainer); + this.shimmer = new ColorHighlightBuilder() + .setBaseColor(baseColor) + .setHighlightColor(highlightColor) + .setBaseAlpha(baseAlpha) + .setHighlightAlpha(highlightAlpha) + .setDropoff(dropOff) + .setAutoStart(false) + .build(); + setEnabled(this.shimmerEnable); + } + /** + * Remove previous views and inflate a new layout using layout resource + */ + private void invalidateLayout(@LayoutRes int layout) + { + setLayout(LayoutInflater.from(getContext()).inflate(layout, this, false)); + } + /** + * Invokes addMaskElements method after inflating. + */ + @Override + protected void onFinishInflate() + { + super.onFinishInflate(); + removeView(shimmerContainer); + addView(shimmerContainer); + addMaskElements(this); + } + + /** + * Adds a masked skeleton views depending on the view tree structure of the [ShimmerLayout]. + * This method will ignore the ViewGroup for creating masked skeletons. + * + * @param parent A parent view for creating the masked skeleton. + */ + private void addMaskElements(final ViewGroup parent) + { + for (int i = 0; i < parent.getChildCount(); i++) + { + final View child = parent.getChildAt(i); + child.post(() -> + { + if (child instanceof ViewGroup) + { + addMaskElements((ViewGroup) child); + } else + { + float marginX = 0f; + float marginY = 0f; + ViewParent grandParent = parent.getParent(); + while (!(grandParent instanceof ShimmerLayout)) + { + if (grandParent instanceof ViewGroup) + { + ViewGroup.LayoutParams params = ((ViewGroup) grandParent).getLayoutParams(); + if (params instanceof MarginLayoutParams) + { + marginX += ((ViewGroup) grandParent).getX(); + marginY += ((ViewGroup) grandParent).getY(); + } + grandParent = grandParent.getParent(); + } else + { + break; + } + } + + View view = new View(getContext()); + view.setLayoutParams(new ViewGroup.LayoutParams(child.getWidth(), child.getHeight())); + view.setX(marginX + parent.getX() + child.getX()); + view.setY(marginY + parent.getY() + child.getY()); + view.setBackgroundColor(baseColor); + Drawable background = drawable; + if (background == null) + { + GradientDrawable backgroundGD = new GradientDrawable(); + backgroundGD.setColor(Color.DKGRAY); + backgroundGD.setCornerRadius(radius); + background = backgroundGD; + } + view.setBackground(background); + maskElements.add(view); + shimmerContainer.addView(view); + } + }); + } + + // Invalidate the whole masked view. + invalidate(); + + // Auto veiled + this.isVeiled = !this.isVeiled; + if (this.isVeiled) + { + unVeil(); + } else + { + veil(); + } + } + + /** + * Make appear the mask. + */ + public void veil() + { + if (!this.isVeiled) + { + this.isVeiled = true; + startShimmer(); + invalidate(); + } + } + + /** + * Make disappear the mask. + */ + public void unVeil() + { + if (this.isVeiled) + { + this.isVeiled = false; + stopShimmer(); + invalidate(); + } + } + + /** + * Starts the shimmer animation. + */ + public void startShimmer() + { + visible(this.shimmerContainer); + if (this.shimmerEnable) + { + this.shimmerContainer.startShimmer(); + } + if (!this.defaultChildVisible) + { + setChildVisibility(false); + } + } + + /** + * Stops the shimmer animation. + */ + public void stopShimmer() + { + invisible(this.shimmerContainer); + this.shimmerContainer.stopShimmer(); + if (!this.defaultChildVisible) + { + setChildVisibility(true); + } + } + + private void setChildVisibility(boolean visible) + { + for (int i = 0; i < getChildCount(); i++) + { + View child = getChildAt(i); + if (child != this.shimmerContainer) + { + visibility(child, visible); + } + } + } + + @Override + public void invalidate() + { + super.invalidate(); + this.shimmerContainer.invalidate(); + } + +} diff --git a/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerParams.java b/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerParams.java new file mode 100644 index 0000000..9de63ea --- /dev/null +++ b/material-elements/java/com/zeoflow/material/elements/shimmer/ShimmerParams.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 ZeoFlow SRL + * + * 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.zeoflow.material.elements.shimmer; + +import android.graphics.drawable.Drawable; + +import androidx.annotation.ColorInt; +import androidx.annotation.FloatRange; +import androidx.annotation.Px; + +@SuppressWarnings({"unused", "RedundantSuppression"}) +public class ShimmerParams +{ + + public Drawable drawable; + @Px + public float radius; + @FloatRange(from = 0.0, to = 1.0) + public float baseAlpha; + @FloatRange(from = 0.0, to = 1.0) + public float highlightAlpha; + public float dropOff; + public boolean shimmerEnable; + public Shimmer shimmer; + public boolean defaultChildVisible; + @ColorInt + int baseColor; + @ColorInt + int highlightColor; + + public ShimmerParams() + { + + } + +} diff --git a/material-elements/java/com/zeoflow/material/elements/shimmer/ViewExtensions.java b/material-elements/java/com/zeoflow/material/elements/shimmer/ViewExtensions.java new file mode 100644 index 0000000..690dc1c --- /dev/null +++ b/material-elements/java/com/zeoflow/material/elements/shimmer/ViewExtensions.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 ZeoFlow SRL + * + * 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.zeoflow.material.elements.shimmer; + +import android.view.View; + +@SuppressWarnings({"unused", "RedundantSuppression"}) +public class ViewExtensions +{ + + public static void visible(View view) + { + view.setVisibility(View.VISIBLE); + } + public static void invisible(View view) + { + view.setVisibility(View.INVISIBLE); + } + public static void visibility(View view, boolean visible) + { + if (visible) + { + visible(view); + } else + { + invisible(view); + } + } + +} \ No newline at end of file diff --git a/material-elements/java/com/zeoflow/material/elements/shimmer/build.gradle b/material-elements/java/com/zeoflow/material/elements/shimmer/build.gradle new file mode 100644 index 0000000..962c69f --- /dev/null +++ b/material-elements/java/com/zeoflow/material/elements/shimmer/build.gradle @@ -0,0 +1,34 @@ +package com.zeoflow.material.elements.shimmer + +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +archivesBaseName = getArchivesBaseName(project.name) +version = rootProject.ext.mdcLibraryVersion + +dependencies { + api("androidx.annotation:annotation:1.1.0") + implementation("androidx.core:core:1.1.0") +} + +android { + sourceSets { + main.manifest.srcFile 'AndroidManifest.xml' + main.java.srcDir '.' + main.java.excludes = [ + '**/build/**', + ] + main.res.srcDirs = [ + 'res', + ] + main.assets.srcDir 'assets' + } +} + +uploadArchives { + repositories { + mavenDeployer { + repository(url: rootProject.ext.mavenRepoUrl) + } + } +} diff --git a/material-elements/java/com/zeoflow/material/elements/shimmer/res/values/attrs.xml b/material-elements/java/com/zeoflow/material/elements/shimmer/res/values/attrs.xml new file mode 100644 index 0000000..7fe4644 --- /dev/null +++ b/material-elements/java/com/zeoflow/material/elements/shimmer/res/values/attrs.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +