2016年11月14日月曜日

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

ViewAnimationUtils.createCircularReveal()

ViewAnimationUtils.createCircularReveal() は、Viewを円形にくり抜くアニメーション(Animator)を作るユーティリティメソッドです。 例えばこんな感じ。 public class SampleActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final View container = findViewById(R.id.container); final int width = container.getWidth(); final int height = container.getHeight(); float startRadius = (float) Math.sqrt(width * width + height * height) / 2; float endRadius = 0; final Animator animator = ViewAnimationUtils.createCircularReveal(container, width / 2, height / 2, startRadius, endRadius); animator.setDuration(3000); animator.start(); } }); } }



FAB の transforming

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

アニメーション以外の本質的なコードは toolsContainer と fab の visibility の切り替えだけ(以下の部分)なんですけど、アニメーションのコード入れると長い... toolsContainer.setVisibility(View.VISIBLE); fab.setVisibility(View.INVISIBLE); これが全体のコードなのですが、長いですね... public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final View toolsContainer = findViewById(R.id.tools_container); final View tools = findViewById(R.id.tools); final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); final ToggleButton toggleButton = (ToggleButton) findViewById(R.id.button); toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { final int fabWidth = fab.getWidth(); final int fabHeight = fab.getHeight(); final int toolsWidth = toolsContainer.getWidth(); final int toolsHeight = toolsContainer.getHeight(); float startRadius = fabHeight / 2f; float endRadius = (float) (Math.sqrt(toolsWidth * toolsWidth + toolsHeight * toolsHeight)); int[] outLocation = new int[2]; toolsContainer.getLocationInWindow(outLocation); int[] fabOutLocation = new int[2]; fab.getLocationInWindow(fabOutLocation); float diff = isChecked ? (outLocation[1] + toolsHeight / 2) - (fabOutLocation[1] + fabHeight / 2) : 0; int centerX = (int) (fabOutLocation[0] + fabWidth / 2 - outLocation[0] - diff); int centerY = toolsHeight / 2; final int FAB_DURATION = 100; final int TOOLS_DURATION = 300; if (isChecked) { final Animator fabAnimator1 = ObjectAnimator.ofFloat(fab, "translationY", diff); fabAnimator1.setDuration(FAB_DURATION); fabAnimator1.setInterpolator(new DecelerateInterpolator()); final Animator fabAnimator2 = ObjectAnimator.ofFloat(fab, "translationX", -diff); fabAnimator2.setDuration(FAB_DURATION); fabAnimator2.setInterpolator(new AccelerateInterpolator()); final ValueAnimator fabAnimator3 = ValueAnimator.ofInt(255, 0); fabAnimator3.setDuration(FAB_DURATION); fabAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { final int alpha = (int) animation.getAnimatedValue(); final Drawable drawable = fab.getDrawable(); drawable.setAlpha(alpha); } }); final Animator toolsContainerAnimator = ViewAnimationUtils.createCircularReveal(toolsContainer, centerX, centerY, startRadius, endRadius); toolsContainerAnimator.setDuration(TOOLS_DURATION); toolsContainerAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); toolsContainer.setVisibility(View.VISIBLE); fab.setVisibility(View.INVISIBLE); } }); tools.setPivotX(centerX); final Animator toolsAnimator = ObjectAnimator.ofPropertyValuesHolder(tools, PropertyValuesHolder.ofFloat("alpha", 0f, 1f), PropertyValuesHolder.ofFloat("scaleX", 0.8f, 1f)); toolsAnimator.setDuration(TOOLS_DURATION); AnimatorSet set = new AnimatorSet(); set.play(toolsContainerAnimator).with(toolsAnimator) .after(fabAnimator1).after(fabAnimator2).after(fabAnimator3); set.start(); } else { final Animator fabAnimator1 = ObjectAnimator.ofFloat(fab, "translationY", 0); fabAnimator1.setDuration(FAB_DURATION); fabAnimator1.setInterpolator(new AccelerateInterpolator()); final Animator fabAnimator2 = ObjectAnimator.ofFloat(fab, "translationX", 0); fabAnimator2.setDuration(FAB_DURATION); fabAnimator2.setInterpolator(new DecelerateInterpolator()); final ValueAnimator fabAnimator3 = ValueAnimator.ofInt(0, 255); fabAnimator3.setDuration(FAB_DURATION); fabAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { final int alpha = (int) animation.getAnimatedValue(); final Drawable drawable = fab.getDrawable(); drawable.setAlpha(alpha); } }); final Animator toolsContainerAnimator = ViewAnimationUtils.createCircularReveal( toolsContainer, centerX, centerY, endRadius, startRadius); toolsContainerAnimator.setDuration(TOOLS_DURATION); toolsContainerAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); toolsContainer.setVisibility(View.INVISIBLE); fab.setVisibility(View.VISIBLE); } }); tools.setPivotX(centerX); final Animator toolsAnimator = ObjectAnimator.ofPropertyValuesHolder(tools, PropertyValuesHolder.ofFloat("alpha", 0f), PropertyValuesHolder.ofFloat("scaleX", 0.8f)); toolsAnimator.setDuration(TOOLS_DURATION); AnimatorSet set = new AnimatorSet(); set.play(toolsContainerAnimator).with(toolsAnimator) .before(fabAnimator1).before(fabAnimator2).before(fabAnimator3); set.start(); } } }); toolsContainer.setVisibility(toggleButton.isChecked() ? View.VISIBLE : View.INVISIBLE); tools.setAlpha(toggleButton.isChecked() ? 1f : 0f); } } アニメーションの長いコードがあるために、ここでやっていること(つまり toolsContainer と fab の visibility を切り替えること)がわかりにくくなっています。
それを解消するために Transition API が使えます(Transition API はもともとそういうための用意されたもののようです)。それは次回に。

↓実行結果



0 件のコメント:

コメントを投稿