目錄
一、前言
二、基礎(chǔ)代碼
(1)自定義View基礎(chǔ)流程
(2)onDraw()方法
(3)onMeasure()方法
三、動(dòng)畫
(1)補(bǔ)間動(dòng)畫
(2)幀動(dòng)畫
(3)屬性動(dòng)畫
四、SVG標(biāo)簽使用
五、練習(xí)Demo
六、Demo地址
七、內(nèi)容推薦
一、前言
自定義控件一直是Android很重要的一部分,但是大部分時(shí)間我們都在處理業(yè)務(wù)邏輯而很少自己去寫控件。因?yàn)楝F(xiàn)在開源的控件比較多,自定義寫起來也比較麻煩。但是當(dāng)我們需要的時(shí)候可能就忘了,所以菜鳥作者就買了本書打算系統(tǒng)的學(xué)習(xí)一遍。這一篇也算是《Android 自定義控件開發(fā)入門與實(shí)戰(zhàn)》讀后感。邊學(xué)邊工作也花了一個(gè)多月寫過幾篇筆記,不過后面的隱藏掉了。感覺有點(diǎn)啰嗦所以就湊成這一篇總結(jié)。
不管感興趣與不感興趣可以瀏覽一遍,積累一下基礎(chǔ)。如果要深入學(xué)習(xí)這里也推薦兩位大神博客
http://www.itdecent.cn/p/146e5cec4863
**https://blog.csdn.net/harvic880925 **啟艦《Android 自定義控件開發(fā)入門與實(shí)戰(zhàn)》

