悬浮按钮FloatingActionButton控件

相信作为Android开发者,对FloatingActionButton肯定很熟悉,我们也经常看到它的身影,比如短信页面,电话页面等等。那么我们应该怎么引用悬浮按钮呢?下面就来分析分析FloatingActionButton的使用。

1.先导入support依赖库

由于FloatingActionButton是在开源库里面的,所以我们要先导入开源库。
(1)先配置build.gradle

gradle版本至少是gradle:1.2.3;

Sdk版本22以上

classpath 'com.android.tools.build:gradle:1.3.0'
compileSdkVersion 23
buildToolsVersion "22.0.1"
targetSdkVersion 23

然后导入依赖库:

compile 'com.android.support:design:23.0.1'
compile 'com.android.support:appcompat-v7:23.0.1'

导库的时候要的版本号为22.2.0以上,不然可能会报错
(2)在style.xml中主题样式需要继承“Theme.AppCompat[…]”

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
 </style>

不然会报错:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.meizu.floatactionbuttontest/com.meizu.floatactionbuttontest.MainActivity}: android.view.InflateException: Binary XML file line #7:  Error inflating class android.support.design.widget.FloatingActionButton

2.控件的具体用法

1.使用android5.0原生自带的FloatActionButton

<android.support.design.widget.FloatingActionButton
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        fab:elevation="6dp"
        fab:pressedTranslationZ="12dp"
        fab:fabSize="normal"
        android:src="@android:drawable/ic_menu_camera" />

(1)先介绍默认(效果图中绿色的):
下面效果图中绿色的那个FloatActionButton是Android默认自带的,从上面的效果图中我们可以看到FloatingActionButton正常显示的情况下有个填充的颜色,有个阴影;点击的时候会有一个rippleColor,并且阴影的范围可以增大。在布局文件中我

只添加了一个图标,一个elevation(这样就可以显示阴影)和一个pressedTranslationZ(这样在它被按下的时候阴影就会增大)。
(2)可定制的属性:

这个填充色以及rippleColor如何自定义呢?

FAB会采用主题中定义的accent color,同时采用colorControlHighlight作为ripple color。不过这两个都是可以自定义的。默认的颜色取的是,theme中的colorAccent,rippleColor默认取的是theme中的colorControlHighlight。

两种方法自定义这两个颜色:
(1)在布局文件中引用自定义颜色值:

xmlns:fab="http://schemas.android.com/apk/res-auto"
fab:backgroundTint="#ff87ffeb"
fab:rippleColor="#33728dff"

(2)可以在style中定义colorAccent

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/primary</item>
        <item name="colorPrimaryDark">@color/primaryDark</item>
        <item name="android:navigationBarColor">@color/primary</item>
        <item name="colorAccent">@color/primary</item>
    </style>
</resources>

当然也可以在代码中调用相关方法,不过backgroundTint稍微麻烦点,因为它使用的是state list。

mFbutton.setRippleColor(Color.GRAY);
fab.setBackgroundTintList(ColorStateList.valueOf("Your color"));

上面的方式都可以改变FAB的颜色

具体属性详细介绍

fab:backgroundTint="#ff87ffeb"
fab:rippleColor="#33728dff"

这两个属性上面已经介绍过,就是改变FAB的颜色和点击是水波纹的颜色

fab:elevation="6dp"
fab:pressedTranslationZ="12dp"

这两个属性是FAB的阴影和轮廓的设置

fab:fabSize="mini"

fabSize默认是normal版本。
这个属性是设置FAB的大小,有两种大小的浮动操作按钮,mini版和normal版,效果图中右上的(比较小的那个)FAB就是通过这几个属性设置后的效果。

特别需要注意的事项

首先,根据安卓系统版本的不同,会出现很多未料到的事情。这些问题似乎都和一个叫做 borderWidth的属性有关, borderWidth需要设置成0:

fab:borderWidth="0dp"

另外,在lollipop以前的版本上,使用这个属性会在周围产生外边距(margin),而21+以上则不会。产生的原因和CardView上所看到的是一样的:lollipop 以前阴影是使用一个drawable来渲染的,使用的是自身的空间来绘制,而Lollipop之后阴影是由系统渲染的。CardView有一个属性可以启 用compat padding。但是浮动操作按钮却没有,所以根据安卓版本添加一个margin就可以了。

android:layout_margin="@dimen/fab_compat_margin"

现在只需在dimen.xml文件中定义一个属性值。
values目录下:

<dimen name="fab_compat_margin">0dp</dimen>

