Android 5.0 SharedElement 简单应用分析(adp-activity-transitions)

我不生成水,只是大自然的搬运工
就是干
好久没写点东西了,最近看到了一个转场动画比较酷炫,今天就来简单滴分析一下。
先看下今天的效果图这里写图片描述
分析下效果: 进入详情页的时候有共享元素,圆形动画,文字部分的上移动画,源码地址是 https://github.com/alexjlockwood/adp-activity-transitions

今天看到的2个类似的库:

https://github.com/naman14/PlayAnimations
https://github.com/hitherejoe/animate

对于5.0的系统要实现转场动画很容易,几句代码分分钟的事情。比如要实现一个简单的转场动画简单的过渡动画,ActivityA里面有一个RecylerView里面的item是一些图片,点击之后跳转到ActivityB里面去查看图片详情。
ActivityA里面的代码如下:

 public void gotoDetail2() {
        Activity activity = TestActivity.this;

        ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                new Pair(imageView2, DetailActivity.IMAGE_TRANSITION_NAME)
        );
        Intent intent = new Intent(activity, DetailActivity.class);
        intent.putExtra(DetailActivity.EXTRA_IMAGE_URL1, R.mipmap.ic_launcher);
        intent.putExtra(DetailActivity.EXTRA_IMAGE_URL2, IMAGE_URL2);
        ActivityCompat.startActivity(activity, intent, options.toBundle());
    }

ActivityB里面的代码如下:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);

        imageView = (ImageView) findViewById(R.id.iv_detail);
        int imageResId = getIntent().getIntExtra(EXTRA_IMAGE_URL1, 0);
        String imageUrl = getIntent().getStringExtra(EXTRA_IMAGE_URL2);
        LGlideUtils.loadImgByUrlForDetail(this, imageUrl, imageView);
        // 这里的IMAGE_TRANSITION_NAME需要与跳转进来使用的transitionName一致
        ViewCompat.setTransitionName(imageView, IMAGE_TRANSITION_NAME);
    }

这样就转场就很容易实现了。。。
但是比如要求在详情界面里面使用的Viewpager来显示所有的图片,从主页进入到详情页,详情页里面滑动了图片,点击返回键回到主页的时候要求滑动到对应的位置,如果不是很明白请参考qq 里面的消息界面点击图片查看详情,只是qq的返回消息界面的时候判断了一下如果在详情页的图片不在消息界面显示的可见范围内就不向上或者向下滚动而已。。。
其实这个实现也很简单。在详情页里面做下操作:

  @Override
    public void finishAfterTransition() {
        Intent data = new Intent();
        data.putExtra("index", 250);
        setResult(RESULT_OK, data);
        super.finishAfterTransition();
    }

这里把当前的图片的索引位置传递给主界面,然后再主界面里面做一些操作:

   private Bundle transitionState;

    private void testListener() {
        setExitSharedElementCallback(new SharedElementCallback() {
            @Override
            public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
                super.onMapSharedElements(names, sharedElements);
                if (transitionState != null) {
                    int index = transitionState.getInt("index", 0);
                    Log.d(TAG, "onMapSharedElements");
                    transitionState = null;
                }
            }
        });
    }

    @Override
    public void onActivityReenter(int resultCode, Intent data) {
        super.onActivityReenter(resultCode, data);
        transitionState = new Bundle(data.getExtras());
        Log.d(TAG, "onActivityReenter");
    }

onActivityReenter里面获取到索引就可以自己去滑动了。。。

简单滴说了下用法,进入今天的主题,现在看下https://github.com/alexjlockwood/adp-activity-transitions里面的代码。
有需要请自己在github上面查看代码,我比较菜那就简单滴分析一下,相当于做个笔记吧。
先看下主界面里面的东西,MainActivity:

