《Android开发安卓艺术探索索》值得买吗

亚马逊意外错误报歉,由于程序执行时,遇到意外错误,您刚刚操作没有执行成功,请稍后重试。或将此错误报告给我们的客服中心:推荐您,确认您的操作无误后,再继续其他操作。您可以通过亚马逊,获得更多的帮助。第三章& View的事件体系
  3.1 View基础知识
  3.1.1 什么是view
    View 是Android中所有控件的基类,是一种界面层的控件的一种抽象,它代表了一个控件。
  3.1.2 View的位置参数
    View的位置主要由它的四个顶点来决定,分别对应于View的四个属性:top,left,right,bottom;需要注意的是这些坐标都是相对于View的父容器来说的;(在Android中X轴和Y轴的正方向分别为右和下)。
    3.0开始新增的属性
    x-& view左上角横坐标;
    y-& view左上角纵坐标;
    translationX-& view左上角相对于父容器的水平偏移量;
    translationY-& view左上角相对于父容器的垂直偏移量;
    View在平移过程中,top和left表示的是原始左上角的位置信息,其值并不会发生变化,此时发生变化的是x,y,translationX,translationY这四个参数。
  3.1.3 MotionEvent和TouchSlop
    1 MotionEvent
    getX和getY方法返回的是当前View左上角的x和y坐标,而getRawC和getRawY方法返回的是相对于手机屏幕左上角的x和y坐标。
    2 TouchSlop
    TouchSlop是系统所能识别出的被认为是滑动的最小距离,是一个常量,通过如下方式可以获得这个常量:ViewConfiguration.get(getContext()).getScaledTouchSlop();
  3.1.4 VelocityTracker,GestureDetector和Scroller
    1&VelocityTracker
    VelocityTracker&速度追踪,追踪手指在滑动过程中的速度(水平垂直两个方向),注意速度可以为负值,当手指从右向左滑动时,水平方向速度即为负值。
    2&GestureDetector
    GestureDetector&手势检测,用于辅助检测用户的单击,滑动,长,双击等行为。
    建议:如果只是监听滑动相关的,在onTouchEvent中实现,如果要监听双击行为就用GestureDetector。
    3 Scroller
    Scroller&弹性滑动,用于实现View的弹性滑动(有过渡效果的滑动),需要配合View的computeScroll方法使用。
  3.2 View的滑动
    实现View滑动的三种方式:
    1 通过View本身的scrollTo/scrollBy方法实现滑动;
    2 通过动画给View施加平移效果实现滑动;
    3 通过改变View的LayoutParams使得View重新布局实现滑动;
  3.2.1 使用scrollTo/scrollBy
    scrollTo/scrollBy的源码如下:
public void scrollTo(int x ,int y){
if(mScrollX!=x||mScrollY!=y){
int oldX=mScrollX;
int oldY=mScrollY;
mScrollX=x;
mScrollY=y;
invalidateParentCaches();
onScrollChanged(mScrollX,mScrollY,oldX,oldY);
if(!awakenScrollBars()){
postInvalidateOnAnimation();
public void scrollBy(int x,int y){
scrollTo(mScrollX+x,mScrollY+y);
    其中mScrollX的值总是等于View左边缘和View内容左边缘在水平方向的距离,mScrollY的值总是等于View的上边缘和View内容上边缘在垂直方向上的距离;
    &scrollTo/scrollBy只能改变View内容的位置而不能改变自身在布局中的位置。
    &也就是说使用scrollTo/scrollBy来实现View的滑动只能将View的内容进行移动,并不能将View本身进行移动。
  3.2.2 使用动画
    使用动画来移动View 主要是操作View的translationX和translationY属性。(传统的View动画和属性动画)
    &View动画是对View的影像操作,它并不能真正改变View的位置参数,包括宽/高。
  3.2.3 改变布局参数
    主要是通过改变View的LayoutParams来实现;下面的代码展示如何给一个mButton1重新设置LayoutParams:
MarginLayoutParams params=(MarginLayoutParams)mButton1.getLayoutParams();
params.width+=100;
params.leftMargin+=100;
mButton1.requestLayout();//或者mButton1.setLayoutParams(params)
  3.2.4各种滑动方式的对比
    scrollTo/scrollBy:操作简单,适合对View内容的滑动;
    &动画:操作简单,主要适用于没有交互的View和实现复杂的动画效果;
    &改变布局参数:操作稍微复杂,适用于有交互的View。
  3.3弹性滑动
  3.3.1使用Scroller
&    注意Scroller产生的滑动也是对View内容的滑动而非View本身位置的改变。
    Scroller的典型使用方式如下:
Scroller scroller=new Scroller(context);
//缓慢滚动到指定位置
private void smoothScrollTo(int destX,int destY){
int scrollx=getScrollX();
int deltaX=destX-scrollX;
//1000ms内滑向destX,效果就是慢慢滑动
scroller.startScroll(scrollX,0,deltaX,0,1000);
invalidate();
public void computeScroll(){
if(puteScrollOffset()){
scrollTo(scroller.getCurrX(),scroller.getCurrY());
postInvalidate();
    Scroller的工作机制:Scroller本身并不能实现View的滑动,它需要配合View的computeScroll方法才能完成弹性滑动的效果,它不断的让View重绘,而每一次重绘距滑动起始时间会有一个时间间隔,通过这个时间间隔Scroller就可以得出View当前的滑动位置,知道了滑动位置就可以通过scrollTo方法来View的滑动。就这样,View的每一次重绘都会导致View进行小幅度的滑动,而多次的小幅度滑动就组成了弹性滑动。
  3.4 View的时间分发机制
  3.4.1 点击事件的传递规则
    点击事件的分发过程主要由三个方法来完成。
    dispatchTouchEvent() :用来进行事件的分发,如果事件能够传递给当前View,此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法影响,表示是否消耗当前事件。
    onInterceptTouchEvent() :在dispatchTouchEvent方法内部调用,判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列中,此方法不会在被调用,返回结果表示是否拦截当前事件。
    onTouchEvent() :在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到其他事件。
    事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件,这个事件序列以down事件开始,中间含有数量不固定的move事件,最终以up事件结束。
    理解了这三个方法的工作过程也就理解了事件的分发机制。伪代码表示三个方法的关系如下:
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume=false;
if(onInterceptTouchEvent(ev)){
consume=onTouchEvent(ev);
consume=child.dispatchTouchEvent(ev);
    具体传递规则:对于一个根ViewGroup来说,点击事件产生以后,首先会传递给它,这时它的dispatchTouchEvent就会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着事件就会交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用,如果这个ViewGroup的onInterceptTouchEvent返回false就表示它不拦截当前事件,这时当前事件就会继续传递给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此反复直到事件被最终处理。
   & 当一个点击事件产生后,它的传递过程遵循如下顺序:Activity-&Window-&View,即事件总是先传递给Activity,Activity在传递给Window,最后Window在传递给顶级View。顶级View接收到事件后,就会按照事件分发机制去分发事件。考虑一种情况,如果一个View的onTouchEvent返回false,那么它的父容器的onTouchEvent将会被调用,依此类推。如果所用的元素都不处理这个事件,那么这个事件将会最终传递给Activity处理,即Activity的onTouchEvent方法被调用。    几个重要结论:
    正常情况下,一个事件序列只能被一个VIew拦截且消耗,因为一旦一个元素拦截了某个事件,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别有两个View同时处理,但是通过特殊手段可以做到,比如一个View将本该自己处理的事件通过onTouchEvent强行传递给其他View处理。
    某个View一旦决定拦截,那么这一个事件序列都只能由它处理,并且它的onInterceptTouchEvent不会再被调用。
    某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么同一事件序列中的其他事件都不会再交给它处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent会被调用。意思就是事件一旦交给一个View处理,那么它就必须消耗掉,否则同一事件序列中剩下的事件就不再交给它来处理了。
    如果View不消耗出ACTION_DOWN以外的其他事件,那么这个点击事件会消失,最终这些消失的点击事件会传递给Activity处理。
    ViewGroup默认不拦截任何事件。
    View没有onInterceptTouchEvent方法,一旦有事件传递给它,那么它的onTouchEvent方法就会被调用。
    View的OnTouchEvent方法默认都会消耗事件(返回true)。
    View的enable属性不影响onTouchEvent的默认返回值。
    onClick会发生的前提是当前View是可点击的,并且它收到了down和up的事件。
    事件的传递过程是由外向内的,即事件总是项传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。(requestDisallowInterceptTouchEvent方法主要设置父元素中的FLAG_DISALLOW_INTERCEPT标记位,一旦设置后,ViewGroup将无法拦截除了ACTION_DOWN以外的其他点击事件)
  3.5 View的滑动冲突
&    常见的滑动冲突场景
     解决滑动冲突的方式:外部拦截法和内部拦截法。
    1 外部拦截法
    点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要此事件就不拦截。
    实现方法主要是重写父容器的onInterceptTouchEvent方法,代码如下:
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted=false;
int x=(int)event.getX();
int y=(int)event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
intercepted=false;
case MotionEvent.ACTION_MOVE:
if(父容器需要当前点击事件){
intercepted=true;
intercepted=false;
case MotionEvent.ACTION_UP:
intercepted=false;
mLastXIntercept=x;
mLastYIntercept=y;
    2 内部拦截法
    父容器不拦截任何事件,所用的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器进行处理。需要配合requestDisallowInterceptTouchEvent方法才能正常工作,同时重写子元素的dispatchTouchEvent方法,代码如下:
    子元素:
public boolean dispatchTouchEvent(MotionEvent event) {
int x=(int)event.getX();
int y=(int)event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
case MotionEvent.ACTION_MOVE:
int deltaX=x-mLastX;
int deltaY=y-mLastY;
if(父容器需要当前点击事件){
getParent().requestDisallowInterceptTouchEvent(false);
case MotionEvent.ACTION_UP:
return super.dispatchTouchEvent(event);
    父元素:
public boolean onInterceptTouchEvent(MotionEvent event) {
int action=event.getAction();
if (action==MotionEvent.ACTION_DOWN){
return false;
return true;
    以上就是滑动处理滑动冲突的典型代码,当面对不同的滑动策略时(场景1,2,3)只需要修改里面的条件即可。
            
阅读(...) 评论()《Android开发艺术探索》值得买吗? - 知乎580被浏览47431分享邀请回答4031 条评论分享收藏感谢收起509 条评论分享收藏感谢收起查看更多回答标签:至少1个,最多5个
接下来根据自己的平时的学习和自己最近要读的两本书《android开发艺术探索》和《android群英传》来梳理记录下自己的学习过程和对于遇到问题如何处理解决,还有将会陆续下一些有关关于如何将Java代码写的更优雅的,结合自己读过的书来做个记录整理型的系列博客。这两本书主要是关于android底层的实现的讲得比较多,对于项目的实战开发,看了一下,《app研发录》挺不错的。对于开始架构的设计上讲得很不错。接下来是个人在Activity学习中,记录下来的一些知识和个人的一些理解,同时附带了自己的学习实例源码。
Activity生命周期
Activity正常生命周期
涉及到activity的生命周期要提的是activity的七个生命周期回调函数,首先看一下其各自的执行顺序。用户参与下的Activity所进行的正常生命周期.
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("MAIN", "onCreate");
protected void onRestart() {
super.onRestart();
Log.i("MAIN","onRestart");
protected void onStart() {
super.onStart();
Log.i("MAIN", "onStart");
protected void onResume() {
super.onResume();
Log.i("MAIN","onResume");
protected void onPause() {
super.onPause();
Log.i("MAIN","onPause");
protected void onStop() {
super.onStop();
Log.i("MAIN","onStop");
protected void onDestroy() {
super.onDestroy();
Log.i("MAIN","onDestroy");
测试结果启动onCreate-&onStart-&onResume屏幕熄灭onPause-&onStop屏幕点亮onStart-&onResume按下HOME键onPause-&onStop按下返回键onPause-&onStop-&onDestroy
各个生命周期特征
onCreate:当打开一个新的Activity时执行,可以用来加载布局或者是加载一些数据。
onStart:这个时候,Activity已经可见,只是处在后台,无法与用户交互。
onReStart:在执行了onStop之后,在此回到该Activity时,会执行,可以用来进行一些恢复性操作。
onResume:此时activity已经可见,而且可以和用户进行交互。
onPause:表示activity正在停止,可进行一些动画停止之类的操作处理。不可过于耗时。
onStop:activity即将停止,可以进行一些稍微耗时的操作。
onDestroy:这个时候,Activity被销毁,可以进行一些回收工作和最终的资源释放。
Activity跳转时,两个activity的生命周期如何变化,通过一个Activity向另一个Activity进行跳转,生命周期的变化时。onCreate-&onStart-&onResume-&onPause-&onCreate-&onStart-&onResume-&onStop粗体部分为第二个Activity的活动周期,可以看出,当第一个onPause执行完之后,第二个activity便开始启动,因此为了让第二个activity启动的速度加快,我们必须要将第一个activity中的onPause方法执行的任务要尽量的少。activity的控制又AMS(Activity Manager Service)来控制一个activity栈和其状态。
Activity异常生命周期
由于系统原因,导致的activity被创建销毁资源相关的系统配置导致的activity被杀死,典型的例子就是在横竖屏切换的时候,系统提供了一个恢复机制,提供了两个回调函数。onSaveInstanceState(),onRestoreInstanceState(),第一个函数在onPause之后会被执行,而且是每次都会被执行,但是对于onRestoreInstanceState()只有在activity被重新创建之后,才会在onStart之后执行,所以在app开启到屏幕方向切换的过程中生命周期的变化为:onCreate-&onStart-&onResume-&onPause-&onSaveInstanceState-&onStop-&onDestroy-&onCreate-&onStart-&onRestoreInstanceState-&onResume在横竖过程中,前一个activity被销毁,又重新创建一个。在横竖的变化中,不难发现的是,我们即使没有对其中的控件做任何处理,其仍然可以在状态切换之后,保持之前的状态,这个是如何实现的呢?在Activity被意外终结时,首先会去调用onSaveInstanceState()来保存数据,然后其会委托Window去保存数据,然后Window在委托其上层容器,ViewGroup,然后其通知哥哥子元素来保存各自的数据,到此view的数据保存完成。资源相关配置有很多,比如屏幕大小发生变化,SIM卡发生变化,字体大小发生变化,这些变化都可能会导致出现activity重启,为了解决这个问题要采取的方式是,设置activity 的configChanges属性,然后添加相应的条件之后,就不会导致因为相关的配置的变化出现activity重启的现象。但是当变化发生的时候,将会有一个回调函数被执行。onConfigurationChanged()。
Activity启动规则
在讲启动规则之前,先要说几个概念。任务栈:每一个应用在打开的时候,都会创建一个或者多个任务栈,里面是activity,每个activity有一个属性为taskAffinity,该属性用来制定其要处在的任务栈的名称标示,默认情况下,activity会继承application,而application则会以包名作为其标示。所以在监听一个应用是否被打开的时候,可以监听其任务栈的名称来判断一个应用是否当前被打开。
Activity四种启动模式
standard:该种模式下,无论当前的activity是否已经存在,都会重新启动一个,处在启动方的栈中,所以不可以在application的context下启动。singleTop:该种模式下,如果当前任务栈中所要启动的activity位于栈顶,则不会重新启动,否则重新启动一个activity,采用该种方式,则会先暂停一下,调用onNewIntent,然后再调用onResume。singleTask:该中模式下,会先去寻找其对应的任务栈,然后在其任务栈中,寻找是否存在该实例,如果不存在,则创建实例,如果存在,则将其置为栈顶,如果又在其上的,则将其清除,如果不存在任务栈,则创建一个任务栈,然后创建实例。singleInstance:单独存放在一个栈中,当该模式启动的activity被创建后会单独处在一个栈中,然后该栈不会创建新的activity。对于启动模式的设定,可以在xml文件中设置,同时也可以在启动的时候,通过addFlags的方式进行设定,两种方式的优先级为动态设置的优先级高。
Activity匹配规则
我们可以通过在activity中的IntentFilter标签中设置相应的过滤信息来唯一的标示确认一个activity,过滤信息有action,category,data,在我们平日里使用手机的时候,进行分享的时候,或者是选择某个应用打开某个文件的时候,都会出现一个应用列表来供我们选择,这个时候,其实是通过intent来打开某个activity的时候,匹配到的一些,过滤信息的匹配元素在匹配上拥有不同的规则。action:一个intent中可以有多个action,但是只要其中一个和activity的相同,就可以匹配成功data:过滤规则中的一个或多个和activity相同,才可以进行匹配。在设置的时候,通过setType和setData只能设置一个,因此需要通过setDataAndType,才可以同时对type和data进行,data由data和type。category:可以不设置,但是如果设置了,其中的每一个都要和activity中对相匹配。
Activity任务栈
任务是一个Activity的集合,它使用栈的方式来管理其中的Activity,这个栈又被称为返回栈(back stack),栈中Activity的顺序就是按照它们被打开的顺序依次存放的。手机的Home界面是大多数任务开始的地方,当用户在Home界面上点击了一个应用的图标时,这个应用的任务就会被转移到前台。如果这个应用目前并没有任何一个任务的话(说明这个应用最近没有被启动过),系统就会去创建一个新的任务,并且将该应用的主Activity放入到返回栈当中。
当一个Activity启动了另外一个Activity的时候,新的Activity就会被放置到返回栈的栈顶并将获得焦点。前一个Activity仍然保留在返回栈当中,但会处于停止状态。当用户按下Back键的时候,栈中最顶端的Activity会被移除掉,然后前一个Activity则会得重新回到最顶端的位置。返回栈中的Activity的顺序永远都不会发生改变,我们只能向栈顶添加Activity,或者将栈顶的Activity移除掉。因此,返回栈是一个典型的后进先出(last in, first out)的数据结构。
任务除了可以被转移到前台之外,当然也是可以被转移到后台的。当用户开启了一个新的任务,或者点击Home键回到主屏幕的时候,之前任务就会被转移到后台了。当任务处于后台状态的时候,返回栈中所有的Activity都会进入停止状态,但这些Activity在栈中的顺序都会原封不动地保留着。
接下来将会写一些关于Android中IPC相关的文章。
0 收藏&&|&&11
你可能感兴趣的文章
1 收藏,7.1k
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
分享到微博?
技术专栏,帮你记录编程中的点滴,提升你对技术的理解收藏感兴趣的文章,丰富自己的知识库
明天提醒我
我要该,理由是:

我要回帖

更多关于 艺术探索 的文章

 

随机推荐