开始之前,先把效果图贴上,一睹为快,相似度还是蛮高的,嘿嘿!
此加载状态提示,我分为了四个部分来写,分别是:
(1)加载当中,LoadIngView
(2)加载之前,即用户下拉的状态,LoadPreView
(3)加载成功,LoadSuccessView
(4)控件组合,LoadView,也即是我们xml布局用到的
下面一一来介绍一下涉及到的主要核心代码:
1.关于LoadInngView的核心代码,主要思想是,通过改变十二条短线圆柱的颜色,实现加载的循环效果。
首先初始化颜色,代码如下
private void initColor(){ colorEvaluator = new ArgbEvaluator(); colors = new int[count]; for(int i = 0;i < count;i++){ colors[i] = (int)colorEvaluator.evaluate(i*1.0f/(count-1),startColor,endColor); } }很简单的一段代码,就是根据颜色插值器ArgbEvaluator,两种颜色,生成在startColor与endColor区间内的12中颜色
其次,就是初始化线段,代码如下
private void initLoadingLines(){ loadingLines = new LoadingLine[count]; for(int i = 0;i <count;i++){ LoadingLine loadingLine = new LoadingLine(); loadingLine.drawColor = colors[i]; loadingLines[i] = loadingLine; } }这段代码目的就是事先生成count条线段,并暂时初始化颜色。
再其次就是,初始化线段绘制的起始结束坐标,
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { float delayAngle = 360.0f / count; LoadingLine loadingLine; double value; for(int i = 0;i < count;i++){ loadingLine = loadingLines[i]; value = startAngle * Math.PI / 180; loadingLine.startX = (int)Math.round(radius * Math.cos(value)); loadingLine.startY = (int)Math.round(radius * Math.sin(value)); loadingLine.endX = (int)Math.round(exactlySize / 2.5f * Math.cos(value)); loadingLine.endY = (int)Math.round(exactlySize / 2.5f * Math.sin(value)); startAngle += delayAngle; } startAngle = 0; }这个就考验你的数学功力了,不说!
最后,颜色的改变,其实也就是实现循环效果的一部分,
private Runnable runnable = new Runnable() { @Override public void run() { postInvalidate(); removeCallbacks(runnable); setColor(startIndex % count); startIndex++; postDelayed(runnable,DELAY_DURATION); } };
/** * 设置显示颜色 * @param index,线段颜色初始化位置 */ private void setColor(int index){ int lineIndex; for(int i = 0;i < count;i++){ lineIndex = index + i; loadingLines[lineIndex >= count?lineIndex - count:lineIndex].drawColor = colors[i]; } }index说明一下,这个是显示颜色数组的第一个颜色,然后依次循环为每一个线段在赋值颜色。
2.LoadPreView:
这个其实没啥好说的,就是一些图形的绘制。难度点在于怎么绘制下拉的状态,正常的图形绘制肯定是不行,所以这里用到了二阶贝塞尔曲线的绘制,绘制Path生成如下:
/** * 重置贝塞尔曲线 */ private void resetBeaierPath(){ /* 四个点 */ final float headerX1 = -mCurrentHeaderRadius; final float headerX2 = mCurrentHeaderRadius; final float headerY = 0; final float footerX1 = -mCurrentFooterRadius; final float footerX2 = mCurrentFooterRadius; final float footerY = delaY; /* 控制点 */ final float anchorX1 = headerX1 / anchorPercent; float anchorY = delaY / anchorPercent; anchorY = anchorY > exactlySize / 1.5f?exactlySize / 1.5f:anchorY; final float anchorX2 = headerX2 / anchorPercent; /* 画贝塞尔曲线 */ mPath.reset(); mPath.moveTo(headerX1, headerY); mPath.quadTo(anchorX1, anchorY, footerX1, footerY); mPath.lineTo(footerX2, footerY); mPath.quadTo(anchorX2, anchorY, headerX2, headerY); mPath.lineTo(headerX1, headerY); }其实就是6个点的确定,(headerX1,headerY), (headerX2,headerY),(footerX1,footerY),(footerX2,footerY),(anchorX1,anchory),(anchorX2,anchorY),前面四个点做为固定点,是贝塞尔曲线闭合的4个点,后面两个是控制点。
另外再说下,这个方法
private void resetValue(float percent){ this.anchorPercent = 1.0f + percent; this.delaY = maxBezierLength * percent; this.mCurrentFooterRadius = (1 - percent >= 0.25f? 1- percent:0.25f) * (exactlySize / 2.0f - ringWidth); float p = 1.0f - percent / 3.0f; this.arrowRadius = exactlySize / 4.5f * p; this.triangleLength = applyDimension(TRIANGLE_LENGTH) * p; this.arcWidth = applyDimension(INNER_ARC_WIDTH) * p; this.mCurrentHeaderRadius = p * (exactlySize / 2.0f - ringWidth); }这是根据下拉的大小来实时更新各个参数的,能实现贝塞尔的下拉效果,这个参数的计算是必须的。
3.LoadSuccessView的绘制,这部分的绘制其实就是一个圆圈加对号以及文字的绘制,其中文字的绘制如下:
/** * 绘制加载成功文字提示 * @param canvas */ private void drawText(Canvas canvas){ canvas.save(); Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); RectF targetRect = new RectF((padding + radius)*2,0,getWidth(),getHeight()); int baseLine = (int)((targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2); canvas.translate((padding + radius)*2,baseLine); canvas.drawText(TEXT,0,0,textPaint); canvas.restore(); }文字的绘制,其中确定基线是重要的,直接影响到你文字绘制的高度问题,基线的确定如代码所写。
4.LoadView,这部分更没啥,动态添加控件嘛,这个不会就去找度娘或者谷歌吧。具体的实现方式可以看代码,在这俺就不贴咧。
源码下载地址,请指正!