二、基礎(chǔ)代碼
《 自定義控件開發(fā)入門與實(shí)戰(zhàn)》這本書我這里主要分成兩部分總結(jié):1.自定義View 2.動(dòng)畫
這里主要根據(jù)練習(xí)時(shí)候?qū)懙腄emo來進(jìn)行基礎(chǔ)的回顧。先從自定義View開始
(1)自定義View基礎(chǔ)流程
- 1.繼承View,或者繼承ViewGroup,以及View/ViewGroup的派生類
- 2.測量 :onMeasure(),用來控制自定義View的寬高
- 3.位置:onLayout(),在繼承ViewGroup的時(shí)候需要重寫該方法,用來控制子View擺放的位置。
- 4.繪圖:onDraw(),自定義View的主要方法,需要重載或重寫該方法來繪制所需要的控件。
先從簡單的開始講起:
(2)onDraw()方法
為什么先說這方法呢 ? 因?yàn)榍懊鎯蓚€(gè)方法不說。我們也可以通過這個(gè)方法來簡單的實(shí)現(xiàn)自定義控件。
先上菜(代碼),在介紹這道菜的好處
fun initPaint(){
//創(chuàng)建畫筆
paint = Paint()
//設(shè)置畫筆顏色
paint?.setColor(Color.WHITE)
//設(shè)置填充樣式 1.Paint.Style.FILL僅填充內(nèi)部 2.Paint.Style.FILL_AND_STROKE填充內(nèi)部和描邊 3.Paint.Style.STROKE僅描邊
paint?.setStyle(Paint.Style.STROKE)
//設(shè)置畫筆寬度 注:畫筆樣式為Paint.Style.FILL時(shí)不顯示效果
paint?.setStrokeWidth(5f)
paint1 = Paint()
//設(shè)置畫筆顏色
paint1?.setColor(Color.GREEN)
//設(shè)置填充樣式 1.Paint.Style.FILL僅填充內(nèi)部 2.Paint.Style.FILL_AND_STROKE填充內(nèi)部和描邊 3.Paint.Style.STROKE僅描邊
paint1?.setStyle(Paint.Style.STROKE)
//設(shè)置畫筆寬度 注:畫筆樣式為Paint.Style.FILL時(shí)不顯示效果
paint1?.setStrokeWidth(5f)
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
//平移畫布 起始點(diǎn)起始點(diǎn)從(X:0,Y:0)變成(X:100,Y:100)
canvas?.translate(0f, 100f)
//裁剪畫布 構(gòu)造方法很多 都是以clip開頭
canvas?.clipRect(0, 0, 500, 500)
//設(shè)置畫布顏色
canvas?.drawColor(Color.BLACK)
// canvas.drawARGB(0,0,0,255);
// canvas.drawRGB(0,0,255);
// 繪制直線 startX/startY:起始點(diǎn)X/Y坐標(biāo) stopX/stopY:終點(diǎn)X/Y坐標(biāo)
canvas?.drawLine(100f, 50f, 450f, 50f, paint)
//繪制點(diǎn)
canvas?.drawPoint(50f, 50f, paint)
//繪制矩形====== >
val rect = Rect(50, 100, 450, 450)//創(chuàng)建矩形工具類
//繪制矩形方法
canvas?.drawRect(rect, paint)//第一種構(gòu)造方法
// Rect/RectF用來保存int/float類型數(shù)值的矩形結(jié)構(gòu)
// RectF rectf = new RectF(200,200,400,400);
// canvas.drawRect(rectf);第二種構(gòu)造方法
// canvas.drawRect(200,200,400,400,paint);第三種構(gòu)造方法
// <====== 繪制矩形方法
// 起始點(diǎn)變成(X:50,Y:100)
canvas?.translate(50f, 100f)
//繪制路徑 ====== >
val path = Path()
//設(shè)置起始點(diǎn)
path.moveTo(100f, 50f)
//第一條直線的終點(diǎn)也是第二條直線的起點(diǎn)
path.lineTo(50f, 200f)
path.lineTo(150f, 100f)
path.lineTo(50f, 100f)
path.lineTo(150f, 200f)
// path.lineTo(100, 50);
//閉環(huán)
path.close()
//繪制路徑方法
canvas?.drawPath(path, paint)
// < ======繪制路徑
//繪制弧線 ===== >
val path1 = Path()
val rect1 = RectF(200f, 50f, 350f, 200f)
//弧線主要方法 oval 生成橢圓的矩形,startAngle 弧開始角度, sweepAngle 持續(xù)角度
path1.arcTo(rect1, 180f, 180f, false)
canvas?.drawPath(path1, paint)
// < ===== 繪制弧線
// 起始點(diǎn)變成(X:50,Y:100)
canvas?.translate(50f, 250f)
//繪制區(qū)域 ==== >
val region = Region(0, 0, 200, 50)
val region1 = Region(150, -50, 300, 50)
drawRegion(canvas, region, paint)
drawRegion(canvas, region1, paint1)
//區(qū)域操作
// (1)Op.DIFFERENCE:顯示region與region1不同區(qū)域
// (2)Op.INTERSECT:顯示region與region1相交區(qū)域
// (3)Op.UNION:顯示region與region1組合在一起區(qū)域
// (4)Op.XOR:顯示region與region1相交之外區(qū)域
// (5)Op.REVERSE_DIFFERENCE:顯示region1與region不同區(qū)域
// (6)Op.REPLACE:顯示region1區(qū)域
region.op(region1, Region.Op.INTERSECT)
paint1?.setColor(Color.GRAY)
drawRegion(canvas, region, paint1)
// < ==== 繪制區(qū)域
//保存當(dāng)前畫布狀態(tài)
canvas?.save()
//恢復(fù)到上一層保存的狀態(tài)
// canvas.restore();
}
fun drawRegion(canvas : Canvas?,region : Region,paint: Paint?){
var iter = RegionIterator(region)
var r = Rect()
while (iter.next(r)){
canvas?.drawRect(r,paint)
}
}
看完代碼我們知道,繪圖的前提是先準(zhǔn)備好一只畫筆(paint),要準(zhǔn)備一只怎樣的畫筆呢。這時(shí)候我們可以根據(jù)Paint 類提供的API來定義畫筆樣式。
畫筆準(zhǔn)備好之后,我們就要考慮繪制什么樣的圖形呢,要繪制在什么地方。這時(shí)候我們需要一張畫布,用來顯示繪制的圖形。
而onDraw(canvas: Canvas?)方法剛好提供了一個(gè)畫布,并且通過Canvas類提供的API,我們可以繪制各式各樣的圖形。當(dāng)然常用的繪圖方法就看上面的代碼。都用詳細(xì)的注釋。
總結(jié):1.準(zhǔn)備畫筆 2.利用畫布繪圖
注意:通常要在自定義View的構(gòu)造函數(shù)中初始化好畫筆,不要在onDraw()方法中去初始畫筆。因?yàn)槊看嗡⑿庐嫴嫉臅r(shí)候都會(huì)不斷調(diào)用ondraw()創(chuàng)建新的畫筆,消耗內(nèi)存....
(3)onMeasure()方法
如果要通過下面屬性來控制自定義View的大小時(shí),我們需要重載onMeasure()方法來控制畫布(自定義View)的大小,
android:layout_width="200dp"
android:layout_height="250dp"
怎么控制呢。
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//widthMeasureSpec/heightMeasureSpec 由mode+size兩部分組成
//獲取模式
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
//獲取數(shù)值
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
when(widthMode){
//父元素決定子元素的確切大小,子元素將被限定在給定的邊界里而忽略它本身的大小 :match_parent 或者具體指 200dp
MeasureSpec.EXACTLY ->{
//根據(jù)需求計(jì)算寬度大小
testWidth = widthSize - 100
}
//子元素至多達(dá)到指定大小的值 : wrap_content
MeasureSpec.AT_MOST ->{}
//父元素不對子元素施加任何束縛,子元素可以得到任意想要的大小
MeasureSpec.UNSPECIFIED ->{}
}
when(heightMode){
MeasureSpec.EXACTLY ->{
//根據(jù)需求計(jì)算高度大小
testHeight = heightSize - 100
}
MeasureSpec.AT_MOST ->{}
MeasureSpec.UNSPECIFIED ->{}
}
//計(jì)算出的寬度和高度 可以通過setMeasuredDimension方法進(jìn)行設(shè)置
setMeasuredDimension(testWidth!!, testHeight!!)
}
通過上面的代碼我們可以簡單的計(jì)算出所需要的寬高。
只通過上面簡單的代碼很難明白測量的意義,所以這里提供一個(gè)博客供大家參考,篇幅太長就不在這里多寫 畢竟是總結(jié)..
https://www.cnblogs.com/yishujun/p/5560838.html
onLayout()方法在這里也不介紹,因?yàn)橹挥迷谧远xViewGroup的時(shí)候才會(huì)使用。邏輯也會(huì)比較復(fù)雜。
三、動(dòng)畫
這本書好幾張都講了動(dòng)畫,不過這里不打算詳細(xì)介紹。自定義View才是重點(diǎn)。所以這邊只貼一部分常用代碼。供回憶用。
(1)補(bǔ)間動(dòng)畫
(代碼使用這里就不貼了)
<!--透明度-->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fromAlpha="0"
android:toAlpha="1"
android:fillAfter = "true"
android:repeatCount = "infinite"
android:repeatMode = "reverse"
android:interpolator = "@android:anim/accelerate_interpolator"
/>
<!--旋轉(zhuǎn)-->
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter = "true"
android:repeatCount = "infinite"
android:repeatMode = "reverse"
android:interpolator = "@android:anim/linear_interpolator"
/>
<!--縮放-->
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fromXScale="0"
android:toXScale="2"
android:fromYScale="0"
android:toYScale="2"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter = "true"
android:repeatCount = "infinite"
android:repeatMode = "reverse"
android:interpolator = "@android:anim/linear_interpolator"
/>
<!--移動(dòng)-->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="1000"
android:duration="3000"
android:fillAfter = "true"
android:repeatCount = "infinite"
android:repeatMode = "reverse"
android:interpolator = "@android:anim/linear_interpolator"
/>
<!--組合動(dòng)畫-->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillAfter="true"
android:interpolator="@android:anim/linear_interpolator"
android:repeatMode="reverse"
>
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:repeatCount="infinite"
/>
<rotate
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="360"
android:repeatCount="infinite"
/>
<scale
android:fromXScale="0"
android:fromYScale="0"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1"
android:repeatCount="infinite"
/>
<translate
android:fromXDelta="0"
android:toXDelta="1000"
android:repeatCount="infinite"
/>
</set>
使用方式:
val loadAnimation = AnimationUtils.loadAnimation(this, R.anim.alpha)
view.startAnimation(TweenAnimation.getAlpha())
(2)幀動(dòng)畫
object FrameAnimation {
/**
* AnimationDrawable常用屬性
* start():開始播放逐幀動(dòng)畫。
* stop():停止播放逐幀動(dòng)畫。
* getDuration(int index):得到指定index的幀的持續(xù)時(shí)間
* getFrame(int index):得到指定index的幀所對應(yīng)的Drawable對象
* getNumberOfFrames():得到當(dāng)前AnimationDrawable的所有幀數(shù)量
* isRunning():判斷當(dāng)前AnimationDrawable是否正在播放
* setOneShot(boolean oneShot):設(shè)置AnimationDrawable是否執(zhí)行一次
* isOneShot():判斷當(dāng)前AnimationDrawable是否執(zhí)行一次
* addFrame(Drawable frame,int duration):為AnimationDrawable添加1幀,并設(shè)置持續(xù)時(shí)間
*/
fun getAnimation(context: Context):AnimationDrawable{
val animationDrawable = AnimationDrawable()
for (x in 1..3){
val resources = context.resources.getIdentifier("frame"+x,"mipmap",context.packageName)
val drawable = context.resources.getDrawable(resources)
animationDrawable.addFrame(drawable,500)
}
animationDrawable.isOneShot = false
return animationDrawable
}
}

使用方式:val animation = FrameAnimation.getAnimation(this)iv_main.setBackgroundDrawable(animation)animation.start()
(3)屬性動(dòng)畫
object ValueAnimation {
/**
* ValueAnimator常用函數(shù)
* ofInt(int... values)、ofFloat(float... values) 、ofObject:設(shè)置動(dòng)畫變化過程中的值
* setDuration(long duration):設(shè)置動(dòng)畫時(shí)長,單位是毫秒
* getAnimatedValue():獲取ValueAnimator在運(yùn)動(dòng)時(shí)當(dāng)前運(yùn)動(dòng)點(diǎn)的值
* start():開始動(dòng)畫
* setRepeatCount(int value):設(shè)置循環(huán)次數(shù),設(shè)置為INFINITE表示無限循環(huán)
* setRepeatMode(int value):設(shè)置循環(huán)模式 ValueAnimation.REVERSE倒序重新開始 ValueAnimation.RESTART正序重新開始
* addUpdateListener(AnimatorUpdateListener listener) : 監(jiān)聽動(dòng)畫過程中值的實(shí)時(shí)變化
* addListener(AnimatorListener listener):監(jiān)聽動(dòng)畫變化時(shí)的4個(gè)狀態(tài)
* removeUpdateListener(AnimatorUpdateListener listener):移除指定監(jiān)聽
* removeAllUpdateListeners():移除所有監(jiān)聽
* removeListener(AnimatorListener listener):移除AnimatorUpdateListener
* removeAllListeners():移除AnimatorListener
* setInterpolator():設(shè)置插值器
* cancel():取消動(dòng)畫
* setStartDelay(long startDelay):延時(shí)多久開始,單位是毫秒
* clone():克隆一個(gè)ValueAnimator實(shí)例
*/
fun init(view : View):ValueAnimator{
val animator = ValueAnimator.ofInt(0, 500)
animator.duration = 3000
animator.repeatCount = ValueAnimator.INFINITE
animator.repeatMode = ValueAnimator.REVERSE
animator.setEvaluator(MyEvaluator)
animator.addUpdateListener({animation ->
val value = animation.animatedValue as Int
view.layout(view.left,view.top,value+view.left,view.bottom)
})
return animator
}
/**
* 開始動(dòng)畫
*/
fun startAnimator(view : View){
val init = init(view)
init.start()
}
/**
* 停止動(dòng)畫
*/
fun stopAnimator(view : View){
val init = init(view)
init.removeAllUpdateListeners()
init.cancel()
}
}
使用方式:ValueAnimation.startAnimator(iv_main)
ObjectAnimation代碼使用:
object ObjectAnimation {
/**
* 組合動(dòng)畫
*/
fun start(tv1 : View){
var background = ObjectAnimator.ofInt(tv1,"BackgroundColor", Color.BLACK,Color.BLUE,Color.RED)
background.setRepeatCount(ValueAnimator.INFINITE)
var alpha = ObjectAnimator.ofFloat(tv1,"alpha",0f,1f,0f,0.5f)
alpha.setRepeatCount(ValueAnimator.INFINITE)
var animatorSet = AnimatorSet()
animatorSet.playTogether(background,alpha)
animatorSet.setDuration(3000)
animatorSet.start()
}
/**
* PropertyValuesHolder 保存了動(dòng)畫過程中所需要操作的屬性和對應(yīng)的值。
* //設(shè)置動(dòng)畫的Evaluator
* public void setEvaluator(TypeEvaluator evaluator)
* //用于設(shè)置ofFloat()所對應(yīng)的動(dòng)畫值列表
* public void setFloatValues(float... values)
* //用于設(shè)置ofInt()所對應(yīng)的動(dòng)畫值列表
* public void setIntValues(int... values)
* //用于設(shè)置ofKeyframes()所對應(yīng)的動(dòng)畫值列表
* public void setKeyframes(Keyframe... values)
* //用于設(shè)置ofObject()所對應(yīng)的動(dòng)畫值列表
* public void setObjectValues(Object... values)
* //設(shè)置動(dòng)畫屬性名
* public void setPropertyName(String propertyName)
*/
fun property(view : View){
val Background = PropertyValuesHolder.ofInt("BackgroundColor", Color.BLACK,Color.BLUE,Color.RED)
val alpha = PropertyValuesHolder.ofFloat("alpha",0f,1f,0f,0.5f)
val holder = ObjectAnimator.ofPropertyValuesHolder(view,Background, alpha)
holder.duration = 3000
holder.interpolator = AccelerateInterpolator()
holder.repeatCount = Animation.INFINITE
holder.repeatMode = REVERSE
holder.start()
}
/**
* KeyFrame 提供方便地控制動(dòng)畫速率問題。
*/
fun keyFrame(view : View){
val holder = PropertyValuesHolder.ofFloat("alpha", 0f,1f)
//fraction表示當(dāng)前的顯示進(jìn)度 value:表示動(dòng)畫當(dāng)前所在的數(shù)值位置。
val keyframe = Keyframe.ofFloat(0.1f, 0.1f)
PropertyValuesHolder.ofKeyframe("alpha",keyframe)
val valuesHolder = ObjectAnimator.ofPropertyValuesHolder(view, holder)
valuesHolder.duration = 3000
valuesHolder.repeatCount = Animation.INFINITE
valuesHolder.repeatMode = REVERSE
valuesHolder.start()
}
/**
* ViewPropertyAnimator Android3.1中新增ViewPropertyAnimator機(jī)制,給默認(rèn)屬性提供了一種更加便捷的用法。
*/
fun viewProperty(view : View){
view.animate().alpha(0.5f).translationX(500f).rotation(180f).setDuration(5000).scaleX(2f)
}
}
使用方式:
R.id.btn_propertyValuesHolder ->{
ObjectAnimation.property(iv_main)
}
R.id.btn_keyFrame ->{
ObjectAnimation.keyFrame(iv_main)
}
R.id.btn_animate ->{
ObjectAnimation.viewProperty(iv_main)
}
objectAnimator文件使用:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueType="floatType"
android:valueFrom="1"
android:valueTo="3"
android:startOffset="0"
android:duration="2000"
android:interpolator="@android:anim/linear_interpolator"
android:propertyName="scaleY"
android:repeatCount="infinite"
android:repeatMode="reverse"/>
使用方式
//xml動(dòng)畫
val loadAnimator = AnimatorInflater.loadAnimator(this, R.animator.object_animation)
loadAnimator.setTarget(iv_main)
loadAnimator.start()
不解釋光貼代碼可能看起來很亂,但動(dòng)畫確實(shí)不是這篇的重點(diǎn),只是書中有提到。所以把練習(xí)時(shí)寫的代碼給貼出來好復(fù)習(xí)。如果看起來很累大家可以跳過這一部分。
四、SVG標(biāo)簽使用
Google在Android 5.0中增加了對SVG圖形的支持。因?yàn)镾VG的占用空間比Bitmap小。所以這里也稍微提一下使用方式。
首先定義SVG標(biāo)簽:
svg_vector.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="50dp"
android:viewportWidth="100"
android:viewportHeight="100"
>
<path
android:name="bar"
android:pathData="M0,23 L60,23 L30,90 Z"
android:strokeWidth="2"
android:strokeColor="@android:color/darker_gray"/>
</vector>
<!--width與height屬性:表示該SVG圖形的具體大小。-->
<!--viewportWidth與viewportHeight屬性:表示SVG圖形劃分的比例。-->
<!--path中字母M表示moveTo,字母L表示lineTo。-->
<!--vector標(biāo)簽指定的是畫布帶下,而path標(biāo)簽則指定的是路徑內(nèi)容-->
<!--(2)path標(biāo)簽-->
<!--android:name:聲明一個(gè)標(biāo)記,類似于ID,便于對其做動(dòng)畫的時(shí)候順利地找到該節(jié)點(diǎn)。-->
<!--android:pathData:對SVG矢量圖的描述。-->
<!--android:strokeWidth:畫筆的寬度。-->
<!--android:fillColor:填充顏色。-->
<!--android:fillAlpha:填充顏色的透明度。-->
<!--android:strokeColor:描邊顏色。-->
<!--android:strokeWidth:描邊寬度。-->
<!--android:strokeAlpha:描邊透明度。-->
<!--android:strokeLineJoin:用于指定折線拐角形狀,取值有miter、round、bevel。-->
<!--android:strokeLineCap:畫出線條的終點(diǎn)形狀,取值有butt/round/square-->
<!--android:strokeMiterLimit:設(shè)置斜角的上限。-->
<!--android:trimPathStart:用于指定路徑從哪開始,取值為0~1,表示路徑開始位置的百分比。-->
<!--android:trimPathEnd:用于指定路徑的結(jié)束位置,取值為0~1,表示路徑結(jié)束位置的百分比。-->
<!--android:trimPathOffset:用于指定結(jié)果路徑的位移距離,取值為0~1,當(dāng)取值為0時(shí),不進(jìn)行位移;當(dāng)取值為1時(shí),位移整條路徑的長度。-->
<!--android:pathData:在path標(biāo)簽中,主要通過pathData屬性來指定SVG圖像的顯示內(nèi)容。-->
<!--pathData屬性除M和L指令以外,還有更多的指令:-->
<!--M = moveto(M X,Y):將畫筆移動(dòng)到指定的坐標(biāo)位置。-->
<!--L = lineto(L X,Y):畫直線到指定的坐標(biāo)位置。-->
<!--H = horizontal lineto(H X):畫水平線到指定的X坐標(biāo)位置。-->
<!--V = vertical lineto(V Y):畫垂直線到指定的Y坐標(biāo)位置。-->
<!--C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三階貝濟(jì)埃曲線。-->
<!--S = smooth curveto(S X2,Y2,ENDX,ENDY):三階貝濟(jì)埃曲線。-->
<!--Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY):二階貝濟(jì)埃曲線。-->
<!--T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射前面路徑后的終點(diǎn)。-->
<!--A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧線。-->
<!--Z = closepath():關(guān)閉路徑。-->
<!--group標(biāo)簽常用屬性:-->
<!--android:name:組的名字,用于與動(dòng)畫相關(guān)聯(lián)-->
<!--android:rotation:指定該組圖像的旋轉(zhuǎn)度數(shù)-->
<!--android:pivotX:定義縮放和旋轉(zhuǎn)改組時(shí)的X參考點(diǎn)-->
<!--android:pivotY:定義縮放和旋轉(zhuǎn)該組時(shí)的Y參考點(diǎn)-->
<!--android:scaleX:指定該組X軸縮放大小-->
<!--android:scaleY:指定該組Y軸縮放大小-->
<!--android:translateX:指定該組沿X軸平移的距離-->
<!--android:translateY:指定該組沿Y軸平移的距離-->
標(biāo)簽與動(dòng)畫綁定在一起
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/svg_vector">
<target
android:name="bar"
android:animation="@animator/svg_animation"/>
</animated-vector>
svg_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="trimPathStart"
android:valueFrom="0"
android:valueTo="1"
android:duration="2000"/>
使用方式:
val create = AnimatedVectorDrawableCompat.create(this, R.drawable.svg_animation)iv_main.setImageDrawable(create)
簡單略過...,一切解釋盡在代碼中

