在码农的世界里,优美的应用体验,来源于程序员对细节的处理以及自我要求的境界,年轻人也是忙忙碌碌的码农中一员,每天、每周,都会留下一些脚印,就是这些创作的内容,有一种执着,就是不知为什么,如果你迷茫,不妨来瞅瞅码农的轨迹。
本文章实现的效果如下图所示:
1 首先是雪花的定义
用来保存雪花的一些基本属性
public class BobbleBean { //位置 Point postion; //初始位置 Point origin; //颜色 int color; //运动的速度 int speed; //半径 float radius;}
2 自定义 View 创建
然后我们创建一个自定义 View 用来绘制雪花效果,在这个自定义View的构造函数中创建一个画笔,同时创建一个保存雪花的集合:
public class CustomSnowView extends View { public CustomSnowView(Context context) { this(context, null); } public CustomSnowView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } //画笔 Paint mPaint; //保存点的集合 List mBobbleBeanList; public CustomSnowView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); mBobbleBeanList = new ArrayList<>(); }}
3 绘制
绘制讲究三步
View的测量——onMeasure()
View的位置确定——onLayout()
View的绘制——onDraw()
3.1 第一步就是测量
通过测量要得出当前画布的精确尺寸
//第一步测量 //默认的View大小 private int mDefaultWidth = dp2px(100); private int mDefaultHeight = dp2px(100); //测量过后的View 的大小 也就是画布的大小 private int mMeasureWidth = 0; private int mMeasureHeight = 0; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取测量计算相关内容 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); if (widthSpecMode == MeasureSpec.EXACTLY) { //当specMode = EXACTLY时,精确值模式,即当我们在布局文件中为View指定了具体的大小 mMeasureWidth = widthSpecSize; } else { //指定默认大小 mMeasureWidth = mDefaultWidth; if (widthSpecMode == MeasureSpec.AT_MOST) { mMeasureWidth = Math.min(mMeasureWidth, widthSpecSize); } } //测量计算View的高 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); if (heightSpecMode == MeasureSpec.EXACTLY) { //当specMode = EXACTLY时,精确值模式,即当我们在布局文件中为View指定了具体的大小 mMeasureHeight = heightSpecSize; } else { //指定默认大小 mMeasureHeight = mDefaultHeight; if (heightSpecMode == MeasureSpec.AT_MOST) { mMeasureHeight = Math.min(mMeasureHeight, heightSpecSize); } } mMeasureHeight = mMeasureHeight - getPaddingBottom() - getPaddingTop(); mMeasureWidth = mMeasureWidth - getPaddingLeft() - getPaddingBottom(); //重新测量 setMeasuredDimension(mMeasureWidth, mMeasureHeight); }
//一个 dp 转 像素的计算 private int dp2px(int dp) { float density = getContext().getResources().getDisplayMetrics().density; return (int) (dp * density + 0.5f); }
3.2 第二步就是 排版
当然这个功能主要用于 ViewGroup 中有多个 View时,在这里我们来根据画布的尺寸来随机生成雪花点的坐标信息
//这里面创建 点 Random mRandom = new Random(); @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); for (int i = 0; i < mMeasureWidth / 3; i++) { BobbleBean lBobbleBean = new BobbleBean(); //生成位置信息 随机 //取值范围是 0 ~ mMeasureWidth int x = mRandom.nextInt(mMeasureWidth); int y = mRandom.nextInt(mMeasureHeight); //绘制使用的位置 lBobbleBean.postion = new Point(x, y); //重置的位置 lBobbleBean.origin = new Point(x, 0); //随机的半径 1 ~ 4 lBobbleBean.radius = mRandom.nextFloat() * 3 + dp2px(1); //随机的速度 3 ~ 6 lBobbleBean.speed = 1 + mRandom.nextInt(3); //随机透明度的白色 lBobbleBean.color = ColorUtil.randomWhiteColor(); mBobbleBeanList.add(lBobbleBean); } }
3.3 第三步就是绘制
循环绘制,每次绘制时就将雪花的 纵坐标添加偏移量 就出现了向下落的动画效果
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制时重新计算位置 for (BobbleBean lBobbleBean : mBobbleBeanList) { Point lPostion = lBobbleBean.postion; //在竖直方向上增加偏移 lPostion.y+=lBobbleBean.speed; //在 x 轴方向上再微微偏移一点 float randValue = mRandom.nextFloat() *2 -0.5f; lPostion.x+=randValue; //边界控制 if(lPostion.y>mMeasureHeight){ lPostion.y = 0; } } //先将这些点全部绘制出来 for (BobbleBean lBobbleBean : mBobbleBeanList) { //修改画笔的颜色 mPaint.setColor(lBobbleBean.color); //绘制 // 参数一 二 圆点位置 // 参数 三 半径 // 参数 四 画笔 canvas.drawCircle(lBobbleBean.postion.x, lBobbleBean.postion.y, lBobbleBean.radius, mPaint); } //循环刷新 10 毫秒刷新一次 postInvalidateDelayed(10L); }
4 第四步就是使用
在布局文件中引用这个自定义View
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#000000" tools:context=".MainActivity"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitXY" android:src="@mipmap/bg_snow" /> <com.studyyoun.demo.CustomSnowView android:layout_width="match_parent" android:layout_height="match_parent" />FrameLayout>
不局限于思维,不局限语言限制,才是编程的最高境界。
以小编的性格,肯定是要录制一套视频的,随后会上传