實現Activity間的共享控件轉場動畫


實現Activity間的共享控件轉場動畫
字數1210 閱讀1936 評論9 喜歡35

Lollipop中有shared_element可以進行元素在activity之間進行共享,網上已經有很多介紹了,然而目前還有大量的kitkat設備,所以說啊,兼容更重要。

如下的方法,可以實現在舊的手機上實現動畫效果。采用了類似於豌豆莢的開眼項目使用的技術。github上可能有在5.0以下的兼容包,但是個人並不推薦使用第三方的UI工具。

Preview
實現原理

最近逛業界良心酷安網,發現了豌豆莢的一款叫做開眼的項目,這個項目的意義就是每天把牆外的東西搬運回來讓村里的阿Q們開開眼,這款app至少目前看還沒有那么毒瘤,還是比較小而美的,於是下載試用了一下。

有個轉場動畫使用了類似上面gif的效果,第一感覺是自定義了一個popwindow,於是起床,打開電腦,對兩個效果的始末進行查詢

adb shell dumpsys window windows | grep -E 'mCurrentFocus'

結果如下,可以發現是2個activity,而不是自定義view實現的

mCurrentFocus=xx/xxxx.ui.activity.FeedActivity}
mCurrentFocus=xx/xxxx.ui.activity.DetailActivity}

反復研究,最后發現原理如下

    FeedActivity將view的top/width/height,內容等信息通過intent進行發送
    DetailActivity設置為背景透明模式,轉場動畫關閉,這里關閉是非常重要的
    DetailActivity接着讀取intent,並根據高度等信息進行動畫繪制,由於DetailActivity的背景是透明的,所以用戶會誤認為是進行了“放大”操作

    在UED中,“放大”與“右轉”是兩種常見的場景切換操作,安利一本叫做《Learn iOS Design》的書,Android開發者也值得借鑒一下

步驟

步驟非常簡單,主要時間是耗在了調試動畫上
1. 設置DetailActivity的主題

<style name="DetailedTheme" parent="AppTheme">
  <!--背景透明-->
  <item name="android:windowIsTranslucent">true</item>
  <item name="android:windowBackground">@android:color/transparent</item>
  <!--無進入動畫-->
  <item name="android:windowAnimationStyle">@null</item>
</style>

2. 獲取FeedActivity中的itemview

樓主在RecyclerView的Adapter中的viewholder中手動加了一個接口,在主界面實現。這里使用的是ObjectAnimator,它在動畫完成后,可以保存動畫結束的布局,而且最后測試機(1080P/2G/4.4)的渲染時間均在警戒線(60fps)的一半左右,性能可以滿足。

@Override public void onItemClick(View v, int position) {
  Parcelable imgInfo = ((CardAdapter) mRvFragCard.getAdapter()).getData().get(position);
  //封裝了width/top等信息
  Position viewPosition = Position.from(v);
  DetailedActivity.startActivity(v.getContext(), viewPosition, imgInfo);
}

3. 在DetailActivity中進行動畫

動畫沒有什么技巧,跟老司機開車一樣,屬於熟練工種,我們這里主要是使用了AnimationSet進行並發動畫

void anim(final Position position, final boolean in,
    final Animator.AnimatorListener listener, View... views) {
  if (isPlaying) {
    return;
  }
  //記住括號哦,我這里調試了一小時
  float delta = ((float) (position.width)) / ((float) (position.height));

  float[] y_img = { position.top - views[0].getTop(), 0 };
  float[] s_img = { 1f, delta };

  float[] y_icn = { views[1].getHeight() * 4, 0 };
  float[] s_icn = { 3f, 1f };

  views[0].setPivotX(views[0].getWidth() / 2);
  views[1].setPivotX(views[1].getWidth() / 2);

  Animator trans_Y =
      ObjectAnimator.ofFloat(views[0], View.TRANSLATION_Y, in ? y_img[0] : y_img[1],
          in ? y_img[1] : y_img[0]);
  Animator scale_X =
      ObjectAnimator.ofFloat(views[0], View.SCALE_X, in ? s_img[0] : s_img[1],
          in ? s_img[1] : s_img[0]);
  Animator scale_Y =
      ObjectAnimator.ofFloat(views[0], View.SCALE_Y, in ? s_img[0] : s_img[1],
          in ? s_img[1] : s_img[0]);
  Animator scale_icn_X =
      ObjectAnimator.ofFloat(views[1], View.SCALE_X, in ? s_icn[0] : s_icn[1],
          in ? s_icn[1] : s_icn[0]);
  Animator scale_icn_Y =
      ObjectAnimator.ofFloat(views[1], View.SCALE_Y, in ? s_icn[0] : s_icn[1],
          in ? s_icn[1] : s_icn[0]);

  Animator trans_icn_Y =
      ObjectAnimator.ofFloat(views[1], View.TRANSLATION_Y, in ? y_icn[0] : y_icn[1],
          in ? y_icn[1] : y_icn[0]);

  AnimatorSet set = new AnimatorSet();

  set.playTogether(trans_Y, scale_X, scale_Y);
  set.playTogether(scale_icn_X, scale_icn_Y, trans_icn_Y);
  set.setDuration(400);
  set.addListener(new Animator.AnimatorListener() {
    @Override public void onAnimationStart(Animator animation) {
      listener.onAnimationStart(animation);
      isPlaying = true;
    }

    @Override public void onAnimationEnd(Animator animation) {
      listener.onAnimationEnd(animation);
      isPlaying = false;
    }

    @Override public void onAnimationCancel(Animator animation) {
      listener.onAnimationCancel(animation);
      isPlaying = false;
    }

    @Override public void onAnimationRepeat(Animator animation) {
      listener.onAnimationRepeat(animation);
    }
  });
  set.setInterpolator(new AccelerateInterpolator());
  set.start();
}

這樣,動畫就搞定了,是不是很簡單?
總結

源碼仍在更新中,后期將加入大量仿iOS的組件

https://github.com/miao1007/AnimeWallpaper

最后,總結一下,這個東西實現不難,但是

    調試動畫太費時間了,暗坑多
    屬性android:windowIsTranslucent暗坑多,大部分手機運行狀態未知
    編碼改動量大,回調接口多,很難把所有業務封裝在一個文件中,源碼亂,后期維護困難

所以除了情懷,很少有專門的人願意這樣寫,我建議在個人app中使用,或者提起商量好了再合作編碼。另外關於狀態欄適配,可以參考這里

謝謝大家的觀看,如果覺得本文有意義的話,不妨點個贊或者分享吧!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM