blob: 41a9f3a1792fa13046d02e146b98b531157937e8 [file] [log] [blame]
package com.airbnb.lottie;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
class RectLayer extends AnimatableLayer {
@Nullable private RoundRectLayer fillLayer;
@Nullable private RoundRectLayer strokeLayer;
RectLayer(RectangleShape rectShape, @Nullable ShapeFill fill,
@Nullable ShapeStroke stroke, Transform transform, Drawable.Callback callback) {
super(callback);
setBounds(transform.getBounds());
setAnchorPoint(transform.getAnchor().createAnimation());
setAlpha(transform.getOpacity().createAnimation());
setPosition(transform.getPosition().createAnimation());
setTransform(transform.getScale().createAnimation());
setRotation(transform.getRotation().createAnimation());
if (fill != null) {
fillLayer = new RoundRectLayer(getCallback());
fillLayer.setColor(fill.getColor().createAnimation());
fillLayer.setShapeAlpha(fill.getOpacity().createAnimation());
fillLayer.setTransformAlpha(transform.getOpacity().createAnimation());
fillLayer.setRectCornerRadius(rectShape.getCornerRadius().createAnimation());
fillLayer.setRectSize(rectShape.getSize().createAnimation());
fillLayer.setRectPosition(rectShape.getPosition().createAnimation());
addLayer(fillLayer);
}
if (stroke != null) {
strokeLayer = new RoundRectLayer(getCallback());
strokeLayer.setIsStroke();
strokeLayer.setColor(stroke.getColor().createAnimation());
strokeLayer.setShapeAlpha(stroke.getOpacity().createAnimation());
strokeLayer.setTransformAlpha(transform.getOpacity().createAnimation());
strokeLayer.setLineWidth(stroke.getWidth().createAnimation());
if (!stroke.getLineDashPattern().isEmpty()) {
List<KeyframeAnimation<Float>> dashPatternAnimations = new ArrayList<>(stroke.getLineDashPattern().size());
for (AnimatableFloatValue dashPattern : stroke.getLineDashPattern()) {
dashPatternAnimations.add(dashPattern.createAnimation());
}
strokeLayer.setDashPattern(dashPatternAnimations, stroke.getDashOffset().createAnimation());
}
strokeLayer.setLineCapType(stroke.getCapType());
strokeLayer.setRectCornerRadius(rectShape.getCornerRadius().createAnimation());
strokeLayer.setRectSize(rectShape.getSize().createAnimation());
strokeLayer.setRectPosition(rectShape.getPosition().createAnimation());
strokeLayer.setLineJoinType(stroke.getJoinType());
addLayer(strokeLayer);
}
}
@Override
public void setAlpha(int alpha) {
super.setAlpha(alpha);
if (fillLayer != null) {
fillLayer.setAlpha(alpha);
}
if (strokeLayer != null) {
strokeLayer.setAlpha(alpha);
}
}
private static class RoundRectLayer extends AnimatableLayer {
private final KeyframeAnimation.AnimationListener<Integer> alphaChangedListener = new KeyframeAnimation.AnimationListener<Integer>() {
@Override
public void onValueChanged(Integer value) {
invalidateSelf();
}
};
private final KeyframeAnimation.AnimationListener<Integer> colorChangedListener = new KeyframeAnimation.AnimationListener<Integer>() {
@Override
public void onValueChanged(Integer value) {
onColorChanged();
}
};
private final KeyframeAnimation.AnimationListener<Float> lineWidthChangedListener = new KeyframeAnimation.AnimationListener<Float>() {
@Override
public void onValueChanged(Float value) {
onLineWidthChanged();
}
};
private final KeyframeAnimation.AnimationListener<Float> dashPatternChangedListener = new KeyframeAnimation.AnimationListener<Float>() {
@Override
public void onValueChanged(Float value) {
onDashPatternChanged();
}
};
private final KeyframeAnimation.AnimationListener<Float> cornerRadiusChangedListener = new KeyframeAnimation.AnimationListener<Float>() {
@Override
public void onValueChanged(Float value) {
invalidateSelf();
}
};
private final KeyframeAnimation.AnimationListener<PointF> rectPositionChangedListener = new KeyframeAnimation.AnimationListener<PointF>() {
@Override
public void onValueChanged(PointF value) {
invalidateSelf();
}
};
private final KeyframeAnimation.AnimationListener<PointF> rectSizeChangedListener = new KeyframeAnimation.AnimationListener<PointF>() {
@Override
public void onValueChanged(PointF value) {
invalidateSelf();
}
};
private final Paint paint = new Paint();
private final RectF fillRect = new RectF();
private KeyframeAnimation<Integer> color;
private KeyframeAnimation<Float> lineWidth;
private KeyframeAnimation<Integer> shapeAlpha;
private KeyframeAnimation<Integer> transformAlpha;
private KeyframeAnimation<Float> rectCornerRadius;
private KeyframeAnimation<PointF> rectPosition;
private KeyframeAnimation<PointF> rectSize;
@Nullable private List<KeyframeAnimation<Float>> lineDashPattern;
@Nullable private KeyframeAnimation<Float> lineDashPatternOffset;
RoundRectLayer(Drawable.Callback callback) {
super(callback);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
}
void setShapeAlpha(KeyframeAnimation<Integer> shapeAlpha) {
if (this.shapeAlpha != null) {
removeAnimation(this.shapeAlpha);
this.shapeAlpha.removeUpdateListener(alphaChangedListener);
}
this.shapeAlpha = shapeAlpha;
addAnimation(shapeAlpha);
shapeAlpha.addUpdateListener(alphaChangedListener);
invalidateSelf();
}
void setTransformAlpha(KeyframeAnimation<Integer> transformAlpha) {
if (this.transformAlpha != null) {
removeAnimation(this.transformAlpha);
this.transformAlpha.removeUpdateListener(alphaChangedListener);
}
this.transformAlpha = transformAlpha;
addAnimation(transformAlpha);
transformAlpha.addUpdateListener(alphaChangedListener);
invalidateSelf();
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public int getAlpha() {
Integer shapeAlpha = this.shapeAlpha == null ? 255 : this.shapeAlpha.getValue();
Integer transformAlpha = this.transformAlpha == null ? 255 : this.transformAlpha.getValue();
int layerAlpha = super.getAlpha();
return (int) ((shapeAlpha / 255f * transformAlpha / 255f * layerAlpha / 255f) * 255);
}
public void setColor(KeyframeAnimation<Integer> color) {
if (this.color != null) {
removeAnimation(this.color);
this.color.removeUpdateListener(colorChangedListener);
}
this.color = color;
addAnimation(color);
color.addUpdateListener(colorChangedListener);
onColorChanged();
}
private void onColorChanged() {
paint.setColor(color.getValue());
invalidateSelf();
}
private void setIsStroke() {
paint.setStyle(Paint.Style.STROKE);
invalidateSelf();
}
void setLineWidth(KeyframeAnimation<Float> lineWidth) {
if (this.lineWidth != null) {
removeAnimation(this.lineWidth);
this.lineWidth.removeUpdateListener(lineWidthChangedListener);
}
this.lineWidth = lineWidth;
addAnimation(lineWidth);
lineWidth.addUpdateListener(lineWidthChangedListener);
onLineWidthChanged();
}
private void onLineWidthChanged() {
paint.setStrokeWidth(lineWidth.getValue());
invalidateSelf();
}
void setDashPattern(List<KeyframeAnimation<Float>> lineDashPattern, KeyframeAnimation<Float> offset) {
if (this.lineDashPattern != null) {
removeAnimation(this.lineDashPattern.get(0));
this.lineDashPattern.get(0).removeUpdateListener(dashPatternChangedListener);
removeAnimation(this.lineDashPattern.get(1));
this.lineDashPattern.get(1).removeUpdateListener(dashPatternChangedListener);
}
if (this.lineDashPatternOffset != null) {
removeAnimation(this.lineDashPatternOffset);
this.lineDashPatternOffset.removeUpdateListener(dashPatternChangedListener);
}
if (lineDashPattern.isEmpty()) {
return;
}
this.lineDashPattern = lineDashPattern;
this.lineDashPatternOffset = offset;
addAnimation(lineDashPattern.get(0));
addAnimation(lineDashPattern.get(1));
lineDashPattern.get(0).addUpdateListener(dashPatternChangedListener);
if (!lineDashPattern.get(1).equals(lineDashPattern.get(1))) {
lineDashPattern.get(1).addUpdateListener(dashPatternChangedListener);
}
addAnimation(offset);
offset.addUpdateListener(dashPatternChangedListener);
onDashPatternChanged();
}
private void onDashPatternChanged() {
if (lineDashPattern == null || lineDashPatternOffset == null) {
throw new IllegalStateException("LineDashPattern is null");
}
float[] values = new float[lineDashPattern.size()];
for (int i = 0; i < lineDashPattern.size(); i++) {
values[i] = lineDashPattern.get(i).getValue();
}
paint.setPathEffect(new DashPathEffect(values, lineDashPatternOffset.getValue()));
invalidateSelf();
}
void setLineCapType(ShapeStroke.LineCapType lineCapType) {
switch (lineCapType) {
case Round:
paint.setStrokeCap(Paint.Cap.ROUND);
break;
case Butt:
paint.setStrokeCap(Paint.Cap.BUTT);
default:
}
}
void setLineJoinType(ShapeStroke.LineJoinType lineJoinType) {
switch (lineJoinType) {
case Bevel:
paint.setStrokeJoin(Paint.Join.BEVEL);
break;
case Miter:
paint.setStrokeJoin(Paint.Join.MITER);
break;
case Round:
paint.setStrokeJoin(Paint.Join.ROUND);
break;
}
}
void setRectCornerRadius(KeyframeAnimation<Float> rectCornerRadius) {
if (this.rectCornerRadius != null) {
removeAnimation(rectCornerRadius);
this.rectCornerRadius.removeUpdateListener(cornerRadiusChangedListener);
}
this.rectCornerRadius = rectCornerRadius;
addAnimation(rectCornerRadius);
rectCornerRadius.addUpdateListener(cornerRadiusChangedListener);
invalidateSelf();
}
void setRectPosition(KeyframeAnimation<PointF> rectPosition) {
if (this.rectPosition != null) {
removeAnimation(this.rectPosition);
this.rectPosition.removeUpdateListener(rectPositionChangedListener);
}
this.rectPosition = rectPosition;
addAnimation(rectPosition);
rectPosition.addUpdateListener(rectPositionChangedListener);
invalidateSelf();
}
void setRectSize(KeyframeAnimation<PointF> rectSize) {
if (this.rectSize != null) {
removeAnimation(this.rectSize);
this.rectSize.removeUpdateListener(rectSizeChangedListener);
}
this.rectSize = rectSize;
addAnimation(rectSize);
rectSize.addUpdateListener(rectSizeChangedListener);
invalidateSelf();
}
@SuppressLint("NewApi")
@Override
public void draw(@NonNull Canvas canvas) {
if (paint.getStyle() == Paint.Style.STROKE && paint.getStrokeWidth() == 0f) {
return;
}
paint.setAlpha(getAlpha());
float halfWidth = rectSize.getValue().x / 2f;
float halfHeight = rectSize.getValue().y / 2f;
fillRect.set(rectPosition.getValue().x - halfWidth,
rectPosition.getValue().y - halfHeight,
rectPosition.getValue().x + halfWidth,
rectPosition.getValue().y + halfHeight);
if (rectCornerRadius.getValue() == 0) {
canvas.drawRect(fillRect, paint);
} else {
canvas.drawRoundRect(fillRect, rectCornerRadius.getValue(), rectCornerRadius.getValue(), paint);
}
}
}
}