前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >笔记——自定义View(三)

笔记——自定义View(三)

作者头像
木溪bo
发布2018-12-27 10:12:24
4280
发布2018-12-27 10:12:24
举报

《个人平时笔记,看到的同学欢迎指正错误》

1、在自定义View中,drawArc()是绘制弧形或者扇形的,drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) ,绘制角度以X轴正方向即正右方为0度位置,顺时针为绘制正角度,逆时针为负;useCenter是否连接圆心,连接为绘画扇形,不连接则绘制弧形。

path.AddCircle(x,y, radius, dir)+canvas.drawPath(path,paint)这种写法,和直接使用canvas.drawCircle(x, y, radius,paint)的效果是一样的,区别只是它的写法更复杂。所以如果只画一个圆,没必要用Path,直接用drawCircle()就行了。drawPath()一般是在绘制组合图形时才会用到的。

2、在自定义view中:插值器和估值器的关系:估值器依赖于插值器,一般依赖于系统给的默认插值器,插值器返回的结果值就是重写的估值器中public Object   evaluate(float fraction, Object startValue, Object endValue)方法中的fraction,插值器动态改变fraction从而影响改变估值器逻辑运算的返回值,形成一个动画运动。如:该文插值器与估值器详解 https://www.jianshu.com/p/2f19fe1e3ca1

3、MeasureSpec封装了父布局ViewGroup传递给子View的布局要求。是要求而并非是强制的,如在子View的onMeasure()还是可以设置setMeasuredDimension(Width, Height);

MeasureSpec通常翻译为”测量规格”,它是一个32位的int数据,其中高2位代表SpecMode即某种测量模式,低30位为SpecSize代表在该模式下的规格大小。 对于顶级View(即DecorView)和普通View来说,MeasureSpec的转换过程略有不同。对于DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定;对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定,MeasureSpec一旦确定后,onMeasure中就可以确定View的测量宽/高

链接:https://www.jianshu.com/p/cf5092fa269https://blog.csdn.net/lfdfhl/article/details/51347818

自定义View测量值的几个Modle.png

当View采用固定宽/高的时候,不管父容器的MeasureSpec是什么,View的MeasureSpec都是精确模式并且其大小遵循Layoutparams中的大小。当View的宽/高是match_parent时,如果父容器的模式是精准模式,那么View也是精准模式并且其大小是父容器的剩余空间;如果父容器是最大模式,那么View也是最大模式并且其大小不会超过父容器的剩余空间。当View的宽/高是wrap_content时,不管父容器的模式是精准还是最大化,View的模式总是最大化并且大小不能超过父容器的剩余空间。UNSPECIFIED这个模式主要用于系统内部多次Measure的情形,一般来说,我们不需要关注此模式。

MeausreSpec1.png

MeausreSpec2.png

ViewGroup的measure()-->onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),这之中会遍历子View然后循环调用measureChild(),这之中会通过getChildMeasureSpec()方法中父ViewGroup的MeasureSpec+子View的LayoutParams一起获取本子View最终生成的MeasureSpec,然后调用子View的child.measure(childWidthMeasureSpec,

childHeightMeasureSpec)到View的onMeasure()-->setMeasureDimension(getDefaultSize(),getDefaultSize())这样一个流程,getDefaultSize()默认返回measureSpec的测量数值,所以继承View进行自定义的wrap_content需要重写。

MeausreSpec3源码.png

结合MeausreSpec1.png图发现一个问题:在该图的最后一行,如果子View在XML布局文件中对于大小的设置采用wrap_content,那么不管父ViewGroup的specMode是MeasureSpec.AT_MOST还是MeasureSpec.EXACTLY对于子View而言系统给它设置的specMode都是MeasureSpec.AT_MOST,并且其大小都是parentLeftSize即父ViewGroup目前剩余的可用空间。这时wrap_content就失去了原本的意义,变成了match_parent一样了,所以自定义View在重写onMeasure()的过程中应该手动处理View的宽或高为wrap_content的情况。 这个在《Android开发艺术探索》4.3.1节中完美解释 第一种情况:如果在xml布局中View的宽和高均用wrap_content.那么需要设置View的宽和高为mWidth和mHeight. 第二种情况:如果在xml布局中View的宽或高其中一个为wrap_content,那么就将该值设置为默认的宽或高,另外的一个值采用系统测量的specSize即可,代码中设置如下,其中给mWidth、mHeight在自定义view中设定默认值: protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec , heightMeasureSpec);    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);    int widthSpceSize = MeasureSpec.getSize(widthMeasureSpec);    int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);    int heightSpceSize=MeasureSpec.getSize(heightMeasureSpec);  if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){          setMeasuredDimension(mWidth, mHeight);  }else if(widthSpecMode==MeasureSpec.AT_MOST){          setMeasuredDimension(mWidth, heightSpceSize);  }else if(heightSpecMode==MeasureSpec.AT_MOST){          setMeasuredDimension(widthSpceSize, mHeight); } }


MeasureSpec.png

