Faire votre bouton d'action flottant presque étendu

Bonjour à tous.

Moins de six mois plus tard, mon application fonctionne, dans laquelle le menu Floating Action Button a été implémenté par une bibliothèque tierce.

À un moment donné, j'ai voulu le rendre plus agréable et plus pratique.

Était


Est devenu


Le cadeau était que je suis tombé sur un morceau de code inefficace ailleurs que j'ai optimisé avec succès.

Historique de création d'application
Historique des widgets du panier

Implémentation

1. Disposition des boutons
fab_layout.xml <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cardView="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.CardView android:id="@+id/fab_spending" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:visibility="invisible" cardView:cardBackgroundColor="@color/red" cardView:cardCornerRadius="12dp" cardView:cardElevation="8dp"> <TextView style="@style/White16" android:layout_width="120dp" android:layout_height="36dp" android:drawableRight="@drawable/remove" android:drawablePadding="6dp" android:gravity="center" android:padding="4dp" android:text="@string/spending"/> </android.support.v7.widget.CardView> <android.support.v7.widget.CardView android:id="@+id/fab_income" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:visibility="invisible" cardView:cardBackgroundColor="@color/green" cardView:cardCornerRadius="12dp" cardView:cardElevation="8dp"> <TextView style="@style/White16" android:layout_width="120dp" android:layout_height="36dp" android:drawableRight="@drawable/add" android:drawablePadding="6dp" android:gravity="center" android:text="@string/income"/> </android.support.v7.widget.CardView> <android.support.v7.widget.CardView android:id="@+id/fab_transfer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:visibility="invisible" cardView:cardBackgroundColor="@color/gunmetal" cardView:cardCornerRadius="12dp" cardView:cardElevation="8dp"> <TextView style="@style/White16" android:layout_width="120dp" android:layout_height="36dp" android:drawableRight="@drawable/autorenew" android:drawablePadding="6dp" android:gravity="center" android:text="@string/transfer"/> </android.support.v7.widget.CardView> </FrameLayout> 


2. animation

2.1 apparence
   <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true"> <!--Move--> <translate android:duration="300" android:fromXDelta="125%" android:fromYDelta="25%" android:interpolator="@android:anim/linear_interpolator" android:toXDelta="0%" android:toYDelta="0%"></translate> <!--Fade In--> <alpha android:duration="300" android:fromAlpha="0.0" android:interpolator="@android:anim/decelerate_interpolator" android:toAlpha="1.0"></alpha> </set>  <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true"> <!--Move--> <translate android:duration="300" android:fromXDelta="75%" android:fromYDelta="210%" android:interpolator="@android:anim/linear_interpolator" android:toXDelta="0%" android:toYDelta="0%"></translate> <!--Fade In--> <alpha android:duration="300" android:fromAlpha="0.0" android:interpolator="@android:anim/decelerate_interpolator" android:toAlpha="1.0"></alpha> </set>  <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true"> <!--Move--> <translate android:duration="300" android:fromXDelta="25%" android:fromYDelta="400%" android:interpolator="@android:anim/linear_interpolator" android:toXDelta="0%" android:toYDelta="0%"></translate> <!--Fade In--> <alpha android:duration="300" android:fromAlpha="0.0" android:interpolator="@android:anim/decelerate_interpolator" android:toAlpha="1.0"></alpha> </set> 


2.1 disparition
sous-menu du bas

 <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true"> <!--Move--> <translate android:duration="300" android:fromXDelta="-125%" android:fromYDelta="-25%" android:interpolator="@android:anim/linear_interpolator" android:toXDelta="0%" android:toYDelta="0%"></translate> <!--Fade Out--> <alpha android:duration="300" android:fromAlpha="1.0" android:interpolator="@android:anim/accelerate_interpolator" android:toAlpha="0.0"></alpha> </set>  <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true"> <!--Move--> <translate android:duration="300" android:fromXDelta="-75%" android:fromYDelta="-210%" android:interpolator="@android:anim/linear_interpolator" android:toXDelta="0%" android:toYDelta="0%"></translate> <!--Fade Out--> <alpha android:duration="300" android:fromAlpha="1.0" android:interpolator="@android:anim/accelerate_interpolator" android:toAlpha="0.0"></alpha> </set>  <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true"> <!--Move--> <translate android:duration="300" android:fromXDelta="-25%" android:fromYDelta="-400%" android:interpolator="@android:anim/linear_interpolator" android:toXDelta="0%" android:toYDelta="0%"></translate> <!--Fade Out--> <alpha android:duration="300" android:fromAlpha="1.0" android:interpolator="@android:anim/accelerate_interpolator" android:toAlpha="0.0"></alpha> </set> 


3. Disposition de l'écran
 <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> <ua.karelov.beans.ui.custom.MyRelativeLayout android:id="@+id/l_root" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_transactions" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:clickable="true" android:fadingEdgeLength="20dp" android:focusable="true" android:requiresFadingEdge="vertical"/> </ua.karelov.beans.ui.custom.MyRelativeLayout> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginRight="16dp" android:layout_marginBottom="46dp" android:rotation="45" android:src="@drawable/close" app:layout_behavior=".ui.custom.FABScroll" /> <include layout="@layout/fab_layout" android:layout_width="match_parent" android:layout_height="500dp" android:layout_gravity="bottom|end" android:layout_marginBottom="46dp"/> </android.support.design.widget.CoordinatorLayout> 


4. Comportement du défilement FAB
 import android.content.Context; import android.support.annotation.NonNull; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.FloatingActionButton; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; public class FABScroll extends FloatingActionButton.Behavior { public FABScroll(Context context, AttributeSet attrs) { super(); } @Override public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); //child -> Floating Action Button if (dyConsumed > 0) { CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams(); int fab_bottomMargin = layoutParams.bottomMargin; child.animate().translationY(child.getHeight() + fab_bottomMargin).setInterpolator(new LinearInterpolator()).start(); } else if (dyConsumed < 0) { child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start(); } } @Override public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) { return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL; } } 


