热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

安卓自定义控件-实现IOS版UC浏览器三点加载动画效果

1.实现分析废话不多说,看下IOS版UC浏览器的加载效果简单画个图看下整个过程1.B圆的圆心移动的坐标为:A圆和B圆的圆心的距离L的中点为圆心O1的下

1.实现分析

废话不多说,看下IOS版UC浏览器的加载效果

这里写图片描述

简单画个图看下整个过程

这里写图片描述
1.B圆的圆心移动的坐标为:A圆和B圆的圆心的距离L的中点为圆心O1的下半圆的运动轨迹经过的坐标,就有一个由B位置到A位置圆周运动的轨迹。
2.C圆的圆心移动的坐标为:B圆和C圆的圆心的距离L的中点为圆心02的上半圆的运动轨迹经过的坐标,就有一个由C位置到B位置圆周运动的轨迹。
3.A圆就特别一些,我分为两个过程:一个是起点P0为A圆心,控制点P1为(L/2,L/2),终点P2为B圆心的二阶贝塞尔曲线;一个是起点P0为B圆心,控制点P1为(L*3/2,-L/2),终点P2为C圆心的二阶贝塞尔曲线
4.A圆的透明度为255,B圆为255*0.8,C圆为255*0.6

4.1 A移动到C,透明度变化255->255*0.6
4.2 B移动到A,透明度变化255*0.8->255
4.3 C移动到B,透明度变化255*0.6->255*0.8

2.代码实现

2.1 需要的变量

 public class ThreePointLoadingView extends View {
// 画笔
private Paint mBallPaint;
// 宽度
private int mWidth;
// 高度
private int mHeight;
// 圆之间的距离
private float mSpace;
// 圆的半径
private float mBallRadius;
// 三个圆合起来的距离(包括间距)
private float mTotalLength;
// A圆心的x坐标
private float mABallX;
// A圆心的y坐标
private float mABallY;
// B圆心的x坐标
private float mBBallX;
// B圆心的y坐标
private float mBBallY;
// C圆心的x坐标
private float mCBallX;
// C圆心的y坐标
private float mCBallY;

// 圆心移动的距离
private float mMoveLength;

// A圆心做二阶贝塞尔曲线的起点、控制点、终点
private PointF mABallP0;
private PointF mABallP1;
private PointF mABallP2;

// A圆心贝塞尔曲线运动时的坐标
private float mABallazierX;
private float mABallazierY;

// 值动画
private ValueAnimator mAnimator;

// 值动画产生的x方向的偏移量
private float mOffsetX = 0;
// 根据mOffsetX算得的y方向的偏移量
private float mOffsetY;

// A圆的起始透明度
private int mABallAlpha = 255;
// B圆的起始透明度
private int mBBallAlpha = (int) (255 * 0.8);
// C圆的起始透明度
private int mCBallAlpha = (int) (255 * 0.6);

2.2 构造时初始化画笔和A圆的三个点

    public ThreePointLoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {

mBallPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);

mBallPaint.setColor(ContextCompat.getColor(getContext(), R.color.material_deep_orange_a200));

mBallPaint.setStyle(Paint.Style.FILL);

mABallP0 = new PointF();
mABallP1 = new PointF();
mABallP2 = new PointF();

}

2.3 测量时初始化圆半径、间距等信息

    @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

// 考虑padding值
mWidth = measureSize(widthMeasureSpec, MeasureUtil.dip2px(getContext(), 180)) + getPaddingLeft() + getPaddingRight();
mHeight = measureSize(heightMeasureSpec, MeasureUtil.dip2px(getContext(), 180)) + getPaddingTop() + getPaddingBottom();

setMeasuredDimension(mWidth, mHeight);

// 间距为宽度10分之一
mSpace = mWidth * 1.0f / 20;

// 半径为宽度50分之一
mBallRadius = mWidth * 1.0f / 50;

// 总的长度为三个圆直径加上之间的间距
mTotalLength = mBallRadius * 6 + mSpace * 2;

// 两个圆圆心的距离
mMoveLength = mSpace + mBallRadius * 2;

// A圆心起始坐标,同时贝塞尔曲线的起始坐标也是这个
mABallazierX = mABallX = (mWidth - mTotalLength) / 2 + mBallRadius;
mABallazierY = mABallY = mHeight / 2;

// A圆心起始点,控制点,终点
mABallP0.set(mABallX, mABallY);
mABallP1.set(mABallX + mMoveLength / 2, mABallY - mMoveLength / 2);
mABallP2.set(mBBallX, mBBallY);

// B圆心的起始坐标
mBBallX = (mWidth - mTotalLength) / 2 + mBallRadius * 3 + mSpace;
mBBallY = mHeight / 2;

// C圆心的起始坐标
mCBallX = (mWidth - mTotalLength) / 2 + mBallRadius * 5 + mSpace * 2;
mCBallY = mHeight / 2;

}

2.4 绘制三个圆并且开启值动画

    @Override
protected void onDraw(Canvas canvas) {

// 根据x方向偏移量求出y方向偏移量
mOffsetY = (float) Math.sqrt(mMoveLength / 2 * mMoveLength / 2 - (mMoveLength / 2 - mOffsetX) * (mMoveLength / 2 - mOffsetX));

// 绘制B圆
mBallPaint.setAlpha(mBBallAlpha);
canvas.drawCircle(mBBallX - mOffsetX,
(float) (mBBallY + mOffsetY),
mBallRadius,
mBallPaint);

// 绘制C圆
mBallPaint.setAlpha(mCBallAlpha);
canvas.drawCircle(mCBallX - mOffsetX,
(float) (mCBallY - mOffsetY),
mBallRadius,
mBallPaint);

// 绘制A圆
mBallPaint.setAlpha(mABallAlpha);
canvas.drawCircle(mABallazierX, mABallazierY, mBallRadius, mBallPaint);

if (mAnimator == null) {
// 启动值动画
startLoading();
}

}

BC圆的移动依赖于:mOffsetY = (float) Math.sqrt(mMoveLength / 2 * mMoveLength / 2 - (mMoveLength / 2 - mOffsetX) * (mMoveLength / 2 - mOffsetX))对应的计算,mMoveLength / 2为半径r,mOffsetX为offset,看草图即可理解,第三象限的情况其实跟第四象限一样的,因为(mMoveLength / 2 - mOffsetX)的平方总是为正
这里写图片描述
A圆的移动则是在值动画中算出坐标点(mABallazierX, mABallazierY),首先看下二阶贝塞尔曲线:
二阶贝塞尔曲线(抛物线):

这里写图片描述

这里写图片描述

原理:由 P0 至 P1 的连续点 Q0,描述一条线段。
由 P1 至 P2 的连续点 Q1,描述一条线段。
由 Q0 至 Q1 的连续点 B(t),描述一条二次贝塞尔曲线。

2.5 值动画的逻辑处理

    // 开启值动画
private void startLoading() {

// 范围在0到圆心移动的距离,这个是以B圆到A圆位置为基准的
mAnimator = ValueAnimator.ofFloat(0, mMoveLength);

// 设置监听
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {

// B圆和C圆对应的X的偏移量
mOffsetX = (float) animation.getAnimatedValue();

float fraction = animation.getAnimatedFraction();

// B移动到A,透明度变化255*0.8->255
mBBallAlpha = (int) (255 * 0.8 + 255 * fraction * 0.2);
// C移动到B,透明度变化255*0.6->255*0.8
mCBallAlpha = (int) (255 * 0.6 + 255 * fraction * 0.2);
// A移动到C,透明度变化255->255*0.6
mABallAlpha = (int) (255 - 255 * fraction * 0.4);

// A圆的分段二阶贝塞尔曲线的处理
if (fraction <0.5) {
// fraction小于0.5时,为A到B过程的情况

// 乘以2是因为贝塞尔公式的t范围在0到1
fraction *= 2;

// 设置当前情况的起始点、控制点、终点
mABallP0.set(mABallX, mABallY);
mABallP1.set(mABallX + mMoveLength / 2, mABallY - mMoveLength / 2);
mABallP2.set(mBBallX, mBBallY);

// 代入贝塞尔公式得到贝塞尔曲线过程的x,y坐标
mABallazierX = getBazierValue(fraction, mABallP0.x, mABallP1.x, mABallP2.x);
mABallazierY = getBazierValue(fraction, mABallP0.y, mABallP1.y, mABallP2.y);
} else {
// fraction大于等于0.5时,为A到B过程之后,再从B到C过程的情况

// 减0.5是因为t要从0开始变化
fraction -= 0.5;
// 乘以2是因为贝塞尔公式的t范围在0到1
fraction *= 2;

// 设置当前情况的起始点、控制点、终点
mABallP0.set(mBBallX, mBBallY);
mABallP1.set(mBBallX + mMoveLength / 2, mBBallY + mMoveLength / 2);
mABallP2.set(mCBallX, mCBallY);

// 代入贝塞尔公式得到贝塞尔曲线过程的x,y坐标
mABallazierX = getBazierValue(fraction, mABallP0.x, mABallP1.x, mABallP2.x);
mABallazierY = getBazierValue(fraction, mABallP0.y, mABallP1.y, mABallP2.y);
}

// 强制刷新
postInvalidate();

}
});
// 动画无限模式
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
// 时长1秒
mAnimator.setDuration(1000);
// 延迟0.5秒执行
mAnimator.setStartDelay(500);
// 开启动画
mAnimator.start();

}

/**
* 二阶贝塞尔公式:B(t)=(1-t)^2*P0+2*t*(1-t)*P1+t^2*P2,(t∈[0,1])
*/

private float getBazierValue(float fraction, float p0, float p1, float p2) {
return (1 - fraction) * (1 - fraction) * p0 + 2 * fraction * (1 - fraction) * p1 + fraction * fraction * p2;
}

2.7 View销毁时的处理

    @Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// 销毁view时取消动画,避免内存泄露
mAnimator.cancel();
}

3.实现效果

这里写图片描述

Demo下载:防IOS-UC浏览器三点加载动画


推荐阅读
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • 深入解析 Android IPC 中的 Messenger 机制
    本文详细介绍了 Android 中基于消息传递的进程间通信(IPC)机制——Messenger。通过实例和源码分析,帮助开发者更好地理解和使用这一高效的通信工具。 ... [详细]
  • ListView简单使用
    先上效果:主要实现了Listview的绑定和点击事件。项目资源结构如下:先创建一个动物类,用来装载数据:Animal类如下:packagecom.example.simplelis ... [详细]
  • Android 自定义指南针视图实现
    本文介绍了如何在Android应用中自定义绘制指南针视图,包括方位角的计算、不同方向的颜色区分以及视图随手势移动的功能实现。 ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • 本文回顾了2017年的转型和2018年的收获,分享了几家知名互联网公司提供的工作机会及面试体验。 ... [详细]
  • 在编译BSP包过程中,遇到了一个与 'gets' 函数相关的编译错误。该问题通常发生在较新的编译环境中,由于 'gets' 函数已被弃用并视为安全漏洞。本文将详细介绍如何通过修改源代码和配置文件来解决这一问题。 ... [详细]
  • 探讨ChatGPT在法律和版权方面的潜在风险及影响,分析其作为内容创造工具的合法性和合规性。 ... [详细]
  • 本文详细介绍了如何在Android 4.4及以上版本中配置WebView以实现内容的自动高度调整和屏幕适配,确保中文显示正常,并提供代码示例。 ... [详细]
  • 使用JS、HTML5和C3创建自定义弹出窗口
    本文介绍如何结合JavaScript、HTML5和C3.js来实现一个功能丰富的自定义弹出窗口。通过具体的代码示例,详细讲解了实现过程中的关键步骤和技术要点。 ... [详细]
  • 主调|大侠_重温C++ ... [详细]
  • Android开发技巧:实现带描边的圆角图片
    本文介绍了一种在Android应用中实现带描边的圆角图片的方法。通过使用BitmapShader类,开发者可以轻松地为图片添加圆角和描边效果,提升应用的视觉体验。 ... [详细]
  • nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 深入解析ESFramework中的AgileTcp组件
    本文详细介绍了ESFramework框架中AgileTcp组件的设计与实现。AgileTcp是ESFramework提供的ITcp接口的高效实现,旨在优化TCP通信的性能和结构清晰度。 ... [详细]
  • MySQL DATETIME 类型长度及使用指南
    本文详细介绍了 MySQL 中 DATETIME 类型的长度要求及其格式规范,并补充了其他常见数据类型的说明,帮助开发者更好地理解和使用这些类型。 ... [详细]
author-avatar
安宁日月_860
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有