• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.transition;
17 
18 import com.android.internal.R;
19 
20 import android.content.Context;
21 import android.content.res.TypedArray;
22 import android.graphics.Matrix;
23 import android.graphics.Path;
24 import android.graphics.PathMeasure;
25 import android.util.AttributeSet;
26 import android.util.PathParser;
27 
28 /**
29  * A PathMotion that takes a Path pattern and applies it to the separation between two points.
30  * The starting point of the Path will be moved to the origin and the end point will be scaled
31  * and rotated so that it matches with the target end point.
32  * <p>This may be used in XML as an element inside a transition.</p>
33  * <pre>
34  * {@code
35  * &lt;changeBounds>
36  *     &lt;patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/>
37  * &lt;/changeBounds>}
38  * </pre>
39  */
40 public class PatternPathMotion extends PathMotion {
41 
42     private Path mOriginalPatternPath;
43 
44     private final Path mPatternPath = new Path();
45 
46     private final Matrix mTempMatrix = new Matrix();
47 
48     /**
49      * Constructs a PatternPathMotion with a straight-line pattern.
50      */
PatternPathMotion()51     public PatternPathMotion() {
52         mPatternPath.lineTo(1, 0);
53         mOriginalPatternPath = mPatternPath;
54     }
55 
PatternPathMotion(Context context, AttributeSet attrs)56     public PatternPathMotion(Context context, AttributeSet attrs) {
57         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PatternPathMotion);
58         try {
59             String pathData = a.getString(R.styleable.PatternPathMotion_patternPathData);
60             if (pathData == null) {
61                 throw new RuntimeException("pathData must be supplied for patternPathMotion");
62             }
63             Path pattern = PathParser.createPathFromPathData(pathData);
64             setPatternPath(pattern);
65         } finally {
66             a.recycle();
67         }
68 
69     }
70 
71     /**
72      * Creates a PatternPathMotion with the Path defining a pattern of motion between two
73      * coordinates. The pattern will be translated, rotated, and scaled to fit between the start
74      * and end points. The pattern must not be empty and must have the end point differ from the
75      * start point.
76      *
77      * @param patternPath A Path to be used as a pattern for two-dimensional motion.
78      */
PatternPathMotion(Path patternPath)79     public PatternPathMotion(Path patternPath) {
80         setPatternPath(patternPath);
81     }
82 
83     /**
84      * Returns the Path defining a pattern of motion between two coordinates.
85      * The pattern will be translated, rotated, and scaled to fit between the start and end points.
86      * The pattern must not be empty and must have the end point differ from the start point.
87      *
88      * @return the Path defining a pattern of motion between two coordinates.
89      * @attr ref android.R.styleable#PatternPathMotion_patternPathData
90      */
getPatternPath()91     public Path getPatternPath() {
92         return mOriginalPatternPath;
93     }
94 
95     /**
96      * Sets the Path defining a pattern of motion between two coordinates.
97      * The pattern will be translated, rotated, and scaled to fit between the start and end points.
98      * The pattern must not be empty and must have the end point differ from the start point.
99      *
100      * @param patternPath A Path to be used as a pattern for two-dimensional motion.
101      * @attr ref android.R.styleable#PatternPathMotion_patternPathData
102      */
setPatternPath(Path patternPath)103     public void setPatternPath(Path patternPath) {
104         PathMeasure pathMeasure = new PathMeasure(patternPath, false);
105         float length = pathMeasure.getLength();
106         float[] pos = new float[2];
107         pathMeasure.getPosTan(length, pos, null);
108         float endX = pos[0];
109         float endY = pos[1];
110         pathMeasure.getPosTan(0, pos, null);
111         float startX = pos[0];
112         float startY = pos[1];
113 
114         if (startX == endX && startY == endY) {
115             throw new IllegalArgumentException("pattern must not end at the starting point");
116         }
117 
118         mTempMatrix.setTranslate(-startX, -startY);
119         float dx = endX - startX;
120         float dy = endY - startY;
121         float distance = (float) Math.hypot(dx, dy);
122         float scale = 1 / distance;
123         mTempMatrix.postScale(scale, scale);
124         double angle = Math.atan2(dy, dx);
125         mTempMatrix.postRotate((float) Math.toDegrees(-angle));
126         patternPath.transform(mTempMatrix, mPatternPath);
127         mOriginalPatternPath = patternPath;
128     }
129 
130     @Override
getPath(float startX, float startY, float endX, float endY)131     public Path getPath(float startX, float startY, float endX, float endY) {
132         double dx = endX - startX;
133         double dy = endY - startY;
134         float length = (float) Math.hypot(dx, dy);
135         double angle = Math.atan2(dy, dx);
136 
137         mTempMatrix.setScale(length, length);
138         mTempMatrix.postRotate((float) Math.toDegrees(angle));
139         mTempMatrix.postTranslate(startX, startY);
140         Path path = new Path();
141         mPatternPath.transform(mTempMatrix, path);
142         return path;
143     }
144 
145 }
146