五、練習(xí)Demo
看完這本書后發(fā)現(xiàn)了幾個(gè)有趣的控件。例:放大鏡 ,刮刮卡
這里就不展示代碼了,都寫在練習(xí)的項(xiàng)目當(dāng)中,若有興趣可以下載下來看看。



六、Demo地址
練習(xí)Demo地址Github:https://github.com/DayorNight/CustomView
另外介紹個(gè)人開發(fā)的一個(gè)Android工具類
Github:https://github.com/DayorNight/BLCS
apk下載體驗(yàn)地址:https://www.pgyer.com/BLCS
也可以掃碼下載
七、內(nèi)容推薦
CSDN:《Android 自定義控件基礎(chǔ)》
《Android 數(shù)據(jù)庫知識(shí)回顧》???????
《Android 《Android移動(dòng)性能實(shí)戰(zhàn)》學(xué)習(xí)筆記》
《Android 下載安裝應(yīng)用APK封裝(適配8.0)》
《Android Notification通知簡單封裝(適配8.0)???????》???????
《Android 仿RxDialog自定義DialogFragment》
如果你覺得寫的不錯(cuò)或者對您有所幫助的話
不妨頂一個(gè)【微笑】,別忘了點(diǎn)贊、收藏、加關(guān)注哈
看在花了這么多時(shí)間整理寫成文章分享給大家的份上,記得手下留情哈
您的每個(gè)舉動(dòng)都是對我莫大的支持


?
