《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
完整开源地址:https://docs.qq.com/doc/DSkNLaERkbnFoS0ZF
}
/**
- 绘制外圈
- @param canvas 画布
*/
private void drawOutCircle(Canvas canvas) {
canvas.save();
canvas.drawCircle(0, 0, mRadius, mLinePaint);
float r2 = mRadius - 0.08f * mRadius;//下圆半径
canvas.drawCircle(0, 0, r2, mLinePaint);
for (int i = 0; i < 22; i++) {//循环画出小黑条
canvas.save();
canvas.rotate(360 / 22f * i);
canvas.drawLine(0, -mRadius, 0, -r2, mFillPaint);
canvas.restore();
}
canvas.restore();
}
protected float dp(float dp) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
}
2.内圈绘制
同样尺寸和最外圆看齐,这里绘制有一丢丢复杂,你需要了解canvas和path的使用
看不懂的可转到canvas和path,如果看了这两篇还问绘制有什么技巧的,可转到这里,会告诉你技巧是什么
/**
- 绘制内圈圆
- @param canvas 画布
*/
private void drawInnerCircle(Canvas canvas) {
canvas.save();
float innerRadius = 0.6f * mRadius;
canvas.drawCircle(0, 0, innerRadius, mLinePaint);
canvas.save();
for (int i = 0; i < 6; i++) {//遍历6条线
canvas.save();
canvas.rotate(60 * i);//每次旋转60°
mPath.moveTo(0, -innerRadius);
mPath.rLineTo(0, innerRadius);//线的路径
for (int j = 1; j < 6; j++) {
mPath.moveTo(-mRadius * 0.02f, innerRadius / 6 * j);
mPath.rLineTo(mRadius * 0.02f * 2, 0);
}//加5条小线
canvas.drawPath(mPath, mLinePaint);//绘制线
canvas.restore();
}
canvas.restore();
}
3.文字的绘制
文字的方向同向,感觉这样看着好些,不管怎么转都可以
//定义测试数据
mAbilityInfo = new String[]{“破坏力”, “速度”, “射程距离”, “持久力”, “精密度”, “成长性”};
mAbilityMark = new int[]{100, 100, 60, 100, 100, 100};
mMarkMapper = new String[]{“A”, “B”, “C”, “D”, “E”};
/**
- 绘制文字
- @param canvas 画布
*/
private void drawInfoText(Canvas canvas) {
float r2 = mRadius - 0.08f * mRadius;//下圆半径
for (int i = 0; i < 6; i++) {
canvas.save();
canvas.rotate(60 * i + 180);
mTextPaint.setTextSize(mRadius * 0.1f);
canvas.drawText(mAbilityInfo[i], 0, r2 - 0.06f * mRadius, mTextPaint);
mTextPaint.setTextSize(mRadius * 0.15f);
canvas.drawText(abilityMark2Str(mAbilityMark[i]), 0, r2 - 0.18f * mRadius, mTextPaint);
canvas.restore();
}
mTextPaint.setTextSize(mRadius * 0.07f);
for (int k = 0; k < 5; k++) {
canvas.drawText(mMarkMapper[k], mRadius * 0.06f, mInnerRadius / 6 * (k + 1) + mRadius * 0.02f - mInnerRadius, mTextPaint);
}
}
/**
- 将分数映射成字符串
- @param mark 分数100~0
- @return
*/
private String abilityMark2Str(int mark) {
if (mark <= 100 && mark > 80) {
return mMarkMapper[0];
} else if (mark <= 80 && mark > 60) {
return mMarkMapper[1];
} else if (mark <= 60 && mark > 40) {
return mMarkMapper[2];
} else if (mark <= 40 && mark > 20) {
return mMarkMapper[3];
} else if (mark <= 20 && mark > 0) {
return mMarkMapper[4];
}
return “∞”;
}
4.最后一步:画内容
本以为就连个点的事,没想到…打了我半页草稿纸(手动表情–可怕)
展现在你眼前的就是个for循环而已,实际上都是通过一点点分析,测试与发现规律算出来的
有什么技巧?草稿纸拿出来画图,计算+分析…,只靠眼睛是不行的
//我不喜欢弄脏画笔,再准备一支吧
mAbilityPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mAbilityPaint.setColor(0x8897C5FE);
mAbilityPath = new Path();
/**
- 绘制能力面
- @param canvas
/
private void drawAbility(Canvas canvas) {
float step = mInnerRadius / 6;//每小段的长度
mAbilityPath.moveTo(0, -mAbilityMark[0] / 20.f * step);//起点
for (int i = 1; i < 6; i++) {
float mark = mAbilityMark[i] / 20.f;
mAbilityPath.lineTo(
(float) (mark * step * Math.cos(Math.PI/180(-30+60*(i-1)))),
(float) (mark * step * Math.sin(Math.PI/180*(-30+60*(i-1)))));
}
mAbilityPath.close();
canvas.drawPath(mAbilityPath, mAbilityPaint);
}
这样就完成了,你以为这样就结束了?这才刚开始呢!
二、数据的提取与封装
刚才用的是测试数据,都写死在View中,这肯定是不行的
现在将数据封装一下,再暴露接口方法,打开View和外界的通路
1.View的尺寸限定
使用宽度作为直径,无视高度,尺寸为圆形区域
如下所示:可看出所有的尺寸都是和按照mRadius来确定的,所以缩放时也会等比
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mRadius = MeasureSpec.getSize(widthMeasureSpec) / 2;
mInnerRadius = 0.6f * mRadius;
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(widthMeasureSpec));
}
2.数据处理
为了方便查看数据间关系,使用Map将能力与数值装一下
private HashMap<String, Integer> mData;//核心数据
//数据的刚才的对接
mData = new HashMap<>();
mData.put(“破坏力”, 100);
mData.put(“速度”, 100);
mData.put(“射程距离”, 60);
mData.put(“持久力”, 100);
mData.put(“精密度”, 100);
mData.put(“成长性”, 100);
mAbilityInfo = mData.keySet().toArray(new String[mData.size()]);
mAbilityMark = mData.values().toArray(new Integer[mData.size()]);
3.数据与字符的映射关系:DataMapper
也就是100~80之间的代表字符串可以自定义,比如"1" 、 “I” 、"☆"随你便
这也是我刚悟到的一种解耦方式,应该算是策略设计模式吧(只能分五个等级)
如果自定义分类情况重写abilityMark2Str方法就行了
/**
- 作者:张风捷特烈
- 时间:2018/12/28 0028:12:21
- 邮箱:1981462002@qq.com
- 说明:数据映射抽象类
*/
public class DataMapper {
protected String[] mapper;
public DataMapper(String[] mapper) {
if (mapper.length != 5) {
throw new IllegalArgumentException(“the length of mapper must be 5”);
}
this.mapper = mapper;
}
public String[] getMapper() {
return mapper;
}
/**
- 数值与字符串的映射关系
- @param mark 数值
- @return 字符串
*/
public String abilityMark2Str(int mark) {
if (mark <= 100 && mark > 80) {
return mapper[0];
} else if (mark <= 80 && mark > 60) {
return mapper[1];
} else if (mark <= 60 && mark > 40) {
return mapper[2];
} else if (mark <= 40 && mark > 20) {
return mapper[3];
} else if (mark <= 20 && mark > 0) {
return mapper[4];
}
return “∞”;
}
}
给一个默认的映射类:
WordMapper
也就是刚才在View里写的那个方法
/**
- 作者:张风捷特烈
- 时间:2018/12/28 0028:12:24
- 邮箱:1981462002@qq.com
- 说明:单词映射
*/
public class WordMapper extends DataMapper {
public WordMapper() {
super(new String[]{“A”, “B”, “C”, “D”, “E”});
}
View里如何修改呢?
//定义成员变量
private DataMapper mDataMapper;//数据与字符串映射规则
//init里
mDataMapper = new WordMapper();//初始化DataMapper–默认WordMapper
//绘制文字的时候由mDataMapper提供数据
private void drawInfoText(Canvas canvas) {
float r2 = mRadius - 0.08f * mRadius;//下圆半径
for (int i = 0; i < 6; i++) {
canvas.save();
canvas.rotate(60 * i + 180);
mTextPaint.setTextSize(mRadius * 0.1f);
canvas.drawText(mAbilityInfo[i], 0, r2 - 0.06f * mRadius, mTextPaint);
mTextPaint.setTextSize(mRadius * 0.15f);
canvas.drawText(
mDataMapper.abilityMark2Str(mAbilityMark[i]), 0, r2 - 0.18f * mRadius, mTextPaint);
canvas.restore();
}
mTextPaint.setTextSize(mRadius * 0.07f);
for (int k = 0; k < 5; k++) {
canvas.drawText(mDataMapper.getMapper()[k], mRadius * 0.06f, mInnerRadius / 6 * (k + 1) + mRadius * 0.02f - mInnerRadius, mTextPaint);
}
}
//暴漏get、set方法—提供外界设置
public DataMapper getDataMapper() {
return mDataMapper;
}
public void setDataMapper(DataMapper dataMapper) {
mDataMapper = dataMapper;
}
//暴漏设置数据方法给外部
public HashMap<String, Integer> getData() {
return mData;
}
public void setData(HashMap<String, Integer> data) {
mData = data;
mAbilityInfo = mData.keySet().toArray(new String[mData.size()]);
mAbilityMark = mData.values().toArray(new Integer[mData.size()]);
invalidate();
}
4.使用方法:
使用DataMapper将字符串抽离出来,并且还可以根据数值来主要以返回字符串
AbilityView abilityView = findViewById(R.id.id_ability_view);
mData = new HashMap<>();
mData.put(“Java”, 100);
mData.put(“Kotlin”, 70);
mData.put(“JavaScript”, 100);
mData.put(“Python”, 60);
mData.put(“Dart”, 50);
mData.put(“C++”, 60);
abilityView.setDataMapper(new DataMapper(new String[]{“神”, “高”, “普”, “新”, “入”}));
abilityView.setData(mData);
ok,搞定,你以为完了?No,精彩继续
三、n条属性任你比
搞了个6个,不得了了吗?可见其中还有一个死的东西,那就是数据条数
这个就麻烦了,如果刚才是0->1的创造
,填充数据是1->2的积累
,那接下来就是2->n的生命
好吧,我又打了半张草稿纸,终于算完了!View一共不到200行代码,感觉很优雅了
有兴趣的自己研究(画画图,打打草稿),没兴趣的直接拿去用,
/**
- 作者:张风捷特烈
- 时间:2018/12/28 0028:7:40
- 邮箱:1981462002@qq.com
- 说明:能力对比图
*/
public class AbilityView extends View {
private static final String TAG = “AbilityView”;
private float mRadius = dp(100);//外圆半径
private float mLineWidth = dp(1);//线宽
private Paint mLinePaint;//线画笔
private Paint mFillPaint;//填充画笔
private Path mPath;
private HashMap<String, Integer> mData;//核心数据
private Paint mTextPaint;
String[] mAbilityInfo;
Integer[] mAbilityMark;
private float mInnerRadius;
private Path mAbilityPath;
private Paint mAbilityPaint;
private DataMapper mDataMapper;//数据与字符串映射规则
p(100);//外圆半径
private float mLineWidth = dp(1);//线宽
private Paint mLinePaint;//线画笔
private Paint mFillPaint;//填充画笔
private Path mPath;
private HashMap<String, Integer> mData;//核心数据
private Paint mTextPaint;
String[] mAbilityInfo;
Integer[] mAbilityMark;
private float mInnerRadius;
private Path mAbilityPath;
private Paint mAbilityPaint;
private DataMapper mDataMapper;//数据与字符串映射规则