MeasureSpec代表一个32位的值,高2位代表SpecMode,低30位代表SpecSize.
SpecMode:测量模式
SpecSize:在某种测量模式下的规格大小。
SpecMode的三种模式:
1.UNSPECIFIED:(父容器不对View有任何限制),要多大给多大,一般用于系统内部,表示一种测量状态。
2.EXACTLY:父容器已经测量出View的所需的(精确大小),这个时候View的最终大小为SpecSize所指定的值,对应于LayoutParams中的match_parent和具体两种模式。
3.AT_MOST:父容器指定可用大小即:SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的表现,它对应于LayoutParams中的wrap_content.
1.measure测量
2.layout布局
3.draw绘制
1.1 measure 测量
1.在Activity/View#onWindowFocusChanged(代表View初始化完毕,宽/高已经准备好)可以获取View的宽高
2.view.post(runnable);初始化完成
3.ViewTreeObserver可以使用回调OnGlobalLayoutListener接口,当View树状态发生改变时onGlobalLayou会被调用多次
2.2 layout 过程
2.3 draw 过程
1.background.draw(canvas)。绘制背景
2.OnDraw(绘制自己)。
3.绘制children(dispatchDraw)。
4.(onDrawScrollBars)绘制装饰。
1.自定义View的分类:
1.继承View重写onDraw。需要自己支持wrap_content,并且padding也需要自己处理
2.继承ViewGroup派生特殊Layout。主要用于自定义布局,即除了传统的系统布局,需要合适的处理ViewGroup的测量,布局的这两个过程,并同时处理子元素的测量和布局。
3.继承特定的ViewGroup(比如TextView,ImageView)。不需要自己支持wrap_content,padding等。
4.继承特定的ViewGroup(比如LinearLayout)。不需要自己处理ViewGroup的测量和布局两个过程。方法二更接近底层。
2.自定义View须知:
1.让View支持wrap_content;
如果不在onMeasure中做特殊处理,那么外界在使用wrap_content时无法达到预期效果,原因是因为onMeasure需要多次测量,可能导致测量不准确。
2.如果有必要,让你的View支持padding。
这事因为直接继承View的控件,如果不在draw方法处理padding,那么padding属性将不起作用。另外在ViewGroup的控件需要在onMeasure和onLayout中考虑padding和子元素的margin对其造成的影响,不然将导致padding和子类的margin失效。
3.尽量不要在View中使用Handler,没必要。
因为View本身提供post系列方法。
4.View中如果有线程或者动画,需要及时停止,参考View#onDetachedFromWindow(在View被销毁时被调用)与之相对的方法onAttachedToWindow。
5.VIew带有滑动嵌套时,需要处理好滑动冲突。
3.自定义View实例:
1.自定义属性步骤:
1.在values目录下创建自定义属性的XML。
......
format属性值:reference 、color、boolean、dimension、float、integer、string、fraction、enum、flag
2.在View的构造方法中解析自定义属性的值并做相应处理
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleView);//回去style中的属性
mColor=typedArray.getColor(R.styleable.CircleView_circle_color,Color.RED);
typedArray.recycle();
3.在XML里使用
自定义Notification:
1.Notifivcation notification = new Notification().Builder(this);
.setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏中的小图片,尺寸一般建议在24×24, 这里也可以设置大图标
.setTicker("有新短消息了!")// 设置显示的提示文字
.setContentTitle("标题")// 设置显示的标题
.setContentText("CONTEXT")// 消息的详细内容
.setFullScreenIntent(pendingIntent, true)//设置为悬挂式
.setContentIntent(pendingIntent) // 关联PendingIntent
.setCategory(Notification.CATEGORY_MESSAGE)//设置类型
.setPriority(Notification.PRIORITY_DEFAULT)//设置优先级
.setColor(Color.GREEN)
.setSubText("副标题")
.setNumber(100) // 在TextView的右方显示的数字,可以在外部定义一个变量,点击累加setNumber(count),这时显示的和
.getNotification(); // 需要注意build()是在API level16及之后增加的,在API11中可以使用getNotificatin()来代替
notify.flags = Notification.FLAG_AUTO_CANCEL;
RemoteViews:
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.activity_nitification);
remoteViews.setTextViewText(R.id.text, "TEXT");//定义Textview
remoteViews.setImageViewResource(R.id.img, R.mipmap.ic_launcher);//定义ImageView
remoteViews.setOnClickPendingIntent(R.id.LeftImg, createPend(MainActivity.class, 1));//设置监听
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
notify.bigContentView = remoteViews;//大格式,无限制大小
//notify.contentView = remoteViews;默认的大小
notify.contentIntent = pendingIntent;
managerNotifition.notify(0, notify);
//创建pendingintent方法
private PendingIntent createPend(Class classes,int requestCode){
PendingIntent pend = null;
Intent intent = new Intent(MainActivity.this,classes);
intent.putExtra("code",""+requestCode);
pend = PendingIntent.getActivity
(MainActivity.this, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
return pend;
}
1.PendingIntent不确定时间发生。Intent立即发生。
2. 要给RemoteViews设置单击事件,需要用到PendingIntent。
3.PendingIntent通过send和cancel方法来发送和取消
4.PendingIntent支持三种意图:
4.1.getActivity();该意图发生时相当于是Context.startAcctivity(intent);
4.2.getServer();
4.3.getBroadcast();
5.PendingIntent的FLAG的理解:
1.FLAG_ONE_SHOT:只能被调用一次,然后她就会自动被cancel,后续的通知单击后无法打开。
2.FLAG_NO_CREATE:不会主动被创建,设计开发中无意义
3.FLAG_CANCEL_CURRENT:当前描述的PendintInten存在,他们头会被cencel,然后系统创建新的PendingIntent,那些被cencel会无法打开。
4.FLAG_UPDATE_CURRENT:PendingIntent已经存在,那么他们都会被更新,即他们的Intent中的Extras会被替换成最新的。(常用)
6.在NotificationManager.notifiid,notification);
如果id是常量,那么多次调用只能弹出一个通知,后续的通知会把前面的通知完全替换掉;
如果每次的id不同多次调用会弹出多个通知。
1.RemoteViews所支持布局的类型:Layout:LinearLayout,FrameLayout,RelativeLayout,GridLayout
View:AnalogClock,Button,Chronometer,ImageButton,ImageView,ProgressBar,
TextView,ViewFlipper,ListViiew,GridView,StackView,AdapterViewFlipper,ViewStub.
2.RemoteViews没有提供findViewBy方法,因此无法直接访问里面的View元素,而必须通过它本身的set方法来实现。
3.通知栏和桌面小布局部分分别由:NotificationManager和AppWidthManager管理,而NotificationManager和AppWidthManager通过Binder分别和SystemServer进程中的NotificationManagerService以及AppWidthSerVice进行通信, 这就构成了跨进程通信,他们在对应的Service中被加载。
然后会通过LayoutInflater去加载RemoteViews中的布局文件;
在SystemService进程中加载后的布局文件是一个普通的View,只不过相对于我们进程它是一个RemoteViews而已,
接着系统会对View执行一系列的界面更新任务,这些任务就是我们之前set方法提交的(set方法对View所做的更新并不是立即执行的,在RemoteViews内部会记录所有的更新操作,具体执行时机要等到RemoteViews加载以后才执行,这样RemotrViews在SystemServer进程显示了,这就是我们看到的通知栏消息和桌面小部件);
当需要更新RemoteViews时,我们需要调用一些列set方法,并通过NotificationManager和AppWidthManager来提交更新任务,
具体的操作也是在SystemServer进程中完成。
6.1:动画种类:android3.0之前 TweenAnimation(补间动画) FrameAnimation(帧动画) android3.0之后增加:Property Animation(属性动画),AnimationSet:一系列动画集合
AnimationUtils:动画工具类,loadAnimation(从XML代码里加载动画);
可以在XML里写(),也可以在代码里写;
AnimationListener:动画监听。
6.2 TweenAnimation(补间动画):透明度,旋转,位移,缩放动画。
6.3 自定义动画继承自Animation
2.将上述Drawable作为View的背景并通过Drawable来播放动画
3. AnimationDrawable drawable = (AnimationDrawable) View.getBackground();
drawable.start();
6.5 LayoutAnimation :作用于ViewGroup指定一个动画,经常用于ListView
1.在res/anim/layout_animation ->android:delay(延迟)
android:animationOrder: normal:顺序 reverse:倒序 random随机
设置一个
android:animation:设置想要的item动画
2.给ListView设置layoutAnimation代码里设置可以用:LayoutAnimationController来实现
6.6 nineoldandroids:动画第三方框架收
6.7 AnimationUpdateListenter:监听动画整个过程
6.8 对任意属性做动画处理:
1.用一个包装类来做处理,间接提供get,set方法
2.属性动画工作原理
FLAG_NOT_FOCUSABLE:表示不需要获取焦点,也不需要接收输入事件,同事会开启FLAG_NOT_TOUCT_MODAL,最终会把事件传递给下层具有焦点的window.
FLAG_NOT_TOUCH_MODAL:系统会将Windows当前以外的单击事件传递给底层的windows,当前的自己处理
FLAG_SHOW_WHEN_LOCKED:让widows显示在锁屏的界面上
Type参数表示Widows的类型,Windows有三种类型,分别是:1,应用的windows:对应着一个Activity
2,子windows:子view不能单独存在,他需要在附属在特定的windows之中,如:dialog就是在子windows
3,系统windows:需要声明权限才能创建widows,比如toast和系统状态栏这些都是系统windows
windows是分层的,层级大的会覆盖在层级晓得上面。
WindowsManager所提供的功能简单,即添加View,更新view,删除view,这三个定义在ViewManager中,而WindowsManager继承了ViewManager-->addView(...),updateViewLayout(...),removeView(...)
Windows内部机制:Widnows是一个抽象的概念,每一个window都对相应着一个View和ViewRootImpl,Window通过ViewRootImpl来建立联系,因此Windows并不是实际存在的,它是以View的形式存在。
1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
好了,总结完成,大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。
1.
HandlerThread使用场景:
假设我们要做一个股市数据实时更新的app,我们可以在网上找个第三方的股市数据接口,然后在我们的app中每隔1分钟(合适的时间)去更新数据,然后更新我们的UI即可。
HandlerThread:实际上就一个Thread,只不过它比普通的Thread多了一个Looper。
创建HandlerThread时要把它启动了,即调用start()方法。然后创建Handler时将HandlerThread中的looper对象传入。
HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();
mHandler = new Handler(thread.getLooper());
mHandler.post(new Runnable(){...});
那么这个Handler对象就是与HandlerThread这个线程绑定了(这时就不再是与UI线程绑定了,这样它处理耗时操作将不会阻塞UI)。
2.IntentService的使用场景:
在Android开发中,我们或许会碰到这么一种业务需求,一项任务分成几个子任务,子任务按顺序先后执行,子任务全部执行完后,这项任务才算成功。那么,利用几个子线程顺序执行是可以达到这个目的的,但是每个线程必须去手动控制,而且得在一个子线程执行完后,再开启另一个子线程。
在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。
用IntentService有什么好处呢?
首先,我们省去了在Service中手动开线程的麻烦,
第二,当操作完成时,我们不用手动停止Service。
3.Android中的线程池:
使用线程池的好处?
(1)重用线程池中的线程池,避免线程池的创建和销毁所带来性能的开销。
(2)能有效的控制线程池的最大并发数,避免线程之间相互抢占而阻塞。
(3)能够对线程简单的管理,并提供定时执行以及指定间隔循环执行等功能。
1.newFixedThreadPool线程固定的线程池,在空闲时并不会被回收,除非线程关闭。
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.execute(runnable);
2.newCachedThreadPool线程数量不固定,只有非核心线程池,最大线程数为Integer.MAX_VALUE;
ExecutorService executorService1 = Executors.newCachedThreadPool();
executorService1.execute(runnable);
3.newScheduledThreadPool核心线程数固定,非核心线程没有限制,并且当非核心线程闲置时会被立即回收。
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
//延迟2000后执行runnable
scheduledExecutorService.schedule(runnable,2000, TimeUnit.MILLISECONDS);
//延迟10后,每隔1000执行一次runnable
scheduledExecutorService.scheduleAtFixedRate(runnable,10,1000,TimeUnit.MILLISECONDS);
4.newSingleThreadExecutor内部只有一个核心线程,确保所有任务都在一个线程中按顺序执行,不需要处理线程同步问题。
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(runnable);