| package com.airbnb.lottie; |
| |
| import android.graphics.Canvas; |
| import android.graphics.Matrix; |
| import android.graphics.Path; |
| import android.support.annotation.Nullable; |
| import android.util.Log; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| class ContentGroup implements DrawingContent, PathContent, |
| BaseKeyframeAnimation.AnimationListener { |
| private static final String TAG = ContentGroup.class.getSimpleName(); |
| private final Matrix matrix = new Matrix(); |
| private final Path path = new Path(); |
| |
| private final List<Content> contents = new ArrayList<>(); |
| private final LottieDrawable lottieDrawable; |
| @Nullable private List<PathContent> pathContents; |
| @Nullable private TransformKeyframeAnimation transformAnimation; |
| |
| ContentGroup(final LottieDrawable lottieDrawable, BaseLayer layer, ShapeGroup shapeGroup) { |
| this.lottieDrawable = lottieDrawable; |
| List<Object> items = shapeGroup.getItems(); |
| if (items.isEmpty()) { |
| return; |
| } |
| |
| Object potentialTransform = items.get(items.size() - 1); |
| if (potentialTransform instanceof AnimatableTransform) { |
| transformAnimation = ((AnimatableTransform) potentialTransform).createAnimation(); |
| //noinspection ConstantConditions |
| transformAnimation.addAnimationsToLayer(layer); |
| transformAnimation.addListener(this); |
| } |
| |
| for (int i = 0; i < items.size(); i++) { |
| Object item = items.get(i); |
| if (item instanceof ShapeFill) { |
| contents.add(new FillContent(lottieDrawable, layer, (ShapeFill) item)); |
| } else if (item instanceof GradientFill) { |
| contents.add(new GradientFillContent(lottieDrawable, layer, (GradientFill) item)); |
| } else if (item instanceof ShapeStroke) { |
| contents.add(new StrokeContent(lottieDrawable, layer, (ShapeStroke) item)); |
| } else if (item instanceof ShapeGroup) { |
| contents.add(new ContentGroup(lottieDrawable, layer, (ShapeGroup) item)); |
| } else if (item instanceof RectangleShape) { |
| contents.add(new RectangleContent(lottieDrawable, layer, (RectangleShape) item)); |
| } else if (item instanceof CircleShape) { |
| contents.add(new EllipseContent(lottieDrawable, layer, (CircleShape) item)); |
| } else if (item instanceof ShapePath) { |
| contents.add(new ShapeContent(lottieDrawable, layer, (ShapePath) item)); |
| } else if (item instanceof PolystarShape) { |
| contents.add(new PolystarContent(lottieDrawable, layer, (PolystarShape) item)); |
| } else if (item instanceof ShapeTrimPath) { |
| contents.add(new TrimPathContent(layer, (ShapeTrimPath) item)); |
| } else //noinspection StatementWithEmptyBody |
| if (item instanceof MergePaths) { |
| if (lottieDrawable.enableMergePathsForKitKatAndAbove()) { |
| contents.add(new MergePathsContent((MergePaths) item)); |
| } else { |
| Log.w(TAG, "Animation contains merge paths but they are disabled."); |
| } |
| } |
| } |
| |
| List<Content> contentsToRemove = new ArrayList<>(); |
| MergePathsContent currentMergePathsContent = null; |
| for (int i = contents.size() - 1; i >= 0; i--) { |
| Content content = contents.get(i); |
| if (content instanceof MergePathsContent) { |
| currentMergePathsContent = (MergePathsContent) content; |
| } |
| if (currentMergePathsContent != null && content != currentMergePathsContent) { |
| currentMergePathsContent.addContentIfNeeded(content); |
| contentsToRemove.add(content); |
| } |
| } |
| |
| Iterator<Content> it = contents.iterator(); |
| while (it.hasNext()) { |
| Content content = it.next(); |
| if (contentsToRemove.contains(content)) { |
| it.remove(); |
| } |
| } |
| } |
| |
| @Override public void onValueChanged() { |
| lottieDrawable.invalidateSelf(); |
| } |
| |
| @Override public void setContents(List<Content> contentsBefore, List<Content> contentsAfter) { |
| // Do nothing with contents after. |
| List<Content> myContentsBefore = new ArrayList<>(contentsBefore.size() + contents.size()); |
| myContentsBefore.addAll(contentsBefore); |
| |
| for (int i = contents.size() - 1; i >= 0; i--) { |
| Content content = contents.get(i); |
| content.setContents(myContentsBefore, contents.subList(0, i)); |
| myContentsBefore.add(content); |
| } |
| } |
| |
| List<PathContent> getPathList() { |
| if (pathContents == null) { |
| pathContents = new ArrayList<>(); |
| for (int i = 0; i < contents.size(); i++) { |
| Content content = contents.get(i); |
| if (content instanceof PathContent) { |
| pathContents.add((PathContent) content); |
| } |
| } |
| } |
| return pathContents; |
| } |
| |
| Matrix getTransformationMatrix() { |
| if (transformAnimation != null) { |
| return transformAnimation.getMatrix(); |
| } |
| matrix.reset(); |
| return matrix; |
| } |
| |
| @Override public Path getPath() { |
| // TODO: cache this somehow. |
| matrix.reset(); |
| if (transformAnimation != null) { |
| matrix.set(transformAnimation.getMatrix()); |
| } |
| path.reset(); |
| for (int i = contents.size() - 1; i >= 0; i--) { |
| Content content = contents.get(i); |
| if (content instanceof PathContent) { |
| path.addPath(((PathContent) content).getPath(), matrix); |
| } |
| } |
| return path; |
| } |
| |
| @Override public void draw(Canvas canvas, Matrix parentMatrix, int parentAlpha) { |
| matrix.set(parentMatrix); |
| int alpha; |
| if (transformAnimation != null) { |
| matrix.preConcat(transformAnimation.getMatrix()); |
| alpha = |
| (int) ((transformAnimation.getOpacity().getValue() / 100f * parentAlpha / 255f) * 255); |
| } else { |
| alpha = parentAlpha; |
| } |
| |
| |
| for (int i = contents.size() - 1; i >= 0; i--) { |
| Object content = contents.get(i); |
| if (content instanceof DrawingContent) { |
| ((DrawingContent) content).draw(canvas, matrix, alpha); |
| } |
| } |
| } |
| } |