Android 自定义View

Android 自定义View,第1张

1直接在XML文件中定义的 ==》布局文件。

2在XML文件中通过style这个属性定义的 ==》在布局中使用自定义属性样式。

3通过defStyleAttr定义的 ==》在View的构造方法中使用自定义属性样式。

4通过defStyleRes定义的 ==》在View的构造方法中使用自定义样式。

5直接在当然工程的theme主题下定义的 ==》AndroidManifestxml中设置。

1、onMeasure 测量自身,自定义View时重写,定义控件的宽高,常在自定义的View中使用

2、Measure 测量自身,方法不可重写,内部调用onMeasure方法,常在自定义的ViewGroup中使用

3、measureChild 测量某个子View,内部调用Measure方法,常在自定义的ViewGroup中使用

4、measureChildren 测量所有子View,内部调用measureChild方法,常在自定义的ViewGroup中使用

在自定义View的开发中,我们重写测量方法,方法里的传参(widthMeasureSpec,heightMeasureSpec)都是由父类提供的,在自定义ViewGroup的开发中,我们可以根据当前布局的测量参数,为布局内的子控件创建新的测量参数,来控制子View在布局的显示大小

1、layout:指定View新的显示位置,用法:viewlayout(left,top,right,bottom);

2、onLayout:设置View的显示位置,用法:重写该方法,定义View的显示规则

3、requestLayout:强制View重新布局,用法:viewrequestLayout();

onFinishInflate -> onAttachedToWindow -> onMeasure -> onSizeChanged -> onLayout -> onDraw -> onDetachedFromWindow

Android的事件分发可以理解为向下分发,向上回传,类似V字型,V字的左边是事件进行向下分发,如果途中没有进行事件的分发拦截,则事件传递到最底层的View,即是最接近屏幕的View。V字的右边是事件的回传,如果中途没有进行事件的消费,则事件传递到最顶层的View,直至消失。

自定义View一共分为两大类,具体如下图:

对于自定义View的类型介绍及使用场景如下图:

在使用自定义View时有很多注意点(坑),希望大家要非常留意:

View的内部本身提供了post系列的方法,完全可以替代Handler的作用,使用起来更加方便、直接。

主要针对View中含有线程或动画的情况: 当View退出或不可见时,记得及时停止该View包含的线程和动画,否则会造成内存泄露问题

当View带有滑动嵌套情况时,必须要处理好滑动冲突,否则会严重影响View的显示效果。

接下来,我将用自定义View中最常用的 继承View 来说明自定义View的具体应用和需要注意的点

在下面的例子中,我将讲解:

下面我将逐个步骤进行说明:

步骤1:创建自定义View类(继承View类)

特别注意:

步骤2:在布局文件中添加自定义View类的组件及显示

至此,一个基本的自定义View已经实现了,运行效果如下图。

接下来继续看自定义View关于属性自定义的问题:

先来看wrap_content & match_parent属性的区别

如果不手动设置支持 wrap_content 属性,那么 wrap_content 属性是不会生效(显示效果同 match_parent )

padding 属性:用于设置控件内容相对控件边缘的边距;

如果不手动设置支持padding属性,那么padding属性在自定义View中是不会生效的。

绘制时考虑传入的padding属性值(四个方向)。

除了常见的以android:开头的系统属性(如下所示),很多场景下自定义View还需要系统所没有的属性,即自定义属性。

实现自定义属性的步骤如下:

下面我将对每个步骤进行具体介绍

对于自定义属性类型 & 格式如下:

至此,一个较为规范的自定义View已经完成了。

Carson_Ho的github: 自定义View的具体应用

不定期分享关于 安卓开发 的干货,追求 短、平、快 ,但 却不缺深度

每个view的坐标系原点为左上角那个点,水平方向为x轴,右正左负,竖直方向为y轴,下正上负。

canvasdrawColor //绘制区域涂上颜色(设置底色/蒙层)

canvasdrawCircle(float centerX(圆心X坐标),float centerY(圆心Y坐标),float radius(圆的半径,单位像素),Paint paint)

canvasdrawBitmap

canvasdrawRect(float left,float top,float right,float bottom,Paint paint) //画矩形