values-v21目录下:

<dimen name="fab_compat_margin">16dp</dimen>

2.自定义FloatActionButton控件

(1)在Attr.xml中自定义属性:

<resources>
    <declare-styleable name="FloatingActionButton">
        <attr name="fab_colorPressed" format="color" />
        <attr name="fab_colorNormal" format="color" />
        <attr name="fab_colorRipple" format="color" />
        <attr name="fab_colorDisabled" format="color" />
        <attr name="fab_shadow" format="boolean" />
        <attr name="fab_type" format="enum">
            <enum name="normal" value="0" />
            <enum name="mini" value="1" />
        </attr>
    </declare-styleable>
</resources>

(2)控件的代码(核心部分)

public class FloatingActionButton extends ImageButton {


    public static final int TYPE_NORMAL = 0;
    public static final int TYPE_MINI = 1;

    private boolean mVisible;

    private int mColorNormal;
    private int mColorPressed;
    private int mColorRipple;
    private int mColorDisabled;
    private boolean mShadow;
    private int mType;
    private int mShadowSize;
    private boolean mMarginsSet;

    public FloatingActionButton(Context context) {
        this(context, null);
    }

    public FloatingActionButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public FloatingActionButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int size = getDimension(
                mType == TYPE_NORMAL ? R.dimen.mz_fab_size_normal : R.dimen.mz_fab_size_mini);
        if (mShadow && !hasLollipopApi()) {
            size += mShadowSize * 2;
            setMarginsWithoutShadow();
        }
        setMeasuredDimension(size, size);
    }

    private void init(Context context, AttributeSet attributeSet) {
        mVisible = true;
        mColorNormal = getColor(R.color.mz_color_normal);
        mColorPressed = darkenColor(mColorNormal);
        mColorRipple = lightenColor(mColorNormal);
        mColorDisabled = getColor(android.R.color.darker_gray);
        mType = TYPE_NORMAL;
        mShadow = true;
        mShadowSize = getDimension(R.dimen.mz_fab_shadow_size);
        if (hasLollipopApi()) {
            StateListAnimator stateListAnimator = AnimatorInflater.loadStateListAnimator(context,
                    R.anim.fab_press_elevation);
            setStateListAnimator(stateListAnimator);
        }
        if (attributeSet != null) {
            initAttributes(context, attributeSet);
        }
        updateBackground();
    }

    private void initAttributes(Context context, AttributeSet attributeSet) {
        TypedArray attr = getTypedArray(context, attributeSet, R.styleable.FloatingActionButton);
        if (attr != null) {
            try {
                mColorNormal = attr.getColor(R.styleable.FloatingActionButton_fab_colorNormal,
                        getColor(R.color.color_normal));
                mColorPressed = attr.getColor(R.styleable.FloatingActionButton_fab_colorPressed,
                        darkenColor(mColorNormal));
                mColorRipple = attr.getColor(R.styleable.FloatingActionButton_fab_colorRipple,
                        lightenColor(mColorNormal));
                mColorDisabled = attr.getColor(R.styleable.FloatingActionButton_fab_colorDisabled,
                        mColorDisabled);
                mShadow = attr.getBoolean(R.styleable.FloatingActionButton_fab_shadow, true);
                mType = attr.getInt(R.styleable.FloatingActionButton_fab_type, TYPE_NORMAL);
            } finally {
                attr.recycle();
            }
        }
    }

    /**
     *定义了不同状态值下与之对应的颜色资源
     */
    private void updateBackground() {
        StateListDrawable drawable = new StateListDrawable();
        drawable.addState(new int[]{android.R.attr.state_pressed}, createDrawable(mColorPressed));
        drawable.addState(new int[]{-android.R.attr.state_enabled}, createDrawable(mColorDisabled));
        drawable.addState(new int[]{}, createDrawable(mColorNormal));
        setBackgroundCompat(drawable);
    }

    private Drawable createDrawable(int color) {
        OvalShape ovalShape = new OvalShape();
        ShapeDrawable shapeDrawable = new ShapeDrawable(ovalShape);
        shapeDrawable.getPaint().setColor(color);

        if (mShadow && !hasLollipopApi()) {
            Drawable shadowDrawable = getResources().getDrawable(mType == TYPE_NORMAL ? R.mipmap.fab_shadow
                    : R.mipmap.fab_shadow_mini);
            LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{shadowDrawable, shapeDrawable});
            layerDrawable.setLayerInset(1, mShadowSize, mShadowSize, mShadowSize, mShadowSize);
            return layerDrawable;
        } else {
            return shapeDrawable;
        }
    }

    private TypedArray getTypedArray(Context context, AttributeSet attributeSet, int[] attr) {
        return context.obtainStyledAttributes(attributeSet, attr, 0, 0);
    }

    private int getColor(int id) {
        return getResources().getColor(id);
    }

    private int getDimension( int id) {
        return getResources().getDimensionPixelSize(id);
    }

    private void setMarginsWithoutShadow() {
        if (!mMarginsSet) {
            if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
                int leftMargin = layoutParams.leftMargin - mShadowSize;
                int topMargin = layoutParams.topMargin - mShadowSize;
                int rightMargin = layoutParams.rightMargin - mShadowSize;
                int bottomMargin = layoutParams.bottomMargin - mShadowSize;
                layoutParams.setMargins(leftMargin, topMargin, rightMargin, bottomMargin);

                requestLayout();
                mMarginsSet = true;
            }
        }
    }

    /**
     * 设置背景
     * @param drawable
     */
    private void setBackgroundCompat(Drawable drawable) {
        if (hasLollipopApi()) {
            float elevation;
            if (mShadow) {
                elevation = getElevation() > 0.0f ? getElevation()
                        : getDimension(R.dimen.mz_fab_elevation_lollipop);
            } else {
                elevation = 0.0f;
            }
            setElevation(elevation);
            RippleDrawable rippleDrawable = new RippleDrawable(new ColorStateList(new int[][]{{}},
                    new int[]{mColorRipple}), drawable, null);
            setOutlineProvider(new ViewOutlineProvider() {  //设置轮廓
                @Override
                public void getOutline(View view, Outline outline) {
                    int size = getDimension(mType == TYPE_NORMAL ? R.dimen.fab_size_normal
                            : R.dimen.fab_size_mini);
                    outline.setOval(0, 0, size, size);
                }
            });
            setClipToOutline(true);
            setBackground(rippleDrawable);
        } else if (hasJellyBeanApi()) {
            setBackground(drawable);
        } else {
            setBackgroundDrawable(drawable);
        }
    }

    /**
     * 设置正常状态下的颜色值
     * @param color
     */
    public void setColorNormal(int color) {
        if (color != mColorNormal) {
            mColorNormal = color;
            updateBackground();
        }
    }

    public void setColorNormalResId(int colorResId) {
        setColorNormal(getColor(colorResId));
    }

    public int getColorNormal() {
        return mColorNormal;
    }

    /**
     * 设置按下的颜色值
     * @param color
     */
    public void setColorPressed(int color) {
        if (color != mColorPressed) {
            mColorPressed = color;
            updateBackground();
        }
    }

    public void setColorPressedResId(int colorResId) {
        setColorPressed(getColor(colorResId));
    }

    public int getColorPressed() {
        return mColorPressed;
    }

    /**
     * 设置水波纹的颜色
     * @param color
     */
    public void setColorRipple(int color) {
        if (color != mColorRipple) {
            mColorRipple = color;
            updateBackground();
        }
    }

    public void setColorRippleResId(int colorResId) {
        setColorRipple(getColor(colorResId));
    }

    public int getColorRipple() {
        return mColorRipple;
    }

    /**
     * 设置是否有阴影
     * @param shadow
     */
    public void setShadow(boolean shadow) {
        if (shadow != mShadow) {
            mShadow = shadow;
            updateBackground();
        }
    }

    public boolean hasShadow() {
        return mShadow;
    }

    /**
     * 设置浮动操作按钮的版本,有NORMAL,MINI两种
     * @param type
     */
    public void setType(int type) {
        if (type != mType) {
            mType = type;
            updateBackground();
        }
    }

    public int getType() {
        return mType;
    }

    public boolean isVisible() {
        return mVisible;
    }


    private boolean hasLollipopApi() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
    }

    private boolean hasJellyBeanApi() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
    }

    private boolean hasHoneycombApi() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
    }

    /**
     * 调色,亮度调低
     * @param color
     * @return
     */
    private static int darkenColor(int color) {
        float[] hsv = new float[3];
        Color.colorToHSV(color, hsv);
        hsv[2] *= 0.9f;
        return Color.HSVToColor(hsv);
    }

    /**
     * 调色,亮度调高
     * @param color
     * @return
     */
    private static int lightenColor(int color) {
        float[] hsv = new float[3];
        Color.colorToHSV(color, hsv);
        hsv[2] *= 1.1f;
        return Color.HSVToColor(hsv);
    }

}

这里写图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值