2015-09-05 15 views
22

Sto utilizzando una vista personalizzata per creare un FloatingActionMenu con FloatingActionButtons. Ho modificato questa classe per farlo funzionare quasi perfettamente, quando provo ad aggiungere un'ombra di recente mi sono imbattuto in un problema con esso, l'ombra è tagliata da un quadrato invisibile a causa della classe che la codifica in un quadrato, credo.FloatingActionButton Shadow Cut by Square

vedere l'immagine qui sotto:

FloatingActionButton

La classe che sto usando è così posso avere più FloatingActionButtons (FAB) in un menu.

Qui è la classe:

package terranovaproductions.newcomicreader; 

import android.animation.Animator; 
import android.animation.AnimatorSet; 
import android.animation.ObjectAnimator; 
import android.animation.TimeInterpolator; 
import android.animation.ValueAnimator; 
import android.content.Context; 
import android.graphics.Color; 
import android.graphics.drawable.ColorDrawable; 
import android.graphics.drawable.Drawable; 
import android.os.Bundle; 
import android.os.Parcelable; 
import android.support.annotation.NonNull; 
import android.support.design.widget.FloatingActionButton; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.GestureDetector; 
import android.view.Gravity; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.ViewGroup; 
import android.view.animation.AnticipateInterpolator; 
import android.view.animation.OvershootInterpolator; 
import android.widget.ImageView; 
import android.widget.TextView; 

import java.util.ArrayList; 

/** 
* Created by charry on 2015/6/11. https://gist.github.com/douo/dfde289778a9b3b6918f and modified by Tristan Wiley 
*/ 
public class FloatingActionMenu extends ViewGroup { 

    static final TimeInterpolator DEFAULT_OPEN_INTERPOLATOR = new OvershootInterpolator(); 
    static final TimeInterpolator DEFAULT_CLOSE_INTERPOLATOR = new AnticipateInterpolator(); 
    private static final long ANIMATION_DURATION = 300; 
    private static final int DEFAULT_CHILD_GRAVITY = Gravity.END | Gravity.BOTTOM; 
    Animator animator = new Animator() { 
     @Override 
     public long getStartDelay() { 
      return 0; 
     } 

     @Override 
     public void setStartDelay(long startDelay) { 

     } 

     @Override 
     public Animator setDuration(long duration) { 
      duration = 2; 
      return null; 
     } 

     @Override 
     public long getDuration() { 
      return 0; 
     } 

     @Override 
     public void setInterpolator(TimeInterpolator value) { 

     } 

     @Override 
     public boolean isRunning() { 
      return true; 
     } 
    }; 
    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 ImageView mIcon; 
    private boolean mOpen; 
    private boolean animating; 
    private boolean mIsSetClosedOnTouchOutside = true; 
    private OnMenuItemClickListener onMenuItemClickListener; 
    private OnMenuToggleListener onMenuToggleListener; 
    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; 
       } 
      }); 
    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 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(mMenuButton.getDrawable()); 
      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(); 
     } 
    } 