canvasdrawRect(RecF rect,Paint paint)

canvasdrawRect(Rect rect,Paint paint)

canvasdrawPoint(float x(点X轴坐标),float y(点Y轴坐标),Paint paint)//画点

点的大小 ->paintsetStrokeWidth(width)

点的形状 ->paintsetStrokeCap(cap)

ROUND(圆形),BUTT(平头),SQUARE(方头)

canvasdrawPoints()//批量画点

canvasdrawOval(float left(左边界点),float top(上边界点),float right(右边界点),float bottom(下边界点),Paint paint) //画椭圆

canvasdrawLine(float startX(起点X轴坐标),float startY(起点Y轴坐标),float stopX(终点X轴坐标),float stopY(终点X轴坐标),Paint paint) (setStyle对直线没有影响)

canvasdrawLines(批量画线)

canvasdrawRoundRect(float left,float top,float right,float bottom,float rx(圆角的横向半径),float ry(圆角的纵向坐标),Paint paint)//画圆角矩形

canvasdrawRoundRect(RectF rect,float rx, float ry,Paint paint)

canvasdrawArc(float left, float top, float right, float bottom, float startAngle(起始角度,顺时针为正,逆时针为负), float sweepAngle(弧形划过角度), boolean useCenter(是否连接到圆心), Paint paint) //绘制弧形或扇形 根据弧形所在椭圆进行绘制

canvasdrawPath() //通过描述路径的方式来绘制图形

pathaddXxx() —添加子图形

pathaddCircle(x,y,radius,dir(路径方向:顺时针/逆时针))

pathxxxTo —画线

pathlineTo()

pathrLineTo()

pathclose() —封闭当前图形

pathsetFillType(PathFillType ft) //设置填充模式

canvasdrawBitmap(Bitmap bitmap,float left,float top,Paint paint);//画bitmap

canvasdrawBitmap(Bitmap bitmap,Rect src,RectF dst,Paint paint)

canvasdrawBitmap(Bitmap bitmap,Rect src,Rect dst,Paint paint)

canvasdrawBitmap(Bitmap bitmap,Matrix matrix,Paint paint)

canvasdrawText(String text,float x(起点x坐标),float y(起点y坐标),Paint paint) //绘制文字

PaintsetStyle //设置绘制模式

FILL 填充模式(默认)

STROKE 画线模式

FILL_AND_STROKE 既画线又填充

PaintsetStrokeWidth //设置线条宽度 (仅在style:Stroke、FILL_AND_STROLE下有效)

PaintsetTextSize //设置文字大小

PaintsetAntiAlias //设置抗锯齿开关

PaintsetTextSize(float textSize)//设置文字大小

PaintsetStrokeJoin(PaintJoin join) //设置拐角的形状

MITER//尖角(默认)

BEVEL//平角

ROUND//圆角

PaintsetStokeMiter(float miter)//设置MITER型拐角的延长线的最大值

设置颜色

直接设置颜色

PaintsetColor(int color)

PaintsetARGB(int a,int r,int g,int b)

PaintsetShader(Shader shader) //设置shader

LinearGradient 线性渐变

RadialGradient 辐射渐变

SweepGradient 扫描渐变

BitmapShader 用bitmap的像素来作为图形或文字的填充

ComposeShader 混合着色器,多个shader混合使用

PaintsetColorFilter(ColorFilter colorFilter) //设置颜色过滤

PaintsetXfermode(Xfermode xfermode) //以要绘制的内容为源图像,以View中已有内容作为目标图像,选取一个PorterDuffMode作为绘制内容的颜色处理方案。

色彩优化

PaintsetDither(boolean dither) //设置抖动来优化色彩深度降低时的绘制效果

PaintsetFilterBitmap(boolean filter) //设置双线性过滤优化Bitmap放大绘制的效果

可以理解为 由马赛克变成模糊状态

PaintsetPathEffect(PathEffect effect)//使用PathEffect设置形状的轮廓效果

CornerPathEffect//把所有的拐角变成圆角

DiscretePathEffect//把线条进行随机的偏离

DashPathEffect//使用虚线

