android - 如何使用动画将按钮的形状从圆角矩形更改为圆形

标签 android

我的 Activity 中有一个圆角矩形按钮。单击按钮后,它应该会稍微压缩/收缩(按钮的高度不应改变)并且应该变为圆形。如何使用 XML 中的 anim 文件夹实现此目的?

目前我的代码是:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <scale
        android:duration="400"
        android:fromXScale="1"
        android:fromYScale="1"
        android:pivotX="50%"
        android:toXScale="0.1"
        android:toYScale="1" />
    <set>
        <alpha
            android:duration="400"
            android:fromAlpha="1"
            android:toAlpha="0" />
    </set>
</set>

最佳答案

Create MorphingButton class paste below code into;

public class MorphingButton extends Button {

private Padding mPadding;
private int mHeight;
private int mWidth;
private int mColor;
private int mCornerRadius;
private int mStrokeWidth;
private int mStrokeColor;

protected boolean mAnimationInProgress;

private StrokeGradientDrawable mDrawableNormal;
private StrokeGradientDrawable mDrawablePressed;

public MorphingButton(Context context) {
    super(context);
    initView();
}

public MorphingButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView();
}

public MorphingButton(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initView();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    if (mHeight == 0 && mWidth == 0 && w != 0 && h != 0) {
        mHeight = getHeight();
        mWidth = getWidth();
    }
}

public StrokeGradientDrawable getDrawableNormal() {
    return mDrawableNormal;
}

public void morph(@NonNull Params params) {
    if (!mAnimationInProgress) {

        mDrawablePressed.setColor(params.colorPressed);
        mDrawablePressed.setCornerRadius(params.cornerRadius);
        mDrawablePressed.setStrokeColor(params.strokeColor);
        mDrawablePressed.setStrokeWidth(params.strokeWidth);

        if (params.duration == 0) {
            morphWithoutAnimation(params);
        } else {
            morphWithAnimation(params);
        }

        mColor = params.color;
        mCornerRadius = params.cornerRadius;
        mStrokeWidth = params.strokeWidth;
        mStrokeColor = params.strokeColor;
    }
}

private void morphWithAnimation(@NonNull final Params params) {
    mAnimationInProgress = true;
    setText(null);
    setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
    setPadding(mPadding.left, mPadding.top, mPadding.right, mPadding.bottom);

    MorphingAnimation.Params animationParams = MorphingAnimation.Params.create(this)
            .color(mColor, params.color)
            .cornerRadius(mCornerRadius, params.cornerRadius)
            .strokeWidth(mStrokeWidth, params.strokeWidth)
            .strokeColor(mStrokeColor, params.strokeColor)
            .height(getHeight(), params.height)
            .width(getWidth(), params.width)
            .duration(params.duration)
            .listener(new MorphingAnimation.Listener() {
                @Override
                public void onAnimationEnd() {
                    finalizeMorphing(params);
                }
            });

    MorphingAnimation animation = new MorphingAnimation(animationParams);
    animation.start();
}

private void morphWithoutAnimation(@NonNull Params params) {
    mDrawableNormal.setColor(params.color);
    mDrawableNormal.setCornerRadius(params.cornerRadius);
    mDrawableNormal.setStrokeColor(params.strokeColor);
    mDrawableNormal.setStrokeWidth(params.strokeWidth);

    if(params.width != 0 && params.height !=0) {
        ViewGroup.LayoutParams layoutParams = getLayoutParams();
        layoutParams.width = params.width;
        layoutParams.height = params.height;
        setLayoutParams(layoutParams);
    }

    finalizeMorphing(params);
}

private void finalizeMorphing(@NonNull Params params) {
    mAnimationInProgress = false;

    if (params.icon != 0 && params.text != null) {
        setIconLeft(params.icon);
        setText(params.text);
    } else if (params.icon != 0) {
        setIcon(params.icon);
    } else if(params.text != null) {
        setText(params.text);
    }

    if (params.animationListener != null) {
        params.animationListener.onAnimationEnd();
    }
}

public void blockTouch() {
    setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }
    });
}

public void unblockTouch() {
    setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return false;
        }
    });
}

private void initView() {
    mPadding = new Padding();
    mPadding.left = getPaddingLeft();
    mPadding.right = getPaddingRight();
    mPadding.top = getPaddingTop();
    mPadding.bottom = getPaddingBottom();

    Resources resources = getResources();
    int cornerRadius = (int) resources.getDimension(R.dimen._10sdp);
    int primaryColor = resources.getColor(R.color.colorAccent);
    int secondaryColor = resources.getColor(R.color.colorPrimary);

    StateListDrawable background = new StateListDrawable();
    mDrawableNormal = createDrawable(primaryColor, cornerRadius, 0);
    mDrawablePressed = createDrawable(secondaryColor, cornerRadius, 0);

    mColor = primaryColor;
    mStrokeColor = primaryColor;
    mCornerRadius = cornerRadius;

    background.addState(new int[]{android.R.attr.state_pressed}, mDrawablePressed.getGradientDrawable());
    background.addState(StateSet.WILD_CARD, mDrawableNormal.getGradientDrawable());

    setBackgroundCompat(background);
}

