手把手教你实现一个丝滑般的控件Q弹效果(滑动+Q弹+渐变)

前言

很多人觉得Android的用户体验没有iOS好,但是近年来,随着众多Android开发者的努力,现在的Android已经有着不输于iOS的实力,用户体验也在直线上升。

今天要和大家分享的是一个丝滑般的控件Q弹效果,希望对大家的学习和工作有所启发和帮助。

废话就不多说了,直接上效果图,让你感受一下到底有多丝滑,又有多Q弹。


这Q弹的动画,妖艳的色彩转换,着实和市面上的普通Switch控件不太一样。下面对它逐一拆解,从0到1实现它。

设计思路

Android系统提供的SwitchButon很好地体现了metiarial design设计风格,但不够妖艳。我希望用两个比较冲突的对比色进行控件的颜色设计,这样摆在浅色背景页面的时,控件可以达到一种直刺眼睛的效果。抓人、妖艳、骚气外放。只要整体页面搭配得当,该控件必定可以“外骚内纯”。微微模糊的光晕可以使得本就亮丽的颜色变得更加妖艳。

为增加设计的可扩展性,可以通过改变颜色从而达到体现不同风格的目的。

实现方式

总体而言,虽然控件的外表非常妖艳,但交互逻辑是比较简单的。不考虑继承自系统Switch,因为并不需要再去了解如何扩展Switch。因此直接继承自View即可。整个逻辑控制在onTouchEvent中实现,声明好各个可配置的属性,如颜色、大小等等。

UI绘制

可以看到,图形由两大部分构成,一个是前面蓝色的圆角矩形(指示器),一个是后面红色的长条矩形(背景条),分别对这两个图形进行绘制即可。onDraw部分的代码如下:

  • 画背景条:
    其中bkgRect代表待绘制矩形区域范围,height、width为控件长宽,bkgBarW、bkgBarH为背景条长宽,由于是圆角矩形,调用canvas的drawRoundRect方法进行绘制,bkgBarPaint控制背景条颜色
//画背景条
RectF bkgRect = new RectF((width - bkgBarW) / 2f, height / 2 - (bkgBarH / 2), (width - bkgBarW) / 2f + bkgBarW, height / 2 + (bkgBarH / 2));
canvas.drawRoundRect(bkgRect, bkgBarH / 4, bkgBarH / 4, bkgBarPaint);
  • 画前景圆角矩形:
    同样的,indicatorRect为待绘制圆角矩形的区域范围,indicatorW、indicatorH为指示器宽高,indicatorX、indicatorY为指示器的中心坐标点,该坐标之后会结合animator进行动态计算,最后调用canvas的drawRoundRect进行绘制
//画指示器
RectF indicatorRect = new RectF(
        indicatorX,
        indicatorY,
        indicatorW + indicatorX,
        (height - indicatorH) / 2 + indicatorH
);
canvas.drawRoundRect(indicatorRect, indicatorH / 6, indicatorH / 6, indicatorPaint);
  • 画图标或文字:
    这部分计算好文字或图标的坐标进行绘制即可,需要注意的是,如果要绘制文字,需要计算出文字的基线位置,方便与指示器在视觉上居中
//画图标
int baseLineY = (int) (indicatorRect.centerY() - textTop / 2 - textBottom / 2);//基线中间点的y轴计算公式
if (status == false) {
    canvas.drawText("♂", indicatorRect.centerX(), baseLineY, textPaint);
} else {
    canvas.drawText("♀", indicatorRect.centerX(), baseLineY, textPaint);
}
  • 动画实现
    为方便控制,定义两个AnimatorSet,分别为animOnSet、animOffSet,animOnSet代表开关选择器打开时的动画合集,animOffSet代表开关选择器关闭时的动画合集。设置为BounceInterpolator即可配置弹性效果。
animatorOn = ValueAnimator.ofFloat(indicatorStartX, indicatorEndX);
animatorOn.setDuration(500);
animatorOn.setInterpolator(new BounceInterpolator());
animatorOn.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        indicatorX = (float) animation.getAnimatedValue();
        postInvalidate();
    }
});
animOnSet = new AnimatorSet();
animOnSet.playTogether(animatorOn, animatorColorOn);


animatorOff = ValueAnimator.ofFloat(indicatorEndX, indicatorStartX);
animatorOff.setDuration(500);
animatorOff.setInterpolator(new BounceInterpolator());
animatorOff.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        indicatorX = (float) animation.getAnimatedValue();
        postInvalidate();
    }
});
animOffSet = new AnimatorSet();
animOffSet.playTogether(animatorColorOff, animatorOff);
  • 加亿点细节
    仔细观察可以注意到,指示器是带有同颜色的阴影的,淡淡的阴影向四周晕开,如同光雾一般。这里使用设计好的阴影背景图切图即可,但为了增加控件通用性,采取了讨巧的办法。通过对Bitmap的处理从而在指示器颜色变化时,绘制的阴影颜色也随之变化。代码如下,bmShdow为待绘制的阴影图片
bmShadow = BitmapFactory.decodeResource(getResources(), R.drawable.img_shadow_rect_blue);
//sex_blue为配置的指示器为“开”状态时的颜色,int值
bmShadow = BitmapUtils.replacePixelColor(bmShadow, sex_blue);

再仔细观察,会发现在指示器动画执行的过程中,指示器颜色也完成了一个渐变过渡。这里有个变换颜色动态计算的操作,依然采用animator进行计算,其中indicatorPaint控制指示器颜色

animatorColorOn = new ValueAnimator();
animatorColorOn.setIntValues(sex_blue, sex_red);
animatorColorOn.setEvaluator(new ArgbEvaluator());
animatorColorOn.setDuration(500);
animatorColorOn.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int color = (int) animation.getAnimatedValue();
        indicatorPaint.setColor(color);
    }
});

animatorColorOff = new ValueAnimator();
animatorColorOff.setIntValues(sex_red, sex_blue);
animatorColorOff.setEvaluator(new ArgbEvaluator());
animatorColorOff.setDuration(500);
animatorColorOff.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int color = (int) animation.getAnimatedValue();
        indicatorPaint.setColor(color);
    }
});

最后

Android的学习是一条漫长的道路,每个地方都是值得学习的内容。

其中,控件设计或开发这个点就无处不体现着“自顶向下”的思想。在实际工作中,我们会遇到各种各样的需求,但是不要慌!只要我们静下心来,弄清需求,理清逻辑,打磨细节,做到这三点,绝大部分控件设计或开发的难题都可以迎刃而解。

我把自己这段时间整理的Android最重要最热门的学习方向资料放在了我的GitHub:https://github.com/xieyuliang/Android-P7-share/blob/master/Android,里面还有不同方向的自学编程路线、面试题集合/面经、及系列技术文章等。

资源持续更新中,欢迎大家一起学习和探讨

猜你喜欢

转载自blog.csdn.net/m0_46962786/article/details/109841974