1.自定义控件的步骤

2.构造函数有什么用

3.自定义属性有什么用

4.初始化一些画笔放在哪里

5.onmesure()如何写

6.invalide源码分析

1.写一个类继承view或者其他控件

public class CustomView extends View {
}

2.在xml中定义自定义的view

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/main_root_ll"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><com.sigestudio.customviewdemo.views.CustomViewandroid:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>

3.写构造方法:

public class CustomView extends View {public CustomView(Context context) {super(context);}public CustomView(Context context, AttributeSet attrs) {super(context, attrs);}
}

为什么要重写构造函数?

大致意思是无法解析我们的CustomView类找不到方法,为什么呢?我们在xml文件引用我们的CustomView类时为其指定了两个android自带的两个属性:layout_width和layout_height,当我们需要使用类似的属性(比如更多的什么id啊、padding啊、margin啊之类)时必须在自定义View的构造方法中添加一个AttributeSet类型的签名来解析这些属性:

网上有很多关于三个构造函数使用时机的说法,但是说法正确的却没有几家,这里正式的给大家科普一下:

  1. 在代码中直接new一个Custom View实例的时候,会调用第一个构造函数.这个没有任何争议.
  2. 在xml布局文件中调用Custom View的时候,会调用第二个构造函数.这个也没有争议.
  3. 在xml布局文件中调用Custom View,并且Custom View标签中还有自定义属性时,这里调用的还是第二个构造函数.

也就是说,系统默认只会调用Custom View的前两个构造函数,至于第三个构造函数的调用,通常是我们自己在构造函数中主动调用的(例如,在第二个构造函数中调用第三个构造函数).

我们自己怎么写?

第一个调用滴第二个,第二个调用第三个

public SportHeadView(Context context) {super(context);Log.d("SportHeadView","SportHeadView one");init();
}public SportHeadView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);Log.d("SportHeadView","SportHeadView two");init();
}public SportHeadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);Log.d("SportHeadView","SportHeadView three");init();
}

没有使用自定义属性:

SportHeadView: SportHeadView two

不知道传啥,用这个!

SportHeadView sportHeadViewTest=new SportHeadView(MainActivity.this);

4.重写ondraw()方法

Android也给我们提供了这两样东西:Paint和Canvas,一个是画笔而另一个呢当然是画布啦~~,我们可以看到在onDraw方法中,画布Canvas作为签名被传递进来,也就是说这个画布是Android为我们准备好的

---------------------

 
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setAntiAlias(true);
}

设置了抗锯齿(一种让图像边缘显得更圆滑光泽动感的碉堡算法):setAntiAlias(true)

5.CustomView的代码让其实现Runnable接口

实现Runnable接口还是在子线程,所以更新ui用:postInvalidate()

public class CustomView extends View implements Runnable {private Paint mPaint;// 画笔private Context mContext;// 上下文环境引用private int radiu;// 圆环半径public CustomView(Context context) {this(context, null);}public CustomView(Context context, AttributeSet attrs) {super(context, attrs);mContext = context;// 初始化画笔initPaint();}/*** 初始化画笔*/private void initPaint() {// 实例化画笔并打开抗锯齿mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);/** 设置画笔样式为描边,圆环嘛……当然不能填充不然就么意思了* * 画笔样式分三种:* 1.Paint.Style.STROKE:描边* 2.Paint.Style.FILL_AND_STROKE:描边并填充* 3.Paint.Style.FILL:填充*/mPaint.setStyle(Paint.Style.STROKE);// 设置画笔颜色为浅灰色mPaint.setColor(Color.LTGRAY);/** 设置描边的粗细,单位:像素px* 注意:当setStrokeWidth(0)的时候描边宽度并不为0而是只占一个像素*/mPaint.setStrokeWidth(10);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 绘制圆环canvas.drawCircle(MeasureUtil.getScreenSize((Activity) mContext)[0] / 2, MeasureUtil.getScreenSize((Activity) mContext)[1] / 2, radiu, mPaint);}@Overridepublic void run() {/** 确保线程不断执行不断刷新界面*/while (true) {try {/** 如果半径小于200则自加否则大于200后重置半径值以实现往复*/if (radiu <= 200) {radiu += 10;// 刷新Viewinvalidate();} else {radiu = 0;}// 每执行一次暂停40毫秒Thread.sleep(40);} catch (InterruptedException e) {e.printStackTrace();}}}
}

总结:.动画的原理:

大家如果了解动画的原理就会知道,一个动画是由无数张连贯的图片构成的,这些图片之间快速地切换再加上我们眼睛的视觉暂留给我们造成了在“动”的假象。

