Android 5.0 SharedElement 简单应用分析(adp-activity-transitions)
来源:互联网 发布:我要复仇知乎 编辑:程序博客网 时间:2024/05/16 16:06
我不生成水,只是大自然的搬运工
好久没写点东西了,最近看到了一个转场动画比较酷炫,今天就来简单滴分析一下。
先看下今天的效果图。
分析下效果: 进入详情页的时候有共享元素,圆形动画,文字部分的上移动画,源码地址是 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
,同时里面用了一下postponeEnterTransition
和startPostponedEnterTransition
以及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
的东西,有兴趣的可以去看下源码。
最后剩下的就请到源码里面一探究竟吧(毕竟我也不知道写什么了),顺便链接一些比较好的效果库
- Android 5.0 SharedElement 简单应用分析(adp-activity-transitions)
- Android SharedElement详解
- Android核心分析(22)-----Android应用框架之Activity
- Android核心分析(22)-----Android应用框架之Activity
- Android核心分析(22)-----Android应用框架之Activity
- Material Design - Activity transitions
- ADP
- Android 进阶 - Activity应用启动分析
- AndroidMaterialDesign动画之Activity Transitions
- Android最新动画框架完全解析(二)——Transitions Framework(Transitions 框架)
- Transitions框架学习(二) —— 应用转场效果
- Activity的绘制流程简单分析(基于android 4.0源码进行分析)
- #Android Training# transitions framework
- Android核心分析之(22)Android应用框架之Activity
- Android核心分析 Android应用框架之Activity
- Android核心分析 Android应用框架之Activity
- Android AlarmClock 闹钟应用 简单分析
- android应用开发之Activity的简单布局切换
- Android 布局 Framelayout 使用
- 遍历datagrid
- mongodb-->db.currentOp()
- 新生月赛第一次被坑题~~
- codevs 1048 石子归并(区间型DP)
- Android 5.0 SharedElement 简单应用分析(adp-activity-transitions)
- 史上最全最强SpringMVC详细示例实战教程
- Linux shell控制台改变显示前缀
- C 内联汇编方法 __asm__ __volatile__
- Call to undefined function socket_create()
- SVN错误“Attempted to lock an already-locked dir”
- android编译系统分析(二)mm编译单个模块
- HDOJ 5326 Work(类似并查集)
- java8中的系统时间获取