自定义首先接触到的时候觉得很有兴趣,便学习了一下
- HenCode自定义View教程,每个章节部分还会有配套的联系项目,难以理解的地方还有视频介绍,很用心的教程了
- HenCoder Android 开发进阶: 自定义 View 1-1 绘制基础
- HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解
- HenCoder Android 开发进阶:自定义 View 1-3 drawText() 文字的绘制
- HenCoder Android 开发进阶:自定义 View 1-4 Canvas 对绘制的辅助 clipXXX() 和 Matrix
- HenCoder Android 开发进阶:自定义 View 1-5 绘制顺序
- HenCoder Android 自定义 View 1-6:属性动画 Property Animation(上手篇)
- 绘制HenCoder Android 自定义 View 1-7:属性动画 Property Animation(进阶篇)
- HenCoder Android 自定义 View 1-8 硬件加速
- HenCoder Android UI 部分 2-1 布局基础
- HenCoder Android UI 部分 2-2 全新定义 View 的尺寸
- HenCoder Android UI 部分 2-3 定制 Layout 的内部布局
- HenCoder 3-1 触摸反馈
-
GcsSloop大神的自定义View教程也非常不错,覆盖前面且详细
基础篇
进阶篇
- 安卓自定义View进阶 - 分类和流程
- 安卓自定义View进阶 - Canvas之绘制图形
- 安卓自定义View进阶 - Canvas之画布操作
- 安卓自定义View进阶 - Canvas之图片文字
- 安卓自定义View进阶 - Path之基本操作
- 安卓自定义View进阶 - Path之贝塞尔曲线
- 安卓自定义View进阶 - Path完结篇
- 安卓自定义View进阶 - PathMeasure
- 安卓自定义View进阶 - Matrix原理
- 安卓自定义View进阶 - Matrix详解
- 安卓自定义View进阶 - Matrix Camera
- 安卓自定义View进阶 - 事件分发机制原理
- 安卓自定义View进阶 - 事件分发机制详解
- 安卓自定义View进阶 - MotionEvent详解
- 安卓自定义View进阶 - 特殊控件的事件处理方案
- 安卓自定义View进阶 - 多点触控详解
- 安卓自定义View进阶 - 手势检测(GestureDecetor)
- 安卓自定义View进阶 - 缩放手势检测(ScaleGestureDecetor)
学习完以上的教程后,还是需要自己动手来实践一下。
1.画线循迹
- 1.画出轨迹 :
先试着完成一个触控画线的功能,就是在屏幕上触控画线,画线需要一个Paint个一个Path,然后还要重写onToucheEvent()记录移动的位置,再连线即可。
onTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//设置起始点
mStartX = event.getX();
mStartY = event.getY();
//清空原有的轨道
mMovePath.reset();
//移动到起始点
mMovePath.moveTo(mStartX, mStartY);
return true;
case MotionEvent.ACTION_MOVE:
final float endX = (mStartX + event.getX()) / 2;
final float endY = (mStartY + event.getY()) / 2;
//画出轨道曲线,quad是二次曲线
mMovePath.quadTo(mStartX, mStartY, endX, endY);
mStartX = event.getX();
mStartY = event.getY();
//重绘
invalidate();
return true;
}
return super.onTouchEvent(event);
}
然后在onDraw()中调用drawPath()即可。
- 2.跟随轨迹移动
private void drawMove(Canvas canvas) {
pathMeasure.setPath(mMovePath, false);
if (isMove) {
//计算图片的旋转角度,tan是当前点切线值,根据切线值计算角度
float degree = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI);
//旋转对应的角度
canvas.rotate(degree, pos[0], pos[1]);
//设置中心点在运行轨道上
mRect.set(pos[0] - mRectWidth, pos[1] - mRectWidth, pos[0] + mRectWidth, pos[1] + mRectWidth);
//绘制draw
if (mDrawable == null) {
canvas.drawRect(mRect, mCarPaint);
} else {
canvas.drawBitmap(mBitmap, null, mRect, mPaint);
}
}
}
随意画一条轨道。点击启动开始动画
正确的完成了轨道。
具体参考Github:DiyViewPracticeDemo
2.雷达图
雷达图的绘制我分为三部分,雷达网的绘制,覆盖区域的绘制,边界文字描述绘制
雷达网的绘制
/**
* 绘制雷达图。
*/
private void drawRader(Canvas canvas) {
radarPaint.setPathEffect(null);
float gap = radius / (dataCount - 1);
//雷达网的绘制
for (int i = 1; i < dataCount; i++) {
//不同环的半径,dataCount-1个环
float currentR = gap * i;
radarPath.reset();
for (int j = 0; j < dataCount; j++) {
//如果是0,也就移到右边的起始点
if (j == 0) {
radarPath.moveTo(centerX + currentR, centerY);
} else {
//其他情况得到60°,120°,180°,240°,300°的cos和sin值,利用cos值得到x轴坐标,sin值得到y轴坐标。
radarPath.lineTo((float) (centerX + currentR * Math.cos(angle * j)), (float) (centerY + currentR * Math.sin(angle * j)));
}
}
//闭合,终点连上起点
radarPath.close();
canvas.drawPath(radarPath, radarPaint);
}
//绘制中心到边界点的虚线
radarPaint.setPathEffect(dashPathEffect);
for (int i = 0; i < dataCount; i++) {
radarPath.reset();
radarPath.moveTo(centerX, centerY);
radarPath.lineTo((float) (centerX + radius * Math.cos(angle * i)), (float) (centerY + radius * Math.sin(angle * i)));
canvas.drawPath(radarPath, radarPaint);
}
}
覆盖区域绘制
//绘制覆盖区域
private void drawRegion(Canvas canvas) {
int sum = 0;
for (int i = 0; i < dataCount; i++) {
double percent = drawed[i] / MAX_Value;
sum += drawed[i];
//得到对应分值的对应的x,y坐标
float x = (float) (centerX + radius * Math.cos(angle * i) * percent);
float y = (float) (centerY + radius * Math.sin(angle * i) * percent);
//绘制过程同雷达网的绘制过程
if (i == 0) {
regionPath.moveTo(x, centerY);
} else {
regionPath.lineTo(x, y);
}
//为边界点绘制小圆点
canvas.drawCircle(x, y, 5, valuePaint);
}
//闭合
regionPath.close();
canvas.drawPath(regionPath, valuePaint);
valuePaint.setAlpha(255 / 2);
valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawPath(regionPath, valuePaint);
canvas.save();
String centerText = "能力值:%d";
canvas.drawText(String.format(Locale.getDefault(), centerText, sum), centerX - centerTextPaint.measureText(centerText) / 2, centerY, centerTextPaint);
canvas.restore();
}
边界文字描述绘制
//绘制边界点的文字描述
private void drawText(Canvas canvas) {
float fontBaseLine = fontMetrics.descent - fontMetrics.ascent;
for (int i = 0; i < dataCount; i++) {
//x,y也就是边界点的坐标
float x = (float) (centerX + (radius + fontBaseLine / 2) * Math.cos(angle * i));
float y = (float) (centerY + (radius + fontBaseLine / 2) * Math.sin(angle * i));
//位于左边的需要左移文字长度大小
if (angle * i >= 0 && angle * i < Math.PI / 2) {//0°<=角度<90°
canvas.drawText(strings[i], x, y, textPaint);
} else if (angle * i >= Math.PI / 2 && angle * i < Math.PI) {//90°<=角度<180°
float dis = textPaint.measureText(strings[i]);//文本长度
canvas.drawText(strings[i], x - dis, y, textPaint);
} else if (angle * i >= Math.PI && angle * i < 3 * Math.PI / 2) {//180°<=角度<270°
float dis = textPaint.measureText(strings[i]);//文本长度
canvas.drawText(strings[i], x - dis, y, textPaint);
} else if (angle * i >= 3 * Math.PI / 2 && angle * i < Math.PI * 2) {//270°<=角度<=360°
canvas.drawText(strings[i], x, y, textPaint);
}
}
}
效果如下图:
具体参考Github:DiyViewPracticeDemo