CoordinatorLayout ignores margins for views with anchor

前端 未结 6 2152
春和景丽
春和景丽 2021-02-12 03:49

Given I\'m using a layout like this:



        
相关标签:
6条回答
  • 2021-02-12 04:19

    Try putting it in a linear layout that have padding:

    <LinearLayout
      width=".."
      height=".."
      paddingBottom="20dp"
      app:layout_anchor="@id/appbar"
      app:layout_anchorGravity="bottom|center_horizontal">
    
      <android.support.design.widget.FloatingActionButton
            android:layout_width="70dp"
            android:layout_height="70dp"
            app:fabSize="normal" />
    
    </LinearLayout>
    
    0 讨论(0)
  • 2021-02-12 04:25

    I suggest an elegant solution for you:

    <android.support.design.widget.FloatingActionButton
        ...
        android:translationY="-20dp"
        ...
    />
    
    0 讨论(0)
  • 2021-02-12 04:32

    I was able to get around this issue by using both layout_anchor layout_anchorGravity along with some padding.. The anchor attribute allows you to have a View position itself relative to another view. However, it doesn't exactly work in the same way that RelativeLayout does. More on that to follow.

    layout_anchor specifies which View your desired View should be positioned (i.e., this View should be placed relative to the View specified by the layout_anchor attribute). Then, layout_anchorGravity specifies which side of the relative View the current View will be positioned, using the typical Gravity values (top, bottom, center_horizontal, etc.).

    The issue with using just these two attributes alone is that the center of the View with the anchors will be placed relative to the other View. For example, if you specify a FloatingActionButton to be anchored to the bottom of a TextView, what really ends up happening is that the the center of the FAB is placed along the bottom edge of the TextView.

    To get around this issue, I applied some padding to the FAB, enough such that the top edge of the FAB was touching the bottom edge of the TextView:

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_anchor="@id/your_buttons_id_here"
        android:layout_anchorGravity="bottom"
        android:paddingTop=16dp" />
    

    You might have to increase the padding to get the desired effect. Hope that helps!

    0 讨论(0)
  • 2021-02-12 04:33

    Easy workaround is to anchor a random layout to where FAB was anchored, give it specific margin, and then anchor FAB to random layout, like this

    <LinearLayout
     android:orientation="horizontal"
     android:id="@+id/fab_layout"
     android:layout_width="5dp"
     android:layout_height="5dp"
     android:layout_marginRight="80dp"
     android:layout_marginEnd="80dp"
     app:layout_anchor="@id/collapsing_toolbar"
     app:layout_anchorGravity="bottom|end"/>
    
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_map"
        app:layout_anchor="@id/fab_layout"
        app:elevation="6dp"
        app:pressedTranslationZ="12dp"
     />
    
    0 讨论(0)
  • 2021-02-12 04:40

    As there might be bugs in the design-support-lib concerning the CoordinatorLayout & margins, I wrote a FrameLayout that implements/copies the same "Behavior" like the FAB and allows to set a padding to simulate the effect:

    Be sure to put it in the android.support.design.widget package as it needs to access some package-scoped classes.

    /*
     * Copyright (C) 2015 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    import android.annotation.TargetApi;
    import android.content.Context;
    import android.graphics.Rect;
    import android.os.Build;
    import android.support.v4.view.ViewCompat;
    import android.support.v4.view.ViewPropertyAnimatorListener;
    import android.util.AttributeSet;
    import android.view.View;
    import android.view.animation.Animation;
    import android.widget.FrameLayout;
    
    import com.company.android.R;
    
    import java.util.List;
    
    @CoordinatorLayout.DefaultBehavior(FrameLayoutWithBehavior.Behavior.class)
    public class FrameLayoutWithBehavior extends FrameLayout {
        public FrameLayoutWithBehavior(final Context context) {
            super(context);
        }
    
        public FrameLayoutWithBehavior(final Context context, final AttributeSet attrs) {
            super(context, attrs);
        }
    
        public FrameLayoutWithBehavior(final Context context, final AttributeSet attrs, final int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public FrameLayoutWithBehavior(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    
        public static class Behavior extends android.support.design.widget.CoordinatorLayout.Behavior<FrameLayoutWithBehavior> {
            private static final boolean SNACKBAR_BEHAVIOR_ENABLED;
            private Rect mTmpRect;
            private boolean mIsAnimatingOut;
            private float mTranslationY;
    
            public Behavior() {
            }
    
            @Override
            public boolean layoutDependsOn(CoordinatorLayout parent, FrameLayoutWithBehavior child, View dependency) {
                return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
            }
    
            @Override
            public boolean onDependentViewChanged(CoordinatorLayout parent, FrameLayoutWithBehavior child, View dependency) {
                if (dependency instanceof Snackbar.SnackbarLayout) {
                    this.updateFabTranslationForSnackbar(parent, child, dependency);
                } else if (dependency instanceof AppBarLayout) {
                    AppBarLayout appBarLayout = (AppBarLayout) dependency;
                    if (this.mTmpRect == null) {
                        this.mTmpRect = new Rect();
                    }
    
                    Rect rect = this.mTmpRect;
                    ViewGroupUtils.getDescendantRect(parent, dependency, rect);
                    if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
                        if (!this.mIsAnimatingOut && child.getVisibility() == VISIBLE) {
                            this.animateOut(child);
                        }
                    } else if (child.getVisibility() != VISIBLE) {
                        this.animateIn(child);
                    }
                }
    
                return false;
            }
    
            private void updateFabTranslationForSnackbar(CoordinatorLayout parent, FrameLayoutWithBehavior fab, View snackbar) {
                float translationY = this.getFabTranslationYForSnackbar(parent, fab);
                if (translationY != this.mTranslationY) {
                    ViewCompat.animate(fab)
                              .cancel();
                    if (Math.abs(translationY - this.mTranslationY) == (float) snackbar.getHeight()) {
                        ViewCompat.animate(fab)
                                  .translationY(translationY)
                                  .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
                                  .setListener((ViewPropertyAnimatorListener) null);
                    } else {
                        ViewCompat.setTranslationY(fab, translationY);
                    }
    
                    this.mTranslationY = translationY;
                }
    
            }
    
            private float getFabTranslationYForSnackbar(CoordinatorLayout parent, FrameLayoutWithBehavior fab) {
                float minOffset = 0.0F;
                List dependencies = parent.getDependencies(fab);
                int i = 0;
    
                for (int z = dependencies.size(); i < z; ++i) {
                    View view = (View) dependencies.get(i);
                    if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) {
                        minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - (float) view.getHeight());
                    }
                }
    
                return minOffset;
            }
    
            private void animateIn(FrameLayoutWithBehavior button) {
                button.setVisibility(View.VISIBLE);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                    ViewCompat.animate(button)
                              .scaleX(1.0F)
                              .scaleY(1.0F)
                              .alpha(1.0F)
                              .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
                              .withLayer()
                              .setListener((ViewPropertyAnimatorListener) null)
                              .start();
                } else {
                    Animation anim = android.view.animation.AnimationUtils.loadAnimation(button.getContext(), R.anim.fab_in);
                    anim.setDuration(200L);
                    anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
                    button.startAnimation(anim);
                }
    
            }
    
            private void animateOut(final FrameLayoutWithBehavior button) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                    ViewCompat.animate(button)
                              .scaleX(0.0F)
                              .scaleY(0.0F)
                              .alpha(0.0F)
                              .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
                              .withLayer()
                              .setListener(new ViewPropertyAnimatorListener() {
                                  public void onAnimationStart(View view) {
                                      Behavior.this.mIsAnimatingOut = true;
                                  }
    
                                  public void onAnimationCancel(View view) {
                                      Behavior.this.mIsAnimatingOut = false;
                                  }
    
                                  public void onAnimationEnd(View view) {
                                      Behavior.this.mIsAnimatingOut = false;
                                      view.setVisibility(View.GONE);
                                  }
                              })
                              .start();
                } else {
                    Animation anim = android.view.animation.AnimationUtils.loadAnimation(button.getContext(), R.anim.fab_out);
                    anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
                    anim.setDuration(200L);
                    anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
                        public void onAnimationStart(Animation animation) {
                            Behavior.this.mIsAnimatingOut = true;
                        }
    
                        public void onAnimationEnd(Animation animation) {
                            Behavior.this.mIsAnimatingOut = false;
                            button.setVisibility(View.GONE);
                        }
                    });
                    button.startAnimation(anim);
                }
    
            }
    
            static {
                SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-12 04:41

    To anchor the FloatingActionButton below the AppBar like this:

    Extend the FloatingActionButton and override offsetTopAndBottom:

    public class OffsetFloatingActionButton extends FloatingActionButton
    {
        public OffsetFloatingActionButton(Context context)
        {
            this(context, null);
        }
    
        public OffsetFloatingActionButton(Context context, AttributeSet attrs)
        {
            this(context, attrs, 0);
        }
    
        public OffsetFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr)
        {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom)
        {
            super.onLayout(changed, left, top, right, bottom);
            ViewCompat.offsetTopAndBottom(this, 0);
        }
    
        @Override
        public void offsetTopAndBottom(int offset)
        {
            super.offsetTopAndBottom((int) (offset + (getHeight() * 0.5f)));
        }
    }
    
    0 讨论(0)
提交回复
热议问题