android自定义水波纹,Android 自定义View一个控件搞定多种水波纹涟漪扩散效果

效果图

18ff455fd55e92a6c50ead3cd4627e3d.png

实现思路

这个效果实现起来并不难,重要的是思路

此View知足了多种水波纹涟漪扩散效果,这要求它能知足不少的变化

根据上面的样式,能够看出此View须要知足如下变化java

圆圈从中心可循环向外扩散git

圆圈之间的扩散间距能够改变github

可控制扩散圆的渐变度web

圆圈能够是线条样式或者实心样式算法

圆圈扩散的速度能够控制ide

适配圆圈不一样大小下的扩散效果svg

具体实现

建立自定义属性

首先为View建立自定义的xml属性

在工程的values目录下新建attrs.xml文件动画

各个属性的做用以下spa

cColor:View控件的颜色

cSpeed:向外扩散的速度

cDensity:圆形波纹扩散的间距

cIsFill:是否开启填充模式,true为实心圆

cIsAlpha:是否开启渐变效果,true为开启

建立自定义View控件

新建RippleView类继承View类,重写它的三个构造方法,获取用户设置的属性,同时指定默认值.net

public RippleView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

// 获取用户配置属性

TypedArray tya = context.obtainStyledAttributes(attrs, R.styleable.mRippleView);

mColor = tya.getColor(R.styleable.mRippleView_cColor, Color.BLUE);

mSpeed = tya.getInt(R.styleable.mRippleView_cSpeed, 1);

mDensity = tya.getInt(R.styleable.mRippleView_cDensity, 10);

mIsFill = tya.getBoolean(R.styleable.mRippleView_cIsFill, false);

mIsAlpha = tya.getBoolean(R.styleable.mRippleView_cIsAlpha, false);

tya.recycle();

init();

}

使用TypedArray读取完自定义的属性后必定要记得调用recycle方法释放掉

重写onMeasure

测量onMeasure,首先须要测量出View的宽和高,并指定View在wrap_content时的最小范围,对于View绘制流程还不熟悉的同窗,能够先去了解下具体的绘制流程

重写onMeasure方法,其中咱们要考虑当View的宽高被指定为wrap_content时的状况,若是咱们不对wrap_content的状况进行处理,那么当使用者指定View的宽高为wrap_content时将没法正常显示出View

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int myWidthSpecMode = MeasureSpec.getMode(widthMeasureSpec);

int myWidthSpecSize = MeasureSpec.getSize(widthMeasureSpec);

int myHeightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

int myHeightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

// 获取宽

if (myWidthSpecMode == MeasureSpec.EXACTLY) {

// match_parent/精确值

mWidth = myWidthSpecSize;

} else {

// wrap_content

mWidth = DensityUtil.dip2px(mContext, 120);

}

// 获取高

if (myHeightSpecMode == MeasureSpec.EXACTLY) {

// match_parent/精确值

mHeight = myHeightSpecSize;

} else {

// wrap_content

mHeight = DensityUtil.dip2px(mContext, 120);

}

// 设置该view的宽高

setMeasuredDimension(mWidth, mHeight);

}

MeasureSpec的状态分为三种EXACTLY、AT_MOST、UNSPECIFIED,这里只要单独指定非精确值EXACTLY以外的状况就行了

本文中使用到的DensityUtil类,是为了将dp转换为px来使用,以便适配不一样的屏幕显示效果

public static int dip2px(Context context, float dpValue) {

final float scale = context.getResources().getDisplayMetrics().density;

return (int) (dpValue * scale + 0.5f);

}

重写onDraw

设计的总体思路以下图所示

958dae5f027552dc0a1794d30b70f3bb.png

先要实现圆形向外扩散的效果

初始化第一个圆

这里的动画效果原本是想使用ValueAnimator属性动画的数值发生器来实现,可是咱们这里有不少的计算需求,因此最后仍是选择使用算法来实现,方便控制圆的一些参数

想要实现扩散的效果,这里思路是在每次更新View时动态改变圆的半径,同时还须要给圆设置渐变度数,因此决定用一个类来保存圆的状态,全部圆都存在一个List里

// 添加第一个圆圈

mRipples = new ArrayList<>();

Circle c = new Circle(0, 255);

mRipples.add(c);

传入Circle类里的两个参数,第一个0表示圆的初始宽度,第二个255表示初始透明度

添加新圆

要想实现不断有圆向外扩散,就须要在第一个圆扩散到必定范围时在圆心处再添加一个圆,这个的范围能够由圆的半径来控制,当List集合中最后一个圆的半径增长到某个值mDensity时,新的圆就从圆心处建立出来

// 添加圆

if (mRipples.size() > 0) {

// 控制第二个圆出来的间距

if (mRipples.get(mRipples.size() - 1).width > DensityUtil.dip2px(mContext, mDensity)) {

mRipples.add(new Circle(0, 255));

}

}

删除List中多余的圆

List中的圆存储的数量不宜过多,多了内存消耗大,须要在当圆的半径超过View的宽度时就删掉这个圆

// 当圆超出View的宽度后删除

if (c.width > mWidth / 2) {

mRipples.remove(i);

}

咱们也能够在外切正方形的顶点处删除这个圆,须要用到勾股定律来计算扩散圆到外切正方形顶点的位置

f4007f9b76da2a5e31594f07fd7708ee.png

如上图所示,得出计算公式为

// 使用勾股定律求得一个外切正方形中心点离顶点的距离

sqrtNumber = (int) (Math.sqrt(mWidth * mWidth + mHeight * mHeight) / 2);

这样就须要修改删除圆的位置了

if (c.width > sprtNumber) {

mRipples.remove(i);

}

控制扩散圆的渐变度

当圆在向View的边缘扩散时,渐变度数的改变须要动态来计算,渐变的计算算法要适配不一样的圆宽度大小,咱们知道透明度是0~255之间的,0表示彻底透明,255表示百分百不透明,计算的时候就是须要将这个数值等份分配到圆的宽度里

这里要区分一点,对于圆来讲,宽度是由圆心从0开始向外递增,而渐变度数则是由圆心从255开始向外递减,当圆与最外围的正方形内切时渐变度必须变为0,由此分析得知,公式以下

透明度 = 255 - 圆的宽度 * (255 / View宽度)

double alpha = 255 - c.width * (255 / ((double) mWidth / 2));

c.alpha = (int) alpha;

GitHub地址

总结

关于自定义View的总结部分在个人其它博客中已经写过蛮多了,有兴趣的能够去看看

作自定义View,思路很重要,当想到一种方法能够实现时,先不要着急的作出来,试着换一个角度再多思考一下还有没有更好的实现方式

本例子还缺乏一些控制逻辑代码,将在以后添加上去并更新在GitHub中

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值