PathDashPathEffect//使用一个Path来绘制虚线

SumPathEffect//组合效果

ComposePathEffect//组合效果,组合有先后顺序

PaintsetShadowLayer(float radius,float dx,float dy,int shadowColor)//添加阴影

PaintsetMaskFilter(MaskFilter maskfilter)//在绘制层上方的附加效果

BlurMaskFilter //模糊效果

new BlurMaskFilter(float radius(模糊范围),BlurMaskFilterBlur style(模糊类型))

EmbossMaskFilter//浮雕效果

new EmbossMaskFilter(float[] direction(光源的方向),float ambient(环境光强度),float specular(炫光系数),float blurRadius(光线范围))

获取绘制的Path

getFillPath(Path src,Path dst)//实际path

getTextPath(Stirng text,int start,int end,float x,float y,Path)/getTextPath(char[] text,int index,int count,float x,float y,Path path)//文字的path

drawTextOnPath()//沿一条Path来绘制文字

StaticLayout //绘制文字,支持换行

paintsetFakeBoldText(booleab fakeBoldText)//是否使用伪粗体

paintsetStrikeThruText()//是否加删除线

paintsetUnderLineText(boolean underlineText)//是否加下划线

paintsetTextSkewX(float skewX)//设置文字横向错切角度

paintsetTextScaleX(float scaleX)//设置文字横向放缩

paintsetLetterSpacing(float letterSpacing)//设置字符间距,默认为0

paintsetTextAlign(PaintAlign align)//LEFT、CENTER、RIGHT默认为LEFT

paintsetTextLocale(Locale locale)/paintsetTextLocales(LocaleList locales) //设置绘制所用的地域

paintsetHinting(int mode)//是否启用字体微调

测量文字尺寸类:

paintgetFontSpacing();//获取推荐的行距

paintgetFontMetrics();//获取point的FontMetrics

baseline:基准线

ascent/descent:普通字符的顶部和底部范围

top/bottom:限制字型的顶部和底部

leading:行的额外间距,即上一行字的bottm与下一行字的top距离

paintgetTextBounds(String text(测量的文字),int start(文字的起始位置),int end(文字的结束位置),Rect bounds(文字显示范围的对象))//获取文字的显示范围

paintmeasureText(String text)//测量文字占用的宽度

measureText()>getTextBounds()

paintgetTextWidths(String text,float[] widths)//获取字符串中每个字符的宽度,并把结果填入参数widths

paintbreakText(String text((要测量的文字),boolean measureForwards(测量的方向),float maxWidth(宽度上限(超出上限会截断文字)),float[] measuredWidth(用于接受数据))//测量完成后会把文字宽度赋给measureWidth[0]

paintgetRunAdvance(CharSequence text,int start(文字的起始坐标),int end(文字的结束坐标),int contextStart(上下文的起始坐标),int ContextEnd(上下文的结束坐标),boolean isRtl(文字的方向),int offset(字数的偏移))//计算某个字符处光标的x坐标

paintgetOffsetForAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)//计算出文字中最接近这个位置的字符偏移量

painthasGlyph(String s)//检查指定的字符串是否是一个单独的字型

canvasclipRect()//范围裁剪

canvasclipPath()//根据范围裁剪

canvastranslate(float dx,float dy)//位移

canvasrotate(float degrees,float px,float py)//旋转

canvasscale(float sx(横向缩放倍数),float sy(纵向缩放倍数),float px,float py)//缩放

canvasskew(float sx(x轴的错切系数),float sy(y轴的错切系数))//错切

canvassetMatrix(matrix)//用Matrix直接替换Canvas当前的变换矩阵

canvasconcat(matrix)//用Canvas当前的变换矩阵和Matrix相乘

Camerarotate()//三维旋转

1、superdraw()//总调度方法

2、superonDraw()

3、dispatchDraw()//绘制子View的方法

绘制顺序:

draw()总调度方法,view的绘制过程都发生在draw()方法里

1、背景(drawBackground()不能重写)-------android:background:/ViewsetBackgroundXxx()

2、主体(onDraw())

3、子View(dispatchDraw())