private StrokeGradientDrawable createDrawable(int color, int cornerRadius, int strokeWidth) {
    StrokeGradientDrawable drawable = new StrokeGradientDrawable(new GradientDrawable());
    drawable.getGradientDrawable().setShape(GradientDrawable.RECTANGLE);
    drawable.setColor(color);
    drawable.setCornerRadius(cornerRadius);
    drawable.setStrokeColor(color);
    drawable.setStrokeWidth(strokeWidth);

    return drawable;
}

@SuppressWarnings("deprecation")
private void setBackgroundCompat(@Nullable Drawable drawable) {
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
        setBackgroundDrawable(drawable);
    } else {
        setBackground(drawable);
    }
}

public void setIcon(@DrawableRes final int icon) {
    // post is necessary, to make sure getWidth() doesn't return 0
    post(new Runnable() {
        @Override
        public void run() {
            Drawable drawable = getResources().getDrawable(icon);
            int padding = (getWidth() / 2) - (drawable.getIntrinsicWidth() / 2);
            setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
            setPadding(padding, 0, 0, 0);
        }
    });
}

public void setIconLeft(@DrawableRes int icon) {
    setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
}

private class Padding {
    public int left;
    public int right;
    public int top;
    public int bottom;
}

public static class Params {
    private int cornerRadius;
    private int width;
    private int height;
    private int color;
    private int colorPressed;
    private int duration;
    private int icon;
    private int strokeWidth;
    private int strokeColor;
    private String text;
    private MorphingAnimation.Listener animationListener;

    private Params() {

    }

    public static Params create() {
        return new Params();
    }

    public Params text(@NonNull String text) {
        this.text = text;
        return this;
    }

    public Params icon(@DrawableRes int icon) {
        this.icon = icon;
        return this;
    }

    public Params cornerRadius(int cornerRadius) {
        this.cornerRadius = cornerRadius;
        return this;
    }

    public Params width(int width) {
        this.width = width;
        return this;
    }

    public Params height(int height) {
        this.height = height;
        return this;
    }

    public Params color(int color) {
        this.color = color;
        return this;
    }

    public Params colorPressed(int colorPressed) {
        this.colorPressed = colorPressed;
        return this;
    }

    public Params duration(int duration) {
        this.duration = duration;
        return this;
    }

    public Params strokeWidth(int strokeWidth) {
        this.strokeWidth = strokeWidth;
        return this;
    }

    public Params strokeColor(int strokeColor) {
        this.strokeColor = strokeColor;
        return this;
    }

    public Params animationListener(MorphingAnimation.Listener animationListener) {
        this.animationListener = animationListener;
        return this;
    }
}
public static class MorphingAnimation {

    public interface Listener {
        void onAnimationEnd();
    }

    public static class Params {

        private float fromCornerRadius;
        private float toCornerRadius;

        private int fromHeight;
        private int toHeight;

        private int fromWidth;
        private int toWidth;

        private int fromColor;
        private int toColor;

        private int duration;

        private int fromStrokeWidth;
        private int toStrokeWidth;

        private int fromStrokeColor;
        private int toStrokeColor;

        private MorphingButton button;
        private MorphingAnimation.Listener animationListener;

        private Params(@NonNull MorphingButton button) {
            this.button = button;
        }

        public static Params create(@NonNull MorphingButton button) {
            return new Params(button);
        }

        public Params duration(int duration) {
            this.duration = duration;
            return this;
        }

        public Params listener(@NonNull MorphingAnimation.Listener animationListener) {
            this.animationListener = animationListener;
            return this;
        }

        public Params color(int fromColor, int toColor) {
            this.fromColor = fromColor;
            this.toColor = toColor;
            return this;
        }

        public Params cornerRadius(int fromCornerRadius, int toCornerRadius) {
            this.fromCornerRadius = fromCornerRadius;
            this.toCornerRadius = toCornerRadius;
            return this;
        }

        public Params height(int fromHeight, int toHeight) {
            this.fromHeight = fromHeight;
            this.toHeight = toHeight;
            return this;
        }

        public Params width(int fromWidth, int toWidth) {
            this.fromWidth = fromWidth;
            this.toWidth = toWidth;
            return this;
        }

        public Params strokeWidth(int fromStrokeWidth, int toStrokeWidth) {
            this.fromStrokeWidth = fromStrokeWidth;
            this.toStrokeWidth = toStrokeWidth;
            return this;
        }

        public Params strokeColor(int fromStrokeColor, int toStrokeColor) {
            this.fromStrokeColor = fromStrokeColor;
            this.toStrokeColor = toStrokeColor;
            return this;
        }

    }

    private Params mParams;

    public MorphingAnimation(@NonNull Params params) {
        mParams = params;
    }

