【学习】Android中View的事件体系(上)——View的滑动和事件分发机制

View的基础知识

什么是View

View是所有控件的基类,它是一种界面层控件的抽象,它代表了一个控件。除了View还有ViewGroup,它也继承了View,意味着View本身就可以是单个控件也可以是由多个控件组成的一组控件,通过这种关系形成了View树的结构。ViewGroup内部可以有子View,子View也可以是ViewGroup

View的位置参数

决定位置的四个属性
top(左上角横坐标)、 left(左上角纵坐标)、right(右下角横坐标)、bottom(右下角纵坐标),这些坐标都是相对于View的父容器来说的,因此它是一种相对坐标。在Android中,x轴和y轴分别向右和下。不仅Android,大部分显示系统都是按这个标准来定义坐标系。看下图 image.png 可得出宽高和坐标的关系
width=right-lfet
height=bottom-top

获得四个位置参数的方法

Left:getLeft(); 返回mLeft
Right:getRight(); 返回mRight
Top:getTop(); 返回mTop
Bottom:getBottom(); 返回mBottom

额外参数

从Android3.0开始,View增加了几个参数:x y,它们是View左上角的坐标,translationX和translationY,它们是View左上角相对于父容器的偏移量。这几个参数也是相对于父容器的坐标,并且translationX和translationY的默认值为0。View也为它们提供了get/set方法。
换算关系:x=left+translationX y=top+translationY。
需要注意:View在平移的过程中top和left表示的是原始左上角的位置信息,它们的值不会改变,此时发生改变的是x,y,translationX,translationY。

MotionEvent和TouchSlop

MotionEvent

手指接触屏幕后产生的一系列事件中,典型事件有3种
1.ACTION_DOWN–手指刚接触屏幕
2.ACTION_MOVE–手指在屏幕上移动
3.ACTION_UP–手机2从屏幕上松开的一瞬间

正常情况下,一次手指触摸屏幕的行为会触发一系列点击事件
1.点击屏幕后离开松开:DOWN->UP
2.点击屏幕滑动一会再松开,DOWN->MOVE->…MOVE->UP

通过MotionEvent对象我们可以得到点击事件发生的xy坐标。为此,系统提供了getX/getY(返回的是相对于当前View左上角的xy坐标)和getRawX/getRawY(返回的是相对于手机屏幕左上角的xy坐标)

TouchSlop

TouchSlop是系统所能识别出的被认为是滑动的最小距离,也就是说当两次滑动之间的距离小于这个常值,系统便不认为是在进行滑动操作,不同设备这个值可能是不同的。
获取该常量的方法:ViewConfiguration.get(getContext()).getScaledTouchSlop(); 该常量的意义:当我们处理滑动时,可以利用这个常量来做一些过滤

VelocityTracker、GestureDetector、Scroller

1.VelocityTracker

速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度

使用方法

在View的onTouchEvent方法种追踪当前单击事件的速度
VelocityTracker v=VelocityTracker.obtain();
v.addMovement(event) //event是onTouchEvent方法的参数
接着,当我们先知道当前的滑动速度时,可采用如下方式获得当前的速度
v.computeCurrentVelocity(100);//调用get方法前必须先计算速度,即调用computeCurrentVelocity方法,这里的速度指一段时间内手指所滑过的像素数,参数为时间间隔(ms)
int x=(int)v.getXVelocity();
int y=(int)v.getYVelocity();
计算公式:速度=(终点位置-起点位置)/时间段
根据公式我们可以得出当手指逆着坐标系的正方向滑动时,所产生的速度就为负值。当不需要使用它时,调用clear和recycle方法来重置并回收内存

GestureDetector

手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。

使用方法

1.创建一个GestureDetector对象并实现OnGestureListener接口,根据需要还可以实现OnDoubleTapListener从而监听双击行为。(原书中的单参数的构造器已经被弃用) 看下图

image.png
可使用setIsLongpressEnabled(false)来解决长按屏幕后无法拖动的现象

2.接管目标View的onTouchEvent方法:在待监听View的onTouchEvent方法中
boolean consume=g.onTouchEvent(event);
return consume;
image.png
看上图注释可以看出g.onTouchEvent(event)如果消费/接管了当前事件会返回true

做完上面两步后,我们就可有选择地实现OnGestureListener和OnDoubleTapListener中的方法。 看下图 image.png
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9KjHn7PK-1654780960602)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a27c01c309c5441cbeafcb36a5302499~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]