再提供一个setter方法对外设置半径值,并在设置了该值后调用invalidate()方法重绘View:

注意的点:

3个构造方法:前面2个最终调用到第3个构造函数

里面的单位是多少?px,所以自定义是如何适配的

onMeasure:宽度和高度不一致

测量模式:int mode=MeasureSpec.getMode(widthMeasureSpec);//通过宽可以获取测量模式,通过高也可以获取测量模式,因为它们不一样。

描边的宽度,要注意

动态产生差值的办法:handler,动画

自定义属性,希望不要乱传入值,有什么办法?

注解的办法

自定义属性要注意什么问题?

有重复的属性,和别人的冲突,编译的时候报错

一些注意的东西:

测量:一般是自己的东西+padding()

测量文字的宽和高。和基线

bottom:正值,top,负值,相对于基线来说,FontMetrics的这几个变量的值都是以baseline为基准的

公式:(bottom-top)/2-bottom+centerY

 // 测量文字的宽高Rect textBounds = new Rect();mPaint.getTextBounds(mStep, 0, mStep.length(), textBounds);int dx = (getWidth() - textBounds.width()) / 2;// 获取画笔的FontMetricsPaint.FontMetrics fontMetrics = mPaint.getFontMetrics();// 计算文字的基线int baseLine = (int) (getHeight() / 2 + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);// 绘制步数文字canvas.drawText(mStep, dx, baseLine, mPaint);

圆弧效果:

 // 设置为 ROUNDmPaint.setStrokeCap(Paint.Cap.ROUND);mPaint.setStrokeJoin(Paint.Jo

自定义控件的步骤!extent view

1.三个构造方法

2.自定义属性

3.xml设置自定义属性

4.onMeaseure

5.onDraw

6.设置一些外包调用方法

每个方法写完,绘制一步一步。就要先调试下,然后下一步。

自定义viewGroup:

1.构造函数

2.不需要属性

3.onMeasure

4.onLayout

分析:

2.1:画-背景圆弧
2.2:画-当前进度圆弧
2.3:画-步数文字
2.4:提供一些方法

2.5:动画效果

