Weli收藏的文章Android动画Android_Speak

Android 共享元素效果

2017-05-08  本文已影响1173人  chauI

Transition

Transition 框架是 Android 4.4 KitKat 中加入的,但在 5.0 才开始被人应用起来,
而且这一部分也涉及了 22.0 的 API,虽然有对应的 support.v4 包,但也还是有点问题。
所以这一部分可以说是 5.0 以上适用的方法。

效果(录制出来的效果有点卡顿):

共享元素效果图
<style name="AppTheme.custom">
      <item name="colorControlHighlight">@color/ControlHighlight</item>
      <item name="android:windowIsTranslucent">true</item>
      <item name="android:windowContentTransitions">true</item>
</style>

这里遇到一点小问题,即上述 Activity 引用的 style 中不仅设置了 android:windowIsTranslucent,也设置了 android:windowIsTranslucent : 让 Activity 的背景为透明,在我测试的时候发现使用共享元素的时候出现了返回时闪屏的现象,解决方法是设置 Activity 背景颜色为透明。
onCreate 中:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getActivity().getWindow().setBackgroundDrawableResource(R.color.transparent);
}

或者在上述的 style 中的 theme 添加:

<item name="android:windowBackground">@android:color/transparent</item>
<LinearLayout>
      <ImageView
          android:id="@+id/img"
          android:transitionName="testImg"
          android:scaleType="centerCrop"
          android:layout_width="match_parent"
          android:layout_height="160dp" />
      ...
</LinearLayout>

在打开的 Activity 的 xml 中

<LinearLayout>
      <ImageView
          android:id="@+id/item_img"
          android:transitionName="testImg"
          android:scaleType="centerCrop"
          android:layout_width="match_parent"
          android:layout_height="380dp" />
      ...
</LinearLayout>

对应的 ImageView 中的android:transitionName属性值必须相同,而对两个控件的大小、id 等属性并无要求。

Intent intent = new Intent(getActivity(),SecondActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat
        .makeSceneTransitionAnimation(getActivity(),
                mImgView,"testImg");
startActivity(intent,options.toBundle());

makeSceneTransitionAnimation 传入的参数中,mImgView 是第一个界面中 ImageView 的实例,第三个参数对应 xml 中的 android:transitionName 的值。

@Override
public void onBackPressed() {
    supportFinishAfterTransition();
}

因为打开新的 Activity 的时候,可能要去加载新的图片,这时候我们需要延迟过渡动画的开始,直到图片加载完成之后再开始动画。否则会出现各种 bug。
所以要在第二个 Activity 中的 onCreate() 中阻止动画的执行:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 延迟共享动画的执行
    postponeEnterTransition();
}

然后在图片加载完成后开始动画:

Glide.with(this)
        .load(data.getImage())
        .priority(Priority.HIGH)
        .diskCacheStrategy(DiskCacheStrategy.ALL)
        .into(new GlideDrawableImageViewTarget(mImageView){
            @Override
            public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
                super.onResourceReady(resource, animation);
                //图片加载完成的回调中,启动过渡动画
                supportStartPostponedEnterTransition();
            }
        });

当然,启动动画不一定要等待图片加载完成再进行,因为还存在着图片加载失败、加载时间过长等问题,这里只是提出一种方法,实际还是自己看情况决定。

以上只是简单的实现了一种效果,关于 Transition 的使用、共享元素在
Fragment 中的使用、多个共享元素的使用等,在这里暂时不打算细讲,可以参考:
使用 Transition FrameWork 实现有意义的转场动画(译)
(译)Android 5.0 页面共享元素过渡
(需科学上网)定义定制动画

利用动画效果实现

Android 5.0 以下兼容共享元素效果图
<style name="AppTheme.custom">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>
public static class ImageBean implements Parcelable {

    private String FilePath;
    private String FileName;
    private Boolean IsSelect;
    private int viewLeft;
    private int viewTop;
    private int viewHeight;
    private int viewWidth;