在开发中比较常用的有:onSingleTapUp(单击)、onFling(快速滑动)、onScroll(拖动)、onLongPress(长按)、onDoubleTap(双击)。在实际开发中可以GestureDetector,完全可以在View的onTouchEvent中实现所需的监听。如果只是监听滑动相关的,建议自己在onTouchEvent实现,如果要监听双击这些行为的话就使用GestureDetector。

Scroller

弹性滑动对象,用于实现View的弹性滑动。当我们使用View的scrollTo/scrollBy方法来进行滑动时,其过程是瞬间完成的,这个没有过渡效果的滑动用户体验不好。这个时候就可以使用Scroller来实现有过渡效果的滑动,其过程不是瞬间完成的,而是在一定的时间间隔内完成的。Scroller本身无法让View弹性滑动,它需要和View的computeScroll方法配合使用才能共同完成这个功能,它只是一个移动计算辅助类,用于跟踪控件滑动轨迹,最终还是通过View的scrollTo/By来完成。 看下图

image.png
image.png

基本使用流程

1.通过Seroller的startScroll开始一个滑动动画控制,里面进行一些轨迹参数的设置和计算
2.在调用startScroll后面调用invalidate()让视图重绘从而触发ViewGroup中的computeScroll被调用
3.在computeScroll中,首先调用Scroller中的computeScrollOffest方法,里面根据当前消耗时间进行轨迹坐标的计算,然后取得计算当前滑动的偏移坐标,调用View的serollTo进行滑动控制

View的滑动

滑动几乎是应用的标配,掌握滑动的方法是实现绚丽的自定义控件的基础。

三种方式可以实现View的滑动

1.通过View本身的scrollTo/By

image.png
看上图,我们可以看到scroolBy实际上也是调用了scrollTo。 image.png
看上图scrollBy实现了基于当前位置的相对滑动,而scrollTo实现了基于所传递参数的绝对滑动。

理解滑动过程中View内部的mScrollX和mScrollY的改变规则

这两个属性可以通过getScrollX/Y得到。在滑动过程中,mScrollX的值总是等于View左边缘和View内容左边缘在水平方向的距离,而mScrollY的值总是等于View上边缘和View内容上边缘在竖直方向的距离。View边缘是指View中的内容的边缘,scrollTo和scrollBy只能改变View内容的位置而不能改变View在布局中的位置。两个属性的单位都是像素,并且当View左边缘在View内容左边缘的右边时,mScrollX为正值,当View上边缘在View内容上边缘的下边时,mScrollY为正值,反之为负值。也就是如果从左向右滑动,那么mScrollX为负值,反之为正值,如果从上往下滑动,mScrollY为负值,反之为正值。 看下图

image.png

2.使用动画

通过动画我们能够让一个View进行平移,而平移就是一种滑动,使用动画来移动View,主要是操作View的translationX和translationY属性,既可以采用传统的View动画,也可以采用属性动画。 image.png
View动画是对View的影像做操作,并不能真正改变View的位置参数,包括宽/高,,如果希望动画后的状态得以保留还必须将fillAfter属性设置为true,否则动画完成后其动画效果会消失。使用属性动画不会有这个问题。

3.改变布局参数

改变布局参数即是改变LayoutParams。比如我们想把一个view向右平移100px,我们只需要将这个View的LayoutParams里的marginLeft参数的值增加100px即可

各种滑动方式的对比

scrollTo/By是View提供的原生方法,其作用是专门用于View的滑动,它可以比较方便地实现滑动效果并且不影响内部元素的单击事件,但不能滑动View本身。使用属性动画没有明显缺点,View动画不能改变自身属性。改变布局没有明显缺点,它的主要适用对象是一些具有交互性的View。看下图

image.png

弹性滑动

思想

将一次大的滑动分成若干次小的滑动并在一个时间段内完成

Scroller

前面已经介绍过它的使用方法,接下来分析源码 看下图 image.png
之前将Scroller的时候我们构造一个Scroller对象并调用它的startScroll方法时,Scroller其实什么也没做,它只保存了我们传递的参数。startX和stratY表示滑动的起点,dx,dy表示滑动的距离,duration表示滑动时间,,这里的滑动指的是View内容滑动而非View本身位置的改变。 可以看到仅仅调用startScroll是无法让View滑动的,让View弹性滑动的是startScroll方法下面的invalidate方法,它会导致视图重绘,在View的draw方法中又会去调用computeScroll方法,它在View中是一个空实现,因此需要我们自己去实现,上面的代码已经实现了computeScroll方法,正是因为它才能实现弹性滑动。当View重绘后会在draw方法中调用computeScroll,而computeScroll又会去向Scroller获取当前的scrollX和scrollY,然后通过scrollTo方法实现滑动,接着又调用postInvalidate方法进行第二次重绘,这一次重绘过程和第一次一样,然后继续向Scroller获取当前的ScrollX和ScrollY,并通过scrollTo方法滑动到新的位置,如此反复知道整个滑动过程结束。

computeScrollOffest的实现

image.png 看上图,留意timePassed和mDuration,这个方法会根据时间的流逝的百分比计算出scrollX和scrollY改变的百分比并算出当前的值,它返回true时表示滑动还未结束,false时表示滑动结束。

总结

Scroller本身不能实现View的滑动,需要配合View的computeScroll方法才能完成弹性滑动的效果,他不断地让View重绘,而每一次重绘距滑动起始时间会有一个时间间隔,通过这个间隔Scroller就可以得出View当前的滑动位置,知道了滑动位置就可以通过scrollTo方法完成View的滑动。就这样,View的每一次重绘都会导致View进行小幅度的滑动,而多次小幅度的滑动就组成了弹性滑动。

动画

我们可以利用动画的特性去完成动画本身完成不了的东西。 image.png
看上图,这个动画本质上没有作用于任何View,而是View利用动画的完成比例来计算出View要滑动的距离。

使用延时策略

核心思想

通过发送一系列延时信息从而达到一种渐进式的效果,具体来说可以使用Handler或View的postDelayed方法,也可以使用现成的sleep方法。对于postDelayed来说,我们可以通过它延时发送消息然后再消息中来进行View的滑动,如果接连不断发送这种延时信息就可以实现弹性滑动的效果。对于sleep方法来收,通过在while循环中不断地滑动View和sleep就可以实现弹性滑动。

View的事件分发机制

点击事件的传递规则

所谓点击事件的分发,其实就是对MotionEvent事件的分发过程,即当一个MotionEvent产生了以后,系统需要把这个事件传递给一个具体的View,而这个传递的过程就是分发过程。点击事件的分发过程由三个方法来共同完成

1.public boolean dispatchTouchEvent(MotionEvent ev)

用来进行事件的分发。如果事件能够传递给当前的View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dsipatchTouchEvent影响,表示是否消耗当前事件

2.public boolean onInterceptTouchEvent(MotionEvent event)

在上述方法的内部调用,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个时间序列中,此方法不会被再次调用,返回结果表示是否拦截当前事件

3.public boolaean onTouchEvent(MotionEvent event)

在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个时间序列中,当前View无法再次接收到事件。

它们的关系

可以用以下伪代码来表示

 boolean consume=false;
	if(onInterceptTouchEvent(ev)){
	consume=onTouchEvent(ev)
}else{
	consume=child.dispatchTouchEvent(ev)
}
	return consume;
} 

对于一个根ViewGroup来说,点击事件产生后,首先会传递给它,此时它的的dispatchTouchEvent就会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着时间就会交给这个ViewGroup处理,即它的onTouchEvent会被调用,如果ViewGroup的onInterceptTouchEvent方法返回flase表示它不拦截当前事件,此时当前时间就会继续传递给它的子元素,接着子元素的dispatchEvent方法就会被调用,如此反复直到事件被处理。

当一个View需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被回调。这时时间如何处理还要看onTouch的返回值,如果返回false,则当前View的onTouchEvent方法会被调用,如果返回true,那么onTouchEvent方法不会被调用。由此可见,给View设置的OnTouchListener,其优先级比onTouchEvent方法高。在onTouchEvent方法中,如果当前设置的有OnClickListener,那么它的onClick方法会被调用。可以看出,平时我们常用的OnClickListener优先级最低,处于事件传递的尾端。

当一个点击事件产生后,它的传递过程遵循如下顺序:Activity->Window->View,即事件总是先传递给Activity,Activity再传递给Window,最后Window再传递给顶级View,顶级View接收到事件后,就会按照事件分发机制去分发事件。考虑一种情况,如果一个View的onTouchEvent返回false,那么它的父容器的onTouchEvent将会被调用,以此类推,如果所有的元素都不处理这个事件,那么这个事件最终会传递给Activity处理,即Activity的onTouchEvent方法会被调用。约等于上级抛任务给下级,下级搞不定,只能交给水平更高的上级,再不行就交给上级的上级去解决。

事件传递机制的结论

1.同一时间序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件,这个事件序列以down事件开始,中间含有数量不定的move事件,最终以up事件结束。

2.正常情况下,一个时间序列只能被一个View拦截且消耗,这一条的原因可以参考第三点,因为一旦一个元素拦截了此事件,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别由两个View同时处理,但是通过特殊手段可以做到,比如一个View将本该自己处理的事件通过onTouchEvent强行传递给其他View处理。

3.某个View一旦决定拦截,那么这一个事件序列都只能由它来处理,并且它的onInterceptTouchEvent不会再被调用。也就是说当一个View决定拦截一个事件后,系统会把同一个事件序列内的其他方法都直接交给它来处理,因此就不用再调用onInterceptTouchEvent去询问它是否要拦截了。

4.某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回false),那么同一事件序列的其他事件都不会交给它来处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent会被调用,意思是事件一旦交给一个View处理,那么它就必须消耗掉,否则同一事件序列中剩下的事件就不再交给它来处理了。

5.如果View不消除ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent并不会被调用,并且当前View可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity处理。

6.ViewGroup默认不拦截任何事件,其源码中的onInterceptTouchEvent方法默认返回false。

7.View没有onInterceptTouchEvent方法,一旦由点击事件传递给它,它的onTouchEvent就会被调用。

8.View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)。View的longClickable属性默认为false,clickable属性要分情况,比如Button的clickable属性默认为true,而Textview默认为false。

9.View的enable属性不影响onTouchEvent的默认返回值。哪怕一个View是disable状态,只要它的clickable或者longClickable有一个为true,它的onTouchEvent就返回true。

10.onClick会发生的前提是当前View是可点击的,并且它收到了down和up事件。

11.事件传递过程是由外向内的,事件总是先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterceptTouchEvent方法可以在子元素干预父元素的事件分发过程,但是ACTION_DOWN除外。

事件分发的源码分析

1.Activity对点击事件的分发过程

点击事件用MotionEvent来表示,当一个点击操作发生时,事件最先传递给当前Activity,由Activity的dispatchTouchEvent来进行事件分发,具体工作由Activity内部的Window完成。Window会将事件传递给decorview,decorview一般是当前界面的底层容器(即setContentView所设置的View的父容器),通过Activity.getWindow.getDecorView()可以获得。
image.png
首先事件开始交给Activity所附属的Window进行分发,如果返回true,整个事件循环就结束了,返回false意味着事件没人处理,所有View的onTouchEvent都返回了false,,那么Activity的onTouchEvent就会被调用。

接下来看Window是如何将事件传递给ViewGroup的,通过源码可得出Window是个抽象类,而Window的superDispatchTouchEvent方法也是个抽象方法。而Window的实现类其实是PhoneWindow。Window类可以控制顶级View的外观和行为策略,它的唯一实现位于android.policy.PhoneWindow中,当你要实例化这个Window类的时候,你并不知道它的细节,因为这个类会被重构,只有一个工厂方法可以使用。 image.png
从上图可以看到PhoneWindow将事件直接传递给了DecorView
image.png
通过((ViewGroup)getWindow().getDecorView().findViewById(android.R.id.content)).getChildAt(0)这种方式可以获取Activity所设置的View,这个mDecor显然就是getWindow().getDecorView()返回的View,而我们通过setContentView设置的View是它的一个子View。目前事件传递到了DecorView这里,由于DecorView继承自FrameLayout且是父View,所以最终时间会传递给View。换句话说,时间肯定会传递到View,不然应用如何相应点击事件呢?重点是事件到了View之后应该如何传递。从这里开始事件已经传递到顶级View了,即在Activity中通过setContentView所设置的View,顶级View也叫根View,一般来说都是ViewGroup。

顶级View对点击事件的分发过程

概述:点击事件到达顶级View,会调用ViewGroup的dispatchTouchEvent方法,ruoguodingjiViewGroup拦截事件即onInterceptTouchEvent返回true,则事件由ViewGroup处理,如果此时ViewGroup的mOnTouchListener被设置,则onTouch会调用,否则onTouchEvent被调用。也就是说都提供的话onTouch会屏蔽onTouchEvent。在onTouchEvent中,如果设置了mOnClickListener,则onClick会被调用。如果顶级ViewGroup不拦截事件,事件会传递到它所在的点击事件链上的子View,此时子View的dispatchTouchEvent会被调用。到此位置,事件已经从顶级View传递给了下一层View,如此循环,完成整个事件的分发。
ViewGroup对点击事件的分发过程主要实现在ViewGroup的dispatchTouchEvent方法中。方法较长,截取部分分析 见下图 image.png
当事件类型为ACTION_DOWN或mFirstTouchTarget!=null时ViewGroup会判断是否要拦截当前事件。当ViewGroup的子元素成功处理时,mFirstTouchTarget会被赋值并指向子元素。换种方式说,当ViewGroup不拦截事件并将事件交由子元素处理时mFirstTouchTarget!=null。反过来,一旦事件由当前ViewGroup拦截时,mFirstTouchTarget!=null不成立,那么当DOWN事件到来时ViewGroup不会再被调用,并且同一序列的其他事件默认交由它处理。

特殊情况

FLAG_DISALLOW_INTERCEPT标记位,通过requestDisallowInterceptTouchEvent方法来设置,一般用于子View中。一旦设置后,ViewGroup将无法拦截除了DOWN以外的事件。(因为DOWN会重置该标记位,导致View中设置的这个标记位无效)所以当面对ACTION_DOWN事件时,ViewGroup总是会调用自己的onInterceptTouchEvent方法来询问自己是否要拦截事件。 image.png 上图遇到DOWN事件时进行了重置。

结论

当ViewGroup决定拦截事件后,那么后续的点击事件将会默认交给它处理并且不再调用它的onInterceptTouchEvent方法,这证实了FLAG_DISALLOW_INTERCEPT的作用是让ViewGroup不再拦截事件,前提是ViewGroup不拦截DOWN事件。onInterceptTouchEvent不是每次时间都会被调用的,如果我们想提前处理所有的点击事件,要选择dispatchTouchEvent方法,只有这个方法能确保每次都会被调用。前提是事件能够传递到当前的ViewGroup。 image.png
上图中当ViewGroup不拦截事件时,首先遍历ViewGroup的所有子元素,然后判断子元素是否能够接收到点击事件。判断是否能接受点击事件主要有两点来衡量1.子元素是否在播放动画和点击事件的左边是否落在子元素的区域内。如果某个子元素满足这两个条件,事件就会传递给它来处理。dispatchTransformedTouchEvent实际上调用的就是子元素的dispatchTouchEvent方法。其参数的child在不是null的情况下会调用child.dispatchTouchEvent方法,这样事件就交由子元素处理了,从而完成了一轮事件分发。

image.png
当子元素的dispatchTouchEvent返回true,mFirstTarget就会被赋值然后跳出循环

image.png
这几行代码完成了mFirstTouchTarget的赋值并终止对子元素的遍历。如果子元素的dispatchTouchEvent返回false,ViewGroup就会把事件分发给下一个子元素(如果有下一个子元素) 其实mFirstTarget真正的复制过程是在addTouchTarget内部完成,从它的内部结构可以看出,它是一种单链表结构。mFirstTarget是否被赋值直接影响到ViewGroup的拦截策略,如果它为null,那么ViewGroup就默认拦截接下来同一序列的所有点击事件 image.png
如果遍历所有子元素后事件都没有被合适地处理原因
1.ViewGroup没有子元素
2.子元素处理了点击事件,但是在dispatchTouchEvent中返回了false,这一般是因为子元素在onTouchEvent中返回了false。
在这两种情况下ViewGroup会自己处理点击事件。

image.png

上图child为null之后,会转到View的dispatchTouchEvent方法,即点击事件交由View来处理

View对点击事件的处理过程(这里的View不包含ViewGroup)

这里的View是一个单独的元素,没有子元素,因此无法向下传递事件,只能自己处理事件。

image.png

上图,首先它会判断有没有设置OnTouchListener,如果onTouchListener中的onTouch方法返回true,那么onTouchEvent就不会被调用,可见onTouchListener的优先级高于onTouchEvent,这样做的好处是方便在外界处理点击事件。 接着分析onTouchEvent的实现

image.png 上图当View处于不可用状态下时View照样会消耗点击事件,尽管它看起来不可用。

image.png
下图,接着如果View设置有代理,那么还会执行TouchDelegate的onTouchEvent方法,其工作机制与OnTouchListener类似。

image.png

image.png
上图当View的CLICKABLE和LONG_CLICKABLE有一个为true时,那么它就会消耗这个事件,返回true,不管它是不是DISABLE状态。当UP事件发生时,会触发performClick方法,如果View设置了OnClickListener,那么performClick方法内部会调用它的onClick方法。看下图

image.png

View的LONG_CLICKABLE属性默认为false,而CLICKABLE属性是否为false和具体View有关,通过setClickable和setLongClickable可以改变它们的属性。setOnClickListener会自动将View的CLICKABLE设为true,setOnLongClickListener会自动将View的LONG_CLICKABLE设为true。看下图 image.png

文末

在这里如果你要想成为架构师或者技术更近一步,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,可点击下方CSDN官方认证卡片免费获取。↓↓↓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值