2.1. 画-背景圆弧

    @Overrideprotected void onDraw(Canvas canvas) {// 2.画背景大圆弧int centerX = mViewWidth / 2;int centerY = mViewHeight / 2;// 设置圆弧画笔的宽度mPaint.setStrokeWidth(mRoundWidth);// 设置为 ROUNDmPaint.setStrokeCap(Paint.Cap.ROUND);mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置画笔颜色mPaint.setColor(mRoundColor);mPaint.setStyle(Paint.Style.STROKE);// 半径int radius = (int) (centerX - mRoundWidth);RectF oval = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius);// 画背景圆弧canvas.drawArc(oval, mStartAngle, mSweepAngle, false, mPaint);}    

2.2:画-当前进度圆弧

        // 画进度圆弧mPaint.setColor(mProgressColor);// 计算当前百分比float percent = (float) mProgressStep/mMaxStep;// 根据当前百分比计算圆弧扫描的角度canvas.drawArc(oval, mStartAngle, percent*mSweepAngle, false, mPaint);

2.3:画-步数文字

        // 重置画笔mPaint.reset();mPaint.setAntiAlias(true);mPaint.setTextSize(mTextSize);mPaint.setColor(mTextColor);String mStep = ((int)(percent*mMaxStep)) + "";// 测量文字的宽高Rect textBounds = new Rect();mPaint.getTextBounds(mStep, 0, mStep.length(), textBounds);int dx = (getWidth() - textBounds.width()) / 2;// 获取画笔的FontMetricsPaint.FontMetrics fontMetrics = mPaint.getFontMetrics();// 计算文字的基线int baseLine = (int) (getHeight() / 2 + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);// 绘制步数文字canvas.drawText(mStep, dx, baseLine, mPaint);

来源: https://www.jianshu.com/p/4e0eb9bb09ab

2.4. 提供一些方法

    // 设置当前最大步数public synchronized void setMaxStep(int maxStep) {if (max < 0) {throw new IllegalArgumentException("max 不能小于0!");}this.mMaxStep = maxStep;}public synchronized int getMaxStep() {return mMaxStep;}// 设置进度public synchronized void setProgress(int progress) {if (progress < 0) {throw new IllegalArgumentException("progress 不能小于0!");}this.progress = progress;// 重新刷新绘制 -> onDraw()invalidate();}public synchronized int getProgress() {return progress;}

完整的代码:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final SportHeadView sportHeadView=findViewById(R.id.tv_my);handler=new Handler(getMainLooper()){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);progress+=100;sportHeadView.setProgress(progress);handler.sendEmptyMessageDelayed(0,200);}};handler.sendEmptyMessage(0);}int progress=100;Handler handler;
}
 
 
 
 
public class SportHeadView extends View {int mViewWidth;
int mViewHeight;
int mRoundWidth=20;
int mRoundColor=R.color.colorAccent;
int mStartAngle=130;
int mSweepAngle=270;public SportHeadView(Context context) {
super(context);
init();
}public SportHeadView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}public SportHeadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}/***
* 初始化,在3个参数里面初始化。感觉还是会奔溃
*/
private void init() {
mPaint=new Paint();
mPaint.setAntiAlias(true);
}/***
* 我们要求是画一个正方形
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);int mode=MeasureSpec.getMode(widthMeasureSpec);//int width= MeasureSpec.getSize(widthMeasureSpec);
int height=MeasureSpec.getSize(heightMeasureSpec);if(width>height){
width=height;
}else {
height=width;
}
mViewWidth=width;
mViewHeight=mViewWidth;
setMeasuredDimension(width,height);
}Paint mPaint;@Override
protected void onDraw(Canvas canvas) {
// 2.画背景大圆弧
int centerX = mViewWidth / 2;
int centerY = mViewHeight / 2;
// 设置圆弧画笔的宽度mPaint.setStrokeWidth(mRoundWidth);
// 设置为 ROUND
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeJoin(Paint.Join.ROUND);
// 设置画笔颜色
mPaint.setColor(mRoundColor);
mPaint.setStyle(Paint.Style.STROKE);
// 半径
int radius = (int) (centerX - mRoundWidth);
RectF oval = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
// 画背景圆弧
canvas.drawArc(oval, mStartAngle, mSweepAngle, false, mPaint);// 画进度圆弧
mPaint.setColor(mProgressColor);
// 计算当前百分比
float percent = (float) mProgressStep/mMaxStep;
// 根据当前百分比计算圆弧扫描的角度
canvas.drawArc(oval, mStartAngle, percent*mSweepAngle, false, mPaint);// 重置画笔
mPaint.reset();
mPaint.setAntiAlias(true);
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
String mStep = ((int)(percent*mMaxStep)) + "";
// 测量文字的宽高
Rect textBounds = new Rect();
mPaint.getTextBounds(mStep, 0, mStep.length(), textBounds);
int dx = (getWidth() - textBounds.width()) / 2;
// 获取画笔的FontMetrics
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
// 计算文字的基线
int baseLine = (int) (getHeight() / 2 + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
// 绘制步数文字
canvas.drawText(mStep, dx, baseLine, mPaint);
}// 设置当前最大步数
public synchronized void setMaxStep(int maxStep) {
if (max < 0) {
throw new IllegalArgumentException("max 不能小于0!");
}
this.mMaxStep = maxStep;
}public synchronized int getMaxStep() {
return mMaxStep;
}
// 设置进度
public synchronized void setProgress(int progress) {
if (progress < 0) {
throw new IllegalArgumentException("progress 不能小于0!");
}
this.progress = progress;this.mProgressStep=this.progress;
// 重新刷新绘制 -> onDraw()
invalidate();
}public synchronized int getProgress() {
return progress;
}int max=3000;
int progress;int mTextSize=40;
int mTextColor=R.color.colorPrimary;
int mMaxStep=3000;
int mProgressStep=0;
int mProgressColor=R.color.colorPrimaryDark;
}

自定义控件其实很简单1/12

http://blog.csdn.net/aigestudio

3.Android 仿QQ运动步数进度效果 keep运动效果(从入门到巅峰)相关推荐

  1. 自定义View | 仿QQ运动步数进度效果

    项目GitHub地址 思路 固定不动的蓝色大圆弧 动画变动的红色小圆弧 中间的步数文字显示 相关的自定义属性 比如固定不动的大圆弧, 我们不能写死他的蓝色颜色属性, 要提供一个颜色的自定义属性给用户自 ...

  2. 自定义View之仿QQ运动步数进度效果

    前言 今天接着上一篇来写关于自定义View方面的东西,我是近期在学习整理这方面的知识点,所以把相关的笔记都放到这个Android自定义View的专栏里了,方便自己下次忘记的时候能回来翻翻,今天的内容是 ...

  3. android计步器简书,自定义View-仿QQ运动步数进度效果

    自定义View-仿QQ运动步数进度效果 一.写在前面 (1) 图一,仿QQ步数运行效果 (2) 图二,完整的圆效果 完整代码请看这 二.正文开始 (1)首先来个三部曲,自定义属性,布局设置,属性获取 ...

  4. 自定义View-仿QQ运动步数进度效果

    自定义View-仿QQ运动步数进度效果 一.写在前面 (1) 图一,仿QQ步数运行效果 (2) 图二,完整的圆效果 完整代码请看这 二.正文开始 (1)首先来个三部曲,自定义属性,布局设置,属性获取 ...

  5. 自定义View-仿QQ运动步数进度效果(完整代码)

    解析请看这自定义View-仿QQ运动步数进度效果 一.attrs.xml <?xml version="1.0" encoding="utf-8"?> ...

  6. android取QQ昵称,Android仿QQ复制昵称效果

    背景: 这几天做一个复制文本的需求,突然看到QQ上复制昵称跟QQ号的效果,觉得很不错,就想要模仿一波,办法比较简单粗暴,反编译QQ获取了那个.9图片,然后就是用PopWindow实现了. 解决办法: ...

  7. android取QQ昵称,Android仿QQ复制昵称效果的实现方法

    背景: 在上一篇文章中,给出了一种复制QQ效果的方案,今天就来讲讲换一种方式实现.主要依赖的是一个开源项目https://github.com/shangmingchao/PopupList. 解决办 ...

  8. Android仿QQ消息拖拽效果(二)

    前言 本文参考辉哥贝塞尔曲线 - QQ消息汽包拖拽,前面我们使用二阶贝塞尔曲线绘制了拖拽圆点效果Android仿QQ消息拖拽效果(一)(二阶贝塞尔曲线使用),这里我们在此基础之上实现仿QQ消息拖拽爆炸 ...

  9. android qq分组展开,Android仿qq分组管理的第三方库

    本文实例为大家分享了Android仿qq分组管理的第三方库,供大家参考,具体内容如下 下面先看效果 我们点击展开与折叠分组的功能在库里面是已经封装好的,只能把它已入到项目中,就可以直接用了,十分的方便 ...

  10. Android仿QQ通讯录分组展示ExpandableListView

    Android仿QQ通讯录分组展示ExpandableListView 核心是重写BaseExpandableListAdpter,其实和之前写的普通的BaseAdapter是类似的, 但是BaseE ...

最新文章

  1. mysql post 中文乱码_mysql/mariaDB中文乱码问题的处理
  2. 让算法工程师破防的瞬间
  3. 定义任务打印gradle下载的jar包位置
  4. HDU - 2973威尔逊定理
  5. JDK1.8 中 ConcurrentHashMap源码分析(一)容器初始化
  6. Eclipse代码自动补全设置
  7. wireshark帮你解析网络包
  8. .net知识和学习方法系列(二十三)嵌套类
  9. python连接阿里云服务器_阿里云服务器python
  10. java使用163邮箱完成发送邮件完成注册
  11. 2023王道数据结构P40题二.1,关于是否会断链的问题
  12. 通用路由封装协议--GRE的简单配置
  13. python课程设计矩阵对角线之和_Python二维数组实现求出3*3矩阵对角线元素的和示例...
  14. 苹果开发者账号(公司级)和邓白氏编码(D-U-N-S)申请记录(2015.06)
  15. 认知世界(1)--学与思
  16. 软件架构与设计(十)-----架构技术
  17. 华为服务器 26块硬盘,产品推广—华为服务器推荐
  18. LeetCode 力扣 算法题解 1109. 航班预订统计(Corporate Flight Bookings) n 个航班,它们分别从 1 到 n 进行编号,请返回每个航班预定的座位总数。
  19. 嵌入式作业STM32采用串口DMA方式发送数据
  20. mimo 雷达成像 matlab,MIMO雷达成像算法研究

热门文章

  1. python圣斗士修炼(十八):访问mysql数据库
  2. Ransomware的斗士——云备份系统
  3. macOS 上安装 PECL
  4. 20180626 STM32 ---利用KILE5 仿真IO口的波形图
  5. 【学术论文】查找论文、查找并理解配套代码(持续更新~)
  6. 使用腾讯云 SCF 云函数压缩 COS 对象存储文件
  7. python day46
  8. WIN10 x64搭建OLLVM4.0 android NDK 编译环境跨坑指南
  9. 伤害世界怎么自建服务器,伤害世界服务器架设图文教程
  10. PS简单入门须知的小技巧