如何使用共享元素转换来缩放textviews?

我能够通过使用ActivityOptions.makeSceneTransitionAnimation使TextViews在两个活动之间完美转换。 不过,我希望在文本过渡时扩大文本的大小。 我可以看到在接触卡转换中放大文本“Alphonso Engelking”的材料设计示例 。

我已经尝试在目标TextView上设置比例属性,并使用changeTransform共享元素转换,但不会缩放,文本在转换时会截断。

如何使用共享元素转换缩放TextViews?

您可以创建一个自定义的过渡动画TextView的文本大小,如下所示:

 public class TextSizeTransition extends Transition { private static final String PROPNAME_TEXT_SIZE = "alexjlockwood:transition:textsize"; private static final String[] TRANSITION_PROPERTIES = { PROPNAME_TEXT_SIZE }; private static final Property<TextView, Float> TEXT_SIZE_PROPERTY = new Property<TextView, Float>(Float.class, "textSize") { @Override public Float get(TextView textView) { return textView.getTextSize(); } @Override public void set(TextView textView, Float textSizePixels) { textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePixels); } }; public TextSizeTransition() { } public TextSizeTransition(Context context, AttributeSet attrs) { super(context, attrs); } @Override public String[] getTransitionProperties() { return TRANSITION_PROPERTIES; } @Override public void captureStartValues(TransitionValues transitionValues) { captureValues(transitionValues); } @Override public void captureEndValues(TransitionValues transitionValues) { captureValues(transitionValues); } private void captureValues(TransitionValues transitionValues) { if (transitionValues.view instanceof TextView) { TextView textView = (TextView) transitionValues.view; transitionValues.values.put(PROPNAME_TEXT_SIZE, textView.getTextSize()); } } @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { if (startValues == null || endValues == null) { return null; } Float startSize = (Float) startValues.values.get(PROPNAME_TEXT_SIZE); Float endSize = (Float) endValues.values.get(PROPNAME_TEXT_SIZE); if (startSize == null || endSize == null || startSize.floatValue() == endSize.floatValue()) { return null; } TextView view = (TextView) endValues.view; view.setTextSize(TypedValue.COMPLEX_UNIT_PX, startSize); return ObjectAnimator.ofFloat(view, TEXT_SIZE_PROPERTY, startSize, endSize); } } 

由于改变TextView的文本大小会导致其布局范围在动画过程中发生变化,因此,只需将ChangeBounds转换到同一个TransitionSet ,就可以使转换正常工作。 您需要做的是在SharedElementCallback手动测量/布置视图的最终状态。

我已经在GitHub上发布了一个示例项目,说明了这个概念(请注意,项目定义了两个Gradle产品风格…一个使用活动转换,另一个使用片段转换)。

我使用了Alex Lockwood的解决方案,并简化了它的使用(它仅适用于TextView的TextSize),我希望这会有所帮助:

 public class Activity2 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity2); EnterSharedElementTextSizeHandler handler = new EnterSharedElementTextSizeHandler(this); handler.addTextViewSizeResource((TextView) findViewById(R.id.timer), R.dimen.small_text_size, R.dimen.large_text_size); } } 

和类EnterSharedElementTextSizeHandler:

 public class EnterSharedElementTextSizeHandler extends SharedElementCallback { private final TransitionSet mTransitionSet; private final Activity mActivity; public Map<TextView, Pair<Integer, Integer>> textViewList = new HashMap<>(); public EnterSharedElementTextSizeHandler(Activity activity) { mActivity = activity; Transition transitionWindow = activity.getWindow().getSharedElementEnterTransition(); if (!(transitionWindow instanceof TransitionSet)) { mTransitionSet = new TransitionSet(); mTransitionSet.addTransition(transitionWindow); } else { mTransitionSet = (TransitionSet) transitionWindow; } activity.setEnterSharedElementCallback(this); } public void addTextViewSizeResource(TextView tv, int sizeBegin, int sizeEnd) { Resources res = mActivity.getResources(); addTextView(tv, res.getDimensionPixelSize(sizeBegin), res.getDimensionPixelSize(sizeEnd)); } public void addTextView(TextView tv, int sizeBegin, int sizeEnd) { Transition textSize = new TextSizeTransition(); textSize.addTarget(tv.getId()); textSize.addTarget(tv.getText().toString()); mTransitionSet.addTransition(textSize); textViewList.put(tv, new Pair<>(sizeBegin, sizeEnd)); } @Override public void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) { for (View v : sharedElements) { if (!textViewList.containsKey(v)) { continue; } ((TextView) v).setTextSize(TypedValue.COMPLEX_UNIT_PX, textViewList.get(v).first); } } @Override public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) { for (View v : sharedElements) { if (!textViewList.containsKey(v)) { continue; } TextView textView = (TextView) v; // Record the TextView's old width/height. int oldWidth = textView.getMeasuredWidth(); int oldHeight = textView.getMeasuredHeight(); // Setup the TextView's end values. textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textViewList.get(v).second); // Re-measure the TextView (since the text size has changed). int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); textView.measure(widthSpec, heightSpec); // Record the TextView's new width/height. int newWidth = textView.getMeasuredWidth(); int newHeight = textView.getMeasuredHeight(); // Layout the TextView in the center of its container, accounting for its new width/height. int widthDiff = newWidth - oldWidth; int heightDiff = newHeight - oldHeight; textView.layout(textView.getLeft() - widthDiff / 2, textView.getTop() - heightDiff / 2, textView.getRight() + widthDiff / 2, textView.getBottom() + heightDiff / 2); } } } 

如果查看ChangeBounds工作方式,它将在视图的左/右/上/下属性上进行操作。

我期望的是,您需要在两个活动中使用相同的文本大小,并在已启动的活动中使用scaleXscaleY属性来根据需要修改文本大小。 然后,在TransitionSet使用ChangeBoundsChangeTransform的组合。

这在Google I / O 2016会谈之一中有所介绍。 在这里可以找到你可以复制到你的代码的转换源。 如果你的IDE抱怨addTarget(TextView.class); 需要API 21,只是删除构造函数,并动态或在您的XML添加目标。

即(注意这是在Kotlin)

 val textResizeTransition = TextResize().addTarget(view.findViewById(R.id.text_view)) 
Interesting Posts