自定义View学习的小记录之第一篇

  • 自定义View学习之QQ计步器
    • 如何实现
      • 1.分析想要得到什么效果
      • 2.确定自定义属性,编写attrs.xml
      • 3.在布局中使用
      • 4.在自定义View中获取自定义属性
      • 5.onMeasure()得到这个控件的的大小
      • 6.再去画具体的控件,绘制外圆弧和内圆弧、相关的文本文字
      • 7.其他
    • 如何使用
    • paint的使用
    • 面试易考

自定义View学习之QQ计步器


有点粗糙,颜色选择上有点儿不太美观辽,不过自己第一次写自定义view,纪念一下。

如何实现

1.分析想要得到什么效果

首先我们需要有一个外圆弧用来做外层框架,一个内圆弧用来看占比情况,一个具体的中心文本记录准确的步数。我们把这三者整合在一个View里面,类似于平常安卓写的ImageView这种一样,可以直接拿来用,封装成一个整体结构。

2.确定自定义属性,编写attrs.xml

首先我们分析下需要哪些自定义属性:

  1. 外圆弧的颜色
  2. 内圆弧的颜色
  3. 圆弧的宽度(内圆弧与外圆弧的宽度一样宽)
  4. 中间那个文本字体的大小
  5. 中间文本字体的颜色
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="QQStepView"><attr name="outerColor" format="color"/><attr name="innerColor" format="color"/><attr name="borderWidth" format="dimension"/><attr name="stepTextSize" format="dimension"/><attr name="stepTextColor" format="color"/></declare-styleable>
</resources>

3.在布局中使用

在定义完attrs.xml之后,既然我们把它创造出来了,我们就需要使用它。
这时候我们就来到了activity_main.xml中使用它了,和我们平常使用Button等差不多,只不过这里是写的我们自己定义的view的名字,接着就是给它属性了,与普通的不同,当我们使用Button时,我们可能使用的是Android:textsize=“20sp”,而在这我们得利用我们自己的,不然你用的还是人家的呢。

<com.example.view_study1.QQStepViewandroid:id="@+id/step_view"android:layout_width="match_parent"android:layout_height="match_parent"app:outerColor="#3BFFE8"app:innerColor="#F4BBF0"app:borderWidth="20dp"app:stepTextSize="30sp"app:stepTextColor="#F4BBF0" />

这里的名字得改成你自己定义的名字哦!

4.在自定义View中获取自定义属性

接下来就是重头戏了,刚刚你所做的不过是把水壶拿过来,而水壶需要注水呀插电啊什么的,这些实际功能并没有去做。所以你得给这个View注入它应该做的事。首先就要激活我们刚刚定义的一系列属性,刚刚还只是一堆纸片人呢,还得你给他涂色,变魔术呢!

        TypedArray array=context.obtainStyledAttributes(attrs,R.styleable.QQStepView);mOuterColor=array.getColor(R.styleable.QQStepView_outerColor,mOuterColor);mInnerColor=array.getColor(R.styleable.QQStepView_innerColor,mInnerColor);mBoderWidth=(int)array.getDimension(R.styleable.QQStepView_borderWidth,mBoderWidth);mStepTextColor=array.getColor(R.styleable.QQStepView_stepTextColor,mStepTextColor);mStepTextSize=array.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize,mStepTextSize);array.recycle();

5.onMeasure()得到这个控件的的大小

这里的意思是我们要得到这一整个view 的框架大小,特别要注意的是为了美观,我们把它设置为正方形,在宽高不一致的情况下取最小值。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//由于宽高是开发者设置的,当设置的长宽不一致的时候,为了美观要保证是个正方形//宽高不一致取最小值,确保是个正方形int width=MeasureSpec.getSize(widthMeasureSpec);int height=MeasureSpec.getSize(heightMeasureSpec);setMeasuredDimension(width>height?height:width,width>height?height:width);}

6.再去画具体的控件,绘制外圆弧和内圆弧、相关的文本文字


解释下这段代码的意思:首先第一句代码是通过宽的一半求的这个控件的中心位置应该不难理解,第二点可能挺让人懵的,为啥半径要用宽的一半减圆弧宽的一半,难道不应该是宽的一半减圆弧的宽度吗?这就要提到画笔的属性了,mOutPaint.setStrokeWidth(mBoderWidth); //原因就在于setStrokeWidth这个方法,并不是往圆内侧增加圆环宽度的,而是往外侧增加一半,往内侧增加一半,详情参考这篇博客https://blog.csdn.net/u014704469/article/details/41277751
也就是说我们的画笔是从管道的中心点开始画的,而不是我们以为的从内圆弧画到外圆弧,所以我们要求的这个半径也不能是真正意义上圆的半径,而应该是提笔开始画的位置,也就是管道的中心点,所以就是减圆弧宽的一半。
接下来分析下为什么是135,270,135是因为我们建立以中心点的坐标轴,x轴在右边,则要开始画的点与x轴形成135°。270则代表着这个圆弧是270°。

