Crazy Android Note
来源:互联网 发布:新闻联播软件 编辑:程序博客网 时间:2024/06/10 19:36
Android事件处理
事件概述:
基于回调的事件处理 主要做法就是重写Android组件特定的回调方法,或者重写Activity的回调方法。Android为绝大部分界面组件都提供了事件响应的回调方法。 基于监听的事件处理 主要做法就是为Android界面组件绑定特定的事件监听器 一般来说,基于回调的事件处理可用于一些具有通用性的事件,基于回调的事件处理代码会显得比较简洁。但对于特定的事件处理,无法使用基于回调的事件处理,只能采用基于监听的事件处理。
1、基于监听的事件处理:
监听的处理模型: EventSource:事件源。事件发生的场所,通常就是各个组件,例如按钮,窗口、菜单等 Event:事件。事件封装了界面组件上发生的特定事情(通常就是一次用户操作)。如果程序需要获得界面组件上多发生事件的相关信息,一般通过Event对象来取得。 Event Listener:事件监听器。负责监听事件源所发生的事件,并对各种事件作出相应的相应。 基于监听的事件处理模型,其中事件源最容易创建,任何界面组件都可以作为事件源;事件的产生无需程序员关心,它是由系统自动生成的;所以实现事件监听器是整个事件处理的核心//全屏显示的设置requestWindowFeature(Window.FEATURE_NO_TITLE);getWindwo().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WidnowManager.LayoutParams.FLAG_FULLSCREEN);//获取屏幕宽高Display display = getWindowManager.getDefaultDisplay();DisplayMetrics metrics = new DisplayMetrics();//屏幕宽高封装到metrics中了display.getMetrics(metrics);实际上可以把事件处理模型简化理解:当事件源组件上发生事件时,系统将会执行该事件源组件上监听器的对应处理方法。与普通Java方法调用不同的是,普通Java程序里的方法是由程序主动调用,事件处理中的事件处理器方法是有系统负责调用的。所谓事件监听器其实就是实现了特定接口的Java类的实例。程序中实现事件监听器通常有几种形式(Button实现点击事件的几种方式): 1、内部类形式: 2、外部类形式: 3、Activity本身作为事件监听器类: 4、匿名内部类形式:
2、基于回调的事件处理:
基于监听的事件处理是一种委托式的事件处理,回调机制则恰恰相反,对于基于回调的事件处理模型,事件源和事件监听器是统一的,或者说事件监听器完全消失了。当用户在GUI组件上激发某个事件时,组件自己特定的方法将会负责处理该事件。为了使用回调机制处理GUI组件上发生的事件,我们需要为该组件提供对应的事件处理方法——而Java又是一种静态语言,无法为某个对象动态的添加方法,因此只能继承GUI组件类,并重新该类的事件处理方法来实现。为实现回调机制的事件处理,Android为所以的GUI组件都提供了一些事件处理的回调方法,一View为例: boolean onKeyDown(int keyCode, KeyEvent event) boolean onKeyLongPress(int keyCode, KeyEvent event) boolean onKeyShortcut(int keyCode, KeyEvent event) boolean onKeyUp(int keyCode, KeyEvent event) boolean onTouchEvent(MotionEvent event) boolean onTrackballEvent(MotionEvent event)对于基于监听的事件处理模型,事件源和事件监听器是分离的,当事件源上发生特定事件时,该事件交给事件监听器负责处理;对于基于回调的事件处理模型,事件源和事件监听器是统一的,当事件源发生特定事件时,该事件还是由事件源本身负责处理(可通过自定义View,重写View的事件处理方法)。基于回调的事件传播: 几乎所有的基于回调的事件处理模型都有一个boolean类型的返回值,该返回值用于标识该处理方法是否能完全处理该事件。 true:该处理方法已完全处理该事件,该事件不会传播出去 false:该处理方法并未完全处理该事件,该事件会传播出去 对于基于回调的事件传播而言,某组件上所发生的事件不仅会激发该组件上的回调方法,也会出发该组件所在的Activity的回调方法——只要事件能传播到该Activity。
相应系统设置的事件 Configuration
Configuration类专门用于描述手机设备上的配置信息,这些配置信息既包括用户特定的配置项,也包括系统的动态设备配置(屏幕的方向,键盘,信号的国家码,网络码等)Configuration cfg = getResource().getConfiguration();重写onConfigurationChanged方法响应系统设置更改 如果需要监听系统配置的更改,则可以考虑重写Activity的onConfigurationChanged方法,该方法是一个基于回调的事件处理方法:当系统设置发生更改时,该方法会自动触发。 为了让程序中动态地更改系统设置,我们可调用Activity的setRequestedOrientation(int)方法修改屏幕的方向:MainActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORINTATION_PORTRAIT);(将屏幕设置为竖屏)
Handler
处理线程间通讯Handler、Looper、Message、MessageQueueMessage:Handler接收和处理的消息对象Looper:每个线程只能拥有一个Looper。它的loop方法负责读取MessageQueue中的消息,读到消息后就把消息交给发送该消息的handler处理MessageQueue:消息队列,它采用FIFO的方法管理Message。程序创建Looper对象时,它会在构造器中创建MessageQueue对象(从代码中观察,Looper为private,顾程序员无法通过构造器创建Looper)。 private Looper(){ mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }Handler:发送消息,处理消息。 程序使用handler发送消息:与Handler发送的消息必须被送到指定的MessageQueue中;也就是说,如果希望Handler正常工作,必须在当前现场中有一个MessageQueue,否则消息就没有MessageQueue进行保存了。不过MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作,必须在当前现场中有一个Looper对象。为保证当前线程中有Looper对象,可分为两种情况: 在UI线程中:系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可以通过Handler处理消息 在子线程中:必须自己创建一个Looper对象,并启动它。创建Looper对象调用它的prepare()方法即可。prepare()方法保证每个线程最多只有一个Looper对象。 Looper.prepare(); /** 这中间的代码就是运行在新创建的Looper中 */ Looper.loop(); public static final void prepare(){ if(sThreadLocal.get() != null){ throw new RuntimeException("only one Looper may be created per thread") } sThreadLocal.set(new Looper()); } 接下来调用的Looper的静态方法loop()来启动它,loop()会使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler处理。 loop()源码: public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { //使用一个死循环,不断的读取MessageQueue中的消息 Message msg = queue.next(); // might block (获取消息队列中的下一个消息,如果没有消息,可能阻塞) if (msg == null) { // No message indicates that the message queue is quitting.(如果消息为null,表面消息队列正在退出) return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. (使用final修饰该标示符,保证在分发消息的过程中线程标示符不会被修改) final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); } }
AsyncTask(Params,Progress,Result)
异步任务,是一个抽象类,通常用于被继承,继承AsyncTask时需要指定三个泛型参数: Params:启动任务执行的输入参数类型,用于doInBackground(Params... params)方法的参数上(如果有),通过execute(Params)传入。 Progress:后台任务执行过程中进度值的类型,用于onProgressUpdate(Progress... values)方法; Result:后台任务完成后返回结果的类型,用于onPostExecute(Result... result)方法;每个AsyncTask只能被执行一次,多次执行会引发异常
Activity和Fragment
Activity在Manifest文件中的配置: exported参数:指定该Activity是否允许被其他应用调用;true表示可以被调用一个Activity可以组合多个Fragment;反过来,一个Fragment也可以被多个Activity复用Fragment可以响应自己的输入事件,并拥有自己的生命周期,但它们的生命周期直接被其所属的Activity的生命周期控制。例如:当Activity暂停时,该Activity内的所有Fragment都会暂停;当Activity被销毁时,该Activity内的所有Fragment都会被销毁。只有当该Activity处于活动状态时,程序员才可通过方法独立地操作Fragment。通常创建Fragment需要实现如下三个方法: onCreate():系统创建Fragment对象后回调该方法,在实现代码中只初始化想要在Fragment中保持的必要组件,当Fragment被暂停或停止后可以恢复 onCreateView():当Fragment绘制界面组件时会回调该方法。该方法必须返回一个View,该View也就是该Fragment所显示的View。 onPause():当用户离开该Fragment时将会回调该方法。在Activity中显示Fragment,需要将Fragment添加到Activity中。将Fragment添加带Activity有两种方式: 1、在布局文件中使用<fragment.../>元素添加Fragment,<fragment.../>元素的android:name属性指定Fragment的实现类 2、在Java代码中通过FragmentTransaction对象的add()方法来添加Fragment。Activity和Fragment相互传递数据: Activity向Fragment传递数据:在Activity中创建Bundle数据包,并调用Fragment的setArgument(Bundle bundle)方法即可将Bundle数据包传递给Fragment.Fragment通过getArgument()方法获取从Activity传递过来的Bundle数据包 Fragment向Activity传递数据或Activity需要在Fragment运行中进行实时通信:在Fragment中定义一个内部调用接口,再让包含该Fragment的Activity实现该回调接口,这样Fragment即可调用该回调方法将数据传递给Activity。Fragment管理与Fragment事务 管理Fragment主要依靠FragmentManager;FragmentManager可以完成以下几个功能: 使用findFragmeById()或findFragmentByTag();方法获取指定的Fragment 使用popBackStack()方法将Fragment从后台栈中弹出(模拟用户按下BACK键) 调用addOnBackStackChangeListener()注册一个监听器,用于监听后台栈的变化 如果需要添加、删除、替换Fragment,则需要借助于FragmentTransaction对象,它代表Activity对Fragment执行的多个改变开发兼顾屏幕分辨率的应用 为了开发兼顾屏幕分辨率的应用,可以考虑在res/目录下为大屏幕、600dpi的屏幕建立相应的资源文件夹:values-large、values-sw600dp;Fragment生命周期: onAttach():当该Fragment被添加到Activity时被调用。该方法只会被调用一次 onCreate(Bundle savedStatus):创建Fragment时被调用,该方法只会被调用一次 onCreateView():每次创建、绘制该Fragment的View组件时回调该方法,Fragment将会显示该方法返回的View组件 onActivityCreated():当Fragment所在的Activity被启动完成后回调该方法 onStart():启动Fragment时被调用 onResume():恢复Fragment时被回调,在onStart()方法后一定会回调该方法 onPause():暂停Fragment时被回调 onStop():停止Fragment时被回调 onDestroyView():销毁该Fragment所包含的View组件时回调 onDetach():将该Fragment从Activity中删除、替换完成时回调该方法,在onDestroy()方法后一定会回调onDetach()方法。该方法只会被调用一次 最常重写的是onCreateView()方法--该方法返回的View是需要在Fragment中显示的View
Intent和IntentFilter
ComponentNameAction、Category属性与intent-filter配置 Intent的Action、Category属性都是一个普通的字符串。其中Action代表该Intent所要完成的一个抽象“动作”,而Category则用于为Action增加额外的附加类别信息。通常两者配合使用。 Intent intent = new Intent(); intent.setAction(MainActivity.CRAZYIN_ACTION);//这里的MainActivity.CRAZYIN_ACTION就是一个字符串 startActivity(intent); 以上为隐式Intent调用,这里只能启动Activity配置中<intent-filter../>配置了该action的Activity。 <action android:name="org.crazyit.intent.action.CRAZYIT_ACITON" /> <intent-filter.../>元素通常包含如下子元素: 0 ~ N个<aciton.../>子元素 0 ~ N个<category.../>子元素 0 ~ 1个<data.../>子元素 一个Intent对象最多只能包括一个Action属性,程序可调用Intent的setAction(String str)方法来设置Action属性;但一个Intent对象可以包括多个Category属性,程序可以通过调用Intent的addCategory(String str)方法为Intent添加Category属性。当程序创建Intent时,该Intent默认启动Category属性值为Intent.CATOGORY_DEFAULT常量的组件。指定Action、Category调用系统Activity 给Intent设置对应的action,隐式调用系统Activity 点击按钮,返回系统Home桌面: intent.setAction(Intent.ACTION_MAIN); intent.addCategoty(Intent.CATEGORY_HOME);Data、Type属性与intent-filter配置 Data属性通常用于向Action属性提供操作的数据。Data属性接受一个Uri对象,一个Uri对象通常通过如下形式的字符串表示: content://com.android.contacts/contacts/1 tel:123 Uri字符串总满足如下格式: scheme://host:port/path 例子中content://com.android.contacts/contacts/1,其中content是scheme部分,com.android.contacts是host部分,Port部分省略了,/contacts/1是path部分 Type属性用于指定该Data属性所指定Uri对应的MIME类型,这种MIME类型可以是任何自定义的MIME类型,只要符合abc/xyz格式的字符串即可。 如果Intent先设置了Data属性,后设置Type属性,那么Type属性将会覆盖Data属性。 如果Intent先设置了Type属性,后设置Data属性,那么Data属性将会覆盖Type属性。 如果希望既有Data属性,也有Type属性,则应该调用Intent的setDataAndType()方法 在Manifest中,声明Data,Type是通过<data.../>元素: <data android:mimeType="" android:scheme="" android:host="" android:port="" android:path="" android:pathPrefix="" android:pathPattern=""/>
资源文件
assets目录存放的资源代表应用无法直接访问的原生资源,应用程序需要通过AssetsManager以二进制流的形式来读取资源,而res目录下的资源,Android SDK会在编译该应用时,自动在R.java文件中为这些资源创建索引,程序可直接通过R资源清单类进行访问。
/res/raw/文件夹下存放任意类型的原生资源(比如音频文件、视频文件等)。在java代码中可通过调用Resource对象的openRawResource(int id)方法来获取该资源的二进制输入流。实际上,如果应用程序需要原生资源,也可以吧这些原生资源保存到/assets/目录下,然后在应用程序中使用AssetsManager来访问这些资源
*res/raw和assets的相同点:
1.两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。
*res/raw和assets的不同点:
1.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
2.res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹
*读取文件资源:
1.读取res/raw下的文件资源,通过以下方式获取输入流来进行写操作
· InputStream is =getResources().openRawResource(R.id.filename);
2.读取assets下的文件资源,通过以下方式获取输入流来进行写操作
· AssetManager am = null;
· am = getAssets();
· InputStream is = am.open(“filename”);
注意1:Google的Android系统处理Assert有个bug,在AssertManager中不能处理单个超过1MB的文件,不然会报异常,raw没这个限制可以放个4MB的Mp3文件没问题。(带验证)
注意2:assets 文件夹是存放不进行编译加工的原生文件,即该文件夹里面的文件不会像 xml, java 文件被预编译,可以存放一些图片,html,js, css 等文件。(带验证)
ClipDrawable:代表从其他位图上截取的一个“图片片段”,通过ClipDrawable对象的setLevel(int level)控制截取图片的部分,level为0时,截取的图片片段为空,level为10000时,截取整张图片
主题和样式的区别:
主题不能作用于单个View组件,主题应该对整个应用中的所有Activity起作用,或对指定的Activity起作用
主题定义的格式应该是改变窗口外观的样式,例如窗口标题、窗口边框等。
使用定义好的主题
1、在代码中:在setContentView()之前执行,setTheme(R.style.CrazyTheme);
2、在Manifest中:在
- Crazy Android Note
- Crazy Android Note Chapter-7
- Crazy Android Note Chapter-8
- Crazy Android Note Chapter-9
- Crazy Android Note Chapter-10
- Crazy Android Note Chapter-11
- Crazy Android Note Chapter-13
- android note
- android note
- Android note
- android note
- android note
- android note
- android note
- Crazy
- Crazy~
- CRAZY
- Styling Views on Android (Without Going Crazy)
- Ubuntu 更新源
- JDBC连接MySQL的URL编码问题
- 修复图片移入移出抖动错位bug
- Android 自定义imageview 图片宽度固定大小高度按比例自适应
- 织梦上传显示302错误
- Crazy Android Note
- iOS---UIButton笔记
- memcache和redis对比
- ecshop 商品详细页出现读取信息有缓存 需要后台清除缓存才能正常 一段时间后产品显示又不正常的
- Xcode高级调试技巧5
- Android 自定义imageview 图片高度固定大小宽度按比例自适应
- 正则化方法:L1和L2 regularization、数据集扩增、dropout
- Firefox访问https出现 ssl_error_weak_server_ephemeral_dh_key错误
- @property和@synthesize 用法