Home | History | Annotate | Download | only in transition
      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.FloatMath;
     27 import android.util.PathParser;
     28 
     29 /**
     30  * A PathMotion that takes a Path pattern and applies it to the separation between two points.
     31  * The starting point of the Path will be moved to the origin and the end point will be scaled
     32  * and rotated so that it matches with the target end point.
     33  * <p>This may be used in XML as an element inside a transition.</p>
     34  * <pre>
     35  * {@code
     36  * &lt;changeBounds>
     37  *     &lt;patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/>
     38  * &lt;/changeBounds>}
     39  * </pre>
     40  */
     41 public class PatternPathMotion extends PathMotion {
     42 
     43     private Path mOriginalPatternPath;
     44 
     45     private final Path mPatternPath = new Path();
     46 
     47     private final Matrix mTempMatrix = new Matrix();
     48 
     49     /**
     50      * Constructs a PatternPathMotion with a straight-line pattern.
     51      */
     52     public PatternPathMotion() {
     53         mPatternPath.lineTo(1, 0);
     54         mOriginalPatternPath = mPatternPath;
     55     }
     56 
     57     public PatternPathMotion(Context context, AttributeSet attrs) {
     58         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PatternPathMotion);
     59         try {
     60             String pathData = a.getString(R.styleable.PatternPathMotion_patternPathData);
     61             if (pathData == null) {
     62                 throw new RuntimeException("pathData must be supplied for patternPathMotion");
     63             }
     64             Path pattern = PathParser.createPathFromPathData(pathData);
     65             setPatternPath(pattern);
     66         } finally {
     67             a.recycle();
     68         }
     69 
     70     }
     71 
     72     /**
     73      * Creates a PatternPathMotion with the Path defining a pattern of motion between two
     74      * coordinates. The pattern will be translated, rotated, and scaled to fit between the start
     75      * and end points. The pattern must not be empty and must have the end point differ from the
     76      * start point.
     77      *
     78      * @param patternPath A Path to be used as a pattern for two-dimensional motion.
     79      */
     80     public PatternPathMotion(Path patternPath) {
     81         setPatternPath(patternPath);
     82     }
     83 
     84     /**
     85      * Returns the Path defining a pattern of motion between two coordinates.
     86      * The pattern will be translated, rotated, and scaled to fit between the start and end points.
     87      * The pattern must not be empty and must have the end point differ from the start point.
     88      *
     89      * @return the Path defining a pattern of motion between two coordinates.
     90      * @attr ref android.R.styleable#PatternPathMotion_patternPathData
     91      */
     92     public Path getPatternPath() {
     93         return mOriginalPatternPath;
     94     }
     95 
     96     /**
     97      * Sets the Path defining a pattern of motion between two coordinates.
     98      * The pattern will be translated, rotated, and scaled to fit between the start and end points.
     99      * The pattern must not be empty and must have the end point differ from the start point.
    100      *
    101      * @param patternPath A Path to be used as a pattern for two-dimensional motion.
    102      * @attr ref android.R.styleable#PatternPathMotion_patternPathData
    103      */
    104     public void setPatternPath(Path patternPath) {
    105         PathMeasure pathMeasure = new PathMeasure(patternPath, false);
    106         float length = pathMeasure.getLength();
    107         float[] pos = new float[2];
    108         pathMeasure.getPosTan(length, pos, null);
    109         float endX = pos[0];
    110         float endY = pos[1];
    111         pathMeasure.getPosTan(0, pos, null);
    112         float startX = pos[0];
    113         float startY = pos[1];
    114 
    115         if (startX == endX && startY == endY) {
    116             throw new IllegalArgumentException("pattern must not end at the starting point");
    117         }
    118 
    119         mTempMatrix.setTranslate(-startX, -startY);
    120         float dx = endX - startX;
    121         float dy = endY - startY;
    122         float distance = distance(dx, dy);
    123         float scale = 1 / distance;
    124         mTempMatrix.postScale(scale, scale);
    125         double angle = Math.atan2(dy, dx);
    126         mTempMatrix.postRotate((float) Math.toDegrees(-angle));
    127         patternPath.transform(mTempMatrix, mPatternPath);
    128         mOriginalPatternPath = patternPath;
    129     }
    130 
    131     @Override
    132     public Path getPath(float startX, float startY, float endX, float endY) {
    133         float dx = endX - startX;
    134         float dy = endY - startY;
    135         float length = distance(dx, dy);
    136         double angle = Math.atan2(dy, dx);
    137 
    138         mTempMatrix.setScale(length, length);
    139         mTempMatrix.postRotate((float) Math.toDegrees(angle));
    140         mTempMatrix.postTranslate(startX, startY);
    141         Path path = new Path();
    142         mPatternPath.transform(mTempMatrix, path);
    143         return path;
    144     }
    145 
    146     private static float distance(float x, float y) {
    147         return FloatMath.sqrt((x * x) + (y * y));
    148     }
    149 }
    150