第 三 章 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.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