    public int getViewLeft() {
        return viewLeft;
    }
    public void setViewLeft(int viewLeft) {
        this.viewLeft = viewLeft;
    }
    public int getViewTop() {
        return viewTop;
    }
    public void setViewTop(int viewTop) {
        this.viewTop = viewTop;
    }
    public int getViewHeight() {
        return viewHeight;
    }
    public void setViewHeight(int viewHeight) {
        this.viewHeight = viewHeight;
    }
    public int getViewWidth() {
        return viewWidth;
    }
    public void setViewWidth(int viewWidth) {
        this.viewWidth = viewWidth;
    }
    public ImageBean(int left,int top,int height,int width){
        viewLeft = left;
        viewTop = top;
        viewHeight = height;
        viewWidth = width;
    }
    public ImageBean(String p, String f){
        FilePath = p;
        FileName = f;
        IsSelect = false;
    }
    public String getFilePath() {
        return FilePath;
    }
    public void setFilePath(String filePath) {
        FilePath = filePath;
    }
    public String getFileName() {
        return FileName;
    }
    public void setFileName(String fileName) {
        FileName = fileName;
    }
    public Boolean getSelect() {
        return IsSelect;
    }
    public void setSelect(Boolean select) {
        IsSelect = select;
    }
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.FilePath);
        dest.writeString(this.FileName);
        dest.writeValue(this.IsSelect);
        dest.writeInt(this.viewLeft);
        dest.writeInt(this.viewTop);
        dest.writeInt(this.viewHeight);
        dest.writeInt(this.viewWidth);
    }
    protected ImageBean(Parcel in) {
        this.FilePath = in.readString();
        this.FileName = in.readString();
        this.IsSelect = (Boolean) in.readValue(Boolean.class.getClassLoader());
        this.viewLeft = in.readInt();
        this.viewTop = in.readInt();
        this.viewHeight = in.readInt();
        this.viewWidth = in.readInt();
    }
    public static final Parcelable.Creator<ImageBean> CREATOR = new Parcelable.Creator<ImageBean>() {
        @Override
        public ImageBean createFromParcel(Parcel source) {
            return new ImageBean(source);
        }
        @Override
        public ImageBean[] newArray(int size) {
            return new ImageBean[size];
        }
    };
}
 private ImageBean img2Location(ImageView imageView,String path){

    int[] location = new int[2];
    imageView.getLocationOnScreen(location);
    ImageBean bean = new ImageBean(
            location[0],location[1],
            imageView.getHeight(),imageView.getWidth());
    bean.setFilePath(path);

    return bean;
}
String path = "..";
ImageView imageView = ...;
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("location",img2Location(imageView,path));
startActivity(intent);
public void initView(@Nullable final View view) {
    getActivity().setTheme(R.style.translucent);
    Glide.with(getActivity()).load(bean.getFilePath())
            .placeholder(R.drawable.black_place_holder).into(mPhotoView);    
    //获取位置数据
    Intent intent = getIntent();
    ImageBean bean = intent.getParcelableExtra("image");
    int mOriginLeft = bean.getViewLeft();
    int mOriginTop = bean.getViewTop();
    int mOriginHeight = bean.getViewHeight();
    int mOriginWidth = bean.getViewWidth();
}
private void finishPhotoViewWithTap(int left,int top){

    //动画的执行时间
    int ANIMATOR_DURATION = 500;

    //控制 x 轴上的移动,将 ImageView 移动到对应的位置
    ValueAnimator translateXAnimator = ValueAnimator.ofFloat(0, left);
    translateXAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            //用 setX 移动控件
            mPhotoView.setX((Float) valueAnimator.getAnimatedValue());
        }
    });
    translateXAnimator.setDuration(ANIMATOR_DURATION);
    translateXAnimator.start();

    ///控制 y 轴上的移动,并监听动画的结束,动画结束时关闭该 Activity
    ValueAnimator translateYAnimator = ValueAnimator.ofFloat(0, top);
    translateYAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            mPhotoView.setY((Float) valueAnimator.getAnimatedValue());
        }
    });
    translateYAnimator.setDuration(ANIMATOR_DURATION);
    translateYAnimator.start();
    translateXAnimator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {
            //动画结束的回调
            animation.removeAllListeners();
            finishActivity();
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });

    //控制缩放的 Animation
    Animation scaleAnimation = new ScaleAnimation(1.0f,0.2f,1.0f,0.2f
            ,left,top);
    //控制透明度的 Animation
    Animation alphaAnimation = new AlphaAnimation(1.0f,0f);

    //用 AnimationSet 将以上两个 Animation 结合到一起
    AnimationSet set = new AnimationSet(true);
    set.setDuration(ANIMATOR_DURATION);
    set.setFillAfter(true);
    set.addAnimation(scaleAnimation);
    set.addAnimation(alphaAnimation);
    mPhotoView.startAnimation(set);
}

原理大概就是如此,实现动画的方法有很多种。
可以参考:
Activity 共享元素转场动画实践
Android共享元素转场动画兼容实践

上一篇下一篇

猜你喜欢

热点阅读