2016年11月14日月曜日

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

ViewAnimationUtils.createCircularReveal()

ViewAnimationUtils.createCircularReveal() は、Viewを円形にくり抜くアニメーション(Animator)を作るユーティリティメソッドです。 例えばこんな感じ。
  1. public class SampleActivity extends AppCompatActivity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(@Nullable Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_sample);  
  7.   
  8.         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {  
  9.             @Override  
  10.             public void onClick(View v) {  
  11.                 final View container = findViewById(R.id.container);  
  12.   
  13.                 final int width = container.getWidth();  
  14.                 final int height = container.getHeight();  
  15.                 float startRadius = (float) Math.sqrt(width * width + height * height) / 2;  
  16.                 float endRadius = 0;  
  17.   
  18.                 final Animator animator = ViewAnimationUtils.createCircularReveal(container,  
  19.                         width / 2, height / 2, startRadius, endRadius);  
  20.                 animator.setDuration(3000);  
  21.                 animator.start();  
  22.             }  
  23.         });  
  24.     }  
  25. }  




FAB の transforming

https://material.google.com/components/buttons-floating-action-button.html#buttons-floating-action-button-transitions の真ん中あたり、toolbar という項目のやつです。

アニメーション以外の本質的なコードは toolsContainer と fab の visibility の切り替えだけ(以下の部分)なんですけど、アニメーションのコード入れると長い...
  1. toolsContainer.setVisibility(View.VISIBLE);  
  2. fab.setVisibility(View.INVISIBLE);  
