1 /*
2  * Copyright (C) 2018 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 androidx.constraintlayout.utils.widget;
17 
18 import android.content.Context;
19 import android.content.res.TypedArray;
20 import android.graphics.Canvas;
21 import android.graphics.Color;
22 import android.graphics.Matrix;
23 import android.graphics.Paint;
24 import android.util.AttributeSet;
25 import android.view.ViewParent;
26 
27 import androidx.constraintlayout.motion.widget.MotionLayout;
28 import androidx.constraintlayout.widget.R;
29 
30 import org.jspecify.annotations.NonNull;
31 
32 /**
33  * A view that is useful for prototyping Views that will move in MotionLayout. <b>Added in 2.0</b>
34  * <p>
35  * This view works with MotionLayout to demonstrate the motion of 25 points on the view.
36  * It is based on MockView which draws a label (by default the view id),
37  * along with diagonals.
38  *
39  * Useful as a deeper understanding of the motion of a view in a MotionLayout
40  *
41  * </p>
42  */
43 public class MotionTelltales extends MockView {
44     private static final String TAG = "MotionTelltales";
45     private Paint mPaintTelltales = new Paint();
46     MotionLayout mMotionLayout;
47     float[] mVelocity = new float[2];
48     Matrix mInvertMatrix = new Matrix();
49     int mVelocityMode = MotionLayout.VELOCITY_POST_LAYOUT;
50     int mTailColor = Color.MAGENTA;
51     float mTailScale = 0.25f;
52 
MotionTelltales(Context context)53     public MotionTelltales(Context context) {
54         super(context);
55         init(context, null);
56     }
57 
MotionTelltales(Context context, AttributeSet attrs)58     public MotionTelltales(Context context, AttributeSet attrs) {
59         super(context, attrs);
60         init(context, attrs);
61     }
62 
MotionTelltales(Context context, AttributeSet attrs, int defStyleAttr)63     public MotionTelltales(Context context, AttributeSet attrs, int defStyleAttr) {
64         super(context, attrs, defStyleAttr);
65         init(context, attrs);
66     }
67 
init(Context context, AttributeSet attrs)68     private void init(Context context, AttributeSet attrs) {
69         if (attrs != null) {
70             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MotionTelltales);
71             final int count = a.getIndexCount();
72             for (int i = 0; i < count; i++) {
73                 int attr = a.getIndex(i);
74                 if (attr == R.styleable.MotionTelltales_telltales_tailColor) {
75                     mTailColor = a.getColor(attr, mTailColor);
76                 } else if (attr == R.styleable.MotionTelltales_telltales_velocityMode) {
77                     mVelocityMode = a.getInt(attr, mVelocityMode);
78                 } else if (attr == R.styleable.MotionTelltales_telltales_tailScale) {
79                     mTailScale = a.getFloat(attr, mTailScale);
80                 }
81             }
82             a.recycle();
83         }
84         mPaintTelltales.setColor(mTailColor);
85         mPaintTelltales.setStrokeWidth(5);
86     }
87 
88     @Override
onAttachedToWindow()89     protected void onAttachedToWindow() {
90         super.onAttachedToWindow();
91 
92     }
93 
94     /**
95      * set the text
96      * @param text
97      */
setText(CharSequence text)98     public void setText(CharSequence text) {
99         mText = text.toString();
100         requestLayout();
101     }
102 
103     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)104     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
105         super.onLayout(changed, left, top, right, bottom);
106         postInvalidate();
107     }
108 
109     @Override
onDraw(@onNull Canvas canvas)110     public void onDraw(@NonNull Canvas canvas) {
111         super.onDraw(canvas);
112         Matrix matrix = getMatrix();
113         matrix.invert(mInvertMatrix);
114         if (mMotionLayout == null) {
115             ViewParent vp = getParent();
116             if (vp instanceof MotionLayout) {
117                 mMotionLayout = (MotionLayout) vp;
118             }
119             return;
120         }
121         int width = getWidth();
122         int height = getHeight();
123         float[] f = {0.1f, 0.25f, 0.5f, 0.75f, 0.9f};
124         for (int y = 0; y < f.length; y++) {
125             float py = f[y];
126             for (int x = 0; x < f.length; x++) {
127                 float px = f[x];
128                 mMotionLayout.getViewVelocity(this, px, py, mVelocity, mVelocityMode);
129                 mInvertMatrix.mapVectors(mVelocity);
130 
131                 float sx = (width * px);
132                 float sy = (height * py);
133                 float ex = sx - (mVelocity[0] * mTailScale);
134                 float ey = sy - (mVelocity[1] * mTailScale);
135                 mInvertMatrix.mapVectors(mVelocity);
136                 canvas.drawLine(sx, sy, ex, ey, mPaintTelltales);
137             }
138 
139         }
140     }
141 }
142