Instantly share code, notes, and snippets.
Last active
January 12, 2018 12:43
-
Star
0
(0)
You must be signed in to star a gist -
Fork
1
(1)
You must be signed in to fork a gist
-
Save chantellosejo/eb315d54be10ce90511f to your computer and use it in GitHub Desktop.
Code to support a floating action button using Material design and animations that expands to show additional actions; supports API 14+
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.bigoven.android.widgets; | |
import android.animation.ObjectAnimator; | |
import android.annotation.TargetApi; | |
import android.content.Context; | |
import android.os.Build; | |
import android.util.AttributeSet; | |
import android.view.LayoutInflater; | |
import android.view.MotionEvent; | |
import android.view.View; | |
import android.widget.LinearLayout; | |
import android.widget.RelativeLayout; | |
import com.bigoven.android.R; | |
import com.bigoven.android.utilities.UiHelper; | |
import java.util.List; | |
/** | |
* | |
This class is a custom layout that allows the user to add a primary floating action button with secondary actions | |
that expand when the primary button is clicked. | |
Copyright (C) 2015 Chantell Osejo | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <http://www.gnu.org/licenses/> | |
*/ | |
public class ExpandableFloatingActionButtonLayout extends RelativeLayout { | |
private final static int DEFAULT_MARGIN = UiHelper.convertDpToPixel(16); | |
private final static int SECONDARY_DEFAULT_MARGIN = UiHelper.convertDpToPixel(8); | |
private final LinearLayout.LayoutParams mPrimaryLayoutParams = new LinearLayout.LayoutParams(FloatingActionButton.GOOGLE_DESIGN_GUIDELINE_RECOMMENDED_DIAMETER_PX, FloatingActionButton.GOOGLE_DESIGN_GUIDELINE_RECOMMENDED_DIAMETER_PX); | |
private final LinearLayout.LayoutParams mSecondaryLayoutParams = new LinearLayout.LayoutParams(FloatingActionButton.GOOGLE_DESIGN_GUIDELINE_RECOMMENDED_MINI_DIAMETER_PX, FloatingActionButton.GOOGLE_DESIGN_GUIDELINE_RECOMMENDED_MINI_DIAMETER_PX); | |
private LinearLayout mButtonContainer; | |
private boolean mExpanded = false; | |
private CompositeOnClickListener mPrimaryButtonOnClickListener; | |
public ExpandableFloatingActionButtonLayout(Context context) { | |
this(context, null); | |
} | |
public ExpandableFloatingActionButtonLayout(Context context, AttributeSet attrs) { | |
this(context, attrs, 0); | |
} | |
public ExpandableFloatingActionButtonLayout(Context context, AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
init(context); | |
} | |
@TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
public ExpandableFloatingActionButtonLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | |
super(context, attrs, defStyleAttr, defStyleRes); | |
init(context); | |
} | |
private void init(Context context) { | |
LayoutInflater.from(context).inflate(R.layout.expandable_floating_action_button_layout, this); | |
mButtonContainer = (LinearLayout) findViewById(R.id.container); | |
mPrimaryLayoutParams.setMargins(DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN); | |
mSecondaryLayoutParams.setMargins(0, SECONDARY_DEFAULT_MARGIN, 0, SECONDARY_DEFAULT_MARGIN); | |
mPrimaryButtonOnClickListener = new CompositeOnClickListener(); | |
} | |
public void addFloatingActionButtons(final FloatingActionButton primaryFloatingActionButton, final List<FloatingActionButton> secondaryActionButtons) { | |
// If this gets called more than once, then we will clean-slate it first. | |
mButtonContainer.removeAllViews(); | |
mButtonContainer.addView(primaryFloatingActionButton, mPrimaryLayoutParams); | |
final ObjectAnimator rotate = ObjectAnimator.ofFloat(primaryFloatingActionButton, ROTATION, 0f, 180f); | |
rotate.setDuration(200); | |
for (FloatingActionButton secondaryActionButton : secondaryActionButtons) { | |
secondaryActionButton.setVisibility(View.GONE); | |
mButtonContainer.addView(secondaryActionButton, 0, mSecondaryLayoutParams); | |
} | |
mPrimaryButtonOnClickListener.addOnClickListener(new OnClickListener() { | |
@Override | |
public void onClick(View view) { | |
mExpanded = !mExpanded; | |
for (int i = 0; i < secondaryActionButtons.size(); i++) { | |
// Increase the delay for every additional button, so we get the effect of a "ripple" of buttons being added | |
// We'll reverse this delay if we're hiding them | |
secondaryActionButtons.get(mExpanded ? i : secondaryActionButtons.size() - 1 - i).setVisibilityAfterDelay(mExpanded ? View.VISIBLE : View.GONE, (i + 1) * 100); | |
} | |
rotate.start(); | |
} | |
}); | |
primaryFloatingActionButton.setOnClickListener(mPrimaryButtonOnClickListener); | |
setOnTouchListener(new OnTouchListener() { | |
@Override | |
public boolean onTouch(View view, MotionEvent motionEvent) { | |
if (motionEvent.getAction() == MotionEvent.ACTION_UP && mExpanded) { | |
primaryFloatingActionButton.performClick(); | |
} | |
return false; | |
} | |
}); | |
} | |
public void setPrimaryButtonOnClickListener(OnClickListener onClickListener) { | |
mPrimaryButtonOnClickListener.addOnClickListener(onClickListener); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment