Android自定义View叶子旋转完整版(六)
发布时间 - 2026-01-11 00:19:06 点击率:次上一篇实现多叶子飘动旋转,今天完成最后的功能。

1、添加右侧旋转枫叶
2、添加滑动条效果,显示百分比
3、修复叶子飘出边框问题
1、添加右侧旋转叶子
Bitmap turnBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.fengshan, null)).getBitmap();
int turnLeafAngle = 0;
private void setTurnLeaf(Canvas canvas) {
Matrix matrix = new Matrix();
turnLeafAngle = turnLeafAngle + 3;
matrix.postTranslate((width - rightCircleWidth/2 - turnBitmap.getWidth()/2),
(height - rightCircleWidth/2 - turnBitmap.getHeight()/2));
matrix.postRotate(turnLeafAngle,
width - rightCircleWidth/2 - turnBitmap.getWidth()/2 + turnBitmap.getWidth()/2,
height - rightCircleWidth/2 - turnBitmap.getHeight()/2 + turnBitmap.getHeight()/2);
canvas.drawBitmap(turnBitmap, matrix, new Paint());
}
代码很明确,首先通过Matrix.postTranslate(float dx, float dy)把turnBitMap定位到最右侧圆圈
再通过Matrix.postRotate(float degress, float dx, float dy);设置旋转角度,每次角度+3°
其中degress为旋转角度,(dx,dy)为旋转中心点坐标
2、添加滑动效果
原理就是覆盖一层不同颜色的图层。根据当前百分比,分别画一个半圆,画一个正方形
a、定义一个圆形Rectf(为什么不是半圆?因为画圆弧的其实角度从水平线右侧开始)
progressArcRectf = new RectF(0, 0, height, height);
b、定义一个长方形Rectf,长方形x坐标起点即时圆形半径
progressRectf = new RectF(height/2, 0, width, height);
c、画出圆弧Canvas.drawArc(Rectf rectf, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
startAngle:起始角度,默认从右侧水平线开始
sweepAngle:为旋转的角度,顺时针旋转
useCenter:true只画出弧线,false则画出圆心到弧线的区域
//画滑动后的背景条
int currentProgressWidht = currentProgress * (width - borderWidth)/100;
if(currentProgressWidht < leftCircleWidth/2) {
//angle取值范围0~90
int angle = 90 * currentProgressWidht / (leftCircleWidth/2);
// 起始的位置
int startAngle = 180 - angle;
// 扫过的角度
int sweepAngle = 2 * angle;
canvas.drawArc(progressArcRectf, startAngle, sweepAngle, false, progressBgPaint);
}else {
//画左边半圆形滑过部分
canvas.drawArc(progressArcRectf, 90, 180, false, progressBgPaint);
progressRectf.left = borderWidth + leftCircleWidth/2;
progressRectf.right = borderWidth + currentProgressWidht;
//画中间滑过部分
canvas.drawRect(progressRectf, progressBgPaint);
}
给LeafView.java添加一个
public void setCurrentProgress(int currentProgress) {
this.currentProgress = currentProgress;
}
3、修复叶子飘动范围
这个简单,就是设置叶子的rect坐标起点+边框距离
赋上所有代码
1、activity_leaf.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content_leaf"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="226dp"
android:layout_height="45dp">
<com.zjcpo.t170313_countdowntimer.LeafView
android:id="@+id/leafView"
android:layout_width="226dp"
android:layout_height="45dp"
android:layout_centerHorizontal="true"
/>
</RelativeLayout>
</RelativeLayout>
2、LeafView.java
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.jar.Attributes;
/**
* Created by jiemiao.zhang on 2017-3-15.
*/
public class LeafView extends View {
private String TAG = "--------LeafView";
private Resources mResources;
//背景图、叶子
private Bitmap mLeafBitmap, bgBitmap, turnBitmap;
//整个控件的宽度和高度
private int width, height;
//最外层边框宽度
private int borderWidth;
//右侧圆形直径
private int rightCircleWidth;
//左侧圆形直径
private int leftCircleWidth;
private Paint bgPaint;
private RectF bgRect;
private Rect bgDestRect;
//进度条实时背景
private Paint progressBgPaint;
//进度条左侧半圆,进度条中间长方形部分Rect
private RectF progressArcRectf, progressRectf;
//当前百分比0~100
private int currentProgress = 0;
//存放叶子lsit
private List<Leaf> leafList;
//叶子的宽和高
private int mLeafWidth, mLeafHeight;
//叶子滑动一周的时间5秒
private final static long cycleTime = 5000;
//叶子数量
private final static int leafNumber = 6;
public LeafView(Context context, AttributeSet attrs) {
super(context, attrs);
mResources = getResources();
mLeafBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.leaf, null)).getBitmap();
mLeafWidth = mLeafBitmap.getWidth();
mLeafHeight = mLeafBitmap.getHeight();
turnBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.fengshan, null)).getBitmap();
bgBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.leaf_kuang, null)).getBitmap();
bgPaint = new Paint();
bgPaint.setColor(mResources.getColor(R.color.bg_color));
//进度条实时背景
progressBgPaint = new Paint();
progressBgPaint.setColor(mResources.getColor(R.color.progress_bg_color));
//获取所有叶子的信息,放入list
leafList = getLeafs(leafNumber);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
borderWidth = height * 10/64;
rightCircleWidth = width * 62/303;
leftCircleWidth = height - 2 * borderWidth;
bgDestRect = new Rect(0, 0 , width, height);
bgRect = new RectF(0, 0 , width, height);
progressArcRectf = new RectF(borderWidth, borderWidth, height - borderWidth, height - borderWidth);
progressRectf = new RectF(borderWidth+(height-2*borderWidth)/2, borderWidth,
width-rightCircleWidth/2, height-borderWidth);
Log.i("leftMarginWidth", (borderWidth + leftCircleWidth/2) + "");
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画背景颜色到画布
canvas.drawRect(bgRect, bgPaint);
if(currentProgress <= 100) {
//画叶子
int size = leafList.size();
for (int i=0; i<size; i++) {
Leaf leaf = leafList.get(i);
//获取叶子坐标
getLocation(leaf);
//获取叶子旋转角度
getRotate(leaf);
canvas.save();
Matrix matrix = new Matrix();
//设置滑动
matrix.postTranslate(leaf.x, leaf.y);
//设置旋转
matrix.postRotate(leaf.rotateAngle, leaf.x + mLeafWidth / 2, leaf.y + mLeafHeight / 2);
//添加叶子到画布
canvas.drawBitmap(mLeafBitmap, matrix, new Paint());
canvas.restore();
//画滑动后的背景条
int currentProgressWidht = currentProgress * (width - borderWidth - rightCircleWidth/2)/100;
if(currentProgressWidht < leftCircleWidth/2) {
//angle取值范围0~90
int angle = 90 * currentProgressWidht / (leftCircleWidth/2);
Log.i(TAG, "angle :" + angle);
// 起始的位置
int startAngle = 180 - angle;
// 扫过的角度
int sweepAngle = 2 * angle;
canvas.drawArc(progressArcRectf, startAngle, sweepAngle, false, progressBgPaint);
}else {
//画左边半圆形滑过部分
canvas.drawArc(progressArcRectf, 90, 180, false, progressBgPaint);
progressRectf.left = borderWidth + leftCircleWidth/2;
progressRectf.right = borderWidth + currentProgressWidht;
//画中间滑过部分
canvas.drawRect(progressRectf, progressBgPaint);
}
}
//调用onDraw()重复滑动
if(currentProgress < 100) {
postInvalidate();
}
}
//画背景图片到画布
canvas.drawBitmap(bgBitmap, null, bgDestRect, null);
//画右边选择风叶
setTurnLeaf(canvas);
//画百分比
setText(canvas);
}
int turnLeafAngle = 0;
private void setTurnLeaf(Canvas canvas) {
Matrix matrix = new Matrix();
turnLeafAngle = turnLeafAngle + 3;
matrix.postTranslate((width - rightCircleWidth/2 - turnBitmap.getWidth()/2),
(height - rightCircleWidth/2 - turnBitmap.getHeight()/2));
matrix.postRotate(turnLeafAngle,
width - rightCircleWidth/2 - turnBitmap.getWidth()/2 + turnBitmap.getWidth()/2,
height - rightCircleWidth/2 - turnBitmap.getHeight()/2 + turnBitmap.getHeight()/2);
canvas.drawBitmap(turnBitmap, matrix, new Paint());
}
//显示百分比数字,大于3%开始显示,到50%停止滑动
private void setText(Canvas canvas) {
Paint paintText = new Paint();
paintText.setColor(Color.WHITE);
paintText.setTextSize(30);
int textX = currentProgress * width / 100;
textX = currentProgress < 50 ? (currentProgress * width / 100) : (width/2);
if(currentProgress > 3) {
canvas.drawText(currentProgress + "%", textX, height/2 + 10,paintText);
}
}
//获取每片叶子在XY轴上的滑动值
private void getLocation(Leaf leaf) {
float betweenTime = leaf.startTime - System.currentTimeMillis();
//周期结束再加一个cycleTime
if(betweenTime < 0) {
leaf.startTime = System.currentTimeMillis() + cycleTime + new Random().nextInt((int) (cycleTime));
betweenTime = cycleTime;
}
//通过时间差计算出叶子的坐标
float fraction = (float) betweenTime / cycleTime;
float x = (int)(width * fraction);
//防止叶子飘出边框
leaf.x = x < borderWidth ? borderWidth : x;
float w = (float) ((float) 2 * Math.PI / width);
int y = (int) (18 * Math.sin(w * x)) + (height-mLeafHeight)/2;
//防止叶子飘出边框
y = y > (height - borderWidth) ? (height - borderWidth) : y;
y = y < borderWidth ? borderWidth : y;
leaf.y = y;
}
//获取每片叶子的旋转角度
private void getRotate(Leaf leaf) {
float scale = ((leaf.startTime - System.currentTimeMillis())%cycleTime)/ (float)cycleTime;
int rotate = (int)(scale * 360);
leaf.rotateAngle = rotate;
}
private class Leaf {
// 叶子的坐标
float x, y;
// 旋转角度
int rotateAngle;
// 起始时间(ms)
long startTime;
}
private List<Leaf> getLeafs(int leafSize) {
List<Leaf> list = new LinkedList<Leaf>();
for (int i=0; i<leafSize; i++) {
list.add(getLeaf());
}
return list;
}
//使叶子初始时间有间隔
int addTime;
private Leaf getLeaf() {
Random random = new Random();
Leaf leaf = new Leaf();
leaf.rotateAngle = random.nextInt(360);
addTime += random.nextInt((int) (cycleTime));
leaf.startTime = System.currentTimeMillis() + cycleTime + addTime;
return leaf;
}
public void setCurrentProgress(int currentProgress) {
this.currentProgress = currentProgress;
}
}
3、LeafActivity.java
public class LeafActivity extends Activity {
private LeafView leafView;
private int mProgress = 0;
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
if (mProgress < 40) {
mProgress += 1;
// 随机800ms以内刷新一次
mHandler.sendEmptyMessageDelayed(1,
new Random().nextInt(800));
leafView.setCurrentProgress(mProgress);
} else {
mProgress += 1;
// 随机1200ms以内刷新一次
mHandler.sendEmptyMessageDelayed(1,
new Random().nextInt(100));
leafView.setCurrentProgress(mProgress);
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leaf);
leafView = (LeafView) findViewById(R.id.leafView);
mHandler.sendEmptyMessageDelayed(1, 3000);
}
}
最后再看下效果
总结
看过前5篇的很好理解,用到的技术点之前都讲到了。这篇主要就是几个百分比函数的计算。
比如设置半圆时弧度如何计算,圆弧对应的百分比,滑动区域长方形的起点坐标计算,去掉边框后的坐标计算
画半圆必须要有一个完整圆形Rect,因为drawArc()从右侧半径水平起始角度,顺时针。然功能要求我们从左侧圆形开始画,所以要通过一个算法,假如当前百分比为4%,需要画30°的圆弧,那么起始角度为165°=180°-15°,画出角度30%
通过matrix.postRotate()实现旋转功能时,必须加上当前view的坐标及二分之一长宽
需要图片等信息的可以从下面的Github地址下载,不过原文比较复杂
参考 https://github.com/Ajian-studio/GALeafLoading
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# Android
# View
# 叶子旋转
# Android自定义View实现QQ运动积分转盘抽奖功能
# Android 自定View实现仿QQ运动步数圆弧及动画效果
# Android自定义View仿微博运动积分动画效果
# Android UI之ImageView实现图片旋转和缩放
# Android使用RotateImageView 旋转ImageView
# Android UI设计系列之ImageView实现ProgressBar旋转效果(1)
# Android自定义View实现QQ音乐中圆形旋转碟子
# Android自定义View实现叶子飘动旋转效果(四)
# Android中imageView图片放大缩小及旋转功能示例代码
# Android自定义View图片按Path运动和旋转
# 画出
# 滑过
# 进度条
# 飘出
# 扫过
# 画中
# 画一
# 顺时针
# 几个
# 很好
# 中心点
# 再看
# 这篇
# 上一篇
# 再加
# 图层
# 长宽
# 大家多多
# 计算出
# 有一个
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
移动端脚本框架Hammer.js
php增删改查怎么学_零基础入门php数据库操作必知基础【教程】
Laravel如何监控和管理失败的队列任务_Laravel失败任务处理与监控
Laravel distinct去重查询_Laravel Eloquent去重方法
Win11关机界面怎么改_Win11自定义关机画面设置【工具】
laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法
Laravel如何生成和使用数据填充?(Seeder和Factory示例)
laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程
Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】
如何在云服务器上快速搭建个人网站?
如何在Tomcat中配置并部署网站项目?
通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】
Laravel如何实现用户角色和权限系统_Laravel角色权限管理机制
php打包exe后无法访问网络共享_共享权限设置方法【教程】
linux top下的 minerd 木马清除方法
Windows10怎样连接蓝牙设备_Windows10蓝牙连接步骤【教程】
如何在不使用负向后查找的情况下匹配特定条件前的换行符
佛山网站制作系统,佛山企业变更地址网上办理步骤?
Laravel如何记录日志_Laravel Logging系统配置与自定义日志通道
Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层
edge浏览器无法安装扩展 edge浏览器插件安装失败【解决方法】
原生JS获取元素集合的子元素宽度实例
北京专业网站制作设计师招聘,北京白云观官方网站?
香港服务器租用每月最低只需15元?
如何快速生成凡客建站的专业级图册?
jQuery validate插件功能与用法详解
Laravel如何使用Vite进行前端资源打包?(配置示例)
JavaScript如何实现倒计时_时间函数如何精确控制
悟空浏览器如何设置小说背景色_悟空浏览器背景色设置【方法】
企业网站制作这些问题要关注
深圳防火门网站制作公司,深圳中天明防火门怎么编码?
CSS3怎么给轮播图加过渡动画_transition加transform实现【技巧】
如何在Windows 2008云服务器安全搭建网站?
常州企业网站制作公司,全国继续教育网怎么登录?
简单实现Android文件上传
node.js报错:Cannot find module 'ejs'的解决办法
手机软键盘弹出时影响布局的解决方法
如何用虚拟主机快速搭建网站?详细步骤解析
Laravel如何配置Horizon来管理队列?(安装和使用)
Laravel如何自定义分页视图?(Pagination示例)
,在苏州找工作,上哪个网站比较好?
公司网站制作价格怎么算,公司办个官网需要多少钱?
如何正确下载安装西数主机建站助手?
Laravel如何处理文件下载请求?(Response示例)
如何获取免费开源的自助建站系统源码?
网页设计与网站制作内容,怎样注册网站?
三星网站视频制作教程下载,三星w23网页如何全屏?
如何快速查询网站的真实建站时间?
HTML透明颜色代码怎么让下拉菜单透明_下拉菜单透明背景指南【技巧】
Laravel如何生成URL和重定向?(路由助手函数)