これが全体のコードなのですが、長いですね...
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.   
  8.         final View toolsContainer = findViewById(R.id.tools_container);  
  9.         final View tools = findViewById(R.id.tools);  
  10.         final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);  
  11.   
  12.         final ToggleButton toggleButton = (ToggleButton) findViewById(R.id.button);  
  13.         toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
  14.             @Override  
  15.             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {  
  16.                 final int fabWidth = fab.getWidth();  
  17.                 final int fabHeight = fab.getHeight();  
  18.   
  19.                 final int toolsWidth = toolsContainer.getWidth();  
  20.                 final int toolsHeight = toolsContainer.getHeight();  
  21.   
  22.                 float startRadius = fabHeight / 2f;  
  23.                 float endRadius = (float) (Math.sqrt(toolsWidth * toolsWidth + toolsHeight * toolsHeight));  
  24.   
  25.                 int[] outLocation = new int[2];  
  26.                 toolsContainer.getLocationInWindow(outLocation);  
  27.   
  28.                 int[] fabOutLocation = new int[2];  
  29.                 fab.getLocationInWindow(fabOutLocation);  
  30.   
  31.                 float diff = isChecked  
  32.                         ? (outLocation[1] + toolsHeight / 2) - (fabOutLocation[1] + fabHeight / 2)  
  33.                         : 0;  
  34.   
  35.                 int centerX = (int) (fabOutLocation[0] + fabWidth / 2 - outLocation[0] - diff);  
  36.                 int centerY = toolsHeight / 2;  
  37.   
  38.                 final int FAB_DURATION = 100;  
  39.                 final int TOOLS_DURATION = 300;  
  40.   
  41.                 if (isChecked) {  
  42.                     final Animator fabAnimator1 = ObjectAnimator.ofFloat(fab, "translationY", diff);  
  43.                     fabAnimator1.setDuration(FAB_DURATION);  
  44.                     fabAnimator1.setInterpolator(new DecelerateInterpolator());  
  45.   
  46.                     final Animator fabAnimator2 = ObjectAnimator.ofFloat(fab, "translationX", -diff);  
  47.                     fabAnimator2.setDuration(FAB_DURATION);  
  48.                     fabAnimator2.setInterpolator(new AccelerateInterpolator());  
  49.   
  50.                     final ValueAnimator fabAnimator3 = ValueAnimator.ofInt(2550);  
  51.                     fabAnimator3.setDuration(FAB_DURATION);  
  52.                     fabAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  53.                         @Override  
  54.                         public void onAnimationUpdate(ValueAnimator animation) {  
  55.                             final int alpha = (int) animation.getAnimatedValue();  
  56.                             final Drawable drawable = fab.getDrawable();  
  57.                             drawable.setAlpha(alpha);  
  58.                         }  
  59.                     });  
  60.   
  61.                     final Animator toolsContainerAnimator = ViewAnimationUtils.createCircularReveal(toolsContainer,  
  62.                             centerX, centerY, startRadius, endRadius);  
  63.                     toolsContainerAnimator.setDuration(TOOLS_DURATION);  
  64.                     toolsContainerAnimator.addListener(new AnimatorListenerAdapter() {  
  65.                         @Override  
  66.                         public void onAnimationStart(Animator animation) {  
  67.                             super.onAnimationStart(animation);  
  68.                             toolsContainer.setVisibility(View.VISIBLE);  
  69.                             fab.setVisibility(View.INVISIBLE);  
  70.                         }  
  71.                     });  
  72.   
  73.                     tools.setPivotX(centerX);  
  74.                     final Animator toolsAnimator = ObjectAnimator.ofPropertyValuesHolder(tools,  
  75.                             PropertyValuesHolder.ofFloat("alpha", 0f, 1f),  
  76.                             PropertyValuesHolder.ofFloat("scaleX"0.8f, 1f));  
  77.                     toolsAnimator.setDuration(TOOLS_DURATION);  
  78.   
  79.                     AnimatorSet set = new AnimatorSet();  
  80.                     set.play(toolsContainerAnimator).with(toolsAnimator)  
  81.                             .after(fabAnimator1).after(fabAnimator2).after(fabAnimator3);  
  82.                     set.start();  
  83.   
  84.                 } else {  
  85.                     final Animator fabAnimator1 = ObjectAnimator.ofFloat(fab, "translationY"0);  
  86.                     fabAnimator1.setDuration(FAB_DURATION);  
  87.                     fabAnimator1.setInterpolator(new AccelerateInterpolator());  
  88.   
  89.                     final Animator fabAnimator2 = ObjectAnimator.ofFloat(fab, "translationX"0);  
  90.                     fabAnimator2.setDuration(FAB_DURATION);  
  91.                     fabAnimator2.setInterpolator(new DecelerateInterpolator());  
  92.   
  93.                     final ValueAnimator fabAnimator3 = ValueAnimator.ofInt(0255);  
  94.                     fabAnimator3.setDuration(FAB_DURATION);  
  95.                     fabAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  96.                         @Override  
  97.                         public void onAnimationUpdate(ValueAnimator animation) {  
  98.                             final int alpha = (int) animation.getAnimatedValue();  
  99.                             final Drawable drawable = fab.getDrawable();  
  100.                             drawable.setAlpha(alpha);  
  101.                         }  
  102.                     });  
  103.   
  104.                     final Animator toolsContainerAnimator = ViewAnimationUtils.createCircularReveal(  
  105.                             toolsContainer, centerX, centerY, endRadius, startRadius);  
  106.                     toolsContainerAnimator.setDuration(TOOLS_DURATION);  
  107.                     toolsContainerAnimator.addListener(new AnimatorListenerAdapter() {  
  108.                         @Override  
  109.                         public void onAnimationEnd(Animator animation) {  
  110.                             super.onAnimationEnd(animation);  
  111.                             toolsContainer.setVisibility(View.INVISIBLE);  
  112.                             fab.setVisibility(View.VISIBLE);  
  113.                         }  
  114.                     });  
  115.   
  116.                     tools.setPivotX(centerX);  
  117.                     final Animator toolsAnimator = ObjectAnimator.ofPropertyValuesHolder(tools,  
  118.                             PropertyValuesHolder.ofFloat("alpha", 0f),  
  119.                             PropertyValuesHolder.ofFloat("scaleX"0.8f));  
  120.                     toolsAnimator.setDuration(TOOLS_DURATION);  
  121.   
  122.                     AnimatorSet set = new AnimatorSet();  
  123.                     set.play(toolsContainerAnimator).with(toolsAnimator)  
  124.                             .before(fabAnimator1).before(fabAnimator2).before(fabAnimator3);  
  125.                     set.start();  
  126.                 }  
  127.             }  
  128.         });  
  129.   
  130.         toolsContainer.setVisibility(toggleButton.isChecked() ? View.VISIBLE : View.INVISIBLE);  
  131.         tools.setAlpha(toggleButton.isChecked() ? 1f : 0f);  
  132.     }  
  133. }  
アニメーションの長いコードがあるために、ここでやっていること(つまり toolsContainer と fab の visibility を切り替えること)がわかりにくくなっています。
それを解消するために Transition API が使えます(Transition API はもともとそういうための用意されたもののようです)。それは次回に。

↓実行結果



0 件のコメント:

コメントを投稿