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,元素的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配置中配置了该action的Activity。            元素通常包含如下子元素:        0 ~ N个子元素        0 ~ N个子元素        0 ~ 1个子元素    一个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是通过元素:            

资源文件

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中:在

更多相关文章

  1. Android应用程序整合第三方API(以高德Map API为例)
  2. Android驱动之 Linux Input子系统之TP——A/B(Slot)协议
  3. Android(安卓)取得应用程序的启动次数和运行时间等信息
  4. iOS Tableview侧滑删除和移动cell的实现
  5. 【 Android(安卓)】RecyclerView 使用方法总结
  6. Android最佳性能实践(三)——高性能编码优化
  7. Android事件处理模型二(基于监听接口的事件处理)
  8. android 如何使用SAX解析XML
  9. 深入Android的消息机制源码详解~Handler,MessageQueue与Looper关

随机推荐

  1. Android(java)学习笔记117:英文朗诵android
  2. android调用市场给软件加评论
  3. android之HttpURLConnection
  4. android神器Stetho调试
  5. 【061】Eclipse 4 Android(安卓)使用技巧
  6. [转]Android设备的屏幕尺寸规格
  7. Android(安卓)MVVM结合DataBinding的简单
  8. android全局处理非捕捉异常
  9. Android中Socket通信之TCP与UDP传输原理
  10. [转]android 一直在最前面的浮动窗口效果