    public void start() {
        StrokeGradientDrawable background = mParams.button.getDrawableNormal();

        ObjectAnimator cornerAnimation =
                ObjectAnimator.ofFloat(background, "cornerRadius", mParams.fromCornerRadius, mParams.toCornerRadius);

        ObjectAnimator strokeWidthAnimation =
                ObjectAnimator.ofInt(background, "strokeWidth", mParams.fromStrokeWidth, mParams.toStrokeWidth);

        ObjectAnimator strokeColorAnimation = ObjectAnimator.ofInt(background, "strokeColor", mParams.fromStrokeColor, mParams.toStrokeColor);
        strokeColorAnimation.setEvaluator(new ArgbEvaluator());

        ObjectAnimator bgColorAnimation = ObjectAnimator.ofInt(background, "color", mParams.fromColor, mParams.toColor);
        bgColorAnimation.setEvaluator(new ArgbEvaluator());

        ValueAnimator heightAnimation = ValueAnimator.ofInt(mParams.fromHeight, mParams.toHeight);
        heightAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int val = (Integer) valueAnimator.getAnimatedValue();
                ViewGroup.LayoutParams layoutParams = mParams.button.getLayoutParams();
                layoutParams.height = val;
                mParams.button.setLayoutParams(layoutParams);
            }
        });

        ValueAnimator widthAnimation = ValueAnimator.ofInt(mParams.fromWidth, mParams.toWidth);
        widthAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int val = (Integer) valueAnimator.getAnimatedValue();
                ViewGroup.LayoutParams layoutParams = mParams.button.getLayoutParams();
                layoutParams.width = val;
                mParams.button.setLayoutParams(layoutParams);
            }
        });

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.setDuration(mParams.duration);
        animatorSet.playTogether(strokeWidthAnimation, strokeColorAnimation, cornerAnimation, bgColorAnimation,
                heightAnimation, widthAnimation);
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (mParams.animationListener != null) {
                    mParams.animationListener.onAnimationEnd();
                }
            }
        });
        animatorSet.start();
    }

}
public class StrokeGradientDrawable {

    private int mStrokeWidth;
    private int mStrokeColor;

    private GradientDrawable mGradientDrawable;
    private float mRadius;
    private int mColor;

    public StrokeGradientDrawable(GradientDrawable drawable) {
        mGradientDrawable = drawable;
    }

    public int getStrokeWidth() {
        return mStrokeWidth;
    }

    public void setStrokeWidth(int strokeWidth) {
        mStrokeWidth = strokeWidth;
        mGradientDrawable.setStroke(strokeWidth, getStrokeColor());
    }

    public int getStrokeColor() {
        return mStrokeColor;
    }

    public void setStrokeColor(int strokeColor) {
        mStrokeColor = strokeColor;
        mGradientDrawable.setStroke(getStrokeWidth(), strokeColor);
    }

    public void setCornerRadius(float radius) {
        mRadius = radius;
        mGradientDrawable.setCornerRadius(radius);
    }

    public void setColor(int color) {
        mColor = color;
        mGradientDrawable.setColor(color);
    }

    public int getColor() {
        return mColor;
    }

    public float getRadius() {
        return mRadius;
    }

    public GradientDrawable getGradientDrawable() {
        return mGradientDrawable;
    }
}
}

Add this widget in your layout.xml

<com.test.widgets.MorphingButton
        android:id="@+id/btnDone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="DONE"/>

In Activity to animate view onButtonClick

write below code inside `MorphingButton` onClick
MorphingButton.Params circle = MorphingButton.Params.create()//To animate in circle from rect-round
                    .duration(500)
                    .cornerRadius((int) getResources().getDimension(R.dimen._100sdp))
                    .width((int) getResources().getDimension(R.dimen._56sdp))
                    .height((int) getResources().getDimension(R.dimen._56sdp))
                    .color(Color.BLUE) // normal state color
                    .colorPressed(Color.GREEN) // pressed state color
                    .icon(R.drawable.ic_loader); // icon
            btnDone.morph(circle);

//To animate from circle to rect-round to this demo purpose
            btnDone.postDelayed(new Runnable() {
                @Override
                public void run() {
                    MorphingButton.Params square = MorphingButton.Params.create()
                            .duration(500)
                            .cornerRadius((int) getResources().getDimension(R.dimen._100sdp))
                            .width((int) getResources().getDimension(R.dimen._100sdp))
                            .height((int) getResources().getDimension(R.dimen._56sdp))
                            .color(Color.BLUE) // normal state color
                            .colorPressed(Color.GREEN) // pressed state color
                            .icon(R.drawable.ic_loader) // icon
                            .text("DONE");
                    btnDone.morph(square);
                }
            }, 5000);

关于android - 如何使用动画将按钮的形状从圆角矩形更改为圆形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40785199/

相关文章:

android - File.listFiles 因无效的 UTF-8 字符而崩溃

android - 适用于 Mac 的 ADT bundle : Eclipse cannot be started

javascript - Ajax 调用不适用于 android 但在 ios Cordova 上运行良好

android - 有什么方法可以使用 googleapi 从 google+ 中的用户名获取用户 ID?

java - 如何模拟(或)测试 Android 中的方法级变量?

java - 从导航 View 调用特定按钮

android - 我在 Android 中使用 ListActivity 时发生内存泄漏

java - 如何删除屏蔽的edittext内容

java.lang.IllegalStateException : HTC Android 6. 0 棉花糖崩溃 - Google Play 快照

java - 如何将 byte[4] 转换为 int (Android)