上图红框实际不可能出现原因: (1) 不可能出现根View的大小为wrap_content但它的一个子View大小为match_parent。 (2) 从根到这个子View的父ViewGroup都是wrap_content,而子View的大小为match_parent。这个极端情况也是不会的,可见情况1的分析. (3)从根到这个子View的父ViewGroup都是wrap_content,而子View大小也为wrap_content。这是个正常情况也正是我们改良后的onMeasure()来专门处理的子View大小为wrap_content的情况。

4、getWidth方法是在layout方法完成后才有的值,所以说在自定义控件的时候在onLayout方法中一般采用getMeasuredWidth来获得控件的宽度,因为getMeasuredWidth在measure后就有了值,而getWidth在layout才有了值。除了onLayout方法中采用getMeasuredWidth方法外在其之外的其他地方一般采用getWidth方法来获取控件的宽度。

Android开发之getMeasuredWidth和getWidth区别从源码分析 https://blog.csdn.net/dmk877/article/details/49734869/

宽高区别.png

5、在自定义View中加载图片资源Bitmap时:我们可以通过设置绘制区域来控制显示的图片位置以及大小。

如下:由变量w,h来控制绘制结束的矩形dst右下角终点坐标,矩形dst的区域显示图片资源

protected void onDraw(Canvas canvas) { super.onDraw(canvas);    mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash6);    // 指定图片绘制区域,这里设置与图片参数大小一致,绘制完整图片     Rect src =new Rect(0,0,mBgBitmap.getWidth(),mBgBitmap.getHeight());      //绘制区域     Rect dst =new Rect(0,0,w,h);     // 绘制图片     canvas.drawBitmap(mBgBitmap, src, dst,null); }

6、scrollBy()内部是scrollTo()实现的累加位移,scrollTo()是相对于初始位置做的移动,注意是初始位置。

scrollTo()和scrollBy()时传入的x,y为正值是反常的不是我们料想的那样,以左上角为圆点“右正左负,上负下正”,这是因为源码中如下

scrollerby

使用scrooler来控制view的移动,实则是view自己让自己移动的。

调用invalidate()刷新界面,从而再次回到computeScroll(),回到在computeScroll()继续处理滑动事件。假如View的滑动已经停止了那就没有必要再次执行invalidate()了。说到底,不是Scroller让View发生了滚动而是View自己在滚动。只不过在这个过程中Scroller在不停地追踪View的滚动,而且提供了许多的辅助而已,比如:可以提供偏移量,耗时,当前位置等等信息。

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

原文:https://blog.csdn.net/lfdfhl/article/details/53143114

代码如下

scroll.png

scroller.png

7、官方不推荐通过该无参的构造方法生成一个canvas。如果要这么做那就需要调用setBitmap()为其设置一个Bitmap。为什么Canvas非要一个Bitmap对象呢?原因很简单:缺少一个载体,Canvas需要一个Bitmap对象来保存像素,如果画的东西没有地方可以保存,又还有什么意义呢?

8、View绘制分三个步骤,顺序是:onMeasure,onLayout,onDraw。经代码亲测,log输出显示:调用invalidate方法只会执行onDraw方法;调用requestLayout方法只会执行onMeasure方法和onLayout方法,并不会执行onDraw方法。所以当我们进行View更新时,若仅View的显示内容发生改变且新显示内容不影响View的大小、位置,则只需调用invalidate方法;若View宽高、位置发生改变且显示内容不变,只需调用requestLayout方法;若两者均发生改变,则需调用两者,按照View的绘制流程,推荐先调用requestLayout方法再调用invalidate方法。

invalidate和postInvalidate:invalidate方法只能用于UI线程中,在非UI线程中,可直接使用postInvalidate方法,这样就省去使用handler的烦恼。

activity和view都有onSaveInstanceState、onRestoreInstanceState,在activity中异常终止情况下这两个方法都会执行onSaveInstanceState->onDestory->onCreate->onRestoreInstanceState,而在按Home键或者启动新Activity仍然会单独触发onSaveInstanceState。

9、如若非使用Relativelayout,一般自定义组件的时候不会去基础RelativeLayout,因为它会进行两次绘制;故在能实现相同功能需求时更多的使用LinearLayout和FrameLayout。

总结:LinearLayout和RelativeLayout的性能差别主要体在onMeasure方法上,RelativeLayout始终要从竖直和水平两个方向对子View进行测量,而Linearlayout,当我们没有在子View中使用layout_weight属性时,LinearLayout只需对子View进行一次测量,反则需要对子View进行两次测量以确定最终大小,所以如果可以我们尽量少用layout_weight属性。在使用这两个布局之前,我们可以先进行衡量,如果需要实现的布局嵌套层次不深或者嵌套层次已经固定了,可以考虑用LinearLayout,相对的,如果某个布局嵌套层次很深,此时应该考虑使用RelativeLayout来减少嵌套层析从而优化布局的性能。

--------------------原文:https://blog.csdn.net/su_1106941640/article/details/53026208

10、getMeasuredWidth和getWidth:在View的默认实现中,View的测量宽/高和最终宽/高一般情况下是相等的,只不过测量宽/高形成于View的measure过程,而最终宽/高形成于View的layout过程,即两者的赋值时机不同,测量宽/高的赋值时机稍微早一些。

绘制过程.png

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.12.10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档