2016年11月14日月曜日

ViewAnimationUtils.createCircularReveal() を使って FAB の transforming を実現する - with Transition API -

ViewAnimationUtils.createCircularReveal() を使って FAB の transforming を実現する
では、直接Activityに複雑なアニメーションを記述しました。それにより、本質的なコード(toolsContainer と fab の visibility の切り替え)がアニメーションのコードに埋もれてしまい、何をやっているのかわかりずらい状況になっていました。
そこで Transition API を使ってアニメーション部分を Activity から引き剥がしました。

完全な実装は
https://github.com/yanzm/FabTransformingSample
にあります。

MainActivity からは Animator オブジェクトが完全になくなり、RecyclerViewやバックキー部分のコードを追加しても前回より短くなっています。 visibility の切り替えなど view のパラメータ値の変更だけになり、何をやっているのかがわかりやすくなりました。 アニメーション部分は FabTransformation というクラスにまとめています。
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.     private static final int HORIZONTAL_FACTOR = 2;  
  4.     private float diff;  
  5.   
  6.     private ViewGroup sceneRoot;  
  7.     private View toolsContainer;  
  8.     private View tools;  
  9.     private FloatingActionButton fab;  
  10.   
  11.     @Override  
  12.     protected void onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.         setContentView(R.layout.activity_main);  
  15.   
  16.         toolsContainer = findViewById(R.id.tools_container);  
  17.         tools = findViewById(R.id.tools);  
  18.         fab = (FloatingActionButton) findViewById(R.id.fab);  
  19.   
  20.         sceneRoot = (ViewGroup) findViewById(R.id.scene_root);  
  21.         sceneRoot.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {  
  22.             @Override  
  23.             public void onGlobalLayout() {  
  24.                 int[] toolsLocation = new int[2];  
  25.                 toolsContainer.getLocationInWindow(toolsLocation);  
  26.   
  27.                 int[] fabLocation = new int[2];  
  28.                 fab.getLocationInWindow(fabLocation);  
  29.   
  30.                 diff = (toolsLocation[1] + toolsContainer.getHeight() / 2)  
  31.                         - (fabLocation[1] + fab.getHeight() / 2);  
  32.   
  33.                 final float pivotX = fabLocation[0] + fab.getWidth() / 2 - toolsLocation[0] - diff * HORIZONTAL_FACTOR;  
  34.                 toolsContainer.setPivotX(pivotX);  
  35.                 tools.setPivotX(pivotX);  
  36.   
  37.                 sceneRoot.getViewTreeObserver().removeOnGlobalLayoutListener(this);  
  38.             }  
  39.         });  
  40.   
  41.   
  42.         fab.setOnClickListener(new View.OnClickListener() {  
  43.             @Override  
  44.             public void onClick(View view) {  
  45.                 changeFabMode(truetrue);  
  46.             }  
  47.         });  
  48.   
  49.         changeFabMode(falsefalse);  
  50.   
  51.         // recycler view setup  
  52.         final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);  
  53.         recyclerView.setAdapter(new AndroidVersionAdapter());  
  54.         recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {  
  55.             @Override  
  56.             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {  
  57.                 super.onScrollStateChanged(recyclerView, newState);  
  58.                 if (newState != RecyclerView.SCROLL_STATE_IDLE) {  
  59.                     if (fab.getVisibility() != View.VISIBLE) {  
  60.                         changeFabMode(falsetrue);  
  61.                     }  
  62.                 }  
  63.             }  
  64.         });  
  65.     }  
  66.   
  67.     @Override  
  68.     public void onBackPressed() {  
  69.         if (fab.getVisibility() != View.VISIBLE) {  
  70.             changeFabMode(falsetrue);  
  71.             return;  
  72.         }  
  73.         super.onBackPressed();  
  74.     }  
  75.   
  76.     private void changeFabMode(boolean transformed, boolean animate) {  
  77.         if (animate) {  
  78.             final TransitionSet transition = new FabTransformation(transformed, fab.getHeight() / 2f);  
  79.             TransitionManager.beginDelayedTransition(sceneRoot, transition);  
  80.         }  
  81.   
  82.         final float baseMargin = getResources().getDimension(R.dimen.fab_margin);  
  83.         final FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) fab.getLayoutParams();  
  84.         params.bottomMargin = (int) (baseMargin - (transformed ? diff : 0));  
  85.         params.setMarginEnd((int) (baseMargin + (transformed ? diff * HORIZONTAL_FACTOR : 0)));  
  86.         fab.setLayoutParams(params);  
  87.   
  88.         toolsContainer.setVisibility(transformed ? View.VISIBLE : View.INVISIBLE);  
  89.         tools.setVisibility(transformed ? View.VISIBLE : View.INVISIBLE);  
  90.         tools.setScaleX(transformed ? 1f : 0.8f);  
  91.         fab.setVisibility(transformed ? View.INVISIBLE : View.VISIBLE);  
  92.     }  
  93. }  
FabTransformation では複数の Transition を組み合わせて FAB の transforming を実現するアニメーションを構築しています。 ここでは Transition API で用意されている ChangeTransformFadeChangeBounds に加えて、ViewAnimationUtils.createCircularReveal() を利用する CircularRevealTransition を作って利用しています。





0 件のコメント:

コメントを投稿