// 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; 
// } 

    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 label = new TextView(getContext()); 


     label.setBackgroundResource(R.drawable.rounded_corners); 

     label.setTextColor(Color.WHITE); 
     label.setText(item.getContentDescription()); 

     Integer paddingSize = (int)label.getTextSize()/3; 

     float scale = getResources().getDisplayMetrics().density; 
     int pxtodp = (int) (6*scale + 0.5f); 

     label.setPadding(paddingSize, paddingSize, paddingSize + pxtodp, paddingSize); 

     addView(label); 
     mMenuItemLabels.add(label); 
     item.setTag(label); 
     item.setOnClickListener(mOnItemClickListener); 
     label.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 + 30; 
     } 
     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 + 20; 
     } 

     setMeasuredDimension(resolveSize(width, widthMeasureSpec), 
       resolveSize(height, heightMeasureSpec)); 
    } 

    @Override 
    public boolean onTouchEvent(@NonNull MotionEvent event) { 
     if (mIsSetClosedOnTouchOutside) { 
      return mGestureDetector.onTouchEvent(event); 
     } else { 
      return super.onTouchEvent(event); 
     } 
    } 

    @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 -= mMenuItems.get(i).getPaddingBottom(); //Add 10px padding 

       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()); 
       label.setBackgroundResource(R.drawable.rounded_corners); 

       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); 
    } 

    public boolean isOpened() { 
     return mOpen; 
    } 

    @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"); 
     } 
    } 

    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); 
    } 

    public interface OnMenuToggleListener { 
     void onMenuToggle(boolean opened); 
    } 


    public interface OnMenuItemClickListener { 
     void onMenuItemClick(FloatingActionMenu fam, int index, FloatingActionButton item); 
    } 

    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(); 
      mMenuButton.animate().rotation(135f).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(); 
      mMenuButton.animate().rotation(0f).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) { 
     } 
    } 
} 

mio layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:fab="http://schemas.android.com/apk/res-auto" 
    android:id="@+id/comicView" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="@color/background_main" 
    android:orientation="vertical"> 

    <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:id="@+id/fab_main" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:src="@drawable/ic_add_white_24dp" 
      fab:fabSize="normal" 
      fab:backgroundTint="@color/material_orange" 
      fab:borderWidth="0dp" 
      fab:elevation="6dp"/> 

     <!-- 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:paddingBottom="@dimen/menu_button_margin" 
      android:src="@drawable/ic_random" 
      fab:fabSize="mini" 
      fab:backgroundTint="@color/material_orange" /> 


     <android.support.design.widget.FloatingActionButton 
      android:id="@+id/fab_download" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:contentDescription="@string/download" 
      android:paddingBottom="@dimen/menu_button_margin" 
      android:src="@drawable/ic_download" 
      fab:fabSize="mini" 
      fab:backgroundTint="@color/material_orange"/> 

     <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:paddingBottom="@dimen/menu_button_margin" 
      android:src="@drawable/ic_open_browser" 
      fab:fabSize="mini" 
      fab:backgroundTint="@color/material_orange"/> 

    </terranovaproductions.newcomicreader.FloatingActionMenu> 


</RelativeLayout> 

Capisco che ci sono modi migliori per fare un FloatingActionMenu ma ho scelto questo modo perché ho messo un sacco di lavoro in esso.

Ho provato a rimuovere il riempimento, aggiungendo un margine. Non sono sicuro su come estendere l'ombra. Sono abbastanza sicuro che nello onLayout ho bisogno di cambiare qualcosa.

Richiedi altre informazioni se necessario.

+0

Hai preso qualcosa dal tuo XML, o è davvero solo la FAM all'interno della RL? Sono sicuro che hai abbreviato questo per un MCVE, ma proverei a postare l'XML effettivo – AdamMc331

+0

perché l'altro è irrilevante. Solo due TextView e ImageView –

+0

Potrebbe non essere irrilevante. Perché la larghezza e l'altezza della FAM match_parent, al contrario di wrap_content? Inoltre, come sta andando in basso a destra senza usare layout_gravity? – AdamMc331

risposta

31

è necessario aggiungere

android:clipChildren="false" 
android:clipToPadding="false" 

alla vista primaria nel layout xml.

+0

ho aggiunto a CoordinatorLayout che contiene fab e tutto ok. Grazie. – javaddroid

+0

grazie.devi anche considerare di applicare un padding adatto alla vista genitore altrimenti non funzionerà. –

7

È possibile provare a rimuovere il riempimento 16dp da FloatingActionMenu e aggiungere margine 16dp a ciascun FloatingActionButton.

+0

marginBottom = 16dp ha funzionato per me – behelit

1

Basta aggiungere un margine al pulsante. Come ha suggerito Orgazm pionerki. Ho aggiunto 5dp, e questo è OK.