//6.1画外圆弧int center=getWidth()/2;//得到中心点位置int radius=getWidth()/2-mBoderWidth/2;//得到半径RectF rectF=new RectF(center-radius,center-radius,center+radius,center+radius);//开始画,需要指定从多少度开始画,画多少度canvas.drawArc(rectF,135,270,false,mOutPaint);

从图片可以看出,内圆弧与外圆弧其实是差不多的,如果你走到了最大步数就是完全覆盖外圆弧了,所以我们只要改变内圆弧要画多少度这个问题就可以了,内圆弧画多少度可以理解为占比问题,内圆弧在外圆弧上占多少的百分比,也就是当前步数比上最大步数,所以只要用这个百分比*整个圆弧的范围,就是你要画的圆弧范围。

//6.2画内圆弧 百分比是该view的使用者从外面传的float sweepAngle=(float)mCurrentStep/mStepMax;canvas.drawArc(rectF,135,sweepAngle*270,false,mInnerPaint);

接下来就是画中心的文本显示步数了,这里最复杂的应该是找到文本要画在哪,也就是所谓的基线。
首先找到起始位置这个是很好理解的。

//6.3画文本 重点是找到文本的基线String  stepText=mCurrentStep+"";Rect textBounds=new Rect();mTextPaint.getTextBounds(stepText,0,stepText.length(),textBounds);//找到起始位置int dx=getWidth()/2-textBounds.width()/2;//找到基线Paint.FontMetricsInt fontMetrics=mTextPaint.getFontMetricsInt();int dy=(fontMetrics.bottom-fontMetrics.top)/2-fontMetrics.bottom;int baseLine=getHeight()/2+dy;canvas.drawText(stepText,dx,baseLine,mTextPaint);里插入代码片

接下来,找基线,由于bottom是正数,top是负数,所以要用fontMetrics.bottom-fontMetrics.top,当除以2的时候得到的就是图中的1,减去fontMetrics.bottom(即图中的2)得到dy,这个时候加上getHeight()/2(即图中所指的3),得到的也就是屏幕中线的位置。

7.其他

以上我们已经把圆弧的大致样子都完成了,但是这样子是没有动画效果的,没有灵魂的,这个时候我们就需要实现动画效果,当然我们的动画效果不能写在view里面,我们得在调用这个view的时候在MainActivity中写。

//7.其他 让他动起来 动画效果不能写在这里面 主要是定义 具体实现要在MainActivitypublic synchronized void setStepMax(int stepMax){this.mStepMax = stepMax;}public synchronized void setCurrentStep(int currentStep){this.mCurrentStep = currentStep;// 不断绘制  onDraw()invalidate();}

如何使用

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final QQStepView qqStepView=findViewById(R.id.step_view);qqStepView.setStepMax(40000);//属性动画ValueAnimator valueAnimator= ObjectAnimator.ofFloat(0,10000);valueAnimator.setDuration(1000);//设置动画加载时长valueAnimator.setInterpolator(new DecelerateInterpolator());valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float currentStep=(float)animation.getAnimatedValue();qqStepView.setCurrentStep((int)currentStep);}});valueAnimator.start();}}

paint的使用

为了以防自己忘记,特意把这次用到的paint属性记录下来

        mOutPaint=new Paint();mOutPaint.setAntiAlias(true);//设置是否抗锯齿mOutPaint.setStrokeWidth(mBoderWidth);  //原因就在于setStrokeWidth这个方法,并不是往圆内侧增加圆环宽度的,而是往外侧增加一半,往内侧增加一半。https://blog.csdn.net/u014704469/article/details/41277751mOutPaint.setColor(mOuterColor);mOutPaint.setStrokeCap(Paint.Cap.ROUND);//设置其是圆形样式刷子开始刷的,如果不设置这个,圆环两端就可能是平切样式mOutPaint.setStyle(Paint.Style.STROKE);//Paint.Style.FILL :填充内部Paint.Style.FILL_AND_STROKE :填充内部和描边Paint.Style.STROKE :仅描边

面试易考

1.为什么ScrollView + ListView 会显示不全
(1)首先我们看下scollView是继承FrameLayout,FrameLayout中的onMeasure()是不断循环查找自己的子View如果子View不是GONE的话就调用ViewGroup的measureChildWithMargins方法。
(2)scrollView重新measureChildWithMargins方法并且对他的childHeightMeasureSpec进行设置值,设置他的高度的测量模式heightMode为MeasureSpec.UNSPECIFIED设置完成以后调用了child.measure(childWidthMeasureSpec, childHeightMeasureSpec);方法,紧接着也就是进入了ListView的onMeasure方法。
(3)ListView中的onMeasure()方法第一个判断意思是如果他的Item的总数大于0并且它的高或者宽有一个模式为MeasureSpec.UNSPECIFIED就会执行这个方法,那么正常情况下系统这个判断就是为true,也就是拿到它的一个item的高度childHeight = child.getMeasuredHeight();紧接着它判断它的高度的模式是否为MeasureSpec.UNSPECIFIED显然也是true所以他就是让它的整个的高度等于childHeight 最后完成setMeasuredDimension(widthSize, heightSize);所以始终只拿到了listView里面一个item的高度也就是造成了我们的ListView无法正常显示
2.为什么要extends View而不能extends LinearLayout?
当继承LinearLayout的时候,他不能出来界面,LinearLayout又继承ViewGroup,而默认的ViewGroup 不会调用onDraw方法
原因是因为画的其实是 draw(Canvas canvas)
if (!dirtyOpaque)
onDraw(canvas);
dispatchDraw(canvas);
onDrawForeground(canvas);
dirtyOpaque是false 才行 其实就是由 privateFlags -> mPrivateFlags决定的

