Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 5.0 SharedElement 簡單應用分析(adp-activity-transitions)

Android 5.0 SharedElement 簡單應用分析(adp-activity-transitions)

編輯:關於Android編程

好久沒寫點東西了,最近看到了一個轉場動畫比較酷炫,今天就來簡單滴分析一下。
先看下今天的效果圖這裡寫圖片描述
分析下效果: 進入詳情頁的時候有共享元素,圓形動畫,文字部分的上移動畫,源碼地址是 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 names, Map 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 names, Map 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 {
        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。

首先要先弄明白兩個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 names, Map 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 sharedElementNames, List sharedElements, List sharedElementSnapshots) {
            if (!mIsReturning) {
                getWindow().setEnterTransition(makeEnterTransition(getSharedElement(sharedElements)));
            }
        }

        private View getSharedElement(List 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的東西,有興趣的可以去看下源碼。

最後剩下的就請到源碼裡面一探究竟吧(畢竟我也不知道寫什麼了),順便鏈接一些比較好的效果庫

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved