谷歌Demo效果展示

android元素共享 android 共享元素动画_android元素共享


改进后效果展示

android元素共享 android 共享元素动画_自定义_02

官方调用

这里不分析源码是怎么实现的,因为我还没彻底搞懂……
所以只简单贴一下调用方法
从ActivityA启动ActivityB时设置共享元素imageview_item和textview_name
ActivityA.java

Intent intent = new Intent(MainActivity.this, DetailActivity.class);
            intent.putExtra(DetailActivity.EXTRA_PARAM_ID, item.getId());
            ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(
                    MainActivity.this,
					new Pair<>(view.findViewById(R.id.imageview_item),
                            DetailActivity.VIEW_NAME_HEADER_IMAGE),
                    new Pair<>(view.findViewById(R.id.textview_name),
                            DetailActivity.VIEW_NAME_HEADER_TITLE)
            );
            ActivityCompat.startActivity(MainActivity.this, intent, activityOptions.toBundle());

同样在ActivityB中也设置这两个共享元素

private ImageView mHeaderImageView;
    private TextView mHeaderTitle;
    @Override
    protected void onCreated(Bundle saveInstanceState){
    	...
		HeaderImageView = findViewById(R.id.imageview_header);
        mHeaderTitle = findViewById(R.id.textview_title);	
        ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
        ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);
        ...
    }

就这么简单。具体代码去看谷歌Demo

但是因为Demo中的textview这个共享元素在两个页面的字体不一样,所以过度很突兀

页面跳转共享元素动画实际上是先加载好ActivityB的textview,然后开始动画,所以看到在进入动画的时候,textview的字体样式突然变成了ActivityB的样式

自定义Transition

ActivityA的代码不变
ActivityB的代码略作修改

private ImageView mHeaderImageView;
private TextView mHeaderTitle;

@Override
protected void onCreate(Bundle savedInstanceState) {
	...
	mHeaderImageView = findViewById(R.id.imageview_header);
	mHeaderTitle = findViewById(R.id.textview_title);
	
	ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
	ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);
	// 配置启动动画的属性
	configEnterExitAnimation(true);
	// 推迟动画启动
	postponeEnterTransition();
	... // 这里加载所需的数据
	// 数据加载好后启动动画
	startPostponedEnterTransition();
}

配置enter和exit的动画属性

private void configEnterExitAnimation(boolean isEnter) {
	// 文字大小和颜色动画
	TextviewTransition textviewTransition = new TextviewTransition(isEnter);
	textviewTransition.addTarget(R.id.textview_title);
	// 文字位置动画
	ChangeBounds textviewChangeBounds = new ChangeBounds();
	textviewChangeBounds.addTarget(R.id.textview_title);
	// ImageView位置动画
	ChangeBounds imageChangeBounds = new ChangeBounds();
	imageChangeBounds.addTarget(VIEW_NAME_HEADER_IMAGE);
	
	TransitionSet set = new TransitionSet()
	   .addTransition(textviewTransition)
	   .addTransition(textviewChangeBounds)
	   .addTransition(imageChangeBounds)
	   .setDuration(1500); // 这里把动画时长调成1.5s,过度效果更明显
	
	this.getWindow().setSharedElementEnterTransition(set);
}

自定义的TextView的Transition类,用来管理文字颜色和大小的动画过度

class TextviewTransition extends Transition {

	private boolean mIsEnter = true;	

    public TextviewTransition(boolean isEnter) {
        mIsEnter = isEnter;
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        if (startValues == null || endValues == null) {
            return null;
        }
        return createEnterAnimator(startValues, endValues);
    }

    private Animator createEnterAnimator(TransitionValues startValues, TransitionValues endValues) {
    	// 文字大小过度动画
        ObjectAnimator textSizeAnimator = ObjectAnimator.ofFloat((TextView) startValues.view,
                new Property<TextView, Float>(Float.class, "textSize") {
                    @Override
                    public void set(TextView object, Float value) {
                        object.setTextSize(TypedValue.COMPLEX_UNIT_PX, value);
                    }

                    @Override
                    public Float get(TextView object) {
                        return object.getTextSize();
                    }
                },
                mIsEnter ? getResources().getDimensionPixelOffset(R.dimen.text_size_start) :
                        getResources().getDimensionPixelOffset(R.dimen.text_size_end),
                mIsEnter ? getResources().getDimensionPixelOffset(R.dimen.text_size_end) :
                        getResources().getDimensionPixelOffset(R.dimen.text_size_start)

        );
		// 文字颜色过度动画
        ObjectAnimator textColorAnimator = ObjectAnimator.ofArgb((TextView) startValues.view,
                new Property<TextView, Integer>(Integer.class, "textColor") {
                    @Override
                    public void set(TextView object, Integer value) {
                        object.setTextColor(value);
                    }

                    @Override
                    public Integer get(TextView object) {
                        return object.getCurrentTextColor();
                    }
                },
                mIsEnter ? getResources().getColor(R.color.start_color, null) :
                        getResources().getColor(R.color.end_color, null),
                mIsEnter ? getResources().getColor(R.color.end_color, null) :
                        getResources().getColor(R.color.start_color, null)
        );

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(textSizeAnimator, textColorAnimator);
        return animatorSet;
    }
}

简单分析下就是TextviewTransition类中包含了两个动画,一个是TextView的大小,另一个是TextView的颜色
然后整体的页面过度动画TransitionSet又包含三个:TextView大小颜色动画(TextviewTransition)、TextView大小和位置动画ImageView大小和位置动画。这样就能在谷歌Demo的基础上让文字过度更加自然。(这里没有处理字重过度的问题 ,感兴趣可以去拓展)

ChangeBounds: View的大小与位置动画
ChangeTransform: View的缩放与旋转动画
ChangeClipBounds: View的裁剪区域(View.getClipBounds())动画
ChangeScroll: 处理View的scrollX与scrollY属性
ChangeImageTransform: 处理ImageView的ScaleType属性效果不是很完美(包括字重过度、动画启动结束时TextView内容换行问题),但已经实现了TextView大小和动画的过度。