- 水平的进度条,支持已达到进度未到达进度的颜色高度定制,进度文字的颜色大小以及左右边距的定制,还能拖动改变进度。
圆环进度条圆环的宽度,颜色,环背景,以及中间文字的相关属性设置
<!--水平进度条的自定义属性-->
>
>
<declare-styleable name="HorizontalProgress">
<!--已到达的进度的颜色-->
<attr name="HorProgressReacherColor" format="color" />
<!--已到达的进度高度-->
<attr name="HorProgressReacherHeight" format="dimension" />
<!--未到达的进度的颜色-->
<attr name="HorProgressUnReacherColor" format="color" />
<!--未到达的进度的高度-->
<attr name="HorProgressUnReacherHeight" format="dimension" />
<!--进度文字的颜色-->
<attr name="HorProgressTextColor" format="color" />
<!--进度文字的大小-->
<attr name="HorProgressTextSize" format="dimension" />
<!--进度文字的左右边距-->
<attr name="HorProgressTextMargin" format="dimension" />
</declare-styleable>
<!--环形进度条的自定义属性-->
<declare-styleable name="RingProgress">
<!--已到达的进度的颜色-->
<attr name="RingProgressRearchColor" format="color" />
<!--未达到的进度的颜色-->
<attr name="RingProgressUnRearchColor" format="color" />
<attr name="RingWidth" format="dimension" />
<!--进度文字的颜色-->
<attr name="RingProgressTextColor" format="color" />
<!--进度文字的大小-->
<attr name="RingProgressTextSize" format="dimension" />
</declare-styleable>
-主要通过属性动画的线性变化的值得到0~目标进度的中间值,重绘形成动画效果
private void initAnimation() {
mAnimator = ValueAnimator.ofFloat(0, 1);
mAnimator.setDuration(5000);
mAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAnimatedValuevalue = (float) animation.getAnimatedValue();
if (mAnimatedValuevalue > mProgress) {
mAnimatedValuevalue = mProgress;
}
upDataDraw();
}
});
}
/**
* 绘制第二条路径
*
* @param canvas
*/
private void drawSecondArc(Canvas canvas) {
float sweepAngle = 180 * mAnimatedValuevalue;
mRectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding);
canvas.drawArc(mRectF, 180, sweepAngle, false, mSecondArcPaint);
}
- 仿华为管家病毒检查控件,
- 仿华为手机管家的圆盘刻度控件,支持刻度线颜色长度,以及中间文字的相关属性定制,能设置动画插值器来改变进度的动画
<!--刻度进度盘的自定义属性-->
<declare-styleable name="CircleProgressPlate">
<!--刻度线长度-->
<attr name="PlatScaleLngth" format="dimension" />
<!--刻度的颜色-->
<attr name="PlatScaleColor" format="color" />
<!--画笔的描边的宽度-->
<attr name="PlatStrokeWidth" format="dimension" />
<!--外圈圆颜色mOutRingColor-->
<attr name="PlatOutCircleColor" format="color" />
<!--文字的颜色-->
<attr name="PlatTextColr" format="color" />
<!--是否需要扫描-->
<attr name="PlatShouldScan" format="boolean" />
<!--扫描的颜色-->
<attr name="PlatScanColor" format="color" />
</declare-styleable>
- 1-3阶贝塞尔 直接调用系统提供的api,
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//一阶贝塞尔
mPath.moveTo(200, 200);
mPath.lineTo(500, 400);
//二阶贝塞尔
/**
* 绝对绘制
* 控制钱的坐标和结束点的坐标
*/
// mPath.quadTo(600,0,800,400);
/**
* 相对位置绘制
* 600相对500是100,0相对400是-400,800相对500是300,400相对400是0
* 所以控制点相对的x ,y 为100,-400 结束点的相对 x ,y是 300,0
*/
mPath.rQuadTo(100,-400,300,0);
//三阶贝塞尔
mPath.moveTo(400,800);
/**
* 控制点1 x,y坐标
* 控制点2 x,y坐标
* 结束点的 x,y坐标
*/
mPath.cubicTo(500,400,700,1200,1000,800);
/**
* 相对位置绘制发,各个点的x,y值分别相对起始点的,y的值
*/
mPath.moveTo(400,1000);
mPath.rCubicTo(100,-400,300,400,600,0);
canvas.drawPath(mPath,mPaint);
}
-高阶贝塞尔根据规律实现高级贝塞尔绘制
-主要是计算贝塞尔点的规则:
/**
* 计算某时刻贝塞尔点的值 x,或y的值
* t 0-1
* values贝塞尔点的x,y的集合
* 算法规则:
* 相邻两个点之间根据贝塞尔公式两两运算,运算的结果存在前面一个值中
* 没运算一轮就减少一个点(那一轮的在最后的一个点)
* 通过双重循环到最后计算得到的值存贮在了数组中的0元素
*/
private float calculatePoint(float t, float... values) {
int length = values.length;
for (int i = length - 1; i >= 0; i--) {
for (int j = 0; j < i; j++) {
//根据上一个点的位置得到下一个点
values[j] = values[j] + (values[j + 1] - values[j]) * t;
}
}
//运算时的的结果永远保存在第一位
return values[0];
}
- 曲线的填充效果
-主要实现是绘制两条路径相同的线,第二条Path在计算路径的时候在for循环中根据规则动态计算各个点坐标
private void initBezier() {
mBgpath.moveTo(0,600);
float[] xPonits = new float[]{0,200, 800, 1000};
//y点坐标的数组
float[] yPonits = new float[]{600,0, 1200, 0};
mBgpath.cubicTo(200,0,800,1200,1000,0);
//进度t值为0.2的时候
// float progress = 0.2f;
//循环刷新的次数,当值足够大,连起来的点就会是一个平滑的曲线了
int fps = 10000;
for (int i = 0; i < fps; i++) {
float t = i / (float) fps;
float x = calculatePoint(t, xPonits);
float y = calculatePoint(t, yPonits);
//path连接的方式
mPath.lineTo(x, y);
//刷新
postInvalidate();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("path", "path的当前点:" + x + "," + y);
}
// calculatePoint(progress,xPonits);
}
- 自定义的下拉粘性控件
-可设置中间的Drawable的显示,以及颜色半径等基础属性设置
<!--粘性下拉控件的自定义属性-->
<declare-styleable name="PullViscousView">
<!--下拉的最大距离-->
<attr name="PullViewPullDownMaxHeight" format="dimension"></attr>
<!--控件的颜色-->
<attr name="PullViewColor" format="color" />
<!--圆的半径-->
<attr name="PullViewCircleRadius" format="dimension" />
<!--切线角的最大角度-->
<attr name="PullViewTangentAngle" format="integer" />
<!--拉到最低点时控件的宽度-->
<attr name="PullViewTargetWidth" format="dimension" />
<!--控件下移的高度-->
<attr name="PullViewDownHeight" format="dimension" />
<!--中间内容的外边距-->
<attr name="PullViewContentMargin" format="dimension" />
<!--中间圆显示的drawable-->
<attr name="PullViewCentreDrawable" format="reference" />
</declare-styleable>
-在控件所在的根布局中重写根布局的触摸事件,根据下来的高度得到进度至,然后去实现控件下拉
/**
* 根布局的触摸监听
* @param v
* @param event
* @return
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = event.getX();
mStartY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float endY = event.getY();
float endX = event.getX();
//下拉的距离
float distance = endY - mStartY;
//下拉的高度除以定义的允许的最大高度可以得到一个进度值
float progress = distance >= ALLOW_PULL_MAXHEIGHT ? 1 : distance / ALLOW_PULL_MAXHEIGHT;
if (mPull_viscous_view != null) {
mPull_viscous_view.setProgress(progress);
}
return true;
case MotionEvent.ACTION_UP:
//当Up的时候控件回弹回去
mPull_viscous_view.pringbBack();
break;
}
return false;
}
-根据进度可以得到圆心坐标和切线角度,绘制两边的贝塞尔曲线核心是根据圆心坐标和切线角运用三角函数动态的计算结束点和控制点
/**
* 更新Path的路径
*/
private void upDataPathLayout() {
float progress = mProgressInterpolator.getInterpolation(mProgress);
//获取当前的宽度和高度
float width = getValueByLine(getWidth(), mTargetWidth, mProgress);
float height = getValueByLine(0, mAllowMaxHeight, mProgress);
//确定圆的相关参数
float cPonitX = width / 2;
float cPonitY = height - mCircleRadius;
float cRedius = mCircleRadius;
//更新圆心的位置
mCirclePointX = cPonitX;
mCirclePointY = cPonitY;
//控制点结束位置的坐标
int endContralY = mDownHeight;
Log.i("X", "回调进来的进度:" + progress + "圆心的坐标:" + mCirclePointX + "," + mCirclePointY);
mPath.reset();
mPath.moveTo(0, 0);
//计算控制点和结束点的位置
//左边控制点的高度
float lControlPointX, lControlPointY;
//左边结束点的x,y
float lEndx, lEndy;
//获取切线角的弧度(将角度变成弧度)
double radians = Math.toRadians(getValueByLine(0, mTangentAngl, progress));
//结束点的X的坐标为圆心的X坐标-半径*sin(radians)
lEndx = (float) (cPonitX - Math.sin(radians) * cRedius);
//结束点的Y坐标等于圆心位置的y坐标+半径*cos(cRedius)
lEndy = (float) (cPonitY + Math.cos(radians) * cRedius);
//控制的y坐标
lControlPointY = getValueByLine(0, endContralY, progress);
//控制点和结束点之前的相差的高度
float diffHeight = lEndy - lControlPointY;
//可以根据两点相差的高度,和切线角度求出两点之间x的差值
float diffWidth = (float) (diffHeight / Math.tan(radians));
//得到控制点的X坐标
lControlPointX = lEndx - diffWidth;
mPath.quadTo(lControlPointX, lControlPointY, lEndx, lEndy);
//将path左象平移一段至cPonitX+(cPonitX-lEndx)
mPath.lineTo(cPonitX + (cPonitX - lEndx), lEndy);
//绘制右边的贝塞尔曲线
mPath.quadTo(cPonitX + cPonitX - lControlPointX, lControlPointY, width, 0);
upDataContentLayout(cPonitX, cPonitY, cRedius);
}
-通过属性动画实现回弹效果的处理
/**
* 平滑的滑到到结束位置
*
* @param distanceX
*/
private void smoothToEnd(final int distanceX) {
ValueAnimator valueAnimator;
if (distanceX > mMenuWidth / 2) {
//动画的初值和结束值
valueAnimator = ValueAnimator.ofInt(getScrollX(), mMenuWidth);
} else {
valueAnimator = ValueAnimator.ofInt(getScrollX(), 0);
}
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scrollTo((Integer) animation.getAnimatedValue(), 0);
}
});
//设置动画插值器,开始慢后面变快
valueAnimator.setInterpolator(new AccelerateInterpolator());
valueAnimator.setDuration(500).start();
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
//超过了二分之一的子菜单宽度则是展开的状态,反之则为关闭
if (distanceX > mMenuWidth / 2) {
mExpand = true;
} else {
mExpand = false;
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
<!--圆角图片的自定义属性-->
<declare-styleable name="RoundImage">
<!--左上的圆角-->
<attr name="roundImageLeftTopRadius" format="dimension" />
<!--右上的圆角-->
<attr name="roundImageRightTopRadius" format="dimension" />
<!--左下的圆角-->
<attr name="roundImageLeftBottomRadius" format="dimension" />
<!--右下的圆角-->
<attr name="roundImageRightButtomRadius" format="dimension" />
<!--整体圆角-->
<attr name="roundImageRadius" format="dimension" />
<!--描边的宽度-->
<attr name="roundImageStrokeWidth" format="dimension" />
<!--描边的颜色-->
<attr name="roundImageStrokeColor" format="color" />
<!--是否显示圆形-->
<attr name="roundImageCyclo" format="boolean" />
</declare-styleable>
<!--自定义指示器的自定义属性-->
<declare-styleable name="CircleIndicator">
<!--半徑-->
<attr name="indicatorRadius" format="dimension" />
<!--画笔的颜色-->
<attr name="indicatorColor" format="color" />
<!--间距-->
<attr name="indicatorSpace" format="dimension" />
<!--环的宽度-->
<attr name="indicatorBorderWidth" format="dimension" />
<!--环的中间的文字的颜色-->
<attr name="indicatorTextColor" format="color" />
<!--环的填充模式(数字、字母、纯色)-->
<attr name="indicatorFillMode" format="integer" />
</declare-styleable>