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 17 package androidx.constraintlayout.motion.widget; 18 19 import static androidx.constraintlayout.widget.ConstraintSet.BASELINE; 20 import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM; 21 import static androidx.constraintlayout.widget.ConstraintSet.END; 22 import static androidx.constraintlayout.widget.ConstraintSet.HORIZONTAL; 23 import static androidx.constraintlayout.widget.ConstraintSet.LEFT; 24 import static androidx.constraintlayout.widget.ConstraintSet.RIGHT; 25 import static androidx.constraintlayout.widget.ConstraintSet.START; 26 import static androidx.constraintlayout.widget.ConstraintSet.TOP; 27 import static androidx.constraintlayout.widget.ConstraintSet.VERTICAL; 28 import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT; 29 30 import android.util.Log; 31 import android.util.Pair; 32 import android.view.View; 33 import android.view.ViewGroup; 34 35 import androidx.constraintlayout.widget.ConstraintSet; 36 37 import java.util.HashMap; 38 import java.util.Objects; 39 40 /** 41 * Utility class to manipulate MotionLayout from the layout editor 42 * 43 * 44 */ 45 public class DesignTool { 46 47 static final HashMap<Pair<Integer, Integer>, String> sAllAttributes = new HashMap<>(); 48 static final HashMap<String, String> sAllMargins = new HashMap<>(); 49 private static final boolean DEBUG = false; 50 private static final boolean DO_NOT_USE = false; 51 private static final String TAG = "DesignTool"; 52 53 static { Pair.create(BOTTOM, BOTTOM)54 sAllAttributes.put(Pair.create(BOTTOM, BOTTOM), "layout_constraintBottom_toBottomOf"); Pair.create(BOTTOM, TOP)55 sAllAttributes.put(Pair.create(BOTTOM, TOP), "layout_constraintBottom_toTopOf"); Pair.create(TOP, BOTTOM)56 sAllAttributes.put(Pair.create(TOP, BOTTOM), "layout_constraintTop_toBottomOf"); Pair.create(TOP, TOP)57 sAllAttributes.put(Pair.create(TOP, TOP), "layout_constraintTop_toTopOf"); Pair.create(START, START)58 sAllAttributes.put(Pair.create(START, START), "layout_constraintStart_toStartOf"); Pair.create(START, END)59 sAllAttributes.put(Pair.create(START, END), "layout_constraintStart_toEndOf"); Pair.create(END, START)60 sAllAttributes.put(Pair.create(END, START), "layout_constraintEnd_toStartOf"); Pair.create(END, END)61 sAllAttributes.put(Pair.create(END, END), "layout_constraintEnd_toEndOf"); Pair.create(LEFT, LEFT)62 sAllAttributes.put(Pair.create(LEFT, LEFT), "layout_constraintLeft_toLeftOf"); Pair.create(LEFT, RIGHT)63 sAllAttributes.put(Pair.create(LEFT, RIGHT), "layout_constraintLeft_toRightOf"); Pair.create(RIGHT, RIGHT)64 sAllAttributes.put(Pair.create(RIGHT, RIGHT), "layout_constraintRight_toRightOf"); Pair.create(RIGHT, LEFT)65 sAllAttributes.put(Pair.create(RIGHT, LEFT), "layout_constraintRight_toLeftOf"); Pair.create(BASELINE, BASELINE)66 sAllAttributes.put(Pair.create(BASELINE, BASELINE), 67 "layout_constraintBaseline_toBaselineOf"); 68 69 sAllMargins.put("layout_constraintBottom_toBottomOf", "layout_marginBottom"); 70 sAllMargins.put("layout_constraintBottom_toTopOf", "layout_marginBottom"); 71 sAllMargins.put("layout_constraintTop_toBottomOf", "layout_marginTop"); 72 sAllMargins.put("layout_constraintTop_toTopOf", "layout_marginTop"); 73 sAllMargins.put("layout_constraintStart_toStartOf", "layout_marginStart"); 74 sAllMargins.put("layout_constraintStart_toEndOf", "layout_marginStart"); 75 sAllMargins.put("layout_constraintEnd_toStartOf", "layout_marginEnd"); 76 sAllMargins.put("layout_constraintEnd_toEndOf", "layout_marginEnd"); 77 sAllMargins.put("layout_constraintLeft_toLeftOf", "layout_marginLeft"); 78 sAllMargins.put("layout_constraintLeft_toRightOf", "layout_marginLeft"); 79 sAllMargins.put("layout_constraintRight_toRightOf", "layout_marginRight"); 80 sAllMargins.put("layout_constraintRight_toLeftOf", "layout_marginRight"); 81 } 82 83 private final MotionLayout mMotionLayout; 84 private MotionScene mSceneCache; 85 private String mLastStartState = null; 86 private String mLastEndState = null; 87 private int mLastStartStateId = -1; 88 private int mLastEndStateId = -1; 89 DesignTool(MotionLayout motionLayout)90 public DesignTool(MotionLayout motionLayout) { 91 mMotionLayout = motionLayout; 92 } 93 getPxFromDp(int dpi, String value)94 private static int getPxFromDp(int dpi, String value) { 95 if (value == null) { 96 return 0; 97 } 98 int index = value.indexOf('d'); 99 if (index == -1) { 100 return 0; 101 } 102 String filteredValue = value.substring(0, index); 103 int dpValue = (int) (Integer.valueOf(filteredValue) * dpi / 160f); 104 return dpValue; 105 } 106 connect(int dpi, ConstraintSet set, View view, HashMap<String, String> attributes, int from, int to)107 private static void connect(int dpi, 108 ConstraintSet set, 109 View view, 110 HashMap<String, String> attributes, 111 int from, 112 int to) { 113 String connection = sAllAttributes.get(Pair.create(from, to)); 114 String connectionValue = attributes.get(connection); 115 116 if (connectionValue != null) { 117 int marginValue = 0; 118 String margin = sAllMargins.get(connection); 119 if (margin != null) { 120 marginValue = getPxFromDp(dpi, attributes.get(margin)); 121 } 122 int id = Integer.parseInt(connectionValue); 123 set.connect(view.getId(), from, id, to, marginValue); 124 } 125 } 126 setBias(ConstraintSet set, View view, HashMap<String, String> attributes, int type)127 private static void setBias(ConstraintSet set, 128 View view, 129 HashMap<String, String> attributes, 130 int type) { 131 String bias = "layout_constraintHorizontal_bias"; 132 if (type == VERTICAL) { 133 bias = "layout_constraintVertical_bias"; 134 } 135 String biasValue = attributes.get(bias); 136 if (biasValue != null) { 137 if (type == HORIZONTAL) { 138 set.setHorizontalBias(view.getId(), Float.parseFloat(biasValue)); 139 } else if (type == VERTICAL) { 140 set.setVerticalBias(view.getId(), Float.parseFloat(biasValue)); 141 } 142 } 143 } 144 setDimensions(int dpi, ConstraintSet set, View view, HashMap<String, String> attributes, int type)145 private static void setDimensions(int dpi, 146 ConstraintSet set, 147 View view, 148 HashMap<String, String> attributes, 149 int type) { 150 String dimension = "layout_width"; 151 if (type == VERTICAL) { 152 dimension = "layout_height"; 153 } 154 String dimensionValue = attributes.get(dimension); 155 if (dimensionValue != null) { 156 int value = WRAP_CONTENT; 157 if (!dimensionValue.equalsIgnoreCase("wrap_content")) { 158 value = getPxFromDp(dpi, dimensionValue); 159 } 160 if (type == HORIZONTAL) { 161 set.constrainWidth(view.getId(), value); 162 } else { 163 set.constrainHeight(view.getId(), value); 164 } 165 } 166 } 167 setAbsolutePositions(int dpi, ConstraintSet set, View view, HashMap<String, String> attributes)168 private static void setAbsolutePositions(int dpi, 169 ConstraintSet set, 170 View view, 171 HashMap<String, String> attributes) { 172 String absoluteX = attributes.get("layout_editor_absoluteX"); 173 if (absoluteX != null) { 174 set.setEditorAbsoluteX(view.getId(), getPxFromDp(dpi, absoluteX)); 175 } 176 String absoluteY = attributes.get("layout_editor_absoluteY"); 177 if (absoluteY != null) { 178 set.setEditorAbsoluteY(view.getId(), getPxFromDp(dpi, absoluteY)); 179 } 180 } 181 182 /** 183 * Get the center point of the animation path of a view 184 * 185 * @param view view to getMap the animation of 186 * @param path array to be filled (x1,y1,x2,y2...) 187 * @param len the desired number of point along animation 188 * @return -1 if not under and animation 0 if not animated or number of point along animation 189 */ getAnimationPath(Object view, float[] path, int len)190 public int getAnimationPath(Object view, float[] path, int len) { 191 if (mMotionLayout.mScene == null) { 192 return -1; 193 } 194 195 MotionController motionController = mMotionLayout.mFrameArrayList.get(view); 196 if (motionController == null) { 197 return 0; 198 } 199 200 motionController.buildPath(path, len); 201 return len; 202 } 203 204 /** 205 * Get the center point of the animation path of a view 206 * 207 * @param view view to getMap the animation of 208 * @param path array to be filled (in groups of 8) (x1,y1,x2,y2...) 209 */ getAnimationRectangles(Object view, float[] path)210 public void getAnimationRectangles(Object view, float[] path) { 211 if (mMotionLayout.mScene == null) { 212 return; 213 } 214 int duration = mMotionLayout.mScene.getDuration(); 215 int frames = duration / 16; 216 217 MotionController motionController = mMotionLayout.mFrameArrayList.get(view); 218 if (motionController == null) { 219 return; 220 } 221 222 motionController.buildRectangles(path, frames); 223 } 224 225 /** 226 * Get the location of the start end and key frames 227 * 228 * @param view the view to track 229 * @param key array to be filled 230 * @return number of key frames + 2 231 */ getAnimationKeyFrames(Object view, float[] key)232 public int getAnimationKeyFrames(Object view, float[] key) { 233 if (mMotionLayout.mScene == null) { 234 return -1; 235 } 236 int duration = mMotionLayout.mScene.getDuration(); 237 int frames = duration / 16; 238 239 MotionController motionController = mMotionLayout.mFrameArrayList.get(view); 240 if (motionController == null) { 241 return 0; 242 } 243 244 motionController.buildKeyFrames(key, null); 245 return frames; 246 } 247 248 /** 249 * @param position 250 * 251 */ setToolPosition(float position)252 public void setToolPosition(float position) { 253 if (mMotionLayout.mScene == null) { 254 mMotionLayout.mScene = mSceneCache; 255 } 256 mMotionLayout.setProgress(position); 257 mMotionLayout.evaluate(true); 258 mMotionLayout.requestLayout(); 259 mMotionLayout.invalidate(); 260 } 261 262 // @TODO: add description 263 264 /** 265 * 266 * @return 267 */ getStartState()268 public String getStartState() { 269 int startId = mMotionLayout.getStartState(); 270 if (mLastStartStateId == startId) { 271 return mLastStartState; 272 } 273 String last = mMotionLayout.getConstraintSetNames(startId); 274 275 if (last != null) { 276 mLastStartState = last; 277 mLastStartStateId = startId; 278 } 279 return mMotionLayout.getConstraintSetNames(startId); 280 } 281 282 // @TODO: add description 283 284 /** 285 * 286 * @return 287 */ getEndState()288 public String getEndState() { 289 int endId = mMotionLayout.getEndState(); 290 291 if (mLastEndStateId == endId) { 292 return mLastEndState; 293 } 294 String last = mMotionLayout.getConstraintSetNames(endId); 295 if (last != null) { 296 mLastEndState = last; 297 mLastEndStateId = endId; 298 } 299 return last; 300 } 301 302 /** 303 * Return the current progress of the current transition 304 * 305 * @return current transition's progress 306 */ getProgress()307 public float getProgress() { 308 return mMotionLayout.getProgress(); 309 } 310 311 /** 312 * Return the current state (ConstraintSet id) as a string 313 * 314 * @return the last state set via the design tool bridge 315 */ getState()316 public String getState() { 317 if (mLastStartState != null && mLastEndState != null) { 318 float progress = getProgress(); 319 float epsilon = 0.01f; 320 if (progress <= epsilon) { 321 return mLastStartState; 322 } else if (progress >= 1 - epsilon) { 323 return mLastEndState; 324 } 325 } 326 return mLastStartState; 327 } 328 329 /** 330 * This sets the constraint set based on a string. (without the "@+id/") 331 * 332 * @param id 333 */ setState(String id)334 public void setState(String id) { 335 if (id == null) { 336 id = "motion_base"; 337 } 338 if (Objects.equals(mLastStartState, id)) { 339 return; 340 } 341 342 if (DEBUG) { 343 System.out.println("================================"); 344 dumpConstraintSet(id); 345 } 346 347 mLastStartState = id; 348 mLastEndState = null; 349 if (id == null && DO_NOT_USE) { // going to base layout 350 if (mMotionLayout.mScene != null) { 351 mSceneCache = mMotionLayout.mScene; 352 mMotionLayout.mScene = null; 353 } 354 355 mMotionLayout.setProgress(0); 356 mMotionLayout.requestLayout(); 357 } 358 359 if (mMotionLayout.mScene == null) { 360 mMotionLayout.mScene = mSceneCache; 361 } 362 363 int rscId = mMotionLayout.lookUpConstraintId(id); 364 mLastStartStateId = rscId; 365 366 if (rscId != 0) { 367 if (rscId == mMotionLayout.getStartState()) { 368 mMotionLayout.setProgress(0); 369 } else if (rscId == mMotionLayout.getEndState()) { 370 mMotionLayout.setProgress(1); 371 } else { 372 mMotionLayout.transitionToState(rscId); 373 mMotionLayout.setProgress(1); 374 } 375 } 376 mMotionLayout.requestLayout(); 377 } 378 379 /** 380 * Utility method, returns true if we are currently in a transition 381 * 382 * @return true if in a transition, false otherwise 383 */ isInTransition()384 public boolean isInTransition() { 385 return mLastStartState != null && mLastEndState != null; 386 } 387 388 /** 389 * This sets the constraint set based on a string. (without the "@+id/") 390 * 391 * @param start 392 * @param end 393 */ setTransition(String start, String end)394 public void setTransition(String start, String end) { 395 if (mMotionLayout.mScene == null) { 396 mMotionLayout.mScene = mSceneCache; 397 } 398 int startId = mMotionLayout.lookUpConstraintId(start); 399 int endId = mMotionLayout.lookUpConstraintId(end); 400 401 mMotionLayout.setTransition(startId, endId); 402 mLastStartStateId = startId; 403 mLastEndStateId = endId; 404 405 mLastStartState = start; 406 mLastEndState = end; 407 } 408 409 /** 410 * this allow disabling autoTransitions to prevent design surface 411 * from being in undefined states 412 * 413 * @param disable 414 */ disableAutoTransition(boolean disable)415 public void disableAutoTransition(boolean disable) { 416 mMotionLayout.disableAutoTransition(disable); 417 } 418 419 /** 420 * Gets the time of the currently set animation. 421 * 422 * @return time in Milliseconds 423 */ getTransitionTimeMs()424 public long getTransitionTimeMs() { 425 return mMotionLayout.getTransitionTimeMs(); 426 } 427 428 /** 429 * Get the keyFrames for the view controlled by this MotionController. 430 * The call is designed to be efficient because it will be called 30x Number of views a second 431 * 432 * @param view the view to return keyframe positions 433 * @param type is pos(0-100) + 1000*mType(1=Attrib, 2=Position, 3=TimeCycle 4=Cycle 5=Trigger 434 * @param pos the x&y position of the keyFrame along the path 435 * @return Number of keyFrames found 436 */ getKeyFramePositions(Object view, int[] type, float[] pos)437 public int getKeyFramePositions(Object view, int[] type, float[] pos) { 438 MotionController controller = mMotionLayout.mFrameArrayList.get((View) view); 439 if (controller == null) { 440 return 0; 441 } 442 return controller.getKeyFramePositions(type, pos); 443 } 444 445 /** 446 * Get the keyFrames for the view controlled by this MotionController. 447 * The call is designed to be efficient because it will be called 30x Number of views a second 448 * 449 * @param view the view to return keyframe positions 450 * @param type if type is -1, skip all keyframes with type != -1 451 * @param info array to fill with info on each keyframe 452 * @return Number of keyFrames found 453 */ getKeyFrameInfo(Object view, int type, int[] info)454 public int getKeyFrameInfo(Object view, int type, int[] info) { 455 MotionController controller = mMotionLayout.mFrameArrayList.get((View) view); 456 if (controller == null) { 457 return 0; 458 } 459 return controller.getKeyFrameInfo(type, info); 460 } 461 462 /** 463 * @param view 464 * @param type 465 * @param x 466 * @param y 467 * @return 468 * 469 */ getKeyFramePosition(Object view, int type, float x, float y)470 public float getKeyFramePosition(Object view, int type, float x, float y) { 471 if (!(view instanceof View)) { 472 return 0f; 473 } 474 475 MotionController mc = mMotionLayout.mFrameArrayList.get((View) view); 476 if (mc == null) { 477 return 0f; 478 } 479 480 return mc.getKeyFrameParameter(type, x, y); 481 } 482 483 /** 484 * @param view 485 * @param position 486 * @param name 487 * @param value 488 * 489 */ setKeyFrame(Object view, int position, String name, Object value)490 public void setKeyFrame(Object view, int position, String name, Object value) { 491 if (DEBUG) { 492 Log.v(TAG, "setKeyFrame " + position + " <" + name + "> " + value); 493 } 494 if (mMotionLayout.mScene != null) { 495 mMotionLayout.mScene.setKeyframe((View) view, position, name, value); 496 mMotionLayout.mTransitionGoalPosition = position / 100f; 497 mMotionLayout.mTransitionLastPosition = 0; 498 mMotionLayout.rebuildScene(); 499 mMotionLayout.evaluate(true); 500 } 501 } 502 503 /** 504 * Move the widget directly 505 * 506 * @param view 507 * @param position 508 * @param type 509 * @param x 510 * @param y 511 * @return 512 * 513 */ setKeyFramePosition(Object view, int position, int type, float x, float y)514 public boolean setKeyFramePosition(Object view, int position, int type, float x, float y) { 515 if (!(view instanceof View)) { 516 return false; 517 } 518 519 if (mMotionLayout.mScene != null) { 520 MotionController controller = mMotionLayout.mFrameArrayList.get(view); 521 position = (int) (mMotionLayout.mTransitionPosition * 100); 522 if (controller != null 523 && mMotionLayout.mScene.hasKeyFramePosition((View) view, position)) { 524 float fx = controller.getKeyFrameParameter(MotionController.HORIZONTAL_PATH_X, 525 x, y); 526 float fy = controller.getKeyFrameParameter(MotionController.VERTICAL_PATH_Y, 527 x, y); 528 // TODO: supports path relative 529 mMotionLayout.mScene.setKeyframe((View) view, position, "motion:percentX", fx); 530 mMotionLayout.mScene.setKeyframe((View) view, position, "motion:percentY", fy); 531 mMotionLayout.rebuildScene(); 532 mMotionLayout.evaluate(true); 533 mMotionLayout.invalidate(); 534 return true; 535 } 536 } 537 return false; 538 } 539 540 /** 541 * @param view 542 * @param debugMode 543 * 544 */ setViewDebug(Object view, int debugMode)545 public void setViewDebug(Object view, int debugMode) { 546 if (!(view instanceof View)) { 547 return; 548 } 549 550 MotionController motionController = mMotionLayout.mFrameArrayList.get(view); 551 if (motionController != null) { 552 motionController.setDrawPath(debugMode); 553 mMotionLayout.invalidate(); 554 } 555 } 556 557 /** 558 * This is a general access to systems in the MotionLayout System 559 * This provides a series of commands used by the designer to access needed logic 560 * It is written this way to minimize the interface between the library and designer. 561 * It allows the logic to be kept only in the library not replicated in the gui builder. 562 * It also allows us to understand understand the version of MotionLayout in use 563 * commands 564 * 0 return the version number 565 * 1 Get the center point of the animation path of a view 566 * 2 Get the location of the start end and key frames 567 * 568 * @param cmd this provide the command needed 569 * @param type support argument for command 570 * @param viewObject if this command references a view this provides access 571 * @param in this allows for an array of float to be the input to the system 572 * @param inLength this provides the length of the input 573 * @param out this provide the output array 574 * @param outLength the length of the output array 575 * @return command dependent -1 is typically an error (do not understand) 576 */ designAccess(int cmd, String type, Object viewObject, float[] in, int inLength, float[] out, int outLength)577 public int designAccess(int cmd, String type, Object viewObject, 578 float[] in, int inLength, float[] out, int outLength) { 579 View view = (View) viewObject; 580 MotionController motionController = null; 581 if (cmd != 0) { 582 if (mMotionLayout.mScene == null) { 583 return -1; 584 } 585 586 if (view != null) { // Can't find the view 587 motionController = mMotionLayout.mFrameArrayList.get(view); 588 if (motionController == null) { 589 return -1; 590 } 591 } else { // currently only cmd == 0 does not require a motion view 592 return -1; 593 } 594 595 } 596 switch (cmd) { 597 case 0: // version 598 return 1; 599 case 1: { // get View path 600 601 int duration = mMotionLayout.mScene.getDuration(); 602 int frames = duration / 16; 603 604 motionController.buildPath(out, frames); 605 return frames; 606 } 607 case 2: { // get key frames 608 609 int duration = mMotionLayout.mScene.getDuration(); 610 int frames = duration / 16; 611 612 motionController.buildKeyFrames(out, null); 613 return frames; 614 } 615 case 3: { // get Attribute 616 617 int duration = mMotionLayout.mScene.getDuration(); 618 @SuppressWarnings("unused") int frames = duration / 16; 619 620 return motionController.getAttributeValues(type, out, outLength); 621 } 622 623 default: 624 return -1; 625 626 } 627 } 628 629 // @TODO: add description 630 631 /** 632 * 633 * @param type 634 * @param target 635 * @param position 636 * @return 637 */ getKeyframe(int type, int target, int position)638 public Object getKeyframe(int type, int target, int position) { 639 if (mMotionLayout.mScene == null) { 640 return null; 641 } 642 return mMotionLayout.mScene.getKeyFrame(mMotionLayout.getContext(), type, target, position); 643 } 644 645 // @TODO: add description 646 647 /** 648 * 649 * @param viewObject 650 * @param x 651 * @param y 652 * @return 653 */ getKeyframeAtLocation(Object viewObject, float x, float y)654 public Object getKeyframeAtLocation(Object viewObject, float x, float y) { 655 View view = (View) viewObject; 656 MotionController motionController = null; 657 if (mMotionLayout.mScene == null) { 658 return -1; 659 } 660 if (view != null) { // Can't find the view 661 motionController = mMotionLayout.mFrameArrayList.get(view); 662 if (motionController == null) { 663 return null; 664 } 665 } else { 666 return null; 667 } 668 ViewGroup viewGroup = ((ViewGroup) view.getParent()); 669 int layoutWidth = viewGroup.getWidth(); 670 int layoutHeight = viewGroup.getHeight(); 671 return motionController.getPositionKeyframe(layoutWidth, layoutHeight, x, y); 672 } 673 674 // @TODO: add description 675 676 /** 677 * 678 * @param keyFrame 679 * @param view 680 * @param x 681 * @param y 682 * @param attribute 683 * @param value 684 * @return 685 */ getPositionKeyframe(Object keyFrame, Object view, float x, float y, String[] attribute, float[] value)686 public Boolean getPositionKeyframe(Object keyFrame, 687 Object view, 688 float x, 689 float y, 690 String[] attribute, 691 float[] value) { 692 if (keyFrame instanceof KeyPositionBase) { 693 KeyPositionBase key = (KeyPositionBase) keyFrame; 694 MotionController motionController = mMotionLayout.mFrameArrayList.get((View) view); 695 motionController.positionKeyframe((View) view, key, x, y, attribute, value); 696 mMotionLayout.rebuildScene(); 697 mMotionLayout.mInTransition = true; 698 return true; 699 } 700 return false; 701 } 702 703 // @TODO: add description 704 705 /** 706 * 707 * @param view 708 * @param type 709 * @param position 710 * @return 711 */ getKeyframe(Object view, int type, int position)712 public Object getKeyframe(Object view, int type, int position) { 713 if (mMotionLayout.mScene == null) { 714 return null; 715 } 716 int target = ((View) view).getId(); 717 return mMotionLayout.mScene.getKeyFrame(mMotionLayout.getContext(), type, target, position); 718 } 719 720 // @TODO: add description 721 722 /** 723 * 724 * @param keyFrame 725 * @param tag 726 * @param value 727 */ setKeyframe(Object keyFrame, String tag, Object value)728 public void setKeyframe(Object keyFrame, String tag, Object value) { 729 if (keyFrame instanceof Key) { 730 Key key = (Key) keyFrame; 731 key.setValue(tag, value); 732 mMotionLayout.rebuildScene(); 733 mMotionLayout.mInTransition = true; 734 } 735 } 736 737 /** 738 * Live setting of attributes on a view 739 * 740 * @param dpi dpi used by the application 741 * @param constraintSetId ConstraintSet id 742 * @param opaqueView the Android View we operate on, passed as an Object 743 * @param opaqueAttributes the list of attributes (hash<string,string>) we pass to the view 744 */ setAttributes(int dpi, String constraintSetId, Object opaqueView, Object opaqueAttributes)745 public void setAttributes(int dpi, 746 String constraintSetId, 747 Object opaqueView, 748 Object opaqueAttributes) { 749 View view = (View) opaqueView; 750 751 @SuppressWarnings("unchecked") 752 HashMap<String, String> attributes = (opaqueAttributes instanceof HashMap) ? 753 (HashMap<String, String>) opaqueAttributes : new HashMap<>(); 754 755 int rscId = mMotionLayout.lookUpConstraintId(constraintSetId); 756 ConstraintSet set = mMotionLayout.mScene.getConstraintSet(rscId); 757 758 if (DEBUG) { 759 Log.v(TAG, "constraintSetId = " + constraintSetId + " " + rscId); 760 } 761 762 if (set == null) { 763 return; 764 } 765 766 set.clear(view.getId()); 767 768 setDimensions(dpi, set, view, attributes, HORIZONTAL); 769 setDimensions(dpi, set, view, attributes, VERTICAL); 770 771 connect(dpi, set, view, attributes, START, START); 772 connect(dpi, set, view, attributes, START, END); 773 connect(dpi, set, view, attributes, END, END); 774 connect(dpi, set, view, attributes, END, START); 775 connect(dpi, set, view, attributes, LEFT, LEFT); 776 connect(dpi, set, view, attributes, LEFT, RIGHT); 777 connect(dpi, set, view, attributes, RIGHT, RIGHT); 778 connect(dpi, set, view, attributes, RIGHT, LEFT); 779 connect(dpi, set, view, attributes, TOP, TOP); 780 connect(dpi, set, view, attributes, TOP, BOTTOM); 781 connect(dpi, set, view, attributes, BOTTOM, TOP); 782 connect(dpi, set, view, attributes, BOTTOM, BOTTOM); 783 connect(dpi, set, view, attributes, BASELINE, BASELINE); 784 785 setBias(set, view, attributes, HORIZONTAL); 786 setBias(set, view, attributes, VERTICAL); 787 788 setAbsolutePositions(dpi, set, view, attributes); 789 790 mMotionLayout.updateState(rscId, set); 791 mMotionLayout.requestLayout(); 792 } 793 794 // @TODO: add description 795 796 /** 797 * 798 * @param set 799 */ dumpConstraintSet(String set)800 public void dumpConstraintSet(String set) { 801 if (mMotionLayout.mScene == null) { 802 mMotionLayout.mScene = mSceneCache; 803 } 804 int setId = mMotionLayout.lookUpConstraintId(set); 805 System.out.println(" dumping " + set + " (" + setId + ")"); 806 try { 807 mMotionLayout.mScene.getConstraintSet(setId).dump(mMotionLayout.mScene); 808 } catch (Exception ex) { 809 Log.e(TAG, "Error while dumping: " + set + " (" + setId + ")", ex); 810 } 811 } 812 } 813