Created
December 5, 2021 06:31
-
-
Save triniwiz/b3f87c8d3d07d0c57f5f2c13ae14d71d to your computer and use it in GitHub Desktop.
Shimmer ported to Typescript
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
/** | |
* The shape of the shimmer's highlight. By default LINEAR is used. | |
*/ | |
enum Shape { | |
/** | |
* Linear gives a ray reflection effect. | |
*/ | |
LINEAR, | |
/** | |
* Radial gives a spotlight effect. | |
*/ | |
RADIAL | |
} | |
/** | |
* Direction of the shimmer's sweep. | |
*/ | |
enum Direction { | |
LEFT_TO_RIGHT, | |
TOP_TO_BOTTOM, | |
RIGHT_TO_LEFT, | |
BOTTOM_TO_TOP | |
} | |
const INFINITE = -1; | |
const RESTART = 1; | |
class TNSShimmer { | |
static COMPONENT_COUNT = 4; | |
positions = Array.create('float', TNSShimmer.COMPONENT_COUNT); | |
colors = Array.create('int', TNSShimmer.COMPONENT_COUNT); | |
bounds = new android.graphics.RectF(); | |
direction = Direction.LEFT_TO_RIGHT; | |
highlightColor = new Color('white'); | |
baseColor = new Color(0x4cffffff); | |
shape = Shape.LINEAR; | |
fixedWidth = 0; | |
fixedHeight = 0; | |
widthRatio = 1; | |
heightRatio = 1; | |
intensity = 0; | |
dropoff = 0.5; | |
tilt = 20; | |
clipToChildren = true; | |
autoStart = false; | |
alphaShimmer = true; | |
repeatCount = INFINITE; | |
repeatMode = RESTART; | |
animationDuration = 1100; | |
repeatDelay = 0; | |
startDelay = 0; | |
constructor() { } | |
width(width: number) { | |
return this.fixedWidth > 0 ? this.fixedWidth : Math.round(this.widthRatio * width); | |
} | |
height(height: number) { | |
return this.fixedHeight > 0 ? this.fixedHeight : Math.round(this.heightRatio * height); | |
} | |
updateColors() { | |
switch (this.shape) { | |
default: | |
case Shape.LINEAR: | |
this.colors[0] = this.baseColor.android; | |
this.colors[1] = this.highlightColor.android; | |
this.colors[2] = this.highlightColor.android; | |
this.colors[3] = this.baseColor.android; | |
break; | |
case Shape.RADIAL: | |
this.colors[0] = this.highlightColor.android; | |
this.colors[1] = this.highlightColor.android; | |
this.colors[2] = this.baseColor.android; | |
this.colors[3] = this.baseColor.android; | |
break; | |
} | |
} | |
updatePositions() { | |
switch (this.shape) { | |
default: | |
case Shape.LINEAR: | |
this.positions[0] = Math.max((1 - this.intensity - this.dropoff) / 2, 0); | |
this.positions[1] = Math.max((1 - this.intensity - 0.001) / 2, 0); | |
this.positions[2] = Math.min((1 + this.intensity + 0.001) / 2, 1); | |
this.positions[3] = Math.min((1 + this.intensity + this.dropoff) / 2, 1); | |
break; | |
case Shape.RADIAL: | |
this.positions[0] = 0; | |
this.positions[1] = Math.min(this.intensity, 1); | |
this.positions[2] = Math.min(this.intensity + this.dropoff, 1); | |
this.positions[3] = 1; | |
break; | |
} | |
} | |
updateBounds(viewWidth: number, viewHeight: number) { | |
const magnitude = Math.max(viewWidth, viewHeight); | |
const rad = Math.PI / 2 - java.lang.Math.toRadians(this.tilt % 90); | |
const hyp = magnitude / Math.sin(rad); | |
const padding = 3 * Math.round((hyp - magnitude) / 2); | |
this.bounds.set(-padding, -padding, this.width(viewWidth) + padding, this.height(viewHeight) + padding); | |
} | |
} | |
abstract class Builder<T extends Builder<T>> { | |
mShimmer: TNSShimmer; | |
constructor() { | |
this.mShimmer = new TNSShimmer(); | |
} | |
// Gets around unchecked cast | |
protected abstract getThis(): T; | |
/** | |
* Copies the configuration of an already built Shimmer to this builder | |
*/ | |
public copyFrom(other: TNSShimmer): T { | |
this.setDirection(other.direction); | |
this.setShape(other.shape); | |
this.setFixedWidth(other.fixedWidth); | |
this.setFixedHeight(other.fixedHeight); | |
this.setWidthRatio(other.widthRatio); | |
this.setHeightRatio(other.heightRatio); | |
this.setIntensity(other.intensity); | |
this.setDropoff(other.dropoff); | |
this.setTilt(other.tilt); | |
this.setClipToChildren(other.clipToChildren); | |
this.setAutoStart(other.autoStart); | |
this.setRepeatCount(other.repeatCount); | |
this.setRepeatMode(other.repeatMode); | |
this.setRepeatDelay(other.repeatDelay); | |
this.setStartDelay(other.startDelay); | |
this.setDuration(other.animationDuration); | |
this.setBaseColor(other.baseColor); | |
this.setHighLightColor(other.highlightColor); | |
return this.getThis(); | |
} | |
public setBaseColor(color: number | string | Color): T { | |
if (color instanceof Color) { | |
this.mShimmer.baseColor = color; | |
} else if (typeof color === 'string' || typeof color === 'number') { | |
this.mShimmer.baseColor = new Color(color as any); | |
} | |
return this.getThis(); | |
} | |
public setHighLightColor(color: number | string | Color): T { | |
if (color instanceof Color) { | |
this.mShimmer.highlightColor = color; | |
} else if (typeof color === 'string' || typeof color === 'number') { | |
this.mShimmer.highlightColor = new Color(color as any); | |
} | |
return this.getThis(); | |
} | |
/** | |
* Sets the direction of the shimmer's sweep. See {@link Direction}. | |
*/ | |
public setDirection(direction: Direction) { | |
this.mShimmer.direction = direction; | |
return this.getThis(); | |
} | |
/** | |
* Sets the shape of the shimmer. See {@link Shape}. | |
*/ | |
public setShape(shape: Shape) { | |
this.mShimmer.shape = shape; | |
return this.getThis(); | |
} | |
/** | |
* Sets the fixed width of the shimmer, in pixels. | |
*/ | |
public setFixedWidth(fixedWidth: number) { | |
if (fixedWidth < 0) { | |
throw new Error("Given invalid width: " + fixedWidth); | |
} | |
this.mShimmer.fixedWidth = fixedWidth; | |
return this.getThis(); | |
} | |
/** | |
* Sets the fixed height of the shimmer, in pixels. | |
*/ | |
public setFixedHeight(fixedHeight: number) { | |
if (fixedHeight < 0) { | |
throw new Error("Given invalid height: " + fixedHeight); | |
} | |
this.mShimmer.fixedHeight = fixedHeight; | |
return this.getThis(); | |
} | |
/** | |
* Sets the width ratio of the shimmer, multiplied against the total width of the layout. | |
*/ | |
public setWidthRatio(widthRatio: number) { | |
if (widthRatio < 0) { | |
throw new Error("Given invalid width ratio: " + widthRatio); | |
} | |
this.mShimmer.widthRatio = widthRatio; | |
return this.getThis(); | |
} | |
/** | |
* Sets the height ratio of the shimmer, multiplied against the total height of the layout. | |
*/ | |
public setHeightRatio(heightRatio) { | |
if (heightRatio < 0) { | |
throw new Error("Given invalid height ratio: " + heightRatio); | |
} | |
this.mShimmer.heightRatio = heightRatio; | |
return this.getThis(); | |
} | |
/** | |
* Sets the intensity of the shimmer. A larger value causes the shimmer to be larger. | |
*/ | |
public setIntensity(intensity: number) { | |
if (intensity < 0) { | |
throw new Error("Given invalid intensity value: " + intensity); | |
} | |
this.mShimmer.intensity = intensity; | |
return this.getThis(); | |
} | |
/** | |
* Sets how quickly the shimmer's gradient drops-off. A larger value causes a sharper drop-off. | |
*/ | |
public setDropoff(dropoff: number) { | |
if (dropoff < 0) { | |
throw new Error("Given invalid dropoff value: " + dropoff); | |
} | |
this.mShimmer.dropoff = dropoff; | |
return this.getThis(); | |
} | |
/** | |
* Sets the tilt angle of the shimmer in degrees. | |
*/ | |
public setTilt(tilt: number) { | |
this.mShimmer.tilt = tilt; | |
return this.getThis(); | |
} | |
/** | |
* Sets the base alpha, which is the alpha of the underlying children, amount in the range [0, | |
* 1]. | |
*/ | |
public setBaseAlpha(alpha: number) { | |
if (alpha < 0) { | |
alpha = 0; | |
} else if (alpha > 1) { | |
alpha = 1; | |
} | |
const intAlpha = (Builder.clamp(0, 1, alpha) * 255); | |
this.mShimmer.baseColor = new Color(intAlpha << 24 | (this.mShimmer.baseColor.android & 0x00FFFFFF)); | |
return this.getThis(); | |
} | |
/** | |
* Sets the shimmer alpha amount in the range [0, 1]. | |
*/ | |
public setHighlightAlpha(alpha: number) { | |
if (alpha < 0) { | |
alpha = 0; | |
} else if (alpha > 1) { | |
alpha = 1; | |
} | |
const intAlpha = Builder.clamp(0, 1, alpha) * 255; | |
this.mShimmer.highlightColor = new Color(intAlpha << 24 | (this.mShimmer.highlightColor.android & 0x00FFFFFF)); | |
return this.getThis(); | |
} | |
/** | |
* Sets whether the shimmer will clip to the childrens' contents, or if it will opaquely draw on | |
* top of the children. | |
*/ | |
public setClipToChildren(status: boolean) { | |
this.mShimmer.clipToChildren = status; | |
return this.getThis(); | |
} | |
/** | |
* Sets whether the shimmering animation will start automatically. | |
*/ | |
public setAutoStart(status) { | |
this.mShimmer.autoStart = status; | |
return this.getThis(); | |
} | |
/** | |
* Sets how often the shimmering animation will repeat. See {@link | |
* android.animation.ValueAnimator#setRepeatCount(int)}. | |
*/ | |
public setRepeatCount(repeatCount: number) { | |
this.mShimmer.repeatCount = repeatCount; | |
return this.getThis(); | |
} | |
/** | |
* Sets how the shimmering animation will repeat. See {@link | |
* android.animation.ValueAnimator#setRepeatMode(int)}. | |
*/ | |
public setRepeatMode(mode) { | |
this.mShimmer.repeatMode = mode; | |
return this.getThis(); | |
} | |
/** | |
* Sets how long to wait in between repeats of the shimmering animation. | |
*/ | |
public setRepeatDelay(millis: number) { | |
if (millis < 0) { | |
throw new Error("Given a negative repeat delay: " + millis); | |
} | |
this.mShimmer.repeatDelay = millis; | |
return this.getThis(); | |
} | |
/** | |
* Sets how long to wait for starting the shimmering animation. | |
*/ | |
public setStartDelay(millis) { | |
if (millis < 0) { | |
throw new Error("Given a negative start delay: " + millis); | |
} | |
this.mShimmer.startDelay = millis; | |
return this.getThis(); | |
} | |
/** | |
* Sets how long the shimmering animation takes to do one full sweep. | |
*/ | |
public setDuration(millis: number) { | |
if (millis < 0) { | |
throw new Error("Given a negative duration: " + millis); | |
} | |
this.mShimmer.animationDuration = millis; | |
return this.getThis(); | |
} | |
public build(): TNSShimmer { | |
this.mShimmer.updateColors(); | |
this.mShimmer.updatePositions(); | |
return this.mShimmer; | |
} | |
private static clamp(min: number, max: number, value: number) { | |
return Math.min(max, Math.max(min, value)); | |
} | |
} | |
class AlphaHighlightBuilder extends Builder<AlphaHighlightBuilder> { | |
public constructor() { | |
super(); | |
this.mShimmer.alphaShimmer = true; | |
} | |
protected getThis(): AlphaHighlightBuilder { | |
return this; | |
} | |
} | |
class ColorHighlightBuilder extends Builder<ColorHighlightBuilder> { | |
constructor() { | |
super(); | |
this.mShimmer.alphaShimmer = false; | |
} | |
/** | |
* Sets the highlight color for the shimmer. | |
*/ | |
public setHighlightColor(color: number | string | Color): ColorHighlightBuilder { | |
if (color instanceof Color) { | |
this.mShimmer.highlightColor = color; | |
} else if (typeof color === 'string' || typeof color === 'number') { | |
this.mShimmer.highlightColor = new Color(color as any); | |
} | |
return this.getThis(); | |
} | |
/** | |
* Sets the base color for the shimmer. | |
*/ | |
public setBaseColor(color: number | string | Color): ColorHighlightBuilder { | |
if (color instanceof Color) { | |
this.mShimmer.baseColor = color; | |
} else if (typeof color === 'string' || typeof color === 'number') { | |
this.mShimmer.baseColor = new Color((this.mShimmer.baseColor.android & 0xFF000000) | (new Color(color as any).android & 0x00FFFFFF)); | |
} | |
return this.getThis(); | |
} | |
protected getThis(): ColorHighlightBuilder { | |
return this; | |
} | |
} | |
@NativeClass() | |
class ShimmerDrawable extends android.graphics.drawable.Drawable { | |
private mUpdateListener: android.animation.ValueAnimator.AnimatorUpdateListener; | |
private mShimmerPaint: android.graphics.Paint; | |
private mDrawRect: android.graphics.Rect; | |
private mShaderMatrix: android.graphics.Matrix; | |
mValueAnimator: android.animation.ValueAnimator; | |
mShimmer: TNSShimmer; | |
constructor() { | |
super(); | |
this.init(); | |
this.mShimmerPaint.setAntiAlias(true); | |
(<any>this.mUpdateListener)._owner = new WeakRef(this); | |
return global.__native(this); | |
} | |
init() { | |
this.mUpdateListener = | |
new android.animation.ValueAnimator.AnimatorUpdateListener({ | |
onAnimationUpdate(animation) { | |
this._owner?.get?.().invalidateSelf(); | |
} | |
}); | |
this.mShimmerPaint = new android.graphics.Paint(); | |
this.mDrawRect = new android.graphics.Rect(); | |
this.mShaderMatrix = new android.graphics.Matrix(); | |
} | |
public setShimmer(shimmer?: TNSShimmer) { | |
this.mShimmer = shimmer; | |
if (this.mShimmer !== null) { | |
this.mShimmerPaint.setXfermode( | |
new android.graphics.PorterDuffXfermode( | |
this.mShimmer.alphaShimmer ? android.graphics.PorterDuff.Mode.DST_IN : android.graphics.PorterDuff.Mode.SRC_IN)); | |
} | |
this.updateShader(); | |
this.updateValueAnimator(); | |
this.invalidateSelf(); | |
} | |
public getShimmer() { | |
return this.mShimmer; | |
} | |
/** | |
* Starts the shimmer animation. | |
*/ | |
public startShimmer() { | |
if (this.mValueAnimator != null && !this.isShimmerStarted() && this.getCallback() != null) { | |
this.mValueAnimator.start(); | |
} | |
} | |
/** | |
* Stops the shimmer animation. | |
*/ | |
public stopShimmer() { | |
if (this.mValueAnimator != null && this.isShimmerStarted()) { | |
this.mValueAnimator.cancel(); | |
} | |
} | |
/** | |
* Return whether the shimmer animation has been started. | |
*/ | |
public isShimmerStarted() { | |
return this.mValueAnimator != null && this.mValueAnimator.isStarted(); | |
} | |
public onBoundsChange(bounds: android.graphics.Rect) { | |
super.onBoundsChange(bounds); | |
this.mDrawRect.set(bounds); | |
this.updateShader(); | |
this.maybeStartShimmer(); | |
} | |
public draw(canvas: android.graphics.Canvas) { | |
if (this.mShimmer === null || this.mShimmerPaint.getShader() === null) { | |
return; | |
} | |
const tiltTan = Math.tan(java.lang.Math.toRadians(this.mShimmer.tilt)); | |
const translateHeight = this.mDrawRect.height() + tiltTan * this.mDrawRect.width(); | |
const translateWidth = this.mDrawRect.width() + tiltTan * this.mDrawRect.height(); | |
let dx; | |
let dy; | |
const animatedValue = | |
this.mValueAnimator != null ? this.mValueAnimator.getAnimatedValue() : 0; | |
switch (this.mShimmer.direction) { | |
default: | |
case Direction.LEFT_TO_RIGHT: | |
dx = this.offset(-translateWidth, translateWidth, animatedValue); | |
dy = 0; | |
break; | |
case Direction.RIGHT_TO_LEFT: | |
dx = this.offset(translateWidth, -translateWidth, animatedValue); | |
dy = 0; | |
break; | |
case Direction.TOP_TO_BOTTOM: | |
dx = 0; | |
dy = this.offset(-translateHeight, translateHeight, animatedValue); | |
break; | |
case Direction.BOTTOM_TO_TOP: | |
dx = 0; | |
dy = this.offset(translateHeight, -translateHeight, animatedValue); | |
break; | |
} | |
this.mShaderMatrix.reset(); | |
this.mShaderMatrix.setRotate(this.mShimmer.tilt, this.mDrawRect.width() / 2, this.mDrawRect.height() / 2); | |
this.mShaderMatrix.postTranslate(dx, dy); | |
this.mShimmerPaint.getShader().setLocalMatrix(this.mShaderMatrix); | |
canvas.drawRect(this.mDrawRect, this.mShimmerPaint); | |
} | |
public setAlpha(alpha: number) { | |
// No-op, modify the Shimmer object you pass in instead | |
} | |
public setColorFilter(...args) { | |
// No-op, modify the Shimmer object you pass in instead | |
} | |
public getOpacity() { | |
return this.mShimmer != null && (this.mShimmer.clipToChildren || this.mShimmer.alphaShimmer) | |
? android.graphics.PixelFormat.TRANSLUCENT | |
: android.graphics.PixelFormat.OPAQUE; | |
} | |
private offset(start: number, end: number, percent: number) { | |
return start + (end - start) * percent; | |
} | |
private updateValueAnimator() { | |
if (this.mShimmer == null) { | |
return; | |
} | |
let started: boolean; | |
if (this.mValueAnimator != null) { | |
started = this.mValueAnimator.isStarted(); | |
this.mValueAnimator.cancel(); | |
this.mValueAnimator.removeAllUpdateListeners(); | |
} else { | |
started = false; | |
} | |
const arr = Array.create('float', 2); | |
arr[0] = 0; | |
arr[1] = 1 + (this.mShimmer.repeatDelay / this.mShimmer.animationDuration) | |
this.mValueAnimator = | |
android.animation.ValueAnimator.ofFloat(arr); | |
this.mValueAnimator.setInterpolator(new android.view.animation.LinearInterpolator()); | |
this.mValueAnimator.setRepeatMode(this.mShimmer.repeatMode); | |
this.mValueAnimator.setStartDelay(this.mShimmer.startDelay); | |
this.mValueAnimator.setRepeatCount(this.mShimmer.repeatCount); | |
this.mValueAnimator.setDuration(this.mShimmer.animationDuration + this.mShimmer.repeatDelay); | |
this.mValueAnimator.addUpdateListener(this.mUpdateListener); | |
if (started) { | |
this.mValueAnimator.start(); | |
} | |
} | |
maybeStartShimmer() { | |
if (this.mValueAnimator != null | |
&& !this.mValueAnimator.isStarted() | |
&& this.mShimmer != null | |
&& this.mShimmer.autoStart | |
&& this.getCallback() != null) { | |
this.mValueAnimator.start(); | |
} | |
} | |
private updateShader() { | |
const bounds = this.getBounds(); | |
const boundsWidth = bounds.width(); | |
const boundsHeight = bounds.height(); | |
if (boundsWidth == 0 || boundsHeight == 0 || this.mShimmer == null) { | |
return; | |
} | |
const width = this.mShimmer.width(boundsWidth); | |
const height = this.mShimmer.height(boundsHeight); | |
let shader: android.graphics.Shader; | |
switch (this.mShimmer.shape) { | |
default: | |
case Shape.LINEAR: | |
const vertical = | |
this.mShimmer.direction == Direction.TOP_TO_BOTTOM | |
|| this.mShimmer.direction == Direction.BOTTOM_TO_TOP; | |
const endX = vertical ? 0 : width; | |
const endY = vertical ? height : 0; | |
shader = | |
new android.graphics.LinearGradient( | |
0, 0, endX, endY, this.mShimmer.colors, this.mShimmer.positions, android.graphics.Shader.TileMode.CLAMP); | |
break; | |
case Shape.RADIAL: | |
shader = | |
new android.graphics.RadialGradient( | |
width / 2, | |
height / 2, | |
(Math.max(width, height) / Math.sqrt(2)), | |
this.mShimmer.colors, | |
this.mShimmer.positions, | |
android.graphics.Shader.TileMode.CLAMP); | |
break; | |
} | |
this.mShimmerPaint.setShader(shader); | |
} | |
} | |
@NativeClass() | |
class ShimmerView extends android.widget.FrameLayout { | |
private mContentPaint = new android.graphics.Paint(); | |
private mShimmerDrawable = new ShimmerDrawable(); | |
private mShowShimmer = true; | |
private mStoppedShimmerBecauseVisibility = false; | |
constructor(param0: android.content.Context); | |
constructor(param0: android.content.Context, param1?: android.util.AttributeSet) { | |
super(param0, param1 || null); | |
this.init( | |
param0, param1 || null | |
); | |
return global.__native(this); | |
} | |
private init(context, attrs) { | |
this.setWillNotDraw(false); | |
if (!this.mShimmerDrawable) { | |
this.mShimmerDrawable = new ShimmerDrawable(); | |
} | |
if (!this.mContentPaint) { | |
this.mContentPaint = new android.graphics.Paint(); | |
} | |
this.mShimmerDrawable.setCallback(this); | |
if (attrs == null) { | |
this.setShimmer(new AlphaHighlightBuilder().build()); | |
return; | |
} | |
} | |
public setShimmer(shimmer: TNSShimmer): ShimmerView { | |
this.mShimmerDrawable.setShimmer(shimmer); | |
if (shimmer != null && shimmer.clipToChildren) { | |
this.setLayerType(android.view.View.LAYER_TYPE_HARDWARE, this.mContentPaint); | |
} else { | |
this.setLayerType(android.view.View.LAYER_TYPE_NONE, null); | |
} | |
return this; | |
} | |
getShimmer(): TNSShimmer { | |
return this.mShimmerDrawable.getShimmer(); | |
} | |
private mSpeed = 1100; | |
public setSpeed(speed: number) { | |
if (speed > 0) { | |
this.mSpeed = speed; | |
} | |
if (this.getShimmer() != null) { | |
const builder = new AlphaHighlightBuilder(); | |
builder.copyFrom(this.getShimmer()); | |
builder.setDuration(speed); | |
this.setShimmer(builder.build()); | |
} | |
} | |
public setLightColor(color: number | string | Color) { | |
if (this.getShimmer() != null) { | |
const builder = new AlphaHighlightBuilder(); | |
builder.copyFrom(this.getShimmer()); | |
builder.setHighLightColor(color); | |
this.setShimmer(builder.build()); | |
} | |
} | |
public setDarkColor(color: number | string | Color) { | |
if (this.getShimmer() != null) { | |
const builder = new AlphaHighlightBuilder(); | |
builder.copyFrom(this.getShimmer()); | |
builder.setBaseColor(color); | |
this.setShimmer(builder.build()); | |
} | |
} | |
public start(speed: number, direction: Direction, repeatCount: number, lightColor, blackColor) { | |
if (this.getShimmer() != null) { | |
const builder = new AlphaHighlightBuilder(); | |
builder.copyFrom(this.getShimmer()); | |
builder.setDuration(speed); | |
builder.setDirection(direction); | |
builder.setRepeatCount(repeatCount); | |
builder.setHighLightColor(lightColor); | |
builder.setBaseColor(blackColor); | |
this.setShimmer(builder.build()); | |
} | |
this.showShimmer(true); | |
} | |
/** | |
* Starts the shimmer animation. | |
*/ | |
public startShimmer() { | |
this.mShimmerDrawable.startShimmer(); | |
} | |
/** | |
* Stops the shimmer animation. | |
*/ | |
public stopShimmer() { | |
this.mStoppedShimmerBecauseVisibility = false; | |
this.mShimmerDrawable.stopShimmer(); | |
} | |
/** | |
* Return whether the shimmer animation has been started. | |
*/ | |
public isShimmerStarted() { | |
return this.mShimmerDrawable.isShimmerStarted(); | |
} | |
/** | |
* Sets the ShimmerDrawable to be visible. | |
* | |
* @param startShimmer Whether to start the shimmer again. | |
*/ | |
public showShimmer(startShimmer: boolean) { | |
this.mShowShimmer = true; | |
if (startShimmer) { | |
this.startShimmer(); | |
} | |
this.invalidate(); | |
} | |
/** | |
* Sets the ShimmerDrawable to be invisible, stopping it in the process. | |
*/ | |
public hideShimmer() { | |
this.stopShimmer(); | |
this.mShowShimmer = false; | |
this.invalidate(); | |
} | |
/** | |
* Return whether the shimmer drawable is visible. | |
*/ | |
public isShimmerVisible() { | |
return this.mShowShimmer; | |
} | |
public onLayout(changed: boolean, left: number, top: number, right: number, bottom: number) { | |
super.onLayout(changed, left, top, right, bottom); | |
const width = this.getWidth(); | |
const height = this.getHeight(); | |
this.mShimmerDrawable.setBounds(0, 0, width, height); | |
} | |
onVisibilityChanged(changedView, visibility: number) { | |
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 (this.mShimmerDrawable == null) { | |
return; | |
} | |
if (visibility != ShimmerView.VISIBLE) { | |
// GONE or INVISIBLE | |
if (this.isShimmerStarted()) { | |
this.stopShimmer(); | |
this.mStoppedShimmerBecauseVisibility = true; | |
} | |
} else if (this.mStoppedShimmerBecauseVisibility) { | |
this.mShimmerDrawable.maybeStartShimmer(); | |
this.mStoppedShimmerBecauseVisibility = false; | |
} | |
} | |
public onAttachedToWindow() { | |
super.onAttachedToWindow(); | |
this.mShimmerDrawable.maybeStartShimmer(); | |
} | |
public onDetachedFromWindow() { | |
super.onDetachedFromWindow(); | |
this.stopShimmer(); | |
} | |
public dispatchDraw(canvas: android.graphics.Canvas) { | |
super.dispatchDraw(canvas); | |
if (this.mShowShimmer) { | |
this.mShimmerDrawable.draw(canvas); | |
} | |
} | |
verifyDrawable(who: android.graphics.drawable.Drawable): boolean { | |
return super.verifyDrawable(who) || who === this.mShimmerDrawable; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment