第 三 章 Andorid 控件架構和自定義控件詳解
3.1 Android 控件架構
每一個Activity都包含一個Window對象,在Android中Window對象通常由PhoneWindow來實現。
3.2 View 的測量
MeasureSpec是一個32位的int值,其中高2位為測量的模式,低30位為測量的大小,在計算中使用位運算的原因是為了提高並優化效率。
View類默認的onMeasure()方法只支持EXACTLY模式,所以如果在自定義控件的時候不能夠重寫onMeasure()方法的話,就只能使用EXACTLY模式。
3.3 View 的繪制
Canvas canvas = new Canvas(bitmap);
傳進去的這個bitmap與這個bitmap創建的Canvas畫布是緊緊聯系在一起的,這個過程我們稱之為裝載畫布。
3.4 ViewGroup 的測量
在自定義ViewGroup時,通常會去重寫onLayout()方法來控制其子View顯示位置的邏輯。同樣,如果需要支持wrap_content屬性,那麼它還必須
重寫onMeasure()方法,這點與View是相同的。
3.5 ViewGroup 的繪制
ViewGroup會使用dispatchDraw()方法來繪制其子View。
3.6 自定義 View
利用LinearGradient Shader和Matrix來實現一個動態的文字閃動效果。
第 四 章 ListView 使用技巧
4.1 ListView 常用優化技巧
4.1.1 使用 ViewHolder 模式提高效率
4.1.2 設置 item 分割線 divider
android:divider="@android:color/darker_gray"
android:dividerHeight="10dp"
android:divider="@null"
4.1.3 隱藏 ListView 的滾動條
android:scrollbars="none"
4.1.4 取消ListView的item點擊效果
` 當點擊ListView中的一項時,系統默認會出現一個點擊效果,在Android5.X上是一個波紋效果,在其下是一個改變背景顏色的效果,可使用listSelector
屬性來取消點擊後的回饋效果。
android:listSelector="#00000000"
android:listSelector="@android:color/transparent"
4.1.5 設置 ListView 需要顯示在第幾項
listView.setSelection(N);
mListView.smoothScrollBy(distance, duration);
mListView.smoothScrollByOffset(offset);
mListView.smoothScrollToPosition(index);
4.1.6 動態修改 ListView
mData.add("new");
mAdapter.notifyDataSetChanged();
4.1.7 遍歷 ListView 中的所有item
for (int i = 0; i < mListView.getChildCount;() i++) {
View view = mListView.getChildAt(i);
}
4.1.8 處理空 ListView
listView.setEmptyView(findViewById(R.id.empty_view));
4.1.9 ListView 滑動監聽
這裡介紹2種ListView滑動事件的方法,一個是OnTouchListener來實現監聽,另一個是OnScrollListener來實現監聽。
OnTouchListener是View中的監聽事件。
OnScrollListener是AbsListView中的監聽事件。
OnScrollListener有2個回調方法——onScrollStateChanged()和onScroll()。
下面介紹在onScroll()中的2個常用的判斷操作:
if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {
//滾動到最後一行
}
if (firstVisibleItem > lastVisibleItemPosition) {
//上滑
} else if (firstVisibleItem < lastVisibleItemPosition) {
//下滑
}
lastVisibleItemPosition = firstVisibleItem;
4.2 ListView 常用擴展
4.2.1 具有彈性的 ListView
重寫overScrollBy()方法,將maxOverScrollY改為設置的值——mMaxOverDistance,當然,為了滿足多分辨率的要求,可以通過屏幕的density來
計算具體的值,讓不同分辨率的彈性距離基本一致,如下:
private void initView(){
DisplayMetrics metrics = mContext.getResorces().getDisplayMetrics();
float density = metrics.density;
mMaxOverDistance = (int) (density * mMaxOverDistance);
}
4.2.2 自動顯示、隱藏布局的 ListView
原理:借助View的OnTouchListener接口來監聽ListView的滑動,通過比較與上次坐標的大小,來判斷滑動的方向,並通過滑動的放心來判斷是否需
要顯示或隱藏對應的布局。
//給ListView增加一個HeaderView,避免第一個Item被Toolbar遮擋
View header = new View(this);
header.setLayoutParams(enw AbsListView.LayoutParams(
AbsListView.LayoutParams.MATCH_PARENT,
(int) getResouces().getDimension(
R.dimen.abc_action_bar_default_height_material)));
mListView.addHeaderView(header);
上面使用abc_action_bar_default_height_material屬性獲取系統ActionBar的高度,定義一個mTouchSlop變量用來獲取系統認為的最低滑動距離,即
超過這個距離的移動,系統就將其定義為滑動狀態了。
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
重寫View的OnTouchListener()方法,實現相應邏輯,加上布局顯示隱藏的位移動畫即可。
注意theme要使用NoActionBar的,不然會引起沖突。
4.2.3 聊天 ListView
getItemViewType()方法用來返回第position個item是何種類型,getViewTypeCount()方法是用來返回不同布局的總數。
1.布局代碼
2.聊天信息Bean
3.ListView的Adapter
通過在getView()方法中判斷getItemViewType(position)的值來決定具體實例化哪一個布局,從而實現在一個ListView中多個布局內容的添加。
4.2.4 動態改變 ListView布局
通常情況下,要動態地改變點擊Item的布局來達到一個Focus的效果,有2種方法。一種是將兩種布局寫在一起,通過控制布局的顯示、隱藏,來達到切換布局的效果;另一種則是在getView()方法中,通過判斷來選擇加載不同的布局。
由於getView()是在初始化的時候調用,後面在點擊Item的時候,並沒有再次調用getView()。所以,必須要讓ListView在點擊之後,再刷新一次。
第 五 章 Andorid Scroll 分析
5.1 滑動效果是如何產生的
5.1.1 Android 坐標系
在觸控事件中使用getRawX()和getRawY()獲取的坐標是Android坐標系的坐標。
5.1.2 視圖坐標系
在觸控事件中使用getX()和getY()獲取的坐標是視圖坐標系的坐標。
5.1.3 觸控事件 MotionEvent
View提供的獲取坐標的方法
getTop():獲取View自身的頂部到父布局頂部的距離。
MotionEvent提供的方法
getX():獲取點擊事件距離控件左邊的距離,即視圖坐標。
getRawX():獲取點擊事件距離整個屏幕左邊的距離,即絕對坐標。
5.2 實現滑動的七種方法
5.2.1 layout 方法
1.使用getX(),getY()來獲取坐標值,layout操作如下:
layout(getLeft() + offsetX,
getTop() + offsetY,
getRight() + offsetX,
getBottom() + offsetY);
2.使用getRawX(),getRawY()來獲取坐標值,layout操作如下:
layout(getLeft() + offsetX,
getTop() + offsetY,
getRight() + offsetX,
getBottom() + offsetY);
//重寫值初始坐標
lastX = rawX;
lastY = rawY;
5.2.2 offsetLeftAndRight() 和 offsetTopAndBottom()
效果同layout方法一樣。
5.2.3 LayoutParams
1.使用布局的LayoutParams
示例代碼如下:
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
params.leftMargin = getLeft() + offsetX;
params.topMargin = getTop() + offsetY;
setLayoutParams(params);
2. 使用ViewGroup的MarginLayoutParams
示例代碼如下:
ViewGroup.LayoutParams params = (ViewGroup.LayoutParams) getLayoutParams();
params.leftMargin = getLeft() + offsetX;
params.topMargin = getTop() + offsetY;
setLayoutParams(params);
5.2.4 scrollTo 和 scrollBy
scrollTo(x, y)表示移動到一個具體的坐標點,而scrollBy(dx, dy)表示移動的增量為dx, dy。
注意:scrollTo, scrollBy移動的是View的content,即讓View的內容移動。
scrollBy以整個屏幕框作為參考系
要實現跟隨手指移動而滑動的效果,就必須將偏移量設為負值。
示例代碼如下:
int offsetX = x - lastX;
int offsetY = y - lastY;
((View) getParent()).scrollBy(-offsetX, -offsetY);
類似的,在使用絕對坐標時,也可以通過scrollTo來實現這一效果。
5.2.5 Scroller
與scrollTo和scrollBy相比, Scroller類可以實現平滑移動的效果, 而不再是瞬間完成的移動。
1.初始化Scroller類
mScroller = new Scroller(context);
2.重寫computeScroll()方法,實現模擬滑動
@Override
public void computeScroll() {
super.computeScroll();
//判斷Scroller是否執行完畢
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(
mScroller.getCurrX(),
mScroller.getCurrY());
//通過重繪來不斷調用computeScroll
invalidate();
}
}
computeScroll()方法是不會自動調用的,只能通過invalidate()->draw()->computeScroll()來間接調用computeScroll()方法.
3.startScroll開啟模擬過程
getScrollX()和getScrollY()方法獲取的是父視圖中content所滑動到的點的坐標,不過要注意這個值得正負,它與在scrollBy、scrollTo中講解
的情況一樣。
case MotiionEvent.ACTION_UP:
//手指離開時,執行滑動過程
View viewGroup = (View) getParent();
mScroller.startScroll(
viewGroup.getScrollX(),
viewGroup.getScrollY(),
-viewGroup.getScrollX(),
-viewGroup.getScrollY());
invalidate();
break;
getScrollX():當前的View的左上角相對於母視圖的左上角的X方向上的偏移量。
5.2.6 屬性動畫
詳情見第七章知識點。
5.2.7 ViewDragHelper
此類為support支持庫中DrawerLayout和SlidingPaneLayout兩個布局的主要支持類。
1.初始化ViewDragHelper
mViewDragHelper = ViewDragHelper.create(this, callback);
第一個參數為要監聽的View,通常是需要一個ViewGroup,即ParentView。第二個參數為整個ViewDragHelper的邏輯核心。
2.攔截事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//將觸摸事件傳遞給ViewDragHelper,此操作必不可少。
mViewDragHelper.processTouchEvent(event);
return true;
}
3.處理computeScroll()
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
4.處理回調Callback
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果當前觸摸的child是mMainView時開始檢測;
return mMainView == child;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
};
通常情況下,只需要返回top和left即可,但當需要更加精確地計算padding等屬性的時候,就需要對left進行一些處理,並返回合適大小的值。
//拖動結束後調用
@Override
public void onViewReleased(View releaseChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起後緩慢移動到指定的位置
if (mMainView.getLeft() < 500) {
//關閉菜單
//相當於Scroller的computeScroll方法
mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
} else {
//打開菜單
mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
5.最後,加載完布局文件後調用onFinishInflate()方法
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMainView = getChildAt(0);
mMenuView = getChildAt(1);
}
並且可以在onSizeChanged()方法中獲取View的寬度
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldH) {
super.onSizeChanged(w, h, oldw, oldH);
mWidth = mMainView.getMeasureWidth();
}
此外,還有幾個有用的方法,如下:
onViewCaptured()
這個事件在用戶觸摸到View後回調
onViewDragStateChanged()
這個事件在正在拖拽狀態改變時回調,比如idle,dragging等狀態。
onViewPositionChanged()
這個事件在位置改變時回調,常用於滑動時更改scale進行縮放等效果。
第六章 Android 繪圖機制與處理技巧
6.1 屏幕的尺寸信息
6.2 2D繪圖基礎
DrawPosText,在指定位置繪制文本。
6.3 Android XML 繪圖
6.4 Android 繪圖技巧
6.4.1 Canvas
Canvas.save():保存畫布。
Canvas.restore():可理解為PhotoShop中的合並圖層操作。
Canvas.translate():可理解為坐標系的平移。
Canvas.rotate():可理解為坐標系的翻轉。
6.4.2 Layer 圖層
Android通過調用saveLayer()方法、saveLayerAlpha()方法將一個圖層入棧,使用restore()方法、restoreToCount()方法將一個圖層出棧。入棧的
時候,所有的操作都發生在這個圖層上,而出棧的時候,則會把圖層繪制在上層Canvas上。
6.5 Android 圖像處理之色彩特效處理
6.5.1 色彩矩陣分析
6.5.1.1 改變偏移量
6.5.1.2 改變顏色系數
6.5.1.3 改變色光屬性
顏色矩陣ColorMatrix
ColorMatrix colorMatrix = new ColorMatrix();
1.色調
colorMatrix.setRotate(0, hue);
Android系統提供了setRotate(int axis, float degree)來幫助我們設置顏色的色調。第一個參數,系統分別用0、1、2來代表Red, Green, Blue三種顏色的處理;而第二個參數,就是需要處理的值。
2.飽和度
colorMatrix.setSaturation(saturation);
Android系統提供了setSaturation(float sat)方法來設置顏色的飽和度,參數即代表設置的顏色的飽和度值。飽和度為0,圖像變為灰度圖像。
3.亮度
colorMatrix.setScale(lum, lum, lum, 1);
當三原色以相同的比例混合時,就會顯示出白色。當亮度為0時,圖像就變為全黑了。
Bitmap bitmap = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
canvas.drawBitmap(bm, 0, 0,Paint);
return bmp;
此外,Android還封裝了矩陣的乘法運算,它提供了postConcat()方法來將矩陣的作用效果混合。在設置好處理的顏色矩陣後,通過使用Paint類的
setColorFilter()方法,將通過imageMatrix構造的ColorMatrixColorFilter對象傳遞進去,並使用這個畫筆繪制原來的圖像,從而將顏色矩陣作用到原圖中。
同時,Android系統也不允許直接修改原圖,類似Photoshop中的鎖定,必須通過原圖創建一個同樣大小的Bitmap,並將原圖繪制到該Bitamp中,以一
個副本的形式來修改圖像。代碼如下所示,bm即為原圖,bmp為創建的副本。
6.5.2 Android 顏色矩陣——ColorMatrix
需要注意的是,我們無法再onCreate()方法中獲得視圖的寬高值,因此只能通過View的post()方法,在視圖創建完畢後獲取其寬高值。
mGroup.post(new Runnable() {
@Override
public void run() {
// 獲取寬高信息
mEtWidth = mGroup.getWidth() / 5;
mEtHeight = mGroup.getHeight() / 4;
addEts();
initMatrix();
}
}
6.5.3 常用圖像顏色矩陣處理效果
6.5.4 像素點分析
Bitmap.getPixels()方法幫我們提取整個Bitmap中的像素點並保存到一個數組中。
bitmap.getPixels(pixels, offset, stride, x, y, width, height);
bitmap.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height);
6.5.5 常用圖像像素點處理效果
6.5.5.1 底片效果
6.5.5.2 老照片效果
6.5.5.3 浮雕效果
6.6 Android 圖像處理之圖形特效處理
6.6.1 Android 變形矩陣——Matrix
matri X .setRotate()——旋轉變換
matri X .setTranslate()——平移變換
matri X .setScale()——縮放變換
matri X .setSkew()——錯切變換
pre()和post()——提供矩陣的前乘和後乘運算
6.6.2 像素塊分析
除了根據Matrix來進行圖形特效處理之外,此外還可以通過drawBitmapMesh()方法來進行處理。
6.7 Android 圖像處理之畫筆特效處理
6.7.1 PorterDuffXfermode
PorterDuffXfermode設置的是兩個圖層交集區域的顯示方式,dst是先畫的圖形,src是後畫的圖形。
一般是先用一個普通畫筆畫一個遮罩層,再用帶PorterDuffXfermode的畫筆將圖像畫在遮罩層上。
給Paint設置一些屬性,能讓它的筆觸和連接處更圓滑點,即Paint.Join.ROUND和Paint.Cap.ROUND。
在設置刮刮卡上層覆蓋圖像數據的時候,應該初始化alpha為0,這是因為在使用PorterDuffXfermode進行圖層混合時,會同時考慮透明通道的值。
6.7.2 Shader
Shader又被稱之為著色器、渲染器,它用來實現一系列的漸變、渲染效果。Android中的Shader包括以下幾種:
1.BitmapShader——位圖 Shader
2.LinearGradient——線性 Shader
3.RadialGradient——光束 Shader
4.SweepGradient——梯度 Shader
5.ComposeShader——混合 Shader
Bitmap產生的是一個圖像,有點像Photoshop中的圖像填充漸變。它的作用是通過Paint對畫布進行指定Bitmap的填充,有如下三種填充模式:
1.CLAMP 拉伸——拉伸的是圖片最後的那一個像素,不斷重復。
2.REPEAT 重復——橫向、縱向不斷重復。
3.MIRROR 鏡像——橫向不斷翻轉重復,縱向不斷翻轉重復。
注:使用matrix.setScale(1F, -1F)可以實現圖片的垂直翻轉。
6.7.3 PathEffect
PathEffect就是用各種筆觸效果來繪制一個路徑。如下是幾種繪制繪制pathEffect的方式:
1.沒效果。
2.CornerPathEffect
將拐角處變得圓滑。
3.DiscreatPathEffect
會使線段上多出許多雜點。
4.DashPathEffect
會使線段變成虛線。
5.PathDashPathEffect
同4,外加能設置點的形狀,比如圓形,三角形。
6.ComposePathEffect
將2種不同的PathEffect組合起來,第一個參數是outer,第二個參數是inner。
6.8 View的孿生兄弟——SurfaceView
6.8.1 SurfaceView 與 View 的區別
1.View主要適用於主動更新的情況,而SurfaceView主要適用於被動地進行更新,如頻繁地刷新。
2.View在主線程中進行畫面的刷新,而SurfaceView通過在子線程中對頁面進行刷新。
3.View在繪圖時沒有實現雙緩沖機制,而SuraceView在底層實現機制中就已經實現了雙緩沖機制。
總結:如果你的自定義View需要頻繁地刷新,或者刷新時處理的數據量比較大,那麼就考慮使用SurfaceView來代替View。
6.8.2 Surface的使用
SufaceView標准模板見代碼集。
6.8.3 SurfaceView的實例
6.8.3.1 正弦曲線
6.8.3.2 繪圖板
模板代碼中,我們在線程中不斷地使用draw()方法來進行繪制,但是有時候繪制不需要那麼頻繁。因此我們在子線程中可以進行sleep操作,
盡可能地節省系統資源。
通過判斷draw()方法所使用的邏輯時長來確定sleep的時長,這是一個非常通用的解決方法。代碼中的100ms是一個經驗值,這個值一般都
在50ms-100ms。
第七章 Android動畫機制與使用技巧
7.1 Android View 動畫框架
View動畫總共分為4種:
1.透明度動畫。
2.旋轉動畫。
3.縮放動畫。
4.位移動畫。
此外,可使用動畫集合將上面的動畫組合起來。
而且,動畫也還有相應的監聽回調:
animation.setAnimationListener...
通過監聽,可以獲取動畫開始,結束和重復事件。
7.2 Android 屬性動畫分析
7.2.1 ObejctAnimator
在使用ObjectAnimator時有一點非常重要,就是操作的屬性必須要有get,set方法,不然ObejctAnimtor就無法起效。
如果要操作的屬性沒有get,set方法,Google在應用層提供了兩個方法解決:
1.使用自定義屬性類或者包裝類間接增加set,get方法。
2.使用ValueAnimtor。
7.2.2 PropertyValuesHolder
針對同一個對象的多個屬性,同時作用多種動畫。
7.2.3 ValueAnimator
ValueAnimator的一般使用方法是在其AnimatorUpdateListener的方法中監聽數值變化,從而完成動畫變換。
animation.getAnimatorValue()獲取其數值。
7.2.4 動畫時間的監聽
給ObjectAnimator設置addListener()監聽即可。
一般只關心onAnimationEnd事件,可以使用AnimatorListenerAdapter來對必要的事件進行監聽。
7.2.5 AnimatorSet
通過playTogether(),playSequentially(),animSet.play.with()、before()、after()這些方法來控制多個動畫的協同工作方式。
7.2.6 在XML中使用屬性動畫
在程序中使用XML定義的屬性動畫:
Animator anim = AnimatorInflater.loadAnimation(this, R.animator.scalex);
anim.setTarget(mMv);
anim.start();
7.2.7 View的animate方法
可以直接驅動屬性動畫,是屬性動畫的一種簡寫方式。
7.3 Android布局動畫
所謂的布局動畫,其實是指作用在ViewGroup上,給ViewGroup增加View時添加一個動畫過渡效果。
最簡單的布局動畫是在ViewGroup中使用:
android:animateLayoutChanges="true"
子View會呈現逐漸顯示得過渡效果。
另外,還可以通過LayoutAnimationController來定義一個子View的過渡效果。
7.4 Interpolators(插值器)
定義動畫變換速率,類似物理中的加速度,其作用主要是控制目標變量的變化值進行對應的變化。
7.5 自定義動畫
實現applyTransformation的邏輯並覆蓋父類的initialize方法來實現一些初始化工作。
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final Matrix matrix = t.getMatrix();
//通過matrix的各種操作來實現動畫
matrix.XXXXXX;
}
接下來,還可以結合矩陣來自定義實現3D效果。使用Camera類可以創建3D動畫效果,它封裝了OpenGL的3D動畫。
7.6 Android 5.X SVG 矢量動畫機制
Google在Android 5.X中增加了對SVG矢量圖形的支持。它與Bitmap相比,SVG最大的優點就是放大不會失真。而且Bitmap需要為不同分辨率
設計多套圖標,而SVG不需要。
7.6.1
標簽
7.6.2 SVG常用指令
7.6.3 SVG 編輯器
7.6.4 Android中使用SVG
Google在Android 5.X中提供了2個新的API來支持SVG:
1.VectorDrawable。
2.AnimatedVectorDrawable。
VectorDrawable可以讓你創建基於XML的SVG圖形,並結合AnimtatedVectorDrawable來實現動畫效果。
7.6.4.1 VectorDrawable
在XML中通過使用
標簽來聲明對VectorDrawable的使用。
其中,viewportHeight,viewportWidth表示SVG圖形劃分的比例。
通過添加
和來標簽來繪制一個SVG圖形,其中就是要SVG時要用到的指令。
7.6.4.2 AnimatorVectorDrawable
AnimatedVectorDrawable的作用就是給VectorDrawable提供動畫效果。它作為一個膠水,來連接VectorDrawable和ObejctAnimator對象。
注意:AnimatorVectorDrawable指定的Target的name屬性,必須和VectorDrawable中的name屬性一致,這樣系統才能找到要實現的動畫元素。
此外,在ObjectAnimator中,如果指定屬性為pathData,需要添加android:valueType="pathType"告訴系統進行pathData變換。
7.6.5 SVG 動畫實例
7.6.5.1 線圖動畫
7.6.5.2 模擬三球儀
7.6.5.3 軌跡動畫
7.7 Android 動畫特效
7.7.1 靈動菜單
7.7.2 計時器動畫
7.7.3 下拉展開動畫
第八章 Activity 和 Activity 調用棧分析
8.1 Activity
8.1.1 起源
8.1.2 Activity 形態
當Activity失去焦點,被一個新的非全屏的Activity或者一個透明的Activity放置在棧頂時,Activity就轉化為Paused形態。
如果一個Activity被另一個Activity完全覆蓋,那麼Activity就會進入Stopped形態。
當Activity被系統回收掉或者Activity從來沒有被創建過,Activity就處於Killed形態。
8.1.3 生命周期
8.1.3.1 Activity 的啟動與銷毀過程
onPause()和onStop():清除Activity的資源,避免浪費。
8.1.3.2 Activity 的暫停和恢復過程
8.1.3.3 Activity 的停止過程
8.1.3.4 Activity 的重新創建過程
Android系統已經默認實現了控件的緩存狀態,以此來減少開發者需要實現的緩存邏輯。
8.2 Android 任務棧簡介
注意:一個Task中的Activity可能來自不同的App,同一個App中的Activity也可能來自不同的Task。
8.3 AndroidMainifest 啟動模式
8.3.1 standard
每次都會創建新的實例,覆蓋在原Activity上。
8.3.2 singleTop
啟動時,系統會判斷當前棧頂Activity是不是要啟動的Activity,如果是則不創建新的Activity而直接引用這個Activity;如果不是則創建新的Activity。
通常適用於接受消息後顯示的界面。
雖然不會創建新的實例,但是系統會在Activity啟動時調用onNewIntent()方法。
8.3.3 singleTask
檢測整個Activity棧中是否存在當前需要的Activity。如果存在,則將該Activity置於棧頂,並將該Activity上的其它Activity銷毀。不過這裡指的是在同
一個App中啟動這個singleTask的Activity,如果是其他程序以singleTask模式來啟動這個Activity,那麼它將創建一個新的Task。
注意:如果啟動的模式為singleTask的Activity已經在後台的一個Task中了,那麼啟動後,後台的這個Task將一起被切換到前台。
通常可以退出整個應用,將主Activity設置為singleTask,在要退出的Activity中轉到主Activity,重寫主Activity的onNewIntent方法,加上finish()方法。
8.3.4 singleInstance
Activity會出現在一個新的Task中,而且該Task中只有該Activity。
通常適用於與程序分離的界面。
注意:如果在一個singleTop或者singleInstance的ActivityA中以startActivityforResult()來啟動ActivityB,那麼系統將直接返回Activity.RESULT_CANCELED,而不會去等待返回。
Android開發者認為,不同Task之間默認是不能傳遞數據的,如果一定傳,只能通過Intent來綁定數據。
8.4 Intent Flag 啟動模式
常用的Flag:
1.Intent.FLAG_ACTIVITY_NEW_TAKS
使用一個新的Task來啟動一個Activity,但啟動的每個Activity都將在一個新的Task中。
通常適用於從Service中啟動Activity的場景,因為Service中不存在Activity棧。
2.FLAG_ACTIVITY_SINGLE_TOP
同andorid:launchMode="singleTop"效果。
3.FLAG_ACITVITY_CLEAR_TOP
同android:launchMode="singleTask"效果。
4.FLAG_ACTIVITY_NO_HISTORY
使用這種模式啟動Activity,當該Activity啟動其他Activity後,該Activity就消失了。
8.5 清空任務棧
1.clearTaskOnLaunch
每次返回該Activity時,都將該Activity之上的Activity清除。
2.finishOnTaskLaunch
當離開這個Activity所處的Task,當用戶再返回的時候,這個Activity就會finish掉。
3.alwaysRetainTaskState
該Activity所在的Task不接受任何清理命令。
8.6 Activity 任務棧使用
第九章 Android 系統信息與安全機制
9.1 Android 系統信息獲取
9.1.1 android.os.Build
包含系統編譯時的大量設備、配置信息。
9.1.2 SystemProperty
包含許多系統配置屬性值和參數。
9.1.3 Android系統信息實例
通過System.getProperty("XXXX")可以獲取到系統的屬性值。此外,在system/build.prop和、/proc目錄下也可以訪問到。
9.2 Android Apk 應用信息獲取值 PackageManager
9.2.1 PackageManager
一些常用的系統封裝信息及其對應的常用方法:
getPackageManager。
1.ActivityInfo。
2.ServiceInfo。
3.ApplicationInfo。
getApplicationInfo。
getApplicationIcon。
getInstalledPackages。
4.PackageInfo。
getInstalledPackages。
5.ResolveInfo。
queryIntentActivities。
queryIntentServices。
resolveActivity。
resolveService。
9.3 Android Apk 應用信息獲取之 ActivityManager
ActivityManager也封裝了不少Bean對象,下面選幾個比較重要的來看看:
ActivityManager.MemoryInfo:獲取全局的內存使用信息。
Debug.MemoryInfo:獲取統計進程下的內存信息。
RunningAppProcessInfo:運行進程的信息。
RunningServiceInfo:運行服務的信息。
9.4 解析 Packages.xml 獲取系統信息
9.5 Android 安全機制
9.5.1 Android 安全機制簡介
9.5.1.1 第一道防線
代碼安全機制——代碼混淆proguard。
9.5.1.2 第二道防線
應用接入權限控制——AndroidMainifest文件權限聲明、權限檢查機制。
9.5.1.3 第三道防線
應用簽名機制——數字證書。
9.5.1.4 第四道防線
Linux內核層安全機制——Uid、訪問權限控制。
9.5.1.5 第五道防線
Android虛擬沙箱機制——沙箱隔離。
9.5.2 Android 系統安全隱患
9.5.2.1 代碼漏洞
9.5.2.2 Root風險
9.5.2.3 安全機制不健全
9.5.2.4 用戶安全意識
9.5.2.5 Android 開發原則與安全
9.5.3 Android Apk 反編譯
9.5.3.1 apktool
9.5.3.2 Dex2jar、jd-gui
9.5.4 Android Apk 加密
即代碼混淆。
第十章 Android 性能優化
10.1 布局優化
10.1.1 Android UI 渲染機制
10.1.2 避免 Overdraw
10.1.3 優化布局層級
10.1.4 避免嵌套過多無用布局。
10.1.4.1 使用
標簽重用 Layout
10.1.4.2 使用
實現View的延遲加載
兩次調用inflate方法會報錯的原因:不管使用哪種方式,一旦
被設置為可見或是被inflate了,就不存在了,而取而代之的是被
inflate的layout,並將這個Layout的ID重新設置為
中通過android:inflate屬性所指定的ID,這也是為什麼兩次調用inflate方法會報錯的原因。
10.1.5 Hierarchy Viewer
通過hierarchyviewer工具,就可以很快地在視圖樹種找到冗余的布局,從而有目的地優化布局。
10.2 內存優化
10.2.1 什麼是內存
通常情況下我們所說的內存就是手機的RAM,它包括以下幾個部分:
1.寄存器
速度最快的存儲場所,因為寄存器位於處理器內部,在程序中無法控制。
2.棧
存放基本類型的數據和對象的引用。
3.堆
堆內存用來存放由new創建的對象和數組。在堆中分配的內存,由Java虛擬機的GC管理。
4.靜態存儲區域
在固定的位置存放應用程序運行時一直存在的數據。
5.常量池
10.2.2 獲取 Android 系統內存信息
10.2.2.1 Process Stats
系統內存監視服務,可通過“Setting-Developer options-Process Stats”來啟動該功能的界面。
10.2.2.2 Meminfo
系統上一個非常重要的內存監視工具,可以通過在“Setting-Apps-Running”中打開界面。
10.2.3 內存回收
10.2.4 內存優化實例
10.2.4.1 Bitmap 優化
1.使用適當分辨率和大小的圖片。
2.及時回收內存。
3.使用圖片緩存。
10.2.4.2 代碼優化
1.對常量使用static修飾符。
2.使用靜態方法,靜態方法會比普通方法提高15%左右的訪問速度。
3.減少不必要的成員變量。
4.減少不必要的對象。
5.盡量不要使用枚舉,少用迭代器。
6.對Cursor、Receiver、Sensor、File等對象,要非常注意它們的創建、回收與注冊、解注冊。
7.避免使用IOC框架,大量使用反射會帶來性能下降。
8.使用RenderScript、OpenGL來進行非常復雜的繪圖操作。
注:RenderScript (渲染腳本)是一種低級的高性能編程語言,用於3D渲染和處理密集型計算(3D播放等和關於CPU密集型的計算)。
OpenGL(全寫Open Graphics Library)是指定義了一個跨編程語言、跨平台的編程接口規格的專業的圖形程序接口。它用於三維圖像(二維的亦可),
是一個功能強大,調用方便的底層圖形庫。
9.使用SurfaceView來替代View進行大量、頻繁的繪圖操作。
10.盡量使用視圖緩存,而不是每次都執行inflate()方法解析視圖。
10.3 Lint 工具
Android Lint工具是Android Studio中集成的一個Android的代碼提示工具。
10.4 使用 Android Studio的Memory Monitor 工具
10.5 使用 TraceView 工具優化 App性能
10.5.1 生成 TraceView 日志的兩種方法
10.5.1.1 通過代碼生成精確范圍的 TraceView 日志
使用Debug類的方法開啟TraceView監聽。通過調用Debug.startmethodTracing()方法開啟監聽,通過調用Debug.stopMethodTracing()方法結束 監聽,我們可以使用這兩個方法來包圍要監聽的代碼塊,例如在onCreate()方法裡調用startMethodTracing()方法來開始監聽,在onDestroy()方法裡使 用stopMethodTracing()方法來結束監聽。
10.5.1.2 通過 Android Device Monitor 生成 TraceView 日志
1.整體監聽
跟蹤每個方法執行的全部過程,這種方式資源消耗較大。
2.抽樣監聽
按照指定的頻率來進行抽樣調查,這種方式需要執行較長時間獲取較准確的樣本數據。
10.5.2 打開 TraceView 日志
10.5.3 分析 TraceView 日志
10.5.3.1 時間軸區域
10.5.3.2 Profile 區域
10.6 使用 MAT 工具分析 App 內存狀態
10.6.1 生成 HPROF 文件
10.6.2 分析 HPROF 文件
1.Histogram。
2.Dominator Tree。
10.7 使用 Dumpsys 命令分析系統狀態
第十一章 搭建雲端服務器
11.1 移動後端服務介紹
Bass,即服務端打包。
11.2 使用 Bmob 創建移動後端服務
11.2.1 數據服務
11.2.2 推送服務
第十二章 Android 5.X 新特性詳解
12.1 Android 5.X UI 設計初步
12.1.1 材料的形態模擬
12.1.2 更加真實的動畫
12.1.3 大色塊的使用
12.2 Material Design 主題
Material Design現在有3種主題可以設置:
@android:style/Theme.Material(dark version)
@android:style/Theme.Material.Light(light version)
@android:style/Theme.Material.Light.DarkActionBar
12.3 Palette(著色板)
Palette使用的代碼流程如下:
//創建Palette對象(Swatch為顏色樣本)
Palette.generateAsync(bitmap,
new PaletteAsyncListener() {
@Override
public void on Generated(Palette palette) {
//通過Palette來獲取對應的色調
Palette.Swatch vibrant = palette.getDarkVibrantSwatch();
//將顏色設置給相應的組件
getActionBar().setBackgroundDrawable(new ColorDrawable(vibrant.getRgb()));
Window window = getWindow();
window.setStatusBarColor(vibrant.getRgb());
}
});
12.4 視圖與陰影
12.4.1 陰影效果
Z = elevation + translationZ
elevation(高度)是靜態的成員,translation(平移)Z可以在代碼中使用來實現動畫效果。
12.5 Tinting 和 Clipping
12.5.1 Tinting(著色)
只需在XML中配置好所需的tint和tintMode就可以了。
12.5.2 Clipping(裁剪)
//獲取Outline
ViewOutlineProvider viewOutlineProvider1 =
new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
//修改outline為特定形狀
outline.setRoundRect(0, 0,
view.getWidth(),
view.getHeight(), 30);
}
};
//獲取Outline
ViewOutlineProvider viewOutlineProvider2 =
new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
//修改outline為特定形狀
outline.setOval(0, 0
view.getWidth(),
view.getHeight());
}
};
//重新設置形狀
view1.setOutlineProvider(viewOutlineProvider1);
view2.setOutlineProvider(viewOutlineProvider2);
12.6 列表與卡片
12.6.1 RecyclerView
12.6.2 CardView
12.7 Activity過渡動畫
Android 5.X提供了三種Transition類型。
1.進入。
2.退出。
3.共享元素。
其中,進入和退出效果包括:
1.explode(分解)——從屏幕中間進或出,移動視圖。
2.slide(滑動)——從屏幕邊緣進或出,移動視圖。
3.fade(淡出)——通過改變屏幕上視圖的不透明度達到添加後移除視圖。
共享元素包括:
1.changeBounds——改變目標視圖的布局邊界。
2.changeClipBounds——裁剪目標視圖邊界
3.changeTransform——改變目標視圖的縮放比例和旋轉角度。
4.changeImageTransform——改變目標圖片的大小和縮放比例。
普通的三種Activity過渡動畫:
在ActivityA中:
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
在ActivityB中:
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
或者在樣式文件中設置如下所示代碼。
- true
設置進入ActivityB的具體的動畫效果:
getWindow().setEnterTransition(new Explode());
getWindow().setEnterTransition(new Slide());
getWindow().setEnterTransition(new Fade());
設置離開ActivityB的具體的動畫效果:
getWindow().setExitTransition(new Explode());
getWindow().setExitTransition(new Slide());
getWindow().setExitTransition(new Fade());
共享元素的動畫效果實現:
在Activity1的布局文件中設置共享的元素,給它增加相應的屬性:
android:transitionName="XXX"
在Activity2的布局文件中,給要實現共享效果的元素也增加相同的屬性:
android:transitionName="XXX"
如果只需一個共享元素,則:
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(
this,
view,
"share").toBundle());
如果有多個共享的元素,則:
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(
this,
Pair.create(view, "share"),
Pair.create(fab, "fab")).toBundle());
12.8 Material Design 動畫效果
12.8.1 Ripple 效果
波紋有邊界是指波紋被限制在控件的邊界中,而波紋超出邊界則是波紋不會限制在控件邊界中,會呈圓形發散出去。
//波紋有邊界
android:background="?android:attr/selectableItemBackground"
//波紋超出邊界
android:background="?android:attr/selectableItemBackgroundBorderless"
同樣,也可以在XML文件中創建具有Ripple效果的XML文件。
12.8.2 Circular Reveal
具體表現為一個View以圓形的形式展開、揭示出來。通過ViewAnimationUtils.createCircularReveal()方法可以創建一個RevealAnimator動畫。
12.8.3 View state changes Animation
1.stateListAnimator
在XML中定義一個StateListAnimator,並添加到Selector中。
//將StateListAnimator添加給一個視圖
android:stateListAnimator="@drawable/anim_change"
在代碼中也可以調用AnimationInflater.loadStateListAnimator()方法,並且通過View.setStateListAnimator()分配動畫到視圖上。
2.animated-selector
使用標簽來給這兩種狀態設置不同的過渡圖片,非常類似幀動畫效果。
12.9 Toolbar
Toolbar與Actionbar最大的區別就是Toolbar更加自由、可控。
12.10 Notification
12.10.1 基本的 Notification
通過Notification.Builder創建一個Notification的builder。
Notification.Builder builder = new Notification.Builder(this);
PendingIntent的使用非常簡單,只需要在普通Intent的基礎上包裝一層就可以了。
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.baidu.com"));
//構造PendingIntent
PendingIntent pendingIntent = PedingIntent.getActivity(this, 0, intent, 0);
與使用AlertDialog一樣,可以給builder對象增加各種屬性。
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentIntent(pendingIntent);
builder.setAutoCancel(pendingIntent);
builder.setLargeIcon(BitmapFactory.decodeResource(getResource(),
R.drawable.ic_launcher));
builder.setContentTitle("Basic Notifications");
builder.setContentText("I am a basic notification");
builder.setSubText("it is really basic");
將Notification顯示出來。
//通過Notificationmanager來發出Notification
NotificationManager notificationManager =
(NotificatioinManager) getSystemService(
NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID_BASIC,
builder.build());
每個Notification都會有一個ID,這個ID就是用來區分不同的App的Notification的。
12.10.2 折疊式 Notification
它擁有兩個視圖狀態,一個是普通狀態下的視圖狀態,另一個是展開狀態下的視圖狀態。
//通過RemoteViews來創建自定義的Notification視圖
RemoteView contentView =
new RemoteViews(getPackageName(), R.layout.notification);
contentView.setTextViewText(R.id.textView, "show me when collapsed");
將一個視圖指定為Notification正常狀態下的視圖。
notification.contentView = contentView;
指示展開時的視圖。
notification.bigContentView = expandedView;
12.10.3 懸掛式 Notification
懸掛式Notification是在Android 5.0中新增的方式,通過setFullScreenIntent,我們很輕松地將一個Notification變成了懸掛式Notification。
12.10.4 顯示等級的 Notification
Android 5.X 中新加入的一種模式——Notification的顯示等級。Android 5.X將Notification分成了三個等級:
1.VISIBILITY_PRIVATE——表示只有當沒有鎖屏的時候會顯示。
2.VISIBILITY_PUBLIC——表示在任何情況下都會顯示。
3.VISIBILITY_SECRET——表示在pin、password等安全鎖和沒有鎖屏的情況下才能夠顯示。
設置Notification等級的方式非常簡單,同樣是借助builder對象。
builder.setVisibility(Notifaction.VISIBILITY_PUBILC);
增加了設置Notification背景顏色的借口。
builder.setColor(Color.RED);
增加了設置Notification的category接口,category用來顯示Notification的顯示位置。
bulider.setCategory(Notification.CATEGORY_MESSAGE);
第十三章 Android 實例提高
13.1 移動迷宮——拼圖游戲
13.2 魔幻矩陣——2048