4、滑动边缘渐变和滑动条(onDrawForeground())-------android:scrollbarXxx/ViewsetXXXScrollBarXXX()

5、前景(onDrawForeground())-------android:foreground/ViewsetForeground()

viewanimate()translationX()//x轴偏移

1、如果是自定义控件,需要添加setter、getter方法

2、ObjectAnimatorofXXX()创建ObjectAnimator对象

3、用start()方法执行动画

setDuration(int duration)//设置动画时长

setInterpolator(Interpolator interpolator)//设置插值器

ViewPropertyAnimatorsetListener()/ObjectAnimatoraddListener()

ViewPropertyAnimatorsetUpdateListener()/ObjectAnimatoraddUpdateListener()

ObjectAnimatoraddPauseListener()

ViewPropertyAnimatorwithStartAction/EndAction()

ArgbEvaluator//颜色渐变动画

PropertyValuesHolder//同一个动画中改变多个属性

PropertyValuesHoldersofKeyframe()//把同一个属性拆分

AnimatorSet//多个动画配合执行

targetSdkVersion>=14,硬件加速默认开启

viewsetLayerType()

LAYER_TYPE_SOFTWARE:使用软件来绘制View Layer,绘制到Bitmap,并顺便关闭硬件加速

LAYER_TYPE_HARDWARE:使用GPU来绘制View Layer,绘制到OpenGL texture(如果硬件加速关闭,那么行为和LAYER_TYPE_SOFTWARE一致)

LAYER_TYPE_NONE:关闭View Layer

View Layer可以加速无invalidate()(例如动画)时的刷新效率,但对于需要调用invalidate()的刷新无法加速

硬件加速并不支持所有的绘制 *** 作

1、测量(measure)

View:View在onMeasuer中会计算自己的尺寸然后保存

ViewGroup:ViewGroup在onMeasure中会调用所有子View的measure让它们进行自我测量,并根据子View

计算出的期望尺寸来计算他们的事迹尺寸和位置然后保存。

2、布局(layout)

View:无子View所以onLayout不做任何处理

ViewGroup:ViewGroup在onLayout中会调用自己所有子View的layout方法,把他们的尺寸、位置传给他们, 让他们完成自我布局。

MeasureSpec = mode + size :父类传递过来给当前View的一个建议值

MeasureSpecgetMode(int spec)//获取模式

MeasureSpecgetSize(int spec)//获取数值

限制分类:

UNSPECIFIED(不限制)

AT_MOST(限制上限)->wrap_content

EXACTLY(限制固定值)->match_parent/具体值

1、重写onMeasure来修改已有的View尺寸

(1)、重写onMeasure方法,调用superonMeasure触发原有的自我测量。

(2)、在superonMeasure下用getMeasureWidth与getMeasureHeigh获取之前测量的结果,使用自己的算法计算新结果。

(3)、调用setMeasureDimension保存新结果。

2、重写onMeasure来全新定制自定义View的尺寸

与1区别,保证计算的同时,保证结果满足父View给出的尺寸限制

(1)重写onMeasure,计算出View的尺寸

(2)使用resolve让子View的计算结果符合父View的限制,也可不使用该方法自己定义

3、重写onMeasure和onLayout来全新定制自定义ViewGroup的内部布局

两个注意点:

子控件间的margin值

1、重写generateLayoutParams()和generateDefaultLayoutParams()

2、获取margin值 MarginLayoutParams lp = (MarginLayoutParams )childgetLayoutParams()

子控件间的padding值

1、测量后直接getPaddingLeft、getPaddingTop、getPaddingRight、getPaddingBottom

重写onMeasure来计算内部布局

(1)调用每个子View的measure来计算子View的尺寸

结合layout_xxx和自己可用空间

(2)计算子View的位置并保存子View的尺寸和位置

(3)计算自己的尺寸并用setMeasureDimension保存

重写onLayout来摆放子View

(1)调用每个子View的layout,让他们保存自己的位置和尺寸

view工作原理

触摸事件

1、ACTION_DOWN:手指刚接触屏幕,按下去的那一瞬间

2、ACTION_MOVE:手指在屏幕上移动

3、ACTION_UP:手指从屏幕上松开的瞬间

事件序列:从ACTION_DOWN -> ACTION_UP

ViewGroup:

DispatchTouchEvent

• return true:表示该View内部消化掉了所有事件

• return false:表示事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法进行消费

• return superdispatchTouchEvent(ev):默认事件将分发给本层的事件拦截onInterceptTouchEvent方法 进行处理

OnInterceptTouchEvent

• return true:表示将事件进行拦截,并将拦截到的事件交由本层控件的onTouchEvent进行处理

• return false:表示不对事件进行拦截,事件得以成功分发到子View

• return superonInterceptTouchEvent(ev):默认表示不拦截该事件,并将事件传递给下一层View的 dispatchTouchEvent

OnTouchEvent 默认false

• return true:表示onTouchEvent处理完事件后消费了此次事件

• return fasle:表示不响应事件,那么该事件将会不断向上层View的onTouchEvent方法传递,直到某个View的 onTouchEvent方法返回true

• return superdispatchTouchEvent(ev):表示不响应事件,结果与return false一样

子View不存在分发:

• DispatchTouchEvent 事件分发

• OnTouchEvent 默认true

如下图为事件分发流程图:

---------------------- 以上总结部分源自Hencoder教程 ------------------------------

看源码public interface AttributeSet,它是一个接口。

看源码注释:它是属性的集合,也就是说我们在xml定义的属性,解析后会存在这个类里,那我们通过这个类就是获取到相应的属性,对应的方法是ResourcesThemeobtainStyledAttributes()。自定义view时,如果我们有自定义属性,你应该会对这个方法很熟悉。所以说,这个类一般是系统调用的,如果我们自己调用,就需要手动检查相关的资源信息,并且需要自己做一些健壮性的判断。

一 什么是自定义XML属性

在我们使用自定义的控件时,很多时候都需要定义一些不同于一般的XML属性前缀(如android:layout_width)的属性,比如这样 app:textColor,这些就是自定义控件需要用到的自定义控件属性。

二 自定义XML属性有什么用

自定义XML属性的作用在于,在采取自定义的控件时,很多时候,系统的一般XML属性已经不能满足需求,比如我们在做一个具有描边效果的TextView时,就需要有额外定义的TextView外边框颜色和TextView内部颜色两种颜色。这时候,使用自定义XML属性,用户就可以很方便地在XML中配置额外的属性。

三 怎么使用自定义XML属性

1定义对应的属性

在values文件夹下新建一个attar_customxml文件:

<xml version="10" encoding="utf-8">

<resources>

<!-- 自定义控件的名称 -->

<declare-styleable name="StrokeTextView">

<!-- 自定义的属性名称 和对应的单位 -->

<attr name="outerColor" format="color|reference" />

<attr name="innnerColor" format="color|reference" />

</declare-styleable>

</resources>

2在XML中定义自定义属性

<RelativeLayout xmlns:android=">

(1)定义位置

定义在 res/values/ 文件夹下,文件名随意取,以 xml 为扩展名。 style 定义在和 layout 不同的 xml 文件中。

(2)定义内容

① 定义 style 的 xml 文件必须以<resources>为根节点

<resources> 下的每个子元素在 编译期 将转化为应用资源对象,通过<style>标签中 name 属性的 value 值可以引用它们,形如 @style/myStyleName 。

② <style>标签的 name 属性唯一指定

③ style 的继承

继承系统 style

继承自定义属性,以下两种方式均可。

通过 链接names 的方式继承自定义属性

④ style 中可用属性

查看类参考 Android官方文档

(1)应用于单一 View

在 xml 布局中,View 节点上增加 style 属性

(2)应用于整个 Activity 或 Application

你把获取的宽高的代码放到onDraw里就对了,因为View在构造函数初始化并未布局处理,此时宽高均为0,待所有控件初始化完毕后,由上级容器对内部各控件进行布局,此时控件才会具有位置与大小属性

以上就是关于Android 自定义View全部的内容,包括:Android 自定义View、Carson带你学Android:手把手教你写一个完整的自定义View、Android自定义控件总结等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9740823.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-01
下一篇2023-05-01

发表评论

登录后才能评论

评论列表(0条)

    保存