Kayan İşlem Düğmelerinin (FAB'ler) bir Yüzer Eylem Menüsü nasıl yapılacağını araştırıyordum ve this question numaralı telefondan geldim. Yorumlarda, this yorumunda FloatingActionMenu seçeneğini sunan bir sınıf verildi. Sınıfı ve xml dosyasını uyguladım ve tek bir sorunla çalışıyor.FloatingActionMenu Sınıf Düğmeler Arasında Boşluk Ekleme
Sorun: Menüde dört FAB vardır, ilk önce menüyü genişletmek için tıklattığınız düğme. Tüm FAB'lar arasında boşluk olmasını istiyorum, ancak boşluk yok ve ben ekleyemiyorum.
<terranovaproductions.newcomicreader.FloatingActionMenu
android:id="@+id/fab_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<!--First button as menu button-->
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/search"
android:paddingBottom="@dimen/menu_button_margin"
fab:fabSize="normal"
android:id="@+id/fab_main" />
<!-- Other button as menu items-->
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_random"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/default_random"
android:src="@drawable/random"
android:paddingBottom="@dimen/menu_button_margin"
fab:fabSize="mini"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/default_search"
android:src="@drawable/search"
android:paddingBottom="@dimen/menu_button_margin"
fab:fabSize="mini"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_browser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/default_browser"
android:src="@drawable/browseropen"
android:paddingBottom="@dimen/menu_button_margin"
fab:fabSize="mini"/>
</terranovaproductions.newcomicreader.FloatingActionMenu>
Bu XML kodunun başka öğeleri ile RelativeLayout içinde:
FloatingActionMenu Class
public class FloatingActionMenu extends ViewGroup {
private static final long ANIMATION_DURATION = 300;
private FloatingActionButton mMenuButton;
private ArrayList<FloatingActionButton> mMenuItems;
private ArrayList<TextView> mMenuItemLabels;
private ArrayList<ItemAnimator> mMenuItemAnimators;
private int mItemMargin;
private AnimatorSet mOpenAnimatorSet = new AnimatorSet();
private AnimatorSet mCloseAnimatorSet = new AnimatorSet();
private static final int DEFAULT_CHILD_GRAVITY = Gravity.END | Gravity.BOTTOM;
private ImageView mIcon;
private boolean mOpen;
private boolean animating;
private boolean mIsSetClosedOnTouchOutside = true;
public interface OnMenuToggleListener {
void onMenuToggle(boolean opened);
}
public interface OnMenuItemClickListener {
void onMenuItemClick(FloatingActionMenu fam, int index, FloatingActionButton item);
}
private OnMenuItemClickListener onMenuItemClickListener;
private OnMenuToggleListener onMenuToggleListener;
public FloatingActionMenu(Context context) {
this(context, null, 0);
}
public FloatingActionMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatingActionMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mMenuItems = new ArrayList<>(5);
mMenuItemAnimators = new ArrayList<>(5);
mMenuItemLabels = new ArrayList<>(5);
mIcon = new ImageView(context);
}
@Override
protected void onFinishInflate() {
bringChildToFront(mMenuButton);
bringChildToFront(mIcon);
super.onFinishInflate();
}
@Override
public void addView(@NonNull View child, int index, LayoutParams params) {
super.addView(child, index, params);
if (getChildCount() > 1) {
if (child instanceof FloatingActionButton) {
addMenuItem((FloatingActionButton) child);
}
} else {
mMenuButton = (FloatingActionButton) child;
mIcon.setImageDrawable(mMenuButton.getDrawable());
addView(mIcon);
mMenuButton.setImageDrawable(null);
createDefaultIconAnimation();
mMenuButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
toggle();
}
});
}
}
public void toggle() {
if (!mOpen) {
open();
} else {
close();
}
}
public void open() {
d("open");
startOpenAnimator();
mOpen = true;
if (onMenuToggleListener != null) {
onMenuToggleListener.onMenuToggle(true);
}
}
public void close() {
startCloseAnimator();
mOpen = false;
if (onMenuToggleListener != null) {
onMenuToggleListener.onMenuToggle(true);
}
}
protected void startCloseAnimator() {
mCloseAnimatorSet.start();
for (ItemAnimator anim : mMenuItemAnimators) {
anim.startCloseAnimator();
}
}
protected void startOpenAnimator() {
mOpenAnimatorSet.start();
for (ItemAnimator anim : mMenuItemAnimators) {
anim.startOpenAnimator();
}
}
public void addMenuItem(FloatingActionButton item) {
mMenuItems.add(item);
mMenuItemAnimators.add(new ItemAnimator(item));
TextView button = new TextView(getContext());
button.setBackgroundResource(R.drawable.rounded_corners);
button.setPadding(8,8,8,8);
button.setTextColor(Color.WHITE);
button.setText(item.getContentDescription());
addView(button);
mMenuItemLabels.add(button);
item.setTag(button);
item.setOnClickListener(mOnItemClickListener);
button.setOnClickListener(mOnItemClickListener);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width;
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height;
final int count = getChildCount();
int maxChildWidth = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
for (int i = 0; i < mMenuItems.size(); i++) {
FloatingActionButton fab = mMenuItems.get(i);
TextView label = mMenuItemLabels.get(i);
maxChildWidth = Math.max(maxChildWidth, label.getMeasuredWidth() + fab.getMeasuredWidth() + mItemMargin);
}
maxChildWidth = Math.max(mMenuButton.getMeasuredWidth(), maxChildWidth);
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width = maxChildWidth;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
int heightSum = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
heightSum += child.getMeasuredHeight();
}
height = heightSum;
}
setMeasuredDimension(resolveSize(width, widthMeasureSpec),
resolveSize(height, heightMeasureSpec));
}
// Rect rect = new Rect();
// Paint paint = new Paint();
//
// @Override
// protected boolean drawChild(@NonNull Canvas canvas, @NonNull View child, long drawingTime) {
// boolean b = super.drawChild(canvas, child, drawingTime);
// paint.setColor(0xFFFF0000);
// paint.setStyle(Paint.Style.STROKE);
// rect.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
// canvas.drawRect(rect, paint);
// return b;
// }
@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
if (mIsSetClosedOnTouchOutside) {
return mGestureDetector.onTouchEvent(event);
} else {
return super.onTouchEvent(event);
}
}
GestureDetector mGestureDetector = new GestureDetector(getContext(),
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return mIsSetClosedOnTouchOutside && isOpened();
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
close();
return true;
}
});
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
System.out.println("onLayout:" + changed);
if (changed) {
int right = r - getPaddingRight();
int bottom = b - getPaddingBottom();
int top = bottom - mMenuButton.getMeasuredHeight();
mMenuButton.layout(right - mMenuButton.getMeasuredWidth(), top, right, bottom);
int dw = (mMenuButton.getMeasuredWidth() - mIcon.getMeasuredWidth())/2;
int dh = (mMenuButton.getMeasuredHeight() - mIcon.getMeasuredHeight())/2;
mIcon.layout(right - mIcon.getMeasuredWidth() - dw, bottom - mIcon.getMeasuredHeight() - dh, right - dw, bottom - dh);
for (int i = 0; i < mMenuItems.size(); i++) {
FloatingActionButton item = mMenuItems.get(i);
TextView label = mMenuItemLabels.get(i);
bottom = top;
top -= item.getMeasuredHeight();
int width = item.getMeasuredWidth();
int d = (mMenuButton.getMeasuredWidth() - width)/2;
item.layout(right - width - d, top, right - d, bottom);
d = (item.getMeasuredHeight() - label.getMeasuredHeight())/2;
label.layout(item.getLeft() - mItemMargin - label.getMeasuredWidth(), item.getTop() + d, item.getLeft() - mItemMargin, item.getTop() + d + label.getMeasuredHeight());
if (!animating) {
if (!mOpen) {
item.setTranslationY(mMenuButton.getTop() - item.getTop());
item.setVisibility(GONE);
label.setVisibility(GONE);
} else {
item.setTranslationY(0);
item.setVisibility(VISIBLE);
label.setVisibility(VISIBLE);
}
}
}
if (!animating && getBackground() != null) {
if (!mOpen) {
getBackground().setAlpha(0);
} else {
getBackground().setAlpha(0xff);
}
}
}
}
private void createDefaultIconAnimation() {
Animator.AnimatorListener listener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
animating = true;
}
@Override
public void onAnimationEnd(Animator animation) {
animating = false;
}
@Override
public void onAnimationCancel(Animator animation) {
animating = false;
}
@Override
public void onAnimationRepeat(Animator animation) {
}
};
ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(
mIcon,
"rotation",
135f,
0f
);
ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(
mIcon,
"rotation",
0f,
135f
);
if (getBackground() != null) {
ValueAnimator hideBackgroundAnimator = ObjectAnimator.ofInt(0xff, 0);
hideBackgroundAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer alpha = (Integer) animation.getAnimatedValue();
//System.out.println(alpha);
getBackground().setAlpha(alpha > 0xff ? 0xff : alpha);
}
});
ValueAnimator showBackgroundAnimator = ObjectAnimator.ofInt(0, 0xff);
showBackgroundAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer alpha = (Integer) animation.getAnimatedValue();
//System.out.println(alpha);
getBackground().setAlpha(alpha > 0xff ? 0xff : alpha);
}
});
mOpenAnimatorSet.playTogether(expandAnimator, showBackgroundAnimator);
mCloseAnimatorSet.playTogether(collapseAnimator, hideBackgroundAnimator);
} else {
mOpenAnimatorSet.playTogether(expandAnimator);
mCloseAnimatorSet.playTogether(collapseAnimator);
}
mOpenAnimatorSet.setInterpolator(DEFAULT_OPEN_INTERPOLATOR);
mCloseAnimatorSet.setInterpolator(DEFAULT_CLOSE_INTERPOLATOR);
mOpenAnimatorSet.setDuration(ANIMATION_DURATION);
mCloseAnimatorSet.setDuration(ANIMATION_DURATION);
mOpenAnimatorSet.addListener(listener);
mCloseAnimatorSet.addListener(listener);
}
static final TimeInterpolator DEFAULT_OPEN_INTERPOLATOR = new OvershootInterpolator();
static final TimeInterpolator DEFAULT_CLOSE_INTERPOLATOR = new AnticipateInterpolator();
public boolean isOpened() {
return mOpen;
}
private class ItemAnimator implements Animator.AnimatorListener {
private View mView;
private boolean playingOpenAnimator;
public ItemAnimator(View v) {
v.animate().setListener(this);
mView = v;
}
public void startOpenAnimator() {
mView.animate().cancel();
playingOpenAnimator = true;
mView.animate().translationY(0).setInterpolator(DEFAULT_OPEN_INTERPOLATOR).start();
}
public void startCloseAnimator() {
mView.animate().cancel();
playingOpenAnimator = false;
mView.animate().translationY((mMenuButton.getTop() - mView.getTop())).setInterpolator(DEFAULT_CLOSE_INTERPOLATOR).start();
}
@Override
public void onAnimationStart(Animator animation) {
if (playingOpenAnimator) {
mView.setVisibility(VISIBLE);
} else {
((TextView) mView.getTag()).setVisibility(GONE);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (!playingOpenAnimator) {
mView.setVisibility(GONE);
} else {
((TextView) mView.getTag()).setVisibility(VISIBLE);
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
}
@Override
public Parcelable onSaveInstanceState() {
d("onSaveInstanceState");
Bundle bundle = new Bundle();
bundle.putParcelable("instanceState", super.onSaveInstanceState());
bundle.putBoolean("mOpen", mOpen);
// ... save everything
return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
d("onRestoreInstanceState");
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mOpen = bundle.getBoolean("mOpen");
// ... load everything
state = bundle.getParcelable("instanceState");
}
super.onRestoreInstanceState(state);
}
@Override
protected void onDetachedFromWindow() {
d("onDetachedFromWindow");
//getBackground().setAlpha(bgAlpha);//reset default alpha
super.onDetachedFromWindow();
}
@Override
public void setBackground(Drawable background) {
if (background instanceof ColorDrawable) {
// after activity finish and relaucher , background drawable state still remain?
int bgAlpha = Color.alpha(((ColorDrawable) background).getColor());
d("bg:" + Integer.toHexString(bgAlpha));
super.setBackground(background);
} else {
throw new IllegalArgumentException("floating only support color background");
}
}
private OnClickListener mOnItemClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (v instanceof FloatingActionButton) {
int i = mMenuItems.indexOf(v);
if (onMenuItemClickListener != null) {
onMenuItemClickListener.onMenuItemClick(FloatingActionMenu.this, i, (FloatingActionButton) v);
}
} else if (v instanceof TextView) {
int i = mMenuItemLabels.indexOf(v);
if (onMenuItemClickListener != null) {
onMenuItemClickListener.onMenuItemClick(FloatingActionMenu.this, i, mMenuItems.get(i));
}
}
close();
}
};
public OnMenuToggleListener getOnMenuToggleListener() {
return onMenuToggleListener;
}
public void setOnMenuToggleListener(OnMenuToggleListener onMenuToggleListener) {
this.onMenuToggleListener = onMenuToggleListener;
}
public OnMenuItemClickListener getOnMenuItemClickListener() {
return onMenuItemClickListener;
}
public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {
this.onMenuItemClickListener = onMenuItemClickListener;
}
protected void d(String msg) {
Log.d("FAM", msg == null ? null : msg);
}
}
Menü XML (aşağıdaki resme bakın).
- ben sınıf ve XML
- hem bireysel düğmeleri marjları ekleyerek denedim
- bireysel düğmeleri XML dolgu ekleyerek denedim: Ne denedim
Düğmelerin yer eklemeye yarayan bir yol bulmak için nereye gittiğini aradım ama şansım olmadı.
Sınıf, eğer içine bakarsanız, bunu anlayabileceğine eminim :) Ne olacağını görmek için toggle() ve open() öğesinin altına bakın. MOpen true olarak ayarlanmış gibi. –