public class MainActivity extends Activity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private static final boolean DEBUG = false;

    static final String EXTRA_STARTING_ALBUM_POSITION = "extra_starting_item_position";
    static final String EXTRA_CURRENT_ALBUM_POSITION = "extra_current_item_position";

    private RecyclerView mRecyclerView;
    private Bundle mTmpReenterState;
    private boolean mIsDetailsActivityStarted;

    private final SharedElementCallback mCallback = new SharedElementCallback() {
        @Override
        public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
            if (mTmpReenterState != null) {
                int startingPosition = mTmpReenterState.getInt(EXTRA_STARTING_ALBUM_POSITION);
                int currentPosition = mTmpReenterState.getInt(EXTRA_CURRENT_ALBUM_POSITION);
                if (startingPosition != currentPosition) {
                    // If startingPosition != currentPosition the user must have swiped to a
                    // different page in the DetailsActivity. We must update the shared element
                    // so that the correct one falls into place.
                    String newTransitionName = ALBUM_NAMES[currentPosition];
                    View newSharedElement = mRecyclerView.findViewWithTag(newTransitionName);
                    if (newSharedElement != null) {
                        names.clear();
                        names.add(newTransitionName);
                        sharedElements.clear();
                        sharedElements.put(newTransitionName, newSharedElement);
                    }
                }

                mTmpReenterState = null;
            } else {
                // If mTmpReenterState is null, then the activity is exiting.
                View navigationBar = findViewById(android.R.id.navigationBarBackground);
                View statusBar = findViewById(android.R.id.statusBarBackground);
                if (navigationBar != null) {
                    names.add(navigationBar.getTransitionName());
                    sharedElements.put(navigationBar.getTransitionName(), navigationBar);
                }
                if (statusBar != null) {
                    names.add(statusBar.getTransitionName());
                    sharedElements.put(statusBar.getTransitionName(), statusBar);
                }
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setExitSharedElementCallback(mCallback);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new GridLayoutManager(this,
                getResources().getInteger(R.integer.activity_main_num_grid_columns)));
        mRecyclerView.setAdapter(new CardAdapter());
    }

    @Override
    protected void onResume() {
        super.onResume();
        mIsDetailsActivityStarted = false;
    }

    @Override
    public void onActivityReenter(int requestCode, Intent data) {
        super.onActivityReenter(requestCode, data);
        mTmpReenterState = new Bundle(data.getExtras());
        int startingPosition = mTmpReenterState.getInt(EXTRA_STARTING_ALBUM_POSITION);
        int currentPosition = mTmpReenterState.getInt(EXTRA_CURRENT_ALBUM_POSITION);
        if (startingPosition != currentPosition) {
            mRecyclerView.scrollToPosition(currentPosition);
        }
        postponeEnterTransition();
        mRecyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                mRecyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
                // TODO: figure out why it is necessary to request layout here in order to get a smooth transition.
                mRecyclerView.requestLayout();
                startPostponedEnterTransition();
                return true;
            }
        });
    }

    private class CardAdapter extends RecyclerView.Adapter<CardHolder> {
        private final LayoutInflater mInflater;

        public CardAdapter() {
            mInflater = LayoutInflater.from(MainActivity.this);
        }

        @Override
        public CardHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
            return new CardHolder(mInflater.inflate(R.layout.album_image_card, viewGroup, false));
        }

        @Override
        public void onBindViewHolder(CardHolder holder, int position) {
            holder.bind(position);
        }

        @Override
        public int getItemCount() {
            return ALBUM_IMAGE_URLS.length;
        }
    }

    private class CardHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private final ImageView mAlbumImage;
        private int mAlbumPosition;

        public CardHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(this);
            mAlbumImage = (ImageView) itemView.findViewById(R.id.main_card_album_image);
        }

        public void bind(int position) {
            Picasso.with(MainActivity.this).load(ALBUM_IMAGE_URLS[position]).into(mAlbumImage);
            mAlbumImage.setTransitionName(ALBUM_NAMES[position]);
            mAlbumImage.setTag(ALBUM_NAMES[position]);
            mAlbumPosition = position;
        }

        @Override
        public void onClick(View v) {
            // TODO: is there a way to prevent user from double clicking and starting activity twice?
            Intent intent = new Intent(MainActivity.this, DetailsActivity.class);
            intent.putExtra(EXTRA_STARTING_ALBUM_POSITION, mAlbumPosition);

            if (!mIsDetailsActivityStarted) {
                mIsDetailsActivityStarted = true;
                startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this,
                        mAlbumImage, mAlbumImage.getTransitionName()).toBundle());
            }
        }
    }
}

代码量不多很简单就是onCreate里面去setExitSharedElementCallback,然后再onActivityReenter里面去获取返回的数据信息在去做mRecyclerView.scrollToPosition,同时里面用了一下postponeEnterTransitionstartPostponedEnterTransition以及SharedElementCallback

这里参考了下http://blog.csdn.net/h183288132/article/details/51120128

首先要先弄明白两个Activity的转换是分几种情况的:
开始时从A进入B:
1.A退出(exit) ,A中的View播放动画
2.B进入(enter) ,B中的View播放动画
当从B退回A时:
1.B返回(return) ,B中的View播放动画
2.A重新进入(reenter) ,A中的View播放动画
对于SharedElementCallback:
onMapSharedElements
装载共享元素
onSharedElementStart
是共享元素开始时候回调,一般是进入的时候使用
onSharedElementEnd
是共享元素结束的时候回调,一般是退出的时候使用
当然还有其他的方法,具体可以看看文档。
每次进入和退出都会回调SharedElementCallback

对于DetailsActivity

package com.alexjlockwood.activity.transitions;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.SharedElementCallback;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.transition.Slide;
import android.transition.Transition;
import android.transition.TransitionSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import java.util.List;
import java.util.Map;

import static com.alexjlockwood.activity.transitions.Constants.ALBUM_IMAGE_URLS;
import static com.alexjlockwood.activity.transitions.MainActivity.EXTRA_CURRENT_ALBUM_POSITION;
import static com.alexjlockwood.activity.transitions.MainActivity.EXTRA_STARTING_ALBUM_POSITION;