5. Nous hériterons de RelativeLayout pour gérer les clics sur un siège vide pour fermer le menu
 import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.RelativeLayout; public class MyRelativeLayout extends RelativeLayout { private boolean layoutClickable = true; public void setLayoutClickable(boolean layoutClickable) { this.layoutClickable = layoutClickable; } public boolean isLayoutClickable() { return layoutClickable; } public MyRelativeLayout(Context context) { super(context); } public MyRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // true if you do not want the children to be clickable. return !layoutClickable; } } 


6. Connectez tout dans le fragment
 private static final double transitionSmall = 0.25; private static final double transitionMediumX = 0.75; private static final double transitionMediumY = 2.1; private static final double transitionBigY = 4; @BindView(R.id.fab) FloatingActionButton fab; @BindView(R.id.fab_spending) CardView fabSpending; @BindView(R.id.fab_income) CardView fabIncome; @BindView(R.id.fab_transfer) CardView fabTransfer; //Animations Animation fadeIn; Animation fadeOut; Animation showFabSpending; Animation hideFabSpending; Animation showFabIncome; Animation hideFabIncome; Animation showFabTransfer; Animation hideFabTransfer; @Override protected void initView() { super.initView(); //Animations fadeIn = AnimationUtils.loadAnimation(getActivity(), R.anim.fade_in); fadeOut = AnimationUtils.loadAnimation(getActivity(), R.anim.fade_out); showFabSpending = AnimationUtils.loadAnimation(getActivity(), R.anim.fab1_show); hideFabSpending = AnimationUtils.loadAnimation(getActivity(), R.anim.fab1_hide); showFabIncome = AnimationUtils.loadAnimation(getActivity(), R.anim.fab2_show); hideFabIncome = AnimationUtils.loadAnimation(getActivity(), R.anim.fab2_hide); showFabTransfer = AnimationUtils.loadAnimation(getActivity(), R.anim.fab3_show); hideFabTransfer = AnimationUtils.loadAnimation(getActivity(), R.anim.fab3_hide); } @OnTouch(R.id.l_root) public boolean clickRoot() { if(!lRoot.isLayoutClickable()) { //Close FAB menu hideFAB(); } return true; } private void expandFAB() { ViewCompat.animate(fab). rotation(-90). withLayer(). setDuration(300). start(); lRoot.setLayoutClickable(false); lRoot.startAnimation(fadeOut); //Floating Action Button 1 FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) fabSpending.getLayoutParams(); layoutParams.rightMargin += (int) (fabSpending.getWidth() * (1 + transitionSmall)); layoutParams.bottomMargin += (int) (fabSpending.getHeight() * transitionSmall); fabSpending.setLayoutParams(layoutParams); fabSpending.startAnimation(showFabSpending); fabSpending.setClickable(true); //Floating Action Button 2 FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) fabIncome.getLayoutParams(); layoutParams2.rightMargin += (int) (fabIncome.getWidth() * transitionMediumX); layoutParams2.bottomMargin += (int) (fabIncome.getHeight() * transitionMediumY); fabIncome.setLayoutParams(layoutParams2); fabIncome.startAnimation(showFabIncome); fabIncome.setClickable(true); //Floating Action Button 3 FrameLayout.LayoutParams layoutParams3 = (FrameLayout.LayoutParams) fabTransfer.getLayoutParams(); layoutParams3.rightMargin += (int) (fabTransfer.getWidth() * transitionSmall); layoutParams3.bottomMargin += (int) (fabTransfer.getHeight() * transitionBigY); fabTransfer.setLayoutParams(layoutParams3); fabTransfer.startAnimation(showFabTransfer); fabTransfer.setClickable(true); } private void hideFAB() { ViewCompat.animate(fab). rotation(45f). withLayer(). setDuration(300). start(); lRoot.setLayoutClickable(true); lRoot.startAnimation(fadeIn); //Floating Action Button 1 FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) fabSpending.getLayoutParams(); layoutParams.rightMargin -= (int) (fabSpending.getWidth() * (1 + transitionSmall)); layoutParams.bottomMargin -= (int) (fabSpending.getHeight() * transitionSmall); fabSpending.setLayoutParams(layoutParams); fabSpending.startAnimation(hideFabSpending); fabSpending.setClickable(false); //Floating Action Button 2 FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) fabIncome.getLayoutParams(); layoutParams2.rightMargin -= (int) (fabIncome.getWidth() * transitionMediumX); layoutParams2.bottomMargin -= (int) (fabIncome.getHeight() * transitionMediumY); fabIncome.setLayoutParams(layoutParams2); fabIncome.startAnimation(hideFabIncome); fabIncome.setClickable(false); //Floating Action Button 3 FrameLayout.LayoutParams layoutParams3 = (FrameLayout.LayoutParams) fabTransfer.getLayoutParams(); layoutParams3.rightMargin -= (int) (fabTransfer.getWidth() * transitionSmall); layoutParams3.bottomMargin -= (int) (fabTransfer.getHeight() * transitionBigY); fabTransfer.setLayoutParams(layoutParams3); fabTransfer.startAnimation(hideFabTransfer); fabTransfer.setClickable(false); } private void initMenu() { fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(lRoot.isLayoutClickable()) { //Display FAB menu expandFAB(); } else { //Close FAB menu hideFAB(); } } }); } //     (          ) fab.animate().translationY(0).setInterpolator(new LinearInterpolator()).start(); 

Source: https://habr.com/ru/post/fr455728/


All Articles