Android自定义控件(高手级)--JOJO同款能力分析图

}
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;//数据与字符串映射规则

public AbilityView(Context context) {
this(context, null);
}

public AbilityView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}

public AbilityView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

private void init() {
mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLinePaint.setStrokeWidth(mLineWidth);
mLinePaint.setStyle(Paint.Style.STROKE);
mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFillPaint.setStrokeWidth(0.05f * mRadius);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(mRadius * 0.1f);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mAbilityPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mAbilityPaint.setColor(0x8897C5FE);
mAbilityPath = new Path();
mPath = new Path();
mData = new HashMap<>();
mDataMapper = new WordMapper();//初始化DataMapper–默认WordMapper
}

@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));
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mAbilityInfo == null) {
return;
}
canvas.translate(mRadius, mRadius);//移动坐标系
drawOutCircle(canvas);
drawInnerCircle(canvas);
drawInfoText(canvas);
drawAbility(canvas);
}

/**

  • 绘制能力面
  • @param canvas
    */
    private void drawAbility(Canvas canvas) {
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

文章不易,如果大家喜欢这篇文章,或者对你有帮助希望大家多多点赞转发关注哦。文章会持续更新的。绝对干货!!!

  • Android进阶学习全套手册
    关于实战,我想每一个做开发的都有话要说,对于小白而言,缺乏实战经验是通病,那么除了在实际工作过程当中,我们如何去更了解实战方面的内容呢?实际上,我们很有必要去看一些实战相关的电子书。目前,我手头上整理到的电子书还算比较全面,HTTP、自定义view、c++、MVP、Android源码设计模式、Android开发艺术探索、Java并发编程的艺术、Android基于Glide的二次封装、Android内存优化——常见内存泄露及优化方案、.Java编程思想 (第4版)等高级技术都囊括其中。

  • Android高级架构师进阶知识体系图
    关于视频这块,我也是自己搜集了一些,都按照Android学习路线做了一个分类。按照Android学习路线一共有八个模块,其中视频都有对应,就是为了帮助大家系统的学习。接下来看一下导图和对应系统视频吧!!!

  • Android对标阿里P7学习视频

  • BATJ大厂Android高频面试题
    这个题库内容是比较多的,除了一些流行的热门技术面试题,如Kotlin,数据库,Java虚拟机面试题,数组,Framework ,混合跨平台开发,等
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

做了一个分类。按照Android学习路线一共有八个模块,其中视频都有对应,就是为了帮助大家系统的学习。接下来看一下导图和对应系统视频吧!!!
[外链图片转存中…(img-bi4ySYLf-1711805218658)]

  • Android对标阿里P7学习视频

[外链图片转存中…(img-nskScSLR-1711805218658)]

  • BATJ大厂Android高频面试题
    这个题库内容是比较多的,除了一些流行的热门技术面试题,如Kotlin,数据库,Java虚拟机面试题,数组,Framework ,混合跨平台开发,等
    [外链图片转存中…(img-d1wK9P0d-1711805218658)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值