1摔敛、Android體系與系統(tǒng)架構(gòu)
1、Android大致分為四層,即Linux內(nèi)核層、庫和運(yùn)行時(shí)翘瓮、Framework層和應(yīng)用層
- Linux層包含了Android系統(tǒng)的核心服務(wù),包括硬件驅(qū)動(dòng)裤翩、進(jìn)程管理资盅、安全系統(tǒng)等。
- Dalvik的特點(diǎn)是運(yùn)行時(shí)編譯踊赠,而在Android5.X版本開始呵扛,ART模式已經(jīng)取代了Dalvik,ART采用的是安裝時(shí)就進(jìn)行編譯臼疫。
2择份、Context
- 創(chuàng)建Context的時(shí)機(jī)就是在創(chuàng)建Context的實(shí)體類的時(shí)候。
2烫堤、Android控件架構(gòu)與自定義控件詳解
1、Android控件架構(gòu)
- findViewById就是在控件樹中以樹的深度優(yōu)先遍歷來查找對(duì)應(yīng)元素凤价。
- 每個(gè)Activity都包含一個(gè)Window對(duì)象鸽斟,在Android中Window對(duì)象通常由PhoneWindow來實(shí)現(xiàn),PhoneWindow將一個(gè)DecorView設(shè)置為整個(gè)應(yīng)用窗口的根View利诺。這里面所有的View的監(jiān)聽事件富蓄,都通過WindowManagerService來進(jìn)行接收,并通過Activity對(duì)象來回調(diào)相應(yīng)的onClickListener慢逾。在顯示上立倍,它將屏幕分為兩部分灭红,一個(gè)是TitleView,另一個(gè)是ContentView口注。ContentView是一個(gè)id為content的FrameLayout变擒。
- requestWindowFeature(Window.FEATURE_NO_TITLE)來設(shè)置全屏,視圖樹中的布局只有Content了寝志,所以調(diào)用requestWindowFeature()方法一定要在調(diào)用setContentView()方法之前才能生效娇斑。
- 當(dāng)程序在onCreate()方法中調(diào)用setContentView()方法后,ActivityManagerService會(huì)回調(diào)onResume()方法材部,此時(shí)系統(tǒng)才會(huì)把整個(gè)DecorView添加到PhoneWindow中毫缆,并讓其顯示出來,從而最終完成界面的繪制乐导。
2苦丁、View的測(cè)量
- MeasureSpec是一個(gè)32位的int值,其中高2位為測(cè)量的模式物臂,低30位為測(cè)量的大小旺拉,在計(jì)算中使用位運(yùn)算的原因是為了提高并優(yōu)化效率。
- View類默認(rèn)的onMeasure()方法只支持EXACTLY模式鹦聪,所以如果在自定義控件的時(shí)候不重寫onMeasure()方法的話账阻,就只能使用EXACTLY模式。
3泽本、ViewGroup的測(cè)量
- ViewGroup的大小為wrap_content時(shí)淘太,ViewGroup就需要對(duì)子View進(jìn)行遍歷,以便獲得所有子View的大小规丽,從而來決定自己的大小蒲牧。而在其他模式下則會(huì)通過具體的指定值來設(shè)置自身的大小。
4赌莺、ViewGroup的繪制
- 不指定ViewGroup的背景顏色冰抢,那么ViewGroup的onDraw()方法都不會(huì)被調(diào)用,但是艘狭,ViewGroup會(huì)使用dispatchDraw()方法來繪制其子View挎扰。
5、自定義View
onFinishInflate():從XML加載組件后回調(diào)巢音。
onSizeChanged():組件大小改變時(shí)回調(diào)遵倦。
有些控件像TextView有給getPaint()方法,可以拿到畫筆官撼,從而我們可以修改繪制效果梧躺。
postInvalidateDelayed():隔幾毫秒刷新一次
-
閃動(dòng)的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坐標(biāo)系
- getRawX()和getRawY()獲取的是Android坐標(biāo)系中的坐標(biāo)掠哥,也就是離屏幕的距離巩踏。
- getX()和getY()獲得的坐標(biāo)是視圖坐標(biāo)系中的坐標(biāo),也就是離父控件的距離续搀。
- getRight()獲取View自身的右邊到其父控件左邊的距離塞琼,getBotttom()獲取View自身的底邊到父控件頂部的距離。
2目代、ScrollTo與ScrollBy
- ScrollTo與ScrollBy移動(dòng)的是View的內(nèi)容屈梁,在ViewGroup使用則移動(dòng)所有子View,在View中使用則移動(dòng)內(nèi)容榛了,所以想要移動(dòng)View可以這樣:getParent.scrollBy(-offsetX在讶,-offsetY);
- ScrollTo與ScrollBy移動(dòng)的效果其實(shí)是手機(jī)屏幕的移動(dòng),內(nèi)容看成是靜止的霜大,所以scrollBy中參數(shù)為正构哺,那么內(nèi)容往負(fù)方向移動(dòng),反之战坤,往正方向曙强。
3、Scroller
不管是ScrollBy還是ScrollTo途茫,子View的平移都是瞬間發(fā)生的碟嘴,通過Scroller類可以實(shí)現(xiàn)平滑移動(dòng)效果,它將每一段距離劃分成N個(gè)非常小的偏移量囊卜,每個(gè)小偏移量通過ScrollBy瞬間移動(dòng)娜扇,但整體上就有一個(gè)平滑效果。
-
computeScroll():使用Scroller類的核心方法栅组,系統(tǒng)在繪制View的時(shí)候會(huì)在draw()方法中調(diào)用該方法雀瓢。這個(gè)方法實(shí)際上就是使用的scrollTo方法,再結(jié)合Scroller對(duì)象玉掸,幫助獲取當(dāng)前的滾動(dòng)值刃麸。
case MotionEvent.ACTION_UP: //手指離開執(zhí)行滑動(dòng)過程 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()){ //判斷是否完成了整個(gè)滑動(dòng) ((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); //getCurr獲取當(dāng)前滑動(dòng)坐標(biāo) invalidate(); //重新繪制來不斷調(diào)用computeScroll() } }
-
ViewDragHelper(側(cè)拉為例)
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() { //何時(shí)開始檢測(cè)觸摸事件 @Override public boolean tryCaptureView(View child, int pointerId) { return mMainView == child; } //處理垂直滑動(dòng) @Override public int clampViewPositionVertical(View child, int top, int dy) { return super.clampViewPositionVertical(child, top, dy); } //處理水平滑動(dòng) @Override public int clampViewPositionHorizontal(View child, int left, int dx) { return left; } //拖動(dòng)結(jié)束后調(diào)用 @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后回調(diào) @Override public void onViewCaptured(View capturedChild, int activePointerId) { super.onViewCaptured(capturedChild, activePointerId); } //拖動(dòng)狀態(tài)改變時(shí)回調(diào) @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); } //位置改變時(shí)回調(diào) @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); } }; }
4、Android繪圖機(jī)制與處理技巧
1司浪、2D繪制基礎(chǔ)
- DrawArc:繪制弧形泊业、扇形,參數(shù)useCenter表示要不要連接圓心閉合啊易。
- DrawPosText:在指定位置繪制文本脱吱。
- Canvas.save()保存畫布,讓后續(xù)操作在新的圖層上操作一樣认罩,而Canvas.restore()圖層合并。
- Canvas.rotate()可用于像時(shí)鐘的刻度線续捂,這些不方便計(jì)算坐標(biāo)垦垂,用旋轉(zhuǎn)更易解決宦搬。
5、Android動(dòng)畫機(jī)制與使用技巧
1劫拗、Android View動(dòng)畫框架
視圖動(dòng)畫:提供了AlphaAnimation间校、RotateAnimation、TranslateAnimation页慷、ScaleAnimation憔足、AnimationSet
優(yōu)點(diǎn):效率比較高且使用方便。
缺點(diǎn):不具備交互性酒繁,當(dāng)某個(gè)View做視圖動(dòng)畫后滓彰,其響應(yīng)事件依然在原來的位置。-
屬性動(dòng)畫:3.0后出現(xiàn)州袒,解決了視圖動(dòng)畫的缺點(diǎn)
ObjectAnimator:要操作的屬性必須具有g(shù)et揭绑、set方法,不然無效
PropertyValuesHolder:相當(dāng)于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布局動(dòng)畫
布局動(dòng)畫是指作用在ViewGroup上他匪,給ViewGroup增加添加View時(shí)添加一個(gè)動(dòng)畫過渡效果。
通過XML屬性android:animateLayoutChanges="true"來開啟默認(rèn)效果夸研,且無法修改替換這個(gè)效邦蜜。
-
還可以通過LayoutAnimationController來定義一個(gè)子View的過渡效果:
LinearLayout ll = (LinearLayout) findViewById(R.id.ll); ScaleAnimation sa = new ScaleAnimation(0,1,0,1); sa.setDuration(2000); //參數(shù)2不為0時(shí),可以設(shè)置View的顯示順序 LayoutAnimationController controller = new LayoutAnimationController(sa,0.5f); //ORDER_RANDOM:隨機(jī) ORDER_REVERSE:反序 ORDER_NORMAL:順序 controller.setOrder(LayoutAnimationController.ORDER_NORMAL); ll.setLayoutAnimation(controller);
3亥至、自定義動(dòng)畫
-
繼承Animation悼沈,重寫:
/** * interpolatedTime插值器時(shí)間因子 */ @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); Matrix matrix = t.getMatrix(); matrix.XXXX //通過matrix各種操作實(shí)現(xiàn)動(dòng)畫 } //初始化操作 @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); }
4、Android 5.X SVG矢量動(dòng)畫機(jī)制
SVG放大不會(huì)失真
<path>標(biāo)簽:
M = moveto(M X,Y):將畫筆移動(dòng)到指定的坐標(biāo)位置抬闯,但未發(fā)生繪制井辆。
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):三次貝賽曲線。
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):映射前面路徑后的終點(diǎn)。
A = eliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧線胀屿。
Z = closepath():關(guān)閉路徑塘揣。
注意:
指令大寫表示絕對(duì)坐標(biāo),參照全局坐標(biāo)系宿崭,小寫表示相對(duì)坐標(biāo)亲铡,參照父容器坐標(biāo)系。
指令和數(shù)據(jù)間的空格可以省略。
同一個(gè)指令出現(xiàn)多次可以只用一個(gè)奖蔓。SVG常用指令:
A:
RX,RY指所在橢圓的半軸大小赞草。
XROTATION指橢圓的X軸與水平方向順時(shí)針方向的夾角。
FLAG1只有兩個(gè)值吆鹤,1表示大角度弧線厨疙,0為小角度弧線。
FLAG2只有兩個(gè)值疑务,確定從起點(diǎn)至終點(diǎn)的方向沾凄,1為順時(shí)針,0為逆時(shí)針知允。
X,Y為終點(diǎn)坐標(biāo)撒蟀。-
Android使用SVG
VectorDrawable:<?xml version="1.0" encoding="utf-8"?> <!--viewportHeight表示200dp劃分100份--> <!--height和width的比例與viewportHeight和viewportWidth的比例必須一致 不然會(huì)發(fā)生圖形壓縮、形變--> <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提供動(dòng)畫效果廊镜。<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>
動(dòng)畫:
<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、清空任務(wù)棧
- android:clearTaskOnLaunch="true":每次返該Activity嗤朴,都將Activity之上的所有Activity清除配椭。
- android:finishOnTaskLaunch="true":當(dāng)離開這個(gè)Activity所處的Task,那么用戶再返回時(shí)雹姊,該Activity就會(huì)被finish股缸。
- android:alwaysRetainTaskState="true":該Activity所在的Task不接受任何清理命令,一直保持當(dāng)前Task狀態(tài)吱雏。
7敦姻、Android性能優(yōu)化
1、布局優(yōu)化
- <ViewStub>與View.GONE:<ViewStub>標(biāo)簽只會(huì)在顯示時(shí)歧杏,才去渲染整個(gè)布局镰惦,而View.GONE,在初始化布局樹的已經(jīng)添加在布局樹上了犬绒。
2旺入、代碼優(yōu)化 - 任何Java類,都占用大約500字節(jié)的內(nèi)存空間凯力,創(chuàng)建一個(gè)類的實(shí)例大約消耗15字節(jié)的內(nèi)存茵瘾。
- 靜態(tài)方法比普通方法提高15%左右的訪問速度。
8咐鹤、Android 5.X新特性
1拗秘、陰影效果
- Z軸由elevation和translationZ。
- 通過 android:elevation="xxdp"在XML布局中使用設(shè)置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過渡動(dòng)畫
-
Android 5.X提供了三種Transition類型:
進(jìn)入:進(jìn)入的過渡動(dòng)畫,決定Activity中所有的視圖怎么進(jìn)入屏幕氧吐。
退出:退出的過渡動(dòng)畫讹蘑,決定Activity中所有的視圖怎么退出屏幕。
共享元素:決定兩個(gè)Activity之間的過渡筑舅,怎么共享它們的視圖座慰。
其中,進(jìn)入和退出效果包括:
explode(分解):從屏幕中間進(jìn)或出翠拣,移動(dòng)視圖版仔。
slide(滑動(dòng)):從屏幕邊緣進(jìn)或出,移動(dòng)視圖误墓。
fade(淡出):通過改變屏幕上視圖的不透明度達(dá)到添加或移除視圖蛮粮。
其中,共享元素包括:
changeBounds:改變目標(biāo)視圖的布局邊界谜慌。
changeClipBounds:裁剪目標(biāo)視圖邊界然想。
changeTransform:改變目標(biāo)視圖的縮放比例和旋轉(zhuǎn)角度。
changeImageTranform:改變目標(biāo)圖片的大小和縮放比例欣范。
進(jìn)入與退出過渡動(dòng)畫:MainActivity: @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) //explode動(dòng)畫為例 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);
共享元素過渡動(dòng)畫:
<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
具體表現(xiàn)為一個(gè)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"/>