では、直接Activityに複雑なアニメーションを記述しました。それにより、本質的なコード(toolsContainer と fab の visibility の切り替え)がアニメーションのコードに埋もれてしまい、何をやっているのかわかりずらい状況になっていました。
そこで Transition API を使ってアニメーション部分を Activity から引き剥がしました。
完全な実装は
https://github.com/yanzm/FabTransformingSample
にあります。
MainActivity からは Animator オブジェクトが完全になくなり、RecyclerViewやバックキー部分のコードを追加しても前回より短くなっています。 visibility の切り替えなど view のパラメータ値の変更だけになり、何をやっているのかがわかりやすくなりました。 アニメーション部分は FabTransformation というクラスにまとめています。
public class MainActivity extends AppCompatActivity {
private static final int HORIZONTAL_FACTOR = 2;
private float diff;
private ViewGroup sceneRoot;
private View toolsContainer;
private View tools;
private FloatingActionButton fab;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolsContainer = findViewById(R.id.tools_container);
tools = findViewById(R.id.tools);
fab = (FloatingActionButton) findViewById(R.id.fab);
sceneRoot = (ViewGroup) findViewById(R.id.scene_root);
sceneRoot.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int[] toolsLocation = new int[2];
toolsContainer.getLocationInWindow(toolsLocation);
int[] fabLocation = new int[2];
fab.getLocationInWindow(fabLocation);
diff = (toolsLocation[1] + toolsContainer.getHeight() / 2)
- (fabLocation[1] + fab.getHeight() / 2);
final float pivotX = fabLocation[0] + fab.getWidth() / 2 - toolsLocation[0] - diff * HORIZONTAL_FACTOR;
toolsContainer.setPivotX(pivotX);
tools.setPivotX(pivotX);
sceneRoot.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
changeFabMode(true, true);
}
});
changeFabMode(false, false);
// recycler view setup
final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setAdapter(new AndroidVersionAdapter());
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState != RecyclerView.SCROLL_STATE_IDLE) {
if (fab.getVisibility() != View.VISIBLE) {
changeFabMode(false, true);
}
}
}
});
}
@Override
public void onBackPressed() {
if (fab.getVisibility() != View.VISIBLE) {
changeFabMode(false, true);
return;
}
super.onBackPressed();
}
private void changeFabMode(boolean transformed, boolean animate) {
if (animate) {
final TransitionSet transition = new FabTransformation(transformed, fab.getHeight() / 2f);
TransitionManager.beginDelayedTransition(sceneRoot, transition);
}
final float baseMargin = getResources().getDimension(R.dimen.fab_margin);
final FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) fab.getLayoutParams();
params.bottomMargin = (int) (baseMargin - (transformed ? diff : 0));
params.setMarginEnd((int) (baseMargin + (transformed ? diff * HORIZONTAL_FACTOR : 0)));
fab.setLayoutParams(params);
toolsContainer.setVisibility(transformed ? View.VISIBLE : View.INVISIBLE);
tools.setVisibility(transformed ? View.VISIBLE : View.INVISIBLE);
tools.setScaleX(transformed ? 1f : 0.8f);
fab.setVisibility(transformed ? View.INVISIBLE : View.VISIBLE);
}
}
FabTransformation では複数の Transition を組み合わせて FAB の transforming を実現するアニメーションを構築しています。
ここでは Transition API で用意されている ChangeTransform、Fade、ChangeBounds に加えて、ViewAnimationUtils.createCircularReveal() を利用する CircularRevealTransition を作って利用しています。
0 件のコメント:
コメントを投稿