自定义波浪加载小球
来源:互联网 发布:c语言取反运算 编辑:程序博客网 时间:2024/05/16 06:58
功能来源于需求 ,给我一盒画笔,我将画出整个世界。
本篇记录一次自定义类似加速球的自定义实现:
效果图
具体实现
为了实现这样的一个效果,就必须自定义View,进行自定义布局初始 化以及定义变量等:
private Paint mPaint; //基本画笔 private Paint textPaint; //文字画笔 private Path path; //路径 private int mWidth = DimentionUtils.px2Dp(getContext(), 50); //默认的view的宽度 private int mHeight = DimentionUtils.px2Dp(getContext(), 50);//默认view的高度 private int textSize = DimentionUtils.px2Sp(getContext(), 10);//默认文字的大小 private String content = "卢";//文字内容 private float curPercent; //波浪线水平移动的速率 private int color; //文字颜色(默认颜色为红色) private float ratio = 0.5f;// 波浪的高度与view的比值,默认0.5 public WaveLoadCircle(Context context) { this(context, null); } public WaveLoadCircle(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public WaveLoadCircle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); if (widthMode == MeasureSpec.EXACTLY) {// 确定值或者match_parent mWidth = width; } if (heightMode == MeasureSpec.EXACTLY) { mHeight = height; } setMeasuredDimension(mWidth, mHeight); textSize = mWidth / 4;//文字大小为宽度的四分之一 textPaint.setTextSize(textSize); } /** * 初始化画笔和路径 * * @param attr */ private void init(AttributeSet attr) { TypedArray arr = getContext().obtainStyledAttributes(attr, R.styleable.WaveLoadCircle); //自定义颜色和文字,默认蓝色 int c = arr.getColor(R.styleable.WaveLoadCircle_color, Color.BLUE); String text = arr.getString(R.styleable.WaveLoadCircle_text); if (c != 0) { Log.i("tag", "init:color " + c); color = c; } if (text != null) { content = text; Log.i("tag", "init:text " + text); } arr.recycle();//回收资源 //初始化画笔 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//去除锯齿 mPaint.setDither(true); mPaint.setStyle(Paint.Style.FILL);//填充 mPaint.setColor(color); //初始化文字画笔 textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//反锯齿标志 textPaint.setColor(color); textPaint.setStyle(Paint.Style.FILL); textPaint.setDither(true); //闭合的波浪路径 path = new Path(); }
自定义属性:
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="WaveLoadCircle"> <attr name="color" format="color"/> <attr name="text" format="string"/> </declare-styleable></resources>
实现这样的View是通过Canvas、Paint、Path来画了四层效果图将其叠加在一起:
那么首先就是画最底下那个文字,这里有个问题就是如何将文字画到画布的中间呢,下面的这句代码其实只是对齐了x轴,也就是水平居中了,所以就是如何才能让其垂直居中呢,
Rect rect = new Rect(0, 0, mWidth, mHeight);paint.setTextAlign(Paint.Align.CENTER);
先来看一个文字绘制时如何的定位:
所以,文字的y轴居中坐标:centerY = (画布高度 - 字体.assent - 字体.descent)/2,这样画到画布的文字才会居中显示:
/** * 为了将文字画在画布的中央,centerY = (画布高度 - 字体.assent - 字体.descent)/2 * * @param canvas * @param paint */ private void drawCentertext(Canvas canvas, Paint paint) { Rect rect = new Rect(0, 0, mWidth, mHeight); paint.setTextAlign(Paint.Align.CENTER); Paint.FontMetrics pf = paint.getFontMetrics(); int centerY = (int) ((mHeight - pf.ascent - pf.descent) / 2); canvas.drawText(content, rect.centerX(), centerY, paint); }
最底下的文字画好以后,就是第二层画波浪线,利用Path来完成这个使命,示意图如下:
图中的灰色就是我们的画布,为了能够实现波浪的波动,因为Path中没有画sin 或者 cos的曲线,所以就选择用贝塞尔曲线来画一段曲线。要想实现波浪的波动,图中左边的起点会不断向右滑动,所以绘制一个上边是图中所示的闭合矩形:
/** * 绘制一个上边是由4段二阶贝塞尔曲线的矩形,区间位置-mWidth ~ mWidth * * @param percent * @return */ public Path getWavePath(float percent) { Path path = new Path(); float x = -mWidth * percent; path.moveTo(x, mHeight * (1 - ratio)); //控制点的相对宽度 int qWidth = mWidth / 4; //控制点的相对高度 int qHeight = qWidth / 2; //第一个波浪 path.rQuadTo(qWidth, qHeight, qWidth * 2, 0); path.rQuadTo(qWidth, -qHeight, qWidth * 2, 0); //第二个波浪 path.rQuadTo(qWidth, qHeight, qWidth * 2, 0); path.rQuadTo(qWidth, -qHeight, qWidth * 2, 0); //右侧的直线 path.lineTo(x + mWidth * 2, mHeight); path.lineTo(x, mHeight); //自动闭合补出左边的直线 path.close(); return path; }
接着裁剪该path的闭合路径:
// 获取path路径为一个上边为贝塞尔曲线的矩形 path = getWavePath(curPercent); Log.i("tag", "onDraw:percent " + curPercent); // 在画布上面裁剪该path canvas.clipPath(path);
在裁剪后的画布上面画圆(画圆的画笔颜色是波浪的颜色):
// 画圆 canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mPaint);
最后一层就是再画一个文字和最底层文字一样,但是颜色为白色的文字:
textPaint.setColor(Color.WHITE); // 再画一个颜色与上面字体颜色不一样的字 drawCentertext(canvas, textPaint);
通过ValueAnimator来设置波浪的移动距离:
// 波浪水平移动的速率 ValueAnimator anim = ValueAnimator.ofFloat(0, 1); anim.setDuration(1000); anim.setRepeatCount(ValueAnimator.INFINITE); // 无限重复 anim.setRepeatMode(ValueAnimator.RESTART);//重头再来 anim.setInterpolator(new LinearInterpolator()); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { curPercent = animation.getAnimatedFraction();// 将0~1 的动画值不断得赋值当前的进度 Log.i("tag", "onAnimationUpdate: " + curPercent); invalidate(); } }); anim.start();
添加一个设置波浪高度的方法:
/** * 设置波浪的高度与view高度的比值(0f ~ 1f) * * @param ratio */ public void setWaveHeightRatio(float ratio) { this.ratio = ratio; invalidate(); }
好了,基本流程就是这样。github源码链接
阅读全文
1 0
- 自定义波浪加载小球
- Android使用Path自定义波浪加载View
- Android使用Path自定义波浪加载View
- 自定义View之小球自由落体弹跳加载控件
- 自定义View之小球自由落体弹跳加载控件
- TextView波浪加载效果
- 自定义view:波浪
- 自定义View 波浪
- 实现一个自定义波浪View
- Android 贝塞尔曲线自定义波浪
- 自定义View--弹性小球
- 自定义拖动小球
- 波浪
- android自定义控件---小球圆周运动
- 自定义小球跟随手指移动
- 自定义view 小球的移动
- Android自定义控件--波浪球SurfaceView实现
- 自定义View——实现波浪动画
- nefu117 素数个数的位数 素数定理+位数公式
- 活动的生存周期
- Qes_20170801_Two components with the same name and precedence
- POJ-2478-Farey Sequence-递推求欧拉函数
- 基于STM32的GPS定位系统开发,以及Ucenter使用笔记
- 自定义波浪加载小球
- mysql实现物化视图详解及视图与物化视图区别
- Android异步消息处理线程机制
- 【单调栈】codeforces 280b
- ccf集合竞价
- vue.js基础-组件
- QT关于屏幕保护程序
- js循环语句与循环嵌套
- 算法---从一个数组(或者集合中)找出和为某个值的下标