Android 自定义 View 之 LeavesLoading,移动开发工程师考试
leaf.startTime = currentTime + new Random().nextInt((int)mLeafFloatTime);leaf.x = mProgressLen;leaf.y = getLeafLocationY(leaf);}}
要想让 Leaf 飘动轨迹为正弦函数,关键在于确定 Leaf 的 Y 轴坐标:
/**
获取叶子的 Y 轴坐标
@param leaf 叶子
@return 经过计算的叶子 Y 轴坐标*/private float getLeafLocationY(Leaf leaf){float w = (float) (Math.PI * 2 / mProgressLen);//角频率 float A;//计算振幅值 switch (leaf.type){case LITTLE:A = mLeafLen/3;break;case MIDDLE:A = mLeaf
Len*2/3;break;default:A = mLeafLen;break;}// (mHeight-mLeafLen)/2 是为了让 Leaf 的 Y 轴起始位置居中 return (float) (A * Math.sin(w * leaf.x + leaf.n)+(mHeight-mLeafLen)/2);}
3.3 叶子飘动时自旋转
这里就涉及到了 Leaf 的绘制,其实 Gif 中的叶子和风扇都可以使用 Canves 直接绘制图案,但是这样就会有两个问题:
难画:想要画出满意图形,并且还要旋转、缩放、平移可要下一番功夫。
灵活性低:如果想换其他样式又得重新设计绘制过程。
因此这里采用 Canves.drawBitmap()
的方式绘制,直接使用已有的图片作为叶子和风扇,同时利用 Canves.drawBitmap()
的一个重载的方法可以很方便的实现旋转、缩放、平移:
void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) ;
就是通过这里的 Matrix 矩阵,它内部封装了 postScale()
、postTranslate
、postRotate()
等方法,可以帮助我们快速的对 Bitmap 进行旋转、缩放、平移还有其他操作。使用时要记得配合 Canves 的 save()
和 restore()
使用,否则达不到想要的效果。
对这方面不熟的朋友可以看看 HenCoder 的自定义 View 教学 1-4 。
绘制 Leaf 的方法:
private void drawLeaves(Canvas canvas){long currentTime = System.currentTimeMillis();for (Leaf leaf : mLeafList) {if (currentTime > leaf.startTime && leaf.startTime != 0){// 获取 leaf 当前的坐标 getLeafLocation(leaf,currentTime);canvas.save();Matrix matrix = new Matrix();// 缩放 自适应 View 的大小 float scaleX = (float) mLeafLen / mLeafBitmapWidth;float scaleY = (float) mLeafLen / mLeafBitmapHeight;matrix.postScale(scaleX,scaleY);// 位移 float transX = leaf.x;float transY = leaf.y;matrix.postTranslate(transX,transY);// 旋转// 计算旋转因子 float rotateFraction = ((currentTime - leaf.startTime) % mLeafRotateTime)/(float)mLeafRotateTime;float rotate;switch (leaf.rotateDir){case CLOCKWISE://顺时针 rotate = rotateFraction * 360 + leaf.rotateAngle;break;default://逆时针 rotate = -rotateFraction * 360 + leaf.rotateAngle;break;}// 旋转中心选择 Leaf 的中心坐标 matrix.postRotate(rotate,transX + mLeafLen / 2,transY + mLeafLen / 2);canvas.drawBitmap(mLeafBitmap,matrix,mBitmapPaint);canvas.restore();}}
3.4 Loading == 100% 出现动画
增加一个判断字段 isLoadingCompleted ,在 onDraw()
中选择对应绘制策略。
isLoadingCompleted 在 setProgress()
中根据 progress 设置:
/**
设置进度(自动刷新)
@param progress 0-100*/public void setProgress(int progress){if (progress < 0){mProgress = 0;}else if (progress > 100){mProgress = 100;}else {mProgress = progress;}if (progress == 100){isLoadingCompleted = true;}else {isLoadingCompleted = false;}// 255 不透明 mCompletedFanPaint.setAlpha(255);postInvalidate();}
LeavesLoading.onDraw()
部分实现:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);......if (isLoadingCompleted){//绘制加载完成特效 drawCompleted(canvas);}else {//绘制扇叶 drawFan(canvas,mFanLen,mBitmapPaint);}//刷新 postInvalidate();}
drawCompleted()
实现:
private void drawCompleted(Canvas canvas) {// 每次绘制风扇透明度递减 10int alpha = mCompletedFanPaint.getAlpha() - 10;if (alpha <= 0){alpha = 0;}mCompletedFanPaint.setAlpha(alpha);// 文字透明度刚好与风扇相反 mCompletedTextPaint.setAlpha(255-alpha);// 计算透明因子 float fraction = alpha / 255f;// 叶片大小 和 文字大小 也是相反变化的 float fanLen = fraction * mFanLen;float textSize = (1 - fraction) * mCompletedTextSize;mCompletedTextPaint.setTextSize(textSize);//测量文字占用空间 Rect bounds = new Rect();mCompletedTextPaint.getTextBounds(LOADING_COMPLETED,0,LOADING_COMPLETED.length(),bounds);// 与 drawLeaf() 相似,不再赘述 drawFan(canvas, (int) fanLen, mCompletedFanPaint);//画文字 canvas.drawText(LOADING_COMPLETED,0,LOADING_COMPLETED.length(),mFanCx-bounds.width()/2f,mFanCy+bounds.height()/2f,mCompletedTextPaint);}流程:计算风扇和文字透明度 -> 计算风扇和文字大小以及文字占用空间 -> 绘制 ,注释写得比较清楚就不赘述了。4. 结束
文章中如有出现任何错误,欢迎大家到评论区留言指正。如果觉得 LeavesLoading 对您有任何帮助,希望可以在 GitHub 得到您的 Star !Thanks:
评论