final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);

得是这个值它才会给你画呀
接下来看看View和ViewGroup的区别

   protected void computeOpaqueFlags() {    if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {      mPrivateFlags |= PFLAG_OPAQUE_BACKGROUND;       } else {          mPrivateFlags &= ~PFLAG_OPAQUE_BACKGROUND;       }      final int flags = mViewFlags;    if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) ||                (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY ||                (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_OUTSIDE_OVERLAY) {      mPrivateFlags |= PFLAG_OPAQUE_SCROLLBARS;  }else {         mPrivateFlags &= ~PFLAG_OPAQUE_SCROLLBARS;     }  }

这是view中的computeOpaqueFlags()函数,而在viewGroup中很明显ViewGroup中不是他想要的值

private void initViewGroup() {
// ViewGroup doesn't draw by default      if (!debugDraw()) {         setFlags(WILL_NOT_DRAW, DRAW_MASK);    }}导致 mPrivateFlags 会重新赋值

自定义View学习之仿QQ运动步数进度效果相关推荐

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

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

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

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

  3. android 自定义view实现仿QQ运动步数进度效果

    最近公司在策划一个新的项目,原型还没出来,再说这公司人都要走没了,估计又要找工作了,所以必须要学习,争取每个写个关于自定义view方面的,这样几个月积累下来,也能学习到东西,今天就带来简单的效果,就是 ...

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

    文章目录 前言 先看效果图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6e4ddec17933496ea4830fa08d8ffbe5.png?x-oss-pr ...

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

    1. 概述   我记得QQ之前是有一个,运动步数的进度效果,今天打开QQ一看发现没有了.具体效果我也不清楚了,我就按照自己大概的印象写一下,这一期我们主要是熟悉Paint画笔的使用:    2. 效果 ...

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

    1.自定义控件的步骤 2.构造函数有什么用 3.自定义属性有什么用 4.初始化一些画笔放在哪里 5.onmesure()如何写 6.invalide源码分析 1.写一个类继承view或者其他控件 pu ...

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

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

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

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

  9. android 自定义园动画,Android 自定View实现仿QQ运动步数圆弧及动画效果

    在之前的Android超精准计步器开发-Dylan计步中的首页用到了一个自定义控件,和QQ运动的界面有点类似,还有动画效果,下面就来讲一下这个View是如何绘制的. 1.先看效果图 2.效果图分析 功 ...

最新文章

  1. LaneATT调试笔记
  2. A fresh look at the genomes enabled by highly accurate long read SMRT sequencing from PacBio
  3. Rocket - diplomacy - AddressAdjuster
  4. H5跟ios、android交互跟数据对接
  5. 如何仅从SQL Server DateTime数据类型返回日期
  6. 登陆失败bdstoken_解决PanDownload登录提示账号登录失败,获取bdstoken失败问题
  7. C++xor cipher异或密码算法(附完整源码)
  8. 20个简单汇编语言程序_20个简单的Photoshop技巧将使您的照片看起来很棒
  9. centos7上安装phpcms
  10. 【erlang ~ 4 days】 Day # 1.1 History
  11. LoRa协议学习工具
  12. UEditor 配置
  13. Nginx使用memcached外置缓存
  14. SQL 事务(Tran | Transation)与 Try...Catch 的使用
  15. SQL- With as 用法
  16. python编程基础及应用(重庆大学):7-7 计算用户输入句子中的单词数量以及单词平均长度
  17. keil接收别人发的工程打不开_PPT转成PDF发你以后还能转回来编辑吗?
  18. 2021-2026年中国数字化转型市场预测:通过应用场景践行数字化优先策略
  19. 前端面试题总结【持续更新···】
  20. 如何将项目部署到服务器:从选择服务器到维护应用程序的全流程指南

热门文章

  1. STM32 IO 简介
  2. 粒子群算法组卷_概率表示的二进制粒子群算法在组卷中的应用
  3. 利用机器学习算法挖掘群控网络黑产设备
  4. 网络规划设计师 视频笔记
  5. 系统学习机器学习之系统认识
  6. 服务器系统可以重装系统,服务器上可以重装操作系统吗
  7. 常用的一些LDO芯片及使用
  8. 信度效度难度区分度是什么意思_【辨析·干货】“效度、信度、难度和区分度”一起学...
  9. win10注入漏洞跳出计算机,win10操作系统本地提权漏洞
  10. 开源框架Volley的使用《一》