自定义View实现百度Loading动画
来源:互联网 发布:js淘宝购物车脚本之家 编辑:程序博客网 时间:2024/05/16 05:27
基本绘制
实现思路:先绘制背景色文字,然后讲Canvas裁切这个path路径,然后绘制白色的文字
绘制正弦曲线
这里使用贝塞尔曲线实现正弦曲线的绘制,使用ValueAnimator不断通知重绘
private Path getActionPath(float curPercent) { final Path path = new Path(); int x = -mWidth; x += curPercent * mWidth; path.moveTo(x, mHeight / 2); // 计算控制点 int quadWidth = mWidth / 4; int quadHeight = mHeight / 20 * 3; // 第一个周期 path.rQuadTo(quadWidth, quadHeight, quadWidth * 2, 0); path.rQuadTo(quadWidth, -quadHeight, quadWidth * 2, 0); // 第二个周期 path.rQuadTo(quadWidth, quadHeight, quadWidth * 2, 0); path.rQuadTo(quadWidth, -quadHeight, quadWidth * 2, 0); // 右侧的直线 path.lineTo(x + mWidth * 2, mHeight); path.lineTo(x, mHeight); path.close(); return path; }
这里涉及到Path类的使用,使用path的rQuadTo()表示相对与上一个点位置进行二阶贝塞尔变换,而使用quadTo()是相对于原始坐标系进行贝塞尔变换.
绘制文字
mTextPaint.setTextSize(mTextHeight);// 先绘制背景色文字mTextPaint.setColor(mColor);//绘制 文字Rect rect = new Rect(0, 0, mWidth, mHeight);mTextPaint.setTextAlign(Paint.Align.CENTER);Paint.FontMetrics metrics = mTextPaint.getFontMetrics();float top = metrics.top;float bottom = metrics.bottom;int centerY = (int) (rect.centerY() - top / 2 - bottom / 2);canvas.drawText(mText, rect.centerX(), centerY, mTextPaint);
实现裁切效果
实现方式1:使用Canvas.clipPath()实现
使用canvas.clipPath()可以将Canvas裁切成成指定的Path效果,前提是曲线必须闭合
完整的onDraw()代码如下
// 先绘制背景色文字,然后讲Canvas裁切这个path路径,然后绘制白色的文字 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.d(TAG, "onDraw: mCurPercent " + mCurPercent); mTextPaint.setTextSize(mTextHeight); // 先绘制背景色文字 mTextPaint.setColor(mColor); //绘制 文字 Rect rect = new Rect(0, 0, mWidth, mHeight); mTextPaint.setTextAlign(Paint.Align.CENTER); Paint.FontMetrics metrics = mTextPaint.getFontMetrics(); float top = metrics.top; float bottom = metrics.bottom; int centerY = (int) (rect.centerY() - top / 2 - bottom / 2); canvas.drawText(mText, rect.centerX(), centerY, mTextPaint); // 生成闭合波浪路径 mPath = getActionPath(mCurPercent); // 将Canvas按照Path的规则进行裁剪 canvas.clipPath(mPath); canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mPaint); mTextPaint.setColor(Color.WHITE); canvas.drawText(mText, rect.centerX(), centerY, mTextPaint); }
实现方式2: 使用xfermode代替Canvas裁切路径
xfermode的模式:
xfermode使用注意:
- 使用xfermode的时候记得先使用Canvas的离屏缓冲,不然会将后绘制的src黑色背景一块绘制上去
- 使用xfermode完毕后及时将paint的xfermode设置为null,避免影响下次绘制
paint.setXfermode(null)
这里主要使用了两种模式: SRC_IN
: 在源图像(后绘制的)和目标图像相交的地方,取源图像 SRC_ATOP
:在源图像和目标图像相交的地方绘制源图像
这两种模式的应用在代码注释里也有相应的解释
onDraw的代码将改写成:
// 先绘制背景色文字,然后讲Canvas裁切这个path路径,然后绘制白色的文字 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.d(TAG, "onDraw: mCurPercent " + mCurPercent); mTextPaint.setTextSize(mTextHeight); // 先绘制背景色文字 mTextPaint.setColor(mColor); //绘制 文字 Rect rect = new Rect(0, 0, mWidth, mHeight); mTextPaint.setTextAlign(Paint.Align.CENTER); Paint.FontMetrics metrics = mTextPaint.getFontMetrics(); float top = metrics.top; float bottom = metrics.bottom; int centerY = (int) (rect.centerY() - top / 2 - bottom / 2); canvas.drawText(mText, rect.centerX(), centerY, mTextPaint); // 启动离屏缓冲 final int saved = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG); final Bitmap circle = getCircleBitmap(mWidth, mHeight); // 生成闭合波浪路径 mPath = getActionPath(mCurPercent); canvas.drawPath(mPath, mPaint); // SRC_IN 在src(bitmap)和dst(path)相交的地方取源图像 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); // 如果直接使用canvas.drawCircle()绘制圆形,canvas绘制圆形之外的部分没有alpha通道 // 而xfermode计算图片层叠样式的原理是通过alpha通道,所以这里必须使用一个bitmap来绘制 // 才能裁切掉多余的矩形 canvas.drawBitmap(circle, 0, 0, mPaint); // SRC_ATOP在src(text)和dst(bitmap+path)相交的地方绘制源图像 mTextPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); mTextPaint.setColor(Color.WHITE); canvas.drawText(mText, rect.centerX(), centerY, mTextPaint); canvas.restoreToCount(saved); // onDraw()结束前记得将xfermode设置为null,避免影响下次绘制 mPaint.setXfermode(null); mTextPaint.setXfermode(null); }
实现方式1完整代码
/** * Created by yangtianrui on 17-8-12. * 使用Cavas Clip实现贴吧小球 */@SuppressLint("DrawAllocation")public class CanvasClipWave extends View { private static final String TAG = "ytr"; private final String mText; private final int mColor; private final Paint mPaint; private final Paint mTextPaint; private Path mPath; private float mCurPercent; private int mTextHeight; private int mWidth; private int mHeight; public CanvasClipWave(Context context) { this(context, null); } public CanvasClipWave(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public CanvasClipWave(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CanvasClipWave); mText = a.getString(R.styleable.CanvasClipWave_text); mColor = a.getColor(R.styleable.CanvasClipWave_color, Color.BLUE); a.recycle(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mColor); // 设置防抖动 mPaint.setDither(true); mTextPaint = new Paint(); mTextPaint.setTypeface(Typeface.DEFAULT_BOLD); // 不断重绘 ValueAnimator va = ValueAnimator.ofFloat(0F, 1F); va.setInterpolator(new LinearInterpolator()); va.setRepeatMode(ValueAnimator.RESTART); va.setRepeatCount(ValueAnimator.INFINITE); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurPercent = (float) animation.getAnimatedValue(); postInvalidate(); } }); va.setDuration(1000); va.start(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int wMode = MeasureSpec.getMode(widthMeasureSpec); final int wSize = MeasureSpec.getSize(widthMeasureSpec); final int hMode = MeasureSpec.getMode(heightMeasureSpec); final int hSize = MeasureSpec.getSize(heightMeasureSpec); if (wMode == MeasureSpec.EXACTLY && hMode == MeasureSpec.EXACTLY) { mTextHeight = hSize / 2; mWidth = wSize; mHeight = hSize; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } // 先绘制背景色文字,然后讲Canvas裁切这个path路径,然后绘制白色的文字 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.d(TAG, "onDraw: mCurPercent " + mCurPercent); mTextPaint.setTextSize(mTextHeight); // 先绘制背景色文字 mTextPaint.setColor(mColor); //绘制 文字 Rect rect = new Rect(0, 0, mWidth, mHeight); mTextPaint.setTextAlign(Paint.Align.CENTER); Paint.FontMetrics metrics = mTextPaint.getFontMetrics(); float top = metrics.top; float bottom = metrics.bottom; int centerY = (int) (rect.centerY() - top / 2 - bottom / 2); canvas.drawText(mText, rect.centerX(), centerY, mTextPaint); // 生成闭合波浪路径 mPath = getActionPath(mCurPercent); // 将Canvas按照Path的规则进行裁剪 canvas.clipPath(mPath); canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mPaint); mTextPaint.setColor(Color.WHITE); canvas.drawText(mText, rect.centerX(), centerY, mTextPaint); } private Path getActionPath(float curPercent) { final Path path = new Path(); int x = -mWidth; x += curPercent * mWidth; path.moveTo(x, mHeight / 2); // 计算控制点 int quadWidth = mWidth / 4; int quadHeight = mHeight / 20 * 3; // 第一个周期 path.rQuadTo(quadWidth, quadHeight, quadWidth * 2, 0); path.rQuadTo(quadWidth, -quadHeight, quadWidth * 2, 0); // 第二个周期 path.rQuadTo(quadWidth, quadHeight, quadWidth * 2, 0); path.rQuadTo(quadWidth, -quadHeight, quadWidth * 2, 0); // 右侧的直线 path.lineTo(x + mWidth * 2, mHeight); path.lineTo(x, mHeight); path.close(); return path; }}
实现方式2完整代码:
/** * Created by yangtianrui on 17-8-13. * 使用Xfermode实现这样的Canvas裁切效果, 代码基本上与CanvasClipWave相同 */@SuppressLint("DrawAllocation")public class XfermodeWave extends View { private static final String TAG = "ytr"; private final String mText; private final int mColor; private final Paint mPaint; private final Paint mTextPaint; private Path mPath; private float mCurPercent; private int mTextHeight; private int mWidth; private int mHeight; public XfermodeWave(Context context) { this(context, null); } public XfermodeWave(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public XfermodeWave(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CanvasClipWave); mText = a.getString(R.styleable.CanvasClipWave_text); mColor = a.getColor(R.styleable.CanvasClipWave_color, Color.BLUE); a.recycle(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mColor); // 设置防抖动 mPaint.setDither(true); mTextPaint = new Paint(); mTextPaint.setTypeface(Typeface.DEFAULT_BOLD); // 不断重绘 ValueAnimator va = ValueAnimator.ofFloat(0F, 1F); va.setInterpolator(new LinearInterpolator()); va.setRepeatMode(ValueAnimator.RESTART); va.setRepeatCount(ValueAnimator.INFINITE); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurPercent = (float) animation.getAnimatedValue(); postInvalidate(); } }); va.setDuration(1000); va.start(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int wMode = MeasureSpec.getMode(widthMeasureSpec); final int wSize = MeasureSpec.getSize(widthMeasureSpec); final int hMode = MeasureSpec.getMode(heightMeasureSpec); final int hSize = MeasureSpec.getSize(heightMeasureSpec); if (wMode == MeasureSpec.EXACTLY && hMode == MeasureSpec.EXACTLY) { mTextHeight = hSize / 2; mWidth = wSize; mHeight = hSize; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } // 先绘制背景色文字,然后讲Canvas裁切这个path路径,然后绘制白色的文字 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.d(TAG, "onDraw: mCurPercent " + mCurPercent); mTextPaint.setTextSize(mTextHeight); // 先绘制背景色文字 mTextPaint.setColor(mColor); //绘制 文字 Rect rect = new Rect(0, 0, mWidth, mHeight); mTextPaint.setTextAlign(Paint.Align.CENTER); Paint.FontMetrics metrics = mTextPaint.getFontMetrics(); float top = metrics.top; float bottom = metrics.bottom; int centerY = (int) (rect.centerY() - top / 2 - bottom / 2); canvas.drawText(mText, rect.centerX(), centerY, mTextPaint); // 启动离屏缓冲 final int saved = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG); final Bitmap circle = getCircleBitmap(mWidth, mHeight); // 生成闭合波浪路径 mPath = getActionPath(mCurPercent); canvas.drawPath(mPath, mPaint); // SRC_IN 在src(bitmap)和dst(path)相交的地方取源图像 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); // 如果直接使用canvas.drawCircle()绘制圆形,canvas绘制圆形之外的部分没有alpha通道 // 而xfermode计算图片层叠样式的原理是通过alpha通道,所以这里必须使用一个bitmap来绘制 // 才能裁切掉多余的矩形 canvas.drawBitmap(circle, 0, 0, mPaint); // SRC_ATOP在src(text)和dst(bitmap+path)相交的地方绘制源图像 mTextPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); mTextPaint.setColor(Color.WHITE); canvas.drawText(mText, rect.centerX(), centerY, mTextPaint); canvas.restoreToCount(saved); // onDraw()结束前记得将xfermode设置为null,避免影响下次绘制 mPaint.setXfermode(null); mTextPaint.setXfermode(null); } /** * 创建一个中间绘制圆形的bitmap */ private Bitmap getCircleBitmap(int width, int height) { Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8); Canvas canvas = new Canvas(bitmap); canvas.drawCircle(width / 2, height / 2, width / 2, mPaint); return bitmap; } private Path getActionPath(float curPercent) { final Path path = new Path(); int x = -mWidth; x += curPercent * mWidth; path.moveTo(x, mHeight / 2); // 计算控制点 int quadWidth = mWidth / 4; int quadHeight = mHeight / 20 * 3; // 第一个周期 path.rQuadTo(quadWidth, quadHeight, quadWidth * 2, 0); path.rQuadTo(quadWidth, -quadHeight, quadWidth * 2, 0); // 第二个周期 path.rQuadTo(quadWidth, quadHeight, quadWidth * 2, 0); path.rQuadTo(quadWidth, -quadHeight, quadWidth * 2, 0); // 右侧的直线 path.lineTo(x + mWidth * 2, mHeight); path.lineTo(x, mHeight); path.close(); return path; }}
阅读全文
0 0
- 自定义View实现百度Loading动画
- 自定义View实现百度Loading动画
- 自定义View实现loading动画加载
- Android 自定义View 实现loading动画
- 【Android自定义View实战】之仿百度加载动画,一种优雅的Loading方式
- Android:自定义view实现动画
- 自定义view+属性动画实现
- 自定义loading 以及动画loading
- android 自定义loading动画
- [cocos2dx]自定义loading动画
- 手把手带你做一个超炫酷loading成功动画view Android自定义view
- 手把手带你做一个超炫酷loading成功动画view Android自定义view
- 手把手带你做一个超炫酷loading成功动画view Android自定义view
- Android开发-使用自定义View实现loading效果
- 自定义View实现圆形进度条及圆形Loading
- 自定义View 实现Loading...(后面的点点点循环出现. .. ..)
- Android自定义progressDialog实现loading载中动画效果
- Android自定义progressDialog实现 loading 载入中 动画效果
- Python(1)--变量及变量的赋值
- Web API 方法的返回类型、格式器、过滤器
- iiiLab视频解析下载网站的几个使用技巧
- 【DevOps系列】容量规则平台
- 机房重构---修改密码
- 自定义View实现百度Loading动画
- Localhost 与127.0.0.1和IP的区别是什么
- maven 中profile 配置
- pstack
- 值得推荐的C/C++框架和库
- 设计模式之工厂模式
- 查
- 报错 : CRTC 63 尝试模式 CRTC 63:尝试 800x600@60Hz 模式输出在 1366x768@60Hz (通过 0)
- UVA1637DoublePatience