From dee36345cd65f2e083262488ea5c215211279dcc Mon Sep 17 00:00:00 2001 From: nico kiewiet Date: Thu, 11 Jul 2019 10:57:09 +0200 Subject: [PATCH 1/3] Fix #122: Add FAN expansion mode for speed dial items. Change FabWithLabelView to ConstraintLayout. - This allows both horizontal and vertical expansion modes to show labels Set layout params for each menu item to mimic expansion mode for top/bottom/left and right Expose hiding of label from FabWithLabelView - For FAN expansion mode, the text is displayed below mini fab - for right or left expansion modes, the text is always disabled. Same as existing behaviour Format code according to code style Update sample app to show FAN expansion mode --- library/build.gradle | 1 + .../android/speeddial/FabWithLabelView.java | 83 +++++-- .../android/speeddial/SpeedDialView.java | 212 +++++++++++++++--- .../res/layout/sd_fab_with_label_view.xml | 16 +- library/src/main/res/values/attrs.xml | 1 + .../sample/usecases/BaseUseCaseActivity.java | 2 + sample/src/main/res/layout/activity_main.xml | 1 + .../src/main/res/menu/menu_base_use_case.xml | 3 + sample/src/main/res/values-it/strings.xml | 1 + sample/src/main/res/values/strings.xml | 1 + 10 files changed, 256 insertions(+), 65 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 09bfadd9..d6e1c583 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -51,5 +51,6 @@ dependencies { api "androidx.appcompat:appcompat:$versions.androidx_appcompat" api "com.google.android.material:material:$versions.android_material" api "androidx.cardview:cardview:$versions.androidx_cardview" + api "androidx.constraintlayout:constraintlayout:$versions.androidx_constraintlayout" annotationProcessor "com.uber.nullaway:nullaway:$versions.nullaway" } diff --git a/library/src/main/java/com/leinardi/android/speeddial/FabWithLabelView.java b/library/src/main/java/com/leinardi/android/speeddial/FabWithLabelView.java index d2eef725..4f270e23 100644 --- a/library/src/main/java/com/leinardi/android/speeddial/FabWithLabelView.java +++ b/library/src/main/java/com/leinardi/android/speeddial/FabWithLabelView.java @@ -26,31 +26,34 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; -import android.view.Gravity; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; +import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.cardview.widget.CardView; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.res.ResourcesCompat; import androidx.core.graphics.drawable.DrawableCompat; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.leinardi.android.speeddial.SpeedDialView.OnActionSelectedListener; +import java.lang.annotation.Retention; + import static com.google.android.material.floatingactionbutton.FloatingActionButton.SIZE_AUTO; import static com.google.android.material.floatingactionbutton.FloatingActionButton.SIZE_MINI; import static com.google.android.material.floatingactionbutton.FloatingActionButton.SIZE_NORMAL; import static com.leinardi.android.speeddial.SpeedDialActionItem.RESOURCE_NOT_SET; +import static java.lang.annotation.RetentionPolicy.SOURCE; /** * View that contains fab button and its label. */ @SuppressWarnings({"unused", "WeakerAccess"}) -public class FabWithLabelView extends LinearLayout { +public class FabWithLabelView extends ConstraintLayout { private static final String TAG = FabWithLabelView.class.getSimpleName(); private TextView mLabelTextView; @@ -66,6 +69,15 @@ public class FabWithLabelView extends LinearLayout { private float mLabelCardViewElevation; @Nullable private Drawable mLabelCardViewBackground; + @Orientation + private int mOrientation = Orientation.HORIZONTAL; + + @Retention(SOURCE) + @IntDef({Orientation.HORIZONTAL, Orientation.VERTICAL}) + public @interface Orientation { + int HORIZONTAL = 0; + int VERTICAL = 1; + } public FabWithLabelView(Context context) { super(context); @@ -92,15 +104,10 @@ public void setVisibility(int visibility) { } } - @Override - public void setOrientation(int orientation) { - super.setOrientation(orientation); + public void setOrientation(@Orientation int orientation) { + mOrientation = orientation; setFabSize(mCurrentFabSize); - if (orientation == VERTICAL) { - setLabelEnabled(false); - } else { - setLabel(mLabelTextView.getText().toString()); - } + setLabel(mLabelTextView.getText().toString()); } /** @@ -113,9 +120,9 @@ public boolean isLabelEnabled() { /** * Enables or disables label of button. */ - private void setLabelEnabled(boolean enabled) { - mIsLabelEnabled = enabled; - mLabelCardView.setVisibility(enabled ? View.VISIBLE : View.GONE); + public void setLabelEnabled(boolean enabled) { + mIsLabelEnabled = enabled && !TextUtils.isEmpty(mLabelTextView.getText()); + mLabelCardView.setVisibility(mIsLabelEnabled ? View.VISIBLE : View.GONE); } /** @@ -254,7 +261,6 @@ private void init(Context context, @Nullable AttributeSet attrs) { mLabelCardView = rootView.findViewById(R.id.sd_label_container); setFabSize(SIZE_MINI); - setOrientation(LinearLayout.HORIZONTAL); setClipChildren(false); setClipToPadding(false); @@ -296,28 +302,59 @@ private void setFabSize(@FloatingActionButton.Size int fabSize) { int fabSizePx = fabSize == SIZE_NORMAL ? normalFabSizePx : miniFabSizePx; LayoutParams rootLayoutParams; LayoutParams fabLayoutParams = (LayoutParams) mFab.getLayoutParams(); - if (getOrientation() == HORIZONTAL) { + if (mOrientation == Orientation.HORIZONTAL) { rootLayoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, fabSizePx); - rootLayoutParams.gravity = Gravity.END; - + LayoutParams cardParams = getHorizontalLabelLayoutParams(mFab); + mLabelCardView.setLayoutParams(cardParams); + int cardViewId = mLabelCardView.getId(); + fabLayoutParams.endToEnd = LayoutParams.PARENT_ID; + fabLayoutParams.startToEnd = cardViewId; + fabLayoutParams.topToTop = LayoutParams.PARENT_ID; + fabLayoutParams.bottomToBottom = LayoutParams.PARENT_ID; if (fabSize == SIZE_NORMAL) { int excessMargin = (normalFabSizePx - miniFabSizePx) / 2; fabLayoutParams.setMargins(fabSideMarginPx - excessMargin, 0, fabSideMarginPx - excessMargin, 0); } else { fabLayoutParams.setMargins(fabSideMarginPx, 0, fabSideMarginPx, 0); - } } else { - rootLayoutParams = new LayoutParams(fabSizePx, ViewGroup.LayoutParams.WRAP_CONTENT); - rootLayoutParams.gravity = Gravity.CENTER_VERTICAL; fabLayoutParams.setMargins(0, 0, 0, 0); + rootLayoutParams = new LayoutParams(fabSizePx, ViewGroup.LayoutParams.WRAP_CONTENT); + int cardViewId = mLabelCardView.getId(); + LayoutParams cardParams = getVerticalLabelLayoutParams(mFab); + mLabelCardView.setLayoutParams(cardParams); + fabLayoutParams.endToEnd = LayoutParams.PARENT_ID; + fabLayoutParams.startToStart = LayoutParams.PARENT_ID; + fabLayoutParams.topToTop = LayoutParams.PARENT_ID; + fabLayoutParams.bottomToTop = cardViewId; } - setLayoutParams(rootLayoutParams); mFab.setLayoutParams(fabLayoutParams); mCurrentFabSize = fabSize; } + private static LayoutParams getVerticalLabelLayoutParams(View mainFabView) { + int fabId = mainFabView.getId(); + LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + layoutParams.startToStart = fabId; + layoutParams.endToEnd = fabId; + layoutParams.topToBottom = fabId; + layoutParams.bottomToBottom = LayoutParams.PARENT_ID; + return layoutParams; + } + + private static LayoutParams getHorizontalLabelLayoutParams(View mainFabView) { + int fabId = mainFabView.getId(); + LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + layoutParams.startToStart = LayoutParams.PARENT_ID; + layoutParams.endToStart = fabId; + layoutParams.topToTop = LayoutParams.PARENT_ID; + layoutParams.bottomToBottom = LayoutParams.PARENT_ID; + return layoutParams; + } + /** * Sets fab drawable. * @@ -335,7 +372,7 @@ private void setFabIcon(@Nullable Drawable mDrawable) { private void setLabel(@Nullable CharSequence sequence) { if (!TextUtils.isEmpty(sequence)) { mLabelTextView.setText(sequence); - setLabelEnabled(getOrientation() == HORIZONTAL); + setLabelEnabled(true); } else { setLabelEnabled(false); } diff --git a/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java b/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java index 395d77ce..67455578 100755 --- a/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java +++ b/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java @@ -34,7 +34,6 @@ import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; -import android.widget.LinearLayout; import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; @@ -47,6 +46,7 @@ import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.PopupMenu; import androidx.cardview.widget.CardView; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.RecyclerView; @@ -67,13 +67,14 @@ import static com.leinardi.android.speeddial.SpeedDialActionItem.RESOURCE_NOT_SET; import static com.leinardi.android.speeddial.SpeedDialView.ExpansionMode.BOTTOM; +import static com.leinardi.android.speeddial.SpeedDialView.ExpansionMode.FAN; import static com.leinardi.android.speeddial.SpeedDialView.ExpansionMode.LEFT; import static com.leinardi.android.speeddial.SpeedDialView.ExpansionMode.RIGHT; import static com.leinardi.android.speeddial.SpeedDialView.ExpansionMode.TOP; import static java.lang.annotation.RetentionPolicy.SOURCE; @SuppressWarnings({"unused", "WeakerAccess", "UnusedReturnValue"}) -public class SpeedDialView extends LinearLayout implements CoordinatorLayout.AttachedBehavior { +public class SpeedDialView extends ConstraintLayout implements CoordinatorLayout.AttachedBehavior { private static final String TAG = SpeedDialView.class.getSimpleName(); private static final String STATE_KEY_SUPER = "superState"; private static final String STATE_KEY_IS_OPEN = "isOpen"; @@ -83,7 +84,7 @@ public class SpeedDialView extends LinearLayout implements CoordinatorLayout.Att private static final int MAIN_FAB_HORIZONTAL_MARGIN_IN_DP = 4; private static final int MAIN_FAB_VERTICAL_MARGIN_IN_DP = -2; private final InstanceState mInstanceState = new InstanceState(); - private List mFabWithLabelViews = new ArrayList<>(); + private final List mFabWithLabelViews = new ArrayList<>(); @Nullable private Drawable mMainFabClosedDrawable = null; @Nullable @@ -113,6 +114,7 @@ public boolean onActionSelected(SpeedDialActionItem actionItem) { } } }; + private int mCircleRadius = -1; public SpeedDialView(Context context) { super(context); @@ -152,16 +154,15 @@ private void setExpansionMode(@ExpansionMode int expansionMode, boolean force) { switch (expansionMode) { case TOP: case BOTTOM: - setOrientation(VERTICAL); for (FabWithLabelView fabWithLabelView : mFabWithLabelViews) { - fabWithLabelView.setOrientation(HORIZONTAL); + fabWithLabelView.setOrientation(FabWithLabelView.Orientation.HORIZONTAL); } break; case LEFT: case RIGHT: - setOrientation(HORIZONTAL); + case FAN: for (FabWithLabelView fabWithLabelView : mFabWithLabelViews) { - fabWithLabelView.setOrientation(VERTICAL); + fabWithLabelView.setOrientation(FabWithLabelView.Orientation.VERTICAL); } break; } @@ -172,11 +173,6 @@ private void setExpansionMode(@ExpansionMode int expansionMode, boolean force) { } } - @Override - public void setOrientation(int orientation) { - super.setOrientation(orientation); - } - public void show() { show(null); } @@ -359,11 +355,16 @@ public FabWithLabelView addActionItem(SpeedDialActionItem actionItem, int positi return replaceActionItem(oldView.getSpeedDialActionItem(), actionItem); } else { FabWithLabelView newView = actionItem.createFabWithLabelView(getContext()); - newView.setOrientation(getOrientation() == VERTICAL ? HORIZONTAL : VERTICAL); + int expansionMode = mInstanceState.mExpansionMode; + newView.setOrientation(expansionMode == TOP || expansionMode == BOTTOM ? + FabWithLabelView.Orientation.HORIZONTAL : FabWithLabelView.Orientation.VERTICAL); + newView.setLabelEnabled(expansionMode == TOP || expansionMode == BOTTOM || expansionMode == FAN); newView.setOnActionSelectedListener(mOnActionSelectedProxyListener); - int layoutPosition = getLayoutPosition(position); - addView(newView, layoutPosition); + mFabWithLabelViews.add(position, newView); + layoutItemsAccordingToExpansionMode(expansionMode, new ArrayList<>(mFabWithLabelViews)); + addView(newView); + if (isOpen()) { if (animate) { showWithAnimationFabWithLabelView(newView, 0); @@ -375,6 +376,150 @@ public FabWithLabelView addActionItem(SpeedDialActionItem actionItem, int positi } } + private void layoutItemsAccordingToExpansionMode(int expansionMode, List fabWithLabelViews) { + if (expansionMode == FAN) { + layoutItemsForFanExpansion(fabWithLabelViews, mMainFab); + } else if (expansionMode == TOP) { + layoutItemsForTopExpansion(fabWithLabelViews, mMainFab); + } else if (expansionMode == BOTTOM) { + layoutItemsForBottomExpansion(fabWithLabelViews, mMainFab); + } else if (expansionMode == RIGHT) { + layoutItemsForRightExpansion(fabWithLabelViews, mMainFab); + } else if (expansionMode == LEFT) { + layoutItemsForLeftExpansion(fabWithLabelViews, mMainFab); + } + } + + private void layoutItemsForFanExpansion(List fabWithLabelViews, View mainFabView) { + LayoutParams mainFabParams = createMainFabLayoutParams(getContext()); + mainFabParams.bottomToBottom = LayoutParams.PARENT_ID; + mainFabParams.endToEnd = LayoutParams.PARENT_ID; + mainFabParams.startToStart = LayoutParams.PARENT_ID; + mainFabView.setLayoutParams(mainFabParams); + for (int i = 0; i < fabWithLabelViews.size(); i++) { + FabWithLabelView fabWithLabelView = fabWithLabelViews.get(i); + LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + params.circleAngle = getLayoutAngle(fabWithLabelViews, i); + params.circleRadius = mCircleRadius; + params.circleConstraint = mainFabView.getId(); + fabWithLabelView.setLayoutParams(params); + } + } + + private static float getLayoutAngle(List fabWithLabelView, int position) { + int numberOfItems = fabWithLabelView.size(); + if (numberOfItems == 0) { + return 0f; + } else { + float segmentSize = 180f / numberOfItems; + return (segmentSize * position + 270 + segmentSize / 2) % 360; + } + } + + private void layoutItemsForTopExpansion(List fabWithLabelViews, View mainFabView) { + LayoutParams fabParams = createMainFabLayoutParams(getContext()); + fabParams.bottomToBottom = LayoutParams.PARENT_ID; + fabParams.endToEnd = LayoutParams.PARENT_ID; + mainFabView.setLayoutParams(fabParams); + if (fabWithLabelViews.size() == 1) { + //first item, align bottom to top of fab + FabWithLabelView fabWithLabelView = fabWithLabelViews.get(0); + LayoutParams params = new LayoutParams(fabWithLabelView.getLayoutParams()); + params.bottomToTop = mainFabView.getId(); + params.endToEnd = mainFabView.getId(); + fabWithLabelView.setLayoutParams(params); + } else if (fabWithLabelViews.size() > 1) { + for (int i = 1; i < fabWithLabelViews.size(); i++) { + FabWithLabelView previousView = fabWithLabelViews.get(i - 1); + FabWithLabelView currentView = fabWithLabelViews.get(i); + LayoutParams updated = new LayoutParams(currentView.getLayoutParams()); + updated.bottomToTop = previousView.getId(); + updated.endToEnd = mainFabView.getId(); + currentView.setLayoutParams(updated); + } + } + } + + private void layoutItemsForBottomExpansion(List fabWithLabelViews, View mainFabView) { + LayoutParams mainFabParams = createMainFabLayoutParams(getContext()); + mainFabParams.endToEnd = LayoutParams.PARENT_ID; + mainFabView.setLayoutParams(mainFabParams); + + if (fabWithLabelViews.size() == 1) { + //first item, align top to bottom of fab + FabWithLabelView fabWithLabelView = fabWithLabelViews.get(0); + LayoutParams params = new LayoutParams(fabWithLabelView.getLayoutParams()); + params.topToBottom = mainFabView.getId(); + params.endToEnd = mainFabView.getId(); + fabWithLabelView.setLayoutParams(params); + } else if (fabWithLabelViews.size() > 1) { + for (int i = 1; i < fabWithLabelViews.size(); i++) { + FabWithLabelView previousView = fabWithLabelViews.get(i - 1); + FabWithLabelView currentView = fabWithLabelViews.get(i); + LayoutParams params = new LayoutParams(currentView.getLayoutParams()); + params.topToBottom = previousView.getId(); + params.endToEnd = mainFabView.getId(); + currentView.setLayoutParams(params); + } + } + } + + private void layoutItemsForRightExpansion(List fabWithLabelViews, View mainFabView) { + LayoutParams mainFabParams = createMainFabLayoutParams(getContext()); + mainFabParams.startToStart = LayoutParams.PARENT_ID; + mainFabView.setLayoutParams(mainFabParams); + + if (fabWithLabelViews.size() == 1) { + //first item, align top to right of fab + FabWithLabelView fabWithLabelView = fabWithLabelViews.get(0); + LayoutParams params = new LayoutParams(fabWithLabelView.getLayoutParams()); + params.startToEnd = mainFabView.getId(); + params.topToTop = mainFabView.getId(); + params.bottomToBottom = mainFabView.getId(); + fabWithLabelView.setLayoutParams(params); + } else if (fabWithLabelViews.size() > 1) { + for (int i = 1; i < fabWithLabelViews.size(); i++) { + FabWithLabelView previousView = fabWithLabelViews.get(i - 1); + FabWithLabelView currentView = fabWithLabelViews.get(i); + LayoutParams params = new LayoutParams(currentView.getLayoutParams()); + params.startToEnd = previousView.getId(); + params.topToTop = mainFabView.getId(); + params.bottomToBottom = mainFabView.getId(); + currentView.setLayoutParams(params); + } + } + } + + private void layoutItemsForLeftExpansion(List fabWithLabelViews, View mainFabView) { + LayoutParams mainFabParams = createMainFabLayoutParams(getContext()); + mainFabParams.endToEnd = LayoutParams.PARENT_ID; + mainFabView.setLayoutParams(mainFabParams); + + if (fabWithLabelViews.size() == 1) { + //first item, align top to left of fab + FabWithLabelView fabWithLabelView = fabWithLabelViews.get(0); + LayoutParams params = new LayoutParams(fabWithLabelView.getLayoutParams()); + params.endToStart = mainFabView.getId(); + params.topToTop = mainFabView.getId(); + params.bottomToBottom = mainFabView.getId(); + fabWithLabelView.setLayoutParams(params); + LayoutParams fabParams = new LayoutParams(mainFabView.getLayoutParams()); + fabParams.startToEnd = fabWithLabelView.getId(); + mainFabView.setLayoutParams(fabParams); + } else if (fabWithLabelViews.size() > 1) { + for (int i = 1; i < fabWithLabelViews.size(); i++) { + FabWithLabelView previousView = fabWithLabelViews.get(i - 1); + FabWithLabelView currentView = fabWithLabelViews.get(i); + LayoutParams params = new LayoutParams(currentView.getLayoutParams()); + params.endToStart = previousView.getId(); + params.topToTop = mainFabView.getId(); + params.bottomToBottom = mainFabView.getId(); + currentView.setLayoutParams(params); + } + } + } + /** * Removes the {@link SpeedDialActionItem} at the specified position in this list. Shifts any subsequent elements * to the left (subtracts one from their indices). @@ -634,14 +779,6 @@ protected void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(state); } - private int getLayoutPosition(int position) { - if (getExpansionMode() == TOP || getExpansionMode() == LEFT) { - return mFabWithLabelViews.size() - position; - } else { - return position + 1; - } - } - @Nullable private SpeedDialActionItem removeActionItem(@Nullable FabWithLabelView view, @Nullable Iterator it, @@ -654,6 +791,8 @@ private SpeedDialActionItem removeActionItem(@Nullable FabWithLabelView view, mFabWithLabelViews.remove(view); } + layoutItemsAccordingToExpansionMode(getExpansionMode(), new ArrayList<>(mFabWithLabelViews)); + if (isOpen()) { if (mFabWithLabelViews.isEmpty()) { close(); @@ -678,7 +817,7 @@ private SpeedDialActionItem removeActionItem(@Nullable FabWithLabelView view) { } private void init(Context context, @Nullable AttributeSet attrs) { - mMainFab = createMainFab(); + mMainFab = createMainFab(attrs); addView(mMainFab); setClipChildren(false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -710,6 +849,8 @@ private void init(Context context, @Nullable AttributeSet attrs) { .SpeedDialView_sdMainFabOpenedBackgroundColor, getMainFabOpenedBackgroundColor())); mOverlayLayoutId = styledAttrs.getResourceId(R.styleable.SpeedDialView_sdOverlayLayout, RESOURCE_NOT_SET); + ConstraintLayout.LayoutParams constraintLayoutParams = new LayoutParams(context, attrs); + mCircleRadius = constraintLayoutParams.circleRadius; } catch (Exception e) { Log.e(TAG, "Failure setting FabWithLabelView icon", e); } finally { @@ -717,14 +858,9 @@ private void init(Context context, @Nullable AttributeSet attrs) { } } - private FloatingActionButton createMainFab() { - FloatingActionButton floatingActionButton = new FloatingActionButton(getContext()); - LayoutParams layoutParams = new LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - layoutParams.gravity = Gravity.END; - int marginHorizontal = UiUtils.dpToPx(getContext(), MAIN_FAB_HORIZONTAL_MARGIN_IN_DP); - int marginVertical = UiUtils.dpToPx(getContext(), MAIN_FAB_VERTICAL_MARGIN_IN_DP); - layoutParams.setMargins(marginHorizontal, marginVertical, marginHorizontal, marginVertical); + private FloatingActionButton createMainFab(@Nullable AttributeSet attrs) { + FloatingActionButton floatingActionButton = new FloatingActionButton(getContext(), attrs); + LayoutParams layoutParams = createMainFabLayoutParams(getContext()); floatingActionButton.setId(R.id.sd_main_fab); floatingActionButton.setUseCompatPadding(true); floatingActionButton.setLayoutParams(layoutParams); @@ -746,6 +882,15 @@ public void onClick(final View view) { return floatingActionButton; } + private static LayoutParams createMainFabLayoutParams(Context context) { + LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + int marginHorizontal = UiUtils.dpToPx(context, MAIN_FAB_HORIZONTAL_MARGIN_IN_DP); + int marginVertical = UiUtils.dpToPx(context, MAIN_FAB_VERTICAL_MARGIN_IN_DP); + layoutParams.setMargins(marginHorizontal, marginVertical, marginHorizontal, marginVertical); + return layoutParams; + } + private void toggle(boolean show, boolean animate) { if (show && mFabWithLabelViews.isEmpty()) { show = false; @@ -931,12 +1076,13 @@ public interface OnActionSelectedListener { } @Retention(SOURCE) - @IntDef({TOP, BOTTOM, LEFT, RIGHT}) + @IntDef({TOP, BOTTOM, LEFT, RIGHT, FAN}) public @interface ExpansionMode { int TOP = 0; int BOTTOM = 1; int LEFT = 2; int RIGHT = 3; + int FAN = 4; } private static class InstanceState implements Parcelable { diff --git a/library/src/main/res/layout/sd_fab_with_label_view.xml b/library/src/main/res/layout/sd_fab_with_label_view.xml index 8a6aed45..69b945c3 100755 --- a/library/src/main/res/layout/sd_fab_with_label_view.xml +++ b/library/src/main/res/layout/sd_fab_with_label_view.xml @@ -19,11 +19,17 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> + + - - diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index e7f74a01..45f9e428 100755 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -32,6 +32,7 @@ + diff --git a/sample/src/main/java/com/leinardi/android/speeddial/sample/usecases/BaseUseCaseActivity.java b/sample/src/main/java/com/leinardi/android/speeddial/sample/usecases/BaseUseCaseActivity.java index 4fcdf088..d005987a 100644 --- a/sample/src/main/java/com/leinardi/android/speeddial/sample/usecases/BaseUseCaseActivity.java +++ b/sample/src/main/java/com/leinardi/android/speeddial/sample/usecases/BaseUseCaseActivity.java @@ -101,6 +101,8 @@ public boolean onMenuItemClick(MenuItem item) { mSpeedDial.setExpansionMode(SpeedDialView.ExpansionMode.BOTTOM); } else if (id == R.id.action_expansion_mode_right) { mSpeedDial.setExpansionMode(SpeedDialView.ExpansionMode.RIGHT); + } else if (id == R.id.action_expansion_mode_fan) { + mSpeedDial.setExpansionMode(SpeedDialView.ExpansionMode.FAN); } else if (id == R.id.action_rotation_angle_0) { mSpeedDial.setMainFabAnimationRotateAngle(0); } else if (id == R.id.action_rotation_angle_45) { diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index ce25926c..8845a1e1 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -49,6 +49,7 @@ app:sdMainFabAnimationRotateAngle="90" app:sdMainFabClosedSrc="@drawable/ic_add_white_24dp" app:sdMainFabOpenedSrc="@drawable/ic_pencil_alt_white_24dp" + app:layout_constraintCircleRadius="150dp" app:sdOverlayLayout="@id/overlay" /> diff --git a/sample/src/main/res/menu/menu_base_use_case.xml b/sample/src/main/res/menu/menu_base_use_case.xml index bca02ff5..7ccc95a2 100644 --- a/sample/src/main/res/menu/menu_base_use_case.xml +++ b/sample/src/main/res/menu/menu_base_use_case.xml @@ -50,6 +50,9 @@ + Sinistra Basso Destra + Ventaglio Angolo rotazione 0 gradi 45 gradi diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml index 87c8ea73..44d79d7d 100755 --- a/sample/src/main/res/values/strings.xml +++ b/sample/src/main/res/values/strings.xml @@ -33,6 +33,7 @@ Left Bottom Right + Fan Rotation angle 0 degrees 45 degrees From 6fc0351400d67a811edef287f2eeaf2cee4e9baf Mon Sep 17 00:00:00 2001 From: nico kiewiet Date: Fri, 16 Aug 2019 07:33:35 +0200 Subject: [PATCH 2/3] Center horizontally on main fab when expanding TOP and BOTTOM --- .../java/com/leinardi/android/speeddial/SpeedDialView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java b/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java index 67455578..cf821bf1 100755 --- a/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java +++ b/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java @@ -428,6 +428,7 @@ private void layoutItemsForTopExpansion(List fabWithLabelViews LayoutParams params = new LayoutParams(fabWithLabelView.getLayoutParams()); params.bottomToTop = mainFabView.getId(); params.endToEnd = mainFabView.getId(); + params.startToStart = mainFabView.getId(); fabWithLabelView.setLayoutParams(params); } else if (fabWithLabelViews.size() > 1) { for (int i = 1; i < fabWithLabelViews.size(); i++) { @@ -436,6 +437,7 @@ private void layoutItemsForTopExpansion(List fabWithLabelViews LayoutParams updated = new LayoutParams(currentView.getLayoutParams()); updated.bottomToTop = previousView.getId(); updated.endToEnd = mainFabView.getId(); + updated.startToStart = mainFabView.getId(); currentView.setLayoutParams(updated); } } @@ -452,6 +454,7 @@ private void layoutItemsForBottomExpansion(List fabWithLabelVi LayoutParams params = new LayoutParams(fabWithLabelView.getLayoutParams()); params.topToBottom = mainFabView.getId(); params.endToEnd = mainFabView.getId(); + params.startToStart = mainFabView.getId(); fabWithLabelView.setLayoutParams(params); } else if (fabWithLabelViews.size() > 1) { for (int i = 1; i < fabWithLabelViews.size(); i++) { @@ -460,6 +463,7 @@ private void layoutItemsForBottomExpansion(List fabWithLabelVi LayoutParams params = new LayoutParams(currentView.getLayoutParams()); params.topToBottom = previousView.getId(); params.endToEnd = mainFabView.getId(); + params.startToStart = mainFabView.getId(); currentView.setLayoutParams(params); } } From 0d6244facbfe119c91ae648cfc420cb8329020d9 Mon Sep 17 00:00:00 2001 From: nico kiewiet Date: Fri, 16 Aug 2019 07:38:17 +0200 Subject: [PATCH 3/3] Limit label length when expansion mode is FAN. This is to mitigate the affect of long labels overlapping. --- .../com/leinardi/android/speeddial/FabWithLabelView.java | 7 +++++++ .../java/com/leinardi/android/speeddial/SpeedDialView.java | 2 ++ 2 files changed, 9 insertions(+) diff --git a/library/src/main/java/com/leinardi/android/speeddial/FabWithLabelView.java b/library/src/main/java/com/leinardi/android/speeddial/FabWithLabelView.java index 4f270e23..66fa5ed8 100644 --- a/library/src/main/java/com/leinardi/android/speeddial/FabWithLabelView.java +++ b/library/src/main/java/com/leinardi/android/speeddial/FabWithLabelView.java @@ -132,6 +132,13 @@ public CardView getLabelBackground() { return mLabelCardView; } + /** + * Returns FAB label text view. + */ + public TextView getLabelTextView() { + return mLabelTextView; + } + /** * Returns the {@link FloatingActionButton}. */ diff --git a/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java b/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java index cf821bf1..c9184c9c 100755 --- a/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java +++ b/library/src/main/java/com/leinardi/android/speeddial/SpeedDialView.java @@ -83,6 +83,7 @@ public class SpeedDialView extends ConstraintLayout implements CoordinatorLayout private static final int ACTION_ANIM_DELAY = 25; private static final int MAIN_FAB_HORIZONTAL_MARGIN_IN_DP = 4; private static final int MAIN_FAB_VERTICAL_MARGIN_IN_DP = -2; + private static final int MAX_EMS_OF_LABEL_FOR_FAN_EXPANSION = 5; private final InstanceState mInstanceState = new InstanceState(); private final List mFabWithLabelViews = new ArrayList<>(); @Nullable @@ -398,6 +399,7 @@ private void layoutItemsForFanExpansion(List fabWithLabelViews mainFabView.setLayoutParams(mainFabParams); for (int i = 0; i < fabWithLabelViews.size(); i++) { FabWithLabelView fabWithLabelView = fabWithLabelViews.get(i); + fabWithLabelView.getLabelTextView().setMaxEms(MAX_EMS_OF_LABEL_FOR_FAN_EXPANSION); LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.circleAngle = getLayoutAngle(fabWithLabelViews, i);