Created
March 24, 2018 18:04
-
-
Save murdly/88a79c2d5a2f58bb9388d3a926b11539 to your computer and use it in GitHub Desktop.
Shine effect
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.graphics.Color; | |
import android.graphics.drawable.GradientDrawable; | |
import android.support.annotation.Nullable; | |
import android.util.AttributeSet; | |
import android.view.View; | |
import android.view.ViewPropertyAnimator; | |
import static android.graphics.drawable.GradientDrawable.LINEAR_GRADIENT; | |
public class Shimmer extends View { | |
private static final float MILLISECONDS_PER_PX = 0.195f; | |
/** | |
* * START_END | |
* x | | |
* x V | |
* x | |
* x --- | |
* x --- | |
* x--- | |
* | |
* --> | |
* | |
* END_START | |
* <-- | |
* | |
* ---x | |
* --- x | |
* A --- x | |
* | x | |
* x | |
* x | |
*/ | |
public enum Direction { | |
START_END, | |
END_START | |
} | |
/** | |
* * TOP_LEFT_BOTTOM_RIGHT | |
* x-- | |
* -x- | |
* --x | |
* | |
* TOP_RIGHT_BOTTOM_LEFT | |
* --x | |
* -x- | |
* x-- | |
*/ | |
public enum Angle { | |
TOP_LEFT_BOTTOM_RIGHT, | |
TOP_RIGHT_BOTTOM_LEFT | |
} | |
private Direction direction = Direction.START_END; | |
private Angle angle = Angle.TOP_LEFT_BOTTOM_RIGHT; | |
private ViewPropertyAnimator animator; | |
private boolean animationStarted; | |
public Shimmer(Context context) { | |
this(context, null); | |
} | |
public Shimmer(Context context, @Nullable AttributeSet attrs) { | |
super(context, attrs); | |
init(); | |
} | |
private void init() { | |
setVisibility(INVISIBLE); | |
setAlpha(0); | |
invalidateDrawable(); | |
} | |
private void invalidateDrawable() { | |
setBackground(createShimmerDrawable(angle)); | |
} | |
private GradientDrawable createShimmerDrawable(Angle angle) { | |
final GradientDrawable gradient = new GradientDrawable(); | |
gradient.setColors(new int[]{ | |
Color.parseColor("#00000000"), | |
Color.parseColor("#40FFFFFF"), | |
Color.parseColor("#00000000") | |
}); | |
gradient.setGradientType(LINEAR_GRADIENT); | |
switch (angle) { | |
case TOP_LEFT_BOTTOM_RIGHT: | |
gradient.setOrientation(GradientDrawable.Orientation.TL_BR); | |
break; | |
case TOP_RIGHT_BOTTOM_LEFT: | |
gradient.setOrientation(GradientDrawable.Orientation.TR_BL); | |
break; | |
} | |
return gradient; | |
} | |
public void setAngle(Angle angle) { | |
this.angle = angle; | |
invalidateDrawable(); | |
} | |
public void setDirection(Direction direction) { | |
this.direction = direction; | |
} | |
public void runShimmer() { | |
if (animationStarted) { | |
stopShimmer(); | |
} | |
final ViewPropertyAnimator animator; | |
if (getMeasuredHeight() > getMeasuredWidth()) { | |
animator = getAnimatorVertical(); | |
} else { | |
animator = getAnimatorHorizontal(); | |
} | |
animator.start(); | |
animationStarted = true; | |
} | |
private void stopShimmer() { | |
if (animator != null) { | |
animator.cancel(); | |
} | |
animator = null; | |
animationStarted = false; | |
} | |
private ViewPropertyAnimator getAnimatorVertical() { | |
if (animator == null) { | |
setVisibility(VISIBLE); | |
setAlpha(1); | |
final float yStart; | |
final float yEnd; | |
switch (direction) { | |
case START_END: | |
yEnd = getMeasuredHeight(); | |
yStart = -2 * getMeasuredHeight(); | |
break; | |
case END_START: | |
yEnd = -2 * getMeasuredHeight(); | |
yStart = getMeasuredHeight(); | |
break; | |
default: | |
yEnd = 0; | |
yStart = 0; | |
} | |
setTranslationY(yStart); | |
final long duration = calculateSpeed(yEnd - yStart); | |
animator = animate() | |
.alpha(0) | |
.translationY(yEnd) | |
.setDuration(duration) | |
.withEndAction(() -> { | |
setAlpha(1); | |
setTranslationY(yStart); | |
setVisibility(View.GONE); | |
}); | |
} | |
return animator; | |
} | |
private ViewPropertyAnimator getAnimatorHorizontal() { | |
if (animator == null) { | |
setVisibility(View.VISIBLE); | |
setAlpha(1); | |
final float xStart; | |
final float xEnd; | |
switch (direction) { | |
case START_END: | |
xEnd = getMeasuredWidth(); | |
xStart = -2 * getMeasuredWidth(); | |
break; | |
case END_START: | |
xEnd = -2 * getMeasuredWidth(); | |
xStart = getMeasuredWidth(); | |
break; | |
default: | |
xEnd = 0; | |
xStart = 0; | |
} | |
setTranslationX(xStart); | |
final long duration = calculateSpeed(xEnd - xStart); | |
animator = animate() | |
.alpha(0) | |
.translationX(xEnd) | |
.setDuration(duration) | |
.withEndAction(() -> { | |
setAlpha(1); | |
setTranslationX(xStart); | |
setVisibility(View.GONE); | |
}); | |
} | |
return animator; | |
} | |
private long calculateSpeed(float dx) { | |
return (long) Math.ceil(Math.abs(dx) * MILLISECONDS_PER_PX); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.support.annotation.NonNull; | |
import android.support.annotation.Nullable; | |
import android.util.AttributeSet; | |
import android.view.View; | |
import android.widget.FrameLayout; | |
public class ShimmerLayout extends FrameLayout { | |
private Shimmer shimmer; | |
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) { | |
super(context, attrs, defStyleAttr); | |
addShimmerView(); | |
} | |
private void addShimmerView() { | |
shimmer = new Shimmer(getContext()); | |
addView(shimmer); | |
} | |
public void run() { | |
if (shimmer != null) { | |
bringChildToFront(shimmer); | |
post(() -> shimmer.runShimmer()); | |
} | |
} | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
final int widthSize = MeasureSpec.getSize(widthMeasureSpec); | |
int heightSize; | |
measureChildren(widthMeasureSpec, heightMeasureSpec); | |
View directChild = null; | |
if (getChildCount() > 0) { | |
directChild = getChildAt(0); | |
} | |
if (directChild != null) { | |
heightSize = directChild.getMeasuredHeight() - directChild.getPaddingBottom(); | |
} else { | |
heightSize = MeasureSpec.getSize(heightMeasureSpec); | |
} | |
setMeasuredDimension(widthSize, heightSize); | |
} | |
public void setDirection(Shimmer.Direction direction) { | |
if (shimmer != null) { | |
shimmer.setDirection(direction); | |
} | |
} | |
public void setAngle(Shimmer.Angle angle) { | |
if (shimmer != null) { | |
shimmer.setAngle(angle); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment