1、Android体系与系统架构
1、Android大致分为四层,即Linux内核层、库和运行时、Framework层和应用层
- Linux层包含了Android系统的核心服务,包括硬件驱动、进程管理、安全系统等。
- Dalvik的特点是运行时编译,而在Android5.X版本开始,ART模式已经取代了Dalvik,ART采用的是安装时就进行编译。
2、Context
- 创建Context的时机就是在创建Context的实体类的时候。
2、Android控件架构与自定义控件详解
1、Android控件架构
- findViewById就是在控件树中以树的深度优先遍历来查找对应元素。
- 每个Activity都包含一个Window对象,在Android中Window对象通常由PhoneWindow来实现,PhoneWindow将一个DecorView设置为整个应用窗口的根View。这里面所有的View的监听事件,都通过WindowManagerService来进行接收,并通过Activity对象来回调相应的onClickListener。在显示上,它将屏幕分为两部分,一个是TitleView,另一个是ContentView。ContentView是一个id为content的FrameLayout。
- requestWindowFeature(Window.FEATURE_NO_TITLE)来设置全屏,视图树中的布局只有Content了,所以调用requestWindowFeature()方法一定要在调用setContentView()方法之前才能生效。
- 当程序在onCreate()方法中调用setContentView()方法后,ActivityManagerService会回调onResume()方法,此时系统才会把整个DecorView添加到PhoneWindow中,并让其显示出来,从而最终完成界面的绘制。
2、View的测量
- MeasureSpec是一个32位的int值,其中高2位为测量的模式,低30位为测量的大小,在计算中使用位运算的原因是为了提高并优化效率。
- View类默认的onMeasure()方法只支持EXACTLY模式,所以如果在自定义控件的时候不重写onMeasure()方法的话,就只能使用EXACTLY模式。
3、ViewGroup的测量
- ViewGroup的大小为wrap_content时,ViewGroup就需要对子View进行遍历,以便获得所有子View的大小,从而来决定自己的大小。而在其他模式下则会通过具体的指定值来设置自身的大小。
4、ViewGroup的绘制
- 不指定ViewGroup的背景颜色,那么ViewGroup的onDraw()方法都不会被调用,但是,ViewGroup会使用dispatchDraw()方法来绘制其子View。
5、自定义View
onFinishInflate():从XML加载组件后回调。
onSizeChanged():组件大小改变时回调。
有些控件像TextView有给getPaint()方法,可以拿到画笔,从而我们可以修改绘制效果。
postInvalidateDelayed():隔几毫秒刷新一次
-
闪动的TextView:
public class FlashTextView extends android.support.v7.widget.AppCompatTextView { private int mViewWidth; private TextPaint mPaint; private LinearGradient mLinearGradient; private Matrix mGradientMatrix; private int mTranslate; public FlashTextView(Context context) { super(context); } public FlashTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public FlashTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (mViewWidth == 0){ mViewWidth = getMeasuredWidth(); if (mViewWidth > 0){ mPaint = getPaint(); mLinearGradient = new LinearGradient(0,0,mViewWidth,0,new int[]{Color.BLUE,0xffffffff,Color.BLUE},null, Shader.TileMode.CLAMP); mPaint.setShader(mLinearGradient); mGradientMatrix = new Matrix(); } } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mGradientMatrix != null){ mTranslate += mViewWidth / 5; if (mTranslate > 2*mViewWidth){ mTranslate = -mViewWidth ; } mGradientMatrix.setTranslate(mTranslate,0); mLinearGradient.setLocalMatrix(mGradientMatrix); postInvalidateDelayed(100); //每隔100ms刷新一次 } } }
3、Android Scroll分析
1、Android坐标系
- getRawX()和getRawY()获取的是Android坐标系中的坐标,也就是离屏幕的距离。
- getX()和getY()获得的坐标是视图坐标系中的坐标,也就是离父控件的距离。
- getRight()获取View自身的右边到其父控件左边的距离,getBotttom()获取View自身的底边到父控件顶部的距离。
2、ScrollTo与ScrollBy
- ScrollTo与ScrollBy移动的是View的内容,在ViewGroup使用则移动所有子View,在View中使用则移动内容,所以想要移动View可以这样:getParent.scrollBy(-offsetX,-offsetY);
- ScrollTo与ScrollBy移动的效果其实是手机屏幕的移动,内容看成是静止的,所以scrollBy中参数为正,那么内容往负方向移动,反之,往正方向。
3、Scroller
不管是ScrollBy还是ScrollTo,子View的平移都是瞬间发生的,通过Scroller类可以实现平滑移动效果,它将每一段距离划分成N个非常小的偏移量,每个小偏移量通过ScrollBy瞬间移动,但整体上就有一个平滑效果。
-
computeScroll():使用Scroller类的核心方法,系统在绘制View的时候会在draw()方法中调用该方法。这个方法实际上就是使用的scrollTo方法,再结合Scroller对象,帮助获取当前的滚动值。
case MotionEvent.ACTION_UP: //手指离开执行滑动过程 View viewGroup = (View) getParent(); mScroller.startScroll(viewGroup.getScrollX(), viewGroup.getScrollY(), -viewGroup.getScrollX(), -viewGroup.getScrollY()); invalidate(); break; @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()){ //判断是否完成了整个滑动 ((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); //getCurr获取当前滑动坐标 invalidate(); //重新绘制来不断调用computeScroll() } }
-
ViewDragHelper(侧拉为例)
public class DragViewGroup extends FrameLayout { private ViewDragHelper mViewDragHelper; private View mMenuView; private View mMainView; public DragViewGroup(@NonNull Context context) { super(context); } public DragViewGroup(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public DragViewGroup(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); } private void init(){ mViewDragHelper = ViewDragHelper.create(this, mCallback); //初始化ViewDragHelper } @Override protected void onFinishInflate() { super.onFinishInflate(); mMenuView = getChildAt(0); mMainView = getChildAt(1); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mViewDragHelper.shouldInterceptTouchEvent(ev); //将事件传递给ViewDragHelper } @Override public boolean onTouchEvent(MotionEvent event) { mViewDragHelper.processTouchEvent(event); //将事件传递给ViewDragHelper return true; } @Override public void computeScroll() { super.computeScroll(); if (mViewDragHelper.continueSettling(true)){ ViewCompat.postInvalidateOnAnimation(this); } } private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() { //何时开始检测触摸事件 @Override public boolean tryCaptureView(View child, int pointerId) { return mMainView == child; } //处理垂直滑动 @Override public int clampViewPositionVertical(View child, int top, int dy) { return super.clampViewPositionVertical(child, top, dy); } //处理水平滑动 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { return left; } //拖动结束后调用 @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); if (mMainView.getLeft() < 500){ mViewDragHelper.smoothSlideViewTo(mMainView,0,0); ViewCompat.postInvalidateOnAnimation(DragViewGroup.this); }else{ mViewDragHelper.smoothSlideViewTo(mMainView,300,0); ViewCompat.postInvalidateOnAnimation(DragViewGroup.this); } } //触摸到View后回调 @Override public void onViewCaptured(View capturedChild, int activePointerId) { super.onViewCaptured(capturedChild, activePointerId); } //拖动状态改变时回调 @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); } //位置改变时回调 @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); } }; }
4、Android绘图机制与处理技巧
1、2D绘制基础
- DrawArc:绘制弧形、扇形,参数useCenter表示要不要连接圆心闭合。
- DrawPosText:在指定位置绘制文本。
- Canvas.save()保存画布,让后续操作在新的图层上操作一样,而Canvas.restore()图层合并。
- Canvas.rotate()可用于像时钟的刻度线,这些不方便计算坐标,用旋转更易解决。
5、Android动画机制与使用技巧
1、Android View动画框架
视图动画:提供了AlphaAnimation、RotateAnimation、TranslateAnimation、ScaleAnimation、AnimationSet
优点:效率比较高且使用方便。
缺点:不具备交互性,当某个View做视图动画后,其响应事件依然在原来的位置。-
属性动画:3.0后出现,解决了视图动画的缺点
ObjectAnimator:要操作的属性必须具有get、set方法,不然无效
PropertyValuesHolder:相当于AnimationSetPropertyValuesHolder pv1 = PropertyValuesHolder.ofFloat("translationX", 300f); PropertyValuesHolder pv2 = PropertyValuesHolder.ofFloat("ScaleX", 1f,0.1f); PropertyValuesHolder pv3 = PropertyValuesHolder.ofFloat("ScaleY", 1f,0.1f); ObjectAnimator.ofPropertyValuesHolder(view,pv1,pv2,pv3).start();
-
View直接使用Animator
view.animate().translationX(100).setDuration(500).withStartAction(new Runnable() { @Override public void run() { } });
2、Android布局动画
布局动画是指作用在ViewGroup上,给ViewGroup增加添加View时添加一个动画过渡效果。
通过XML属性android:animateLayoutChanges="true"来开启默认效果,且无法修改替换这个效。
-
还可以通过LayoutAnimationController来定义一个子View的过渡效果:
LinearLayout ll = (LinearLayout) findViewById(R.id.ll); ScaleAnimation sa = new ScaleAnimation(0,1,0,1); sa.setDuration(2000); //参数2不为0时,可以设置View的显示顺序 LayoutAnimationController controller = new LayoutAnimationController(sa,0.5f); //ORDER_RANDOM:随机 ORDER_REVERSE:反序 ORDER_NORMAL:顺序 controller.setOrder(LayoutAnimationController.ORDER_NORMAL); ll.setLayoutAnimation(controller);
3、自定义动画
-
继承Animation,重写:
/** * interpolatedTime插值器时间因子 */ @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); Matrix matrix = t.getMatrix(); matrix.XXXX //通过matrix各种操作实现动画 } //初始化操作 @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); }
4、Android 5.X SVG矢量动画机制
SVG放大不会失真
<path>标签:
M = moveto(M X,Y):将画笔移动到指定的坐标位置,但未发生绘制。
L = lineto(L X,Y):画直线到指定的坐标位置。
H = horizontal lineto(H X):画水平线到指定的X坐标位置。
V = vertical lineto(V Y):画垂直线到指定的Y坐标位置。
C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次贝赛曲线。
S = smooth curveto(S X2,Y2,ENDX,ENDY):三次贝赛曲线。
Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY):二次贝赛曲线。
T = smooth quadratic Belzier curve(T ENDX,ENDY):映射前面路径后的终点。
A = eliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线。
Z = closepath():关闭路径。
注意:
指令大写表示绝对坐标,参照全局坐标系,小写表示相对坐标,参照父容器坐标系。
指令和数据间的空格可以省略。
同一个指令出现多次可以只用一个。SVG常用指令:
A:
RX,RY指所在椭圆的半轴大小。
XROTATION指椭圆的X轴与水平方向顺时针方向的夹角。
FLAG1只有两个值,1表示大角度弧线,0为小角度弧线。
FLAG2只有两个值,确定从起点至终点的方向,1为顺时针,0为逆时针。
X,Y为终点坐标。-
Android使用SVG
VectorDrawable:<?xml version="1.0" encoding="utf-8"?> <!--viewportHeight表示200dp划分100份--> <!--height和width的比例与viewportHeight和viewportWidth的比例必须一致 不然会发生图形压缩、形变--> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="200dp" android:width="200dp" android:viewportHeight="100" android:viewportWidth="100"> <group android:name="test" android:rotation="0"> <path android:fillColor="@android:color/holo_blue_light" android:pathData="M 25 50 a 25,25 0 1,0 50,0" /> </group> </vector>
AnimatedVectorDrawable:
AnimatedVectorDrawable的作用就是给VectorDrawable提供动画效果。<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/vector"> <!--name必须与VectorDrawable中name保持一致--> <target android:name="test" android:animation="@animator/anim"/> </animated-vector>
动画:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="4000" android:propertyName="rotation" android:valueFrom="0" android:valueTo="360"/> <!--propertyName选择控制什么属性-->
使用:
<ImageView android:id="@+id/iv_image" android:layout_width="wrap_content" android:src="@drawable/animation_vector" android:layout_height="wrap_content"/> ((Animatable)imageView.getDrawable()).start();
6、Activity
1、清空任务栈
- android:clearTaskOnLaunch="true":每次返该Activity,都将Activity之上的所有Activity清除。
- android:finishOnTaskLaunch="true":当离开这个Activity所处的Task,那么用户再返回时,该Activity就会被finish。
- android:alwaysRetainTaskState="true":该Activity所在的Task不接受任何清理命令,一直保持当前Task状态。
7、Android性能优化
1、布局优化
- <ViewStub>与View.GONE:<ViewStub>标签只会在显示时,才去渲染整个布局,而View.GONE,在初始化布局树的已经添加在布局树上了。
2、代码优化 - 任何Java类,都占用大约500字节的内存空间,创建一个类的实例大约消耗15字节的内存。
- 静态方法比普通方法提高15%左右的访问速度。
8、Android 5.X新特性
1、阴影效果
- Z轴由elevation和translationZ。
- 通过 android:elevation="xxdp"在XML布局中使用设置View的视图高度,也可以通过代码view.setTranslationZ(XXX)。
2、Tinting(着色)
- android:tint="@color/XXX"
android:tintMode="multiply" 模式
3、Clipping(裁剪)
-
首先需要使用ViewOutlineProvider来修改outline,然后再通过setOutlineProvider将outline作用给视图。
ViewOutlineProvider viewOutlineProvider = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { outline.setOval(0,0,view.getWidth(),view.getHeight()); //修改为特定形状 } }; view.setClipToOutline(true); view.setOutlineProvider(viewOutlineProvider);
4、CardView
-
悬浮效果
<android.support.v7.widget.CardView android:layout_gravity="center" app:cardBackgroundColor="@color/colorAccent" app:cardElevation="10dp" app:cardCornerRadius="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/btn" android:gravity="center" android:text="11111" android:layout_width="200dp" android:layout_height="200dp"/> </android.support.v7.widget.CardView>
5、Activity过渡动画
-
Android 5.X提供了三种Transition类型:
进入:进入的过渡动画,决定Activity中所有的视图怎么进入屏幕。
退出:退出的过渡动画,决定Activity中所有的视图怎么退出屏幕。
共享元素:决定两个Activity之间的过渡,怎么共享它们的视图。
其中,进入和退出效果包括:
explode(分解):从屏幕中间进或出,移动视图。
slide(滑动):从屏幕边缘进或出,移动视图。
fade(淡出):通过改变屏幕上视图的不透明度达到添加或移除视图。
其中,共享元素包括:
changeBounds:改变目标视图的布局边界。
changeClipBounds:裁剪目标视图边界。
changeTransform:改变目标视图的缩放比例和旋转角度。
changeImageTranform:改变目标图片的大小和缩放比例。
进入与退出过渡动画:MainActivity: @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) //explode动画为例 public void explode(View view){ Intent intent = new Intent(this,TestActivity.class); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); } TestAvtivity: getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); getWindow().setEnterTransition(new Explode()); setContentView(XXX);
共享元素过渡动画:
<Button android:layout_width="wrap_content" android:transitionName="fab" android:layout_height="wrap_content" android:background="@mipmap/ic_launcher" android:onClick="share"/> @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public void share(View view){ Intent intent = new Intent(this,TestActivity.class); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this,view,"fab").toBundle()); } TestActivity: <ImageView android:layout_gravity="center" android:transitionName="fab" android:layout_width="wrap_content" android:background="@mipmap/ic_launcher" android:layout_height="wrap_content"/>
6、Ripple水波纹效果
- android:background="?android:attr/selectableItemBackgroundBorderless" 无边界
- android:background="?android:attr/selectableItemBackground" 有边界
7、Circular Reveal
具体表现为一个View以圆形的形式展示、揭示出来。
final View view = findViewById(R.id.iv);
view.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onClick(View v) {
Animator animator
= ViewAnimationUtils.createCircularReveal(view, view.getWidth() / 2,
view.getHeight() / 2, 0, view.getWidth());
animator.setDuration(2000);
animator.start();
}
});
8、View state changes Animation
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<set>
<objectAnimator
android:propertyName="rotationX"
android:duration="@android:integer/config_longAnimTime"
android:valueTo="360"
android:valueType="floatType"
/>
</set>
</item>
<item android:state_pressed="false">
<set>
<objectAnimator
android:propertyName="rotationX"
android:duration="@android:integer/config_longAnimTime"
android:valueTo="0"
android:valueType="floatType"
/>
</set>
</item>
</selector>
布局使用:
<Button
android:layout_gravity="center"
android:stateListAnimator="@animator/state_list_anim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>