先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
正文
点击空白或者直接将 SVG 拖拽指定区域进行转换
- 将转换好的 Android 格式的 vector 导入 AS
- 开始制作动画关联
//1.在 /res/aniamator 文件夹下 定义动画
<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android=“http://schemas.android.com/apk/res/android”
android:propertyName=“trimPathStart”
android:valueFrom=“1”
android:valueTo=“0”
android:duration=“2000”
//2. 在/res/drawable/ 定义 vector
<?xml version="1.0" encoding="utf-8"?>
//3. 在/res/drawable/ 关联动画和 vector
<?xml version="1.0" encoding="utf-8"?>
- 效果
警车灯闪烁
今日头条下拉刷新动画
来一个复杂组合动画,请看下面效果图:
- 准备 vector 数据
- 定义顺时针执行动画并做 pathData 变换
这里拿其中一个位置变化来举例说明:
<?xml version="1.0" encoding="utf-8"?><set xmlns:android=“http://schemas.android.com/apk/res/android”
android:ordering=“sequentially”>//按顺序执行
//依次执行 pathData 位置变换
<objectAnimator
android:duration=“600”
android:interpolator=“@android:interpolator/decelerate_cubic”
android:propertyName=“pathData”
android:valueFrom="
M100,30
L180,30
M180,30
L180,90
M180,90
L100,90
M100,90
L100,30"
android:valueTo="
M100,120
L180,120
M180,120
L180,180
M180,180
L100,180
M100,180
L100,120"
android:valueType=“pathType” />
<objectAnimator
android:duration=“600”
android:interpolator=“@android:interpolator/decelerate_cubic”
android:propertyName=“pathData”
android:valueFrom="
M100,120
L180,120
M180,120
L180,180
M180,180
L100,180
M100,180
L100,120"
android:valueTo="
M20,120
L100,120
M100,120
L100,180
M100,180
L20,180
M20,180
L20,120"
android:valueType=“pathType” />
<objectAnimator
android:duration=“600”
android:interpolator=“@android:interpolator/decelerate_cubic”
android:propertyName=“pathData”
android:valueFrom="
M20,120
L100,120
M100,120
L100,180
M100,180
L20,180
M20,180
L20,120"
android:valueTo="
M20,30
L100,30
M100,30
L100,90
M100,90
L20,90
M20,90
L20,30"
android:valueType=“pathType” />
如果对标签中的定义还不了解的先去看下文章中 path 标签
中的说明。如果不理解标签意思,根本就看不懂。
- 进行关联
<animated-vector xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:tools=“http://schemas.android.com/tools”
android:drawable=“@drawable/ic_toutiao”
tools:targetApi=“lollipop”>
- 代码控制重复执行
class SVGDemo1Activity : AppCompatActivity() {
var reStartTT = @SuppressLint(“HandlerLeak”)
object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
startAnimatabe(R.drawable.line_animated_toutiao, true)
}
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_svg)
//水滴动画
startWaterDropAnimator.setOnClickListener {
startAnimatabe(R.drawable.line_animated_vector, false)
}
//搜索动画
startSearchAnimator.setOnClickListener {
startAnimatabe(R.drawable.line_animated_search, false)
}
//执行警车动画
startPoliceCarAnimator.setOnClickListener {
startAnimatabe(R.drawable.line_animated_car, false)
}
//执行头条动画
startTTAnimator.setOnClickListener {
startAnimatabe(R.drawable.line_animated_toutiao, true)
}
}
private fun startAnimatabe(lineAnimatedVector: Int, isRegister: Boolean): Animatable {
val animatedVectorDrawable = AnimatedVectorDrawableCompat.create(this, lineAnimatedVector)
iv.setImageDrawable(animatedVectorDrawable)
val animatable = iv.drawable as Animatable
animatable.start()
animatedVectorDrawable!!.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable?) {
super.onAnimationEnd(drawable)
if (!isRegister) return
animatedVectorDrawable.unregisterAnimationCallback(this)
//重新开始在 xml 设置 restart 无效暂时用 Handler 实现了。
reStartTT.sendEmptyMessage(0)
}
})
return animatable
}
}
绘制中国地图
该篇之前实现 SVG pathData 都是利用 ImageView 来实现,并不是所有的场合都适合上面的方式,比如我想要实现 pathData 区域点击,那么上面所讲的方式应该是不能实现,下面我们以一个实例来看怎么自定义 View 实现 PathData 和 pathData 区域点击事件。
下面我们利用 path 来绘制一个中国地图,先来看一个最终效果图,如下:
看起来是不是很炫,还不错,嘿嘿,下面我们就来看一下如何实现。
- 准备地图 SVG
-
首先去下载地图数据
-
选择下载免费的地图数据
- 找到对应的国家点击下载 svg 数据
- 选择对应的地图数据,我这里下载的是高质量的 SVG
- SVG to Vector xml
将下载好的 china.svg 格式的文件转为 vector 节点的 xml 数据 或者用 AS 自带转也行,看个人爱好。
转好之后放入 AS 中,如下所示
现在有了这些数据,我们就可以解析 xml path
节点,拿到 pathData
数据我们不就可以绘制 path 了嘛。下面就开始解析 xml ,解析的方法很多种,我们这里用 dom 解析。
- 开始解析 xml
解析 xml 有很多种方式,这里就直接使用 DOM
解析,pathData2Path 我这里直接用 Android SDK 提供的 android.support.v4.graphics#PathParser
由于源码中它被标注了 hide
属性 ,我们需要直接将它 copy 到我们自己项目中, 具体转化请看如下代码:
/**
- 开始解析 xml
*/
public fun dom2xml(stream: InputStream?): MutableList {
mapDataLists.clear()
//dom
val newInstance = DocumentBuilderFactory.newInstance()
val newDocumentBuilder = newInstance.newDocumentBuilder()
//拿到 Docment 对象
val document = newDocumentBuilder.parse(stream)
//获取 xml 中属于 path 节点的所有信息
val elementsByTagName = document.getElementsByTagName(PATH_TAG)
//定义四个点,确定整个 map 的范围
var left = -1f
var right = -1f
var top = -1f
var bottom = -1f
//开始遍历标签,拿到 path 数据组
for (pathData in 0 until elementsByTagName.length) {
val item = elementsByTagName.item(pathData) as Element
val name = item.getAttribute(“android:name”)
val fillColor = item.getAttribute(“android:fillColor”)
val strokeColor = item.getAttribute(“android:strokeColor”)
val strokeWidth = item.getAttribute(“android:strokeWidth”)
val pathData = item.getAttribute(“android:pathData”)
val path = PathParser.createPathFromPathData(pathData)
mapDataLists.add(MapData(name, fillColor, strokeColor, strokeWidth, path))
//获取控件的宽高
val rect = RectF()
//获取到每个省份的边界
path.computeBounds(rect, true)
//遍历取出每个path中的left取所有的最小值
left = if (left == -1f) rect.left else Math.min(left, rect.left)
//遍历取出每个path中的right取所有的最大值
right = if (right == -1f) rect.right else Math.max(right, rect.right)
//遍历取出每个path中的top取所有的最小值
top = if (top == -1f) rect.top else Math.min(top, rect.top)
//遍历取出每个path中的bottom取所有的最大值
bottom = if (bottom == -1f) rect.bottom else Math.max(bottom, rect.bottom)
}
//MAP 的矩形区域
MAP_RECTF = RectF(left, top, right, bottom)
return mapDataLists;
}
复制代码
- 进行控件测量适配横竖屏切换和宽高定义 wrap_content 模式
/**
- 开始测量
*/
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//测量模式
var widthMode = MeasureSpec.getMode(widthMeasureSpec)
var heightMode = MeasureSpec.getMode(heightMeasureSpec)
//测量大小
widthSize = MeasureSpec.getSize(widthMeasureSpec)
heightSize = MeasureSpec.getSize(heightMeasureSpec)
if (!MAP_RECTF.isEmpty && mMapRectHeight != 0f && mMapRectWidth != 0f) {
//显示比例
scaleHeightValues = heightSize / mMapRectHeight
scaleWidthValues = widthSize / mMapRectWidth
}
//xml 文件中宽高 wrap_content
if (widthMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.AT_MOST) {
//如果是横屏宽保留最大,高需要适配
if (widthSize < heightSize && mMapRectHeight != 0f) {
setMeasuredDimension(widthSize, (mMapRectHeight * scaleWidthValues).toInt())
} else {
setMeasuredDimension(widthSize, heightSize)
}
} else {
setMeasuredDimension(widthSize, heightSize)
}
}
- 开始绘制 path
/**
- 绘制 Map 数据
*/
@SuppressLint(“Range”)
private fun drawMap(canvas: Canvas) {
canvas.save()
if (widthSize > heightSize) {
canvas.scale(scaleWidthValues, scaleHeightValues)
} else {
canvas.scale(scaleWidthValues, scaleWidthValues)
}
mapDataList.forEach { data ->
run {
if (data.isSelect) {
drawPath(data, canvas, Color.RED)
} else {
drawPath(data, canvas, Color.parseColor(data.fillColor))
}
}
}
canvas.restore()
canvas.drawText(“中国🇨🇳地图”, widthSize / 2 - mPaintTextTitle.measureText(“中国🇨🇳地图”) / 2f, 100f, mPaintTextTitle)
}
/**
- 开始绘制 Path
*/
private fun drawPath(
data: MapData,
canvas: Canvas,
学习分享
①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
*/
private fun drawPath(
data: MapData,
canvas: Canvas,
学习分享
①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包
[外链图片转存中…(img-zEL7UsEV-1713698877925)]
[外链图片转存中…(img-wFnYDorj-1713698877925)]
[外链图片转存中…(img-L1ezgTGL-1713698877925)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-Kv2TjWzu-1713698877926)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!