public class DetailsActivity extends Activity {
    private static final String TAG = DetailsActivity.class.getSimpleName();
    private static final boolean DEBUG = false;

    private static final String STATE_CURRENT_PAGE_POSITION = "state_current_page_position";

    private final SharedElementCallback mCallback = new SharedElementCallback() {
        @Override
        public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
            if (mIsReturning) {
                ImageView sharedElement = mCurrentDetailsFragment.getAlbumImage();
                if (sharedElement == null) {
                    // If shared element is null, then it has been scrolled off screen and
                    // no longer visible. In this case we cancel the shared element transition by
                    // removing the shared element from the shared elements map.
                    names.clear();
                    sharedElements.clear();
                } else if (mStartingPosition != mCurrentPosition) {
                    // If the user has swiped to a different ViewPager page, then we need to
                    // remove the old shared element and replace it with the new shared element
                    // that should be transitioned instead.
                    names.clear();
                    names.add(sharedElement.getTransitionName());
                    sharedElements.clear();
                    sharedElements.put(sharedElement.getTransitionName(), sharedElement);
                }
            }
        }

        @Override
        public void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
            if (!mIsReturning) {
                getWindow().setEnterTransition(makeEnterTransition(getSharedElement(sharedElements)));
            }
        }

        private View getSharedElement(List<View> sharedElements) {
            for (final View view : sharedElements) {
                if (view instanceof ImageView) {
                    return view;
                }
            }
            return null;
        }
    };

    private Transition makeEnterTransition(View sharedElement) {
        View rootView = mCurrentDetailsFragment.getView();
        assert rootView != null;

        TransitionSet enterTransition = new TransitionSet();

        // Play a circular reveal animation starting beneath the shared element.
        Transition circularReveal = new CircularReveal(sharedElement);
        circularReveal.addTarget(rootView.findViewById(R.id.details_header_container));
        enterTransition.addTransition(circularReveal);

        // Slide the cards in through the bottom of the screen.
        Transition cardSlide = new Slide(Gravity.BOTTOM);
        cardSlide.addTarget(rootView.findViewById(R.id.details_text_container));
        enterTransition.addTransition(cardSlide);

        final ImageView backgroundImage = (ImageView) rootView.findViewById(R.id.details_background_image);
        backgroundImage.setAlpha(0f);
        enterTransition.addListener(new TransitionListenerAdapter() {
            @Override
            public void onTransitionEnd(Transition transition) {
//                backgroundImage.animate().alpha(1f).setDuration(2000);
            }
        });

        enterTransition.setDuration(400);
        return enterTransition;
    }

    private DetailsFragment mCurrentDetailsFragment;
    private int mCurrentPosition;
    private int mStartingPosition;
    private boolean mIsReturning;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_details);
        postponeEnterTransition();
        setEnterSharedElementCallback(mCallback);

        mStartingPosition = getIntent().getIntExtra(EXTRA_STARTING_ALBUM_POSITION, 0);
        if (savedInstanceState == null) {
            mCurrentPosition = mStartingPosition;
        } else {
            mCurrentPosition = savedInstanceState.getInt(STATE_CURRENT_PAGE_POSITION);
        }

        ViewPager pager = (ViewPager) findViewById(R.id.pager);
        pager.setAdapter(new DetailsFragmentPagerAdapter(getFragmentManager()));
        pager.setCurrentItem(mCurrentPosition);
        pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                mCurrentPosition = position;
            }
        });
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_CURRENT_PAGE_POSITION, mCurrentPosition);
    }

    @Override
    public void finishAfterTransition() {
        mIsReturning = true;
        Intent data = new Intent();
        data.putExtra(EXTRA_STARTING_ALBUM_POSITION, mStartingPosition);
        data.putExtra(EXTRA_CURRENT_ALBUM_POSITION, mCurrentPosition);
        setResult(RESULT_OK, data);
        super.finishAfterTransition();
    }

    private class DetailsFragmentPagerAdapter extends FragmentStatePagerAdapter {
        public DetailsFragmentPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return DetailsFragment.newInstance(position, mStartingPosition);
        }

        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            super.setPrimaryItem(container, position, object);
            mCurrentDetailsFragment = (DetailsFragment) object;
        }

        @Override
        public int getCount() {
            return ALBUM_IMAGE_URLS.length;
        }
    }
}

这里注意在onSharedElementStart里面使用了makeEnterTransition动画里面加了进入的时候的圆形动画和文字上移动画,这个用transition的代码形式可以简单的实现。。。

如果看过转场动画源码的大神都知道其实ShareElements 里面调用还是transition,然后transition里面主要使用了ViewOverlay的东西,有兴趣的可以去看下源码。

最后剩下的就请到源码里面一探究竟吧(毕竟我也不知道写什么了),顺便链接一些比较好的效果库

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值