Android 4.0日历(calendar)源码分析之CalendarController(事件分发)

日历在主体上只有一个AllInOneActivity.java,然后分别是各种Fragment。这就需要一个中介来统一处理他们的关系,AllInOneActivity和Fragment之间,以及不同的Fragment之间的通信(主要是事件),都是通过CalendarController这个类来完成的。

当在某个Fragment中想要发出一个事件的时候,该Fragment会用到自己实例化的CalendarController对象(mController),例如下面的样子:

1
2
3
4
5
6
7
mController.sendEvent(mContext, EventType.GO_TO, day, day, -1,
   
         mIsMiniMonth ? ViewType.CURRENT : ViewType.DETAIL,
   
         CalendarController.EXTRA_GOTO_DATE
   
                 | CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null , null );


这里的sendEvent函数有很多参数,但是他们最终都会调用  CalendarController中重载了的public void sendEvent(Objectsender, final EventInfo event),而之前的那些零散的参数都被封装在了这两个参数里。

 

sendEvent函数最终是要把EventInfo(事件信息)传递给事件处理对象(eventHandler),一个事件的处理对象可能还不止一个,因此需要遍历一次所有存在的事件处理对象,并调用这些对象的handleEvent(event)函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
for (Iterator<Entry<Integer, EventHandler>> handlers =
   
         eventHandlers.entrySet().iterator(); handlers.hasNext();) {
   
     Entry<Integer, EventHandler> entry = handlers.next();
   
     int key = entry.getKey();
   
     if (mFirstEventHandler != null && key == mFirstEventHandler.first) {
   
         // If this was the 'first' handler it was already handled
   
         continue ;
   
     }
   
     EventHandler eventHandler = entry.getValue();
   
     if (eventHandler != null
   
             && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) {
   
         if (mToBeRemovedEventHandlers.contains(key)) {
   
             continue ;
   
         }
   
         eventHandler.handleEvent(event);
   
         handled = true ;
   
     }
   
}


 

那么事件处理对象,究竟是什么东西呢,其实他们非常平常,基本上,那些能通过自己的CalendarController成员变量mController发送事件的类本身也是事件处理对象,也就是我们熟悉的AllInOneActivity、MonthByWeekFragment、AgendaFragment、DayFragment。

这些类都继承了EventHandler这个接口(也就是事件处理对象的原型),并实现了其中的voidhandleEvent(EventInfo event);方法。EventHandler这个接口是在CalendarController中定义的,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public interface EventHandler {
   
     long getSupportedEventTypes();
   
     void handleEvent(EventInfo event);
   
   
   
     /**
   
      * This notifies the handler that the database has changed and it should
   
      * update its view.
   
      */
   
     void eventsChanged();
   
}


刚才提到了遍历事件处理对象,也给出了遍历的那段代码,可以看到其实实在遍历这个集合eventHandlers,eventHandlers中的成员是通过public void registerEventHandler(int key, EventHandler eventHandler)方法得到的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
     public void registerEventHandler(int key, EventHandler eventHandler) {
   
         synchronized ( this ) {
   
             if (mDispatchInProgressCounter > 0) {
   
                 mToBeAddedEventHandlers.put(key, eventHandler);
   
             } else {
   
                 eventHandlers.put(key, eventHandler);
   
             }
   
         }
   
}


在mDispatchInProgressCounter> 0)(正在处理中的事件个数)的条件下,这时候只能将注册的新EventHandler放在mToBeAddedEventHandlers里,表示当前还不能立即处理,因为上次的还没处理完,如果mDispatchInProgressCounter<0则放在普通的eventHandlers里面,当sendEvent发生的时候,马上调用这些eventHandlers的handleevent方法。

mDispatchInProgressCounter正在处理的事件个数,是在sendEvent方法中被synchronized包括起来的程序段中赋值的。

 

在整个项目中搜素registerEventHandler(我不会告诉你我用的是SourceInsight工具)发现调用了次方法的地方如下:


AllInOneActivity中对四个视图的fragment进行了注册,当然并不是同时,假如当前是月视图,注册的当然是MonthByWeekFragment,要想了解如何注册的请看AllInOneActivity的setMainPane方法。或者参考我讲解AllInOneActivity的那篇文章。

 

其实,我们应该注意到,registerEventHandler只是对Fragment进行了注册(还有一些非AllInOneActivity的activity这里不讲解),但是事件处理对象中还有重要的AllInOneActivity,AllInOneActivity也有handleEvent的能力,AllInOneActivity为什么没有自己给自己注册一下呢,既然没有注册那么AllInOneActivity中的handleEvent方法不是永远不会被调用么?

 

当然不是AllInOneActivity也给自己注册了的,只不过是调用CalendarController的registerFirstEventHandler方法,方法定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     public void registerFirstEventHandler(int key, EventHandler eventHandler) {
   
         synchronized ( this ) {
   
             registerEventHandler(key, eventHandler);
   
             if (mDispatchInProgressCounter > 0) {
   
                 mToBeAddedFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
   
             } else {
   
                 mFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
   
             }
   
         }
   
}


之所以要区分于其他,是因为整个日历的架构中要求优先调用AllInOneActivity的handleEvent。假如mFirstEventHandler不为空,则最先处理他,registerFirstEventHandler也在方法之类调用了registerEventHandler,这样AllInOneActivity就有优先和普通两种身份。

 

  • 不管是registerFirstEventHandler还是registerEventHandler都有一个key参数,key是标识是不是同一个handler的键。

 

通过sendEvent发来的事件请求不一定都需要相应的handler来处理,如以下情况:

handler != null &&(handler.getSupportedEventTypes() & event.eventType) != 0

Handler不存在的情况

事件类型不在支持的类型中

第一种情况好像还没遇到过,第二种情况的意思是,当我告知某个handler需要处理一个事件时,先判断这个handler自身是否支持该事件的处理,通过调用handler的getSupportedEventTypes方法就可以得到他所支持的事件类型。这里我们以AllInOneActivity为例:

getSupportedEventTypes本来是CalendarController的接口EventHandler的一个抽象方法,因为AllInOneActivity继承了该接口,因此AllInOneActivity重写了该方法,AllInOneActivity的实现如下:

1
2
3
4
5
     public long getSupportedEventTypes() {
   
         return EventType.GO_TO | EventType.VIEW_EVENT | EventType.UPDATE_TITLE;
   
}


可以看到AllInOneActivity能处理的事件也只有三种,跳转、查看日程、更新日历title上的日期。对于其他的handler,能处理的可能更少。

我们来看看其他的handler支持哪些事件。可能还没有找出所有的handler。

MonthByWeekFragment:

   public long getSupportedEventTypes() {

       return EventType.GO_TO | EventType.EVENTS_CHANGED;

}

DayFragment                       

   public long getSupportedEventTypes() {

       return EventType.GO_TO | EventType.EVENTS_CHANGED;

}

AgendaFragment

   public long getSupportedEventTypes() {

       return EventType.GO_TO | EventType.EVENTS_CHANGED |((mUsedForSearch)?EventType.SEARCH:0);

}

SearchActivity

    public long getSupportedEventTypes() {

        return EventType.VIEW_EVENT |EventType.DELETE_EVENT;

    }

……………………此处略去若干字…………….

getSupportedEventTypes()方法之后紧跟着handleEvent方法,这里就不一一列出handleEvent的具体实现了

当没有hanlder来处理这些不支持的事件的时候,sendEvent会继续执行下面的代码,这些特殊的事件往往都和视图没有什么区别,在任何视图打开都是相同的结果,比如打开设置界面,打开搜索界面,打开选择要显示的日历界面等。代码如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (event.eventType == EventType.LAUNCH_SETTINGS) {
   
       launchSettings();
   
       return ;
   
   }
   
   
   
   // Launch Calendar Visible Selector
   
   if (event.eventType == EventType.LAUNCH_SELECT_VISIBLE_CALENDARS) {
   
       launchSelectVisibleCalendars();
   
       return ;
   
   }


他会直接调用相应的函数,而不是交给handler处理。

Handler的这种处理机制运用了java中典型的回调机制,和观察者模式。

### 回答1: qml qtquick2.0日历是一种用于显示日期和管理时间的工具。它可以在Qt应用程序中通过使用Qt Quick语言来创建和定制。 qml qtquick2.0日历提供了一个简单而强大的界面,可以让用户轻松地切换日期、查看特定日期的详细信息、创建和管理事件等。它可以显示一周、一个月或一个年的日期,并且具有各种功能,如添加和删除事件、设置提醒、查找特定日期等。 qml qtquick2.0日历还可以根据用户的偏好进行自定义。用户可以选择不同的主题样式、日期格式、语言等。同时,它还支持在日历中添加自定义的视图或功能,以满足特定应用程序的需求。 qml qtquick2.0日历使用Qt Quick的动画特性和平滑的过渡效果,使用户操作更加流畅。它还可以与其他Qt组件和功能集成,如数据库、网络连接等。 总之,qml qtquick2.0日历是一个功能强大且易于使用的工具,可以帮助开发人员在Qt应用程序中添加日期和时间管理的功能。通过使用它,用户可以方便地查看和管理日期,创建和管理事件,以及自定义日历界面和功能。 ### 回答2: QML(Qt Meta-Object Language)是一种基于JavaScript语法的声明式编程语言,用于创建用户界面。Qt Quick是Qt提供的一组用于创建跨平台和高性能用户界面的库,而Qt Quick 2.0则是Qt Quick的一个重要版本,引入了许多新的特性和改进。 Qt Quick 2.0中提供了一个日历组件,可以很方便地在应用程序中显示日历和时间。使用这个日历组件,我们可以轻松地实现一些常见的日历功能,如选择日期、显示特定日期的详细信息等。 在QML中,我们可以通过引入Qt Quick的日历模块来使用日历组件。首先,我们需要使用[import语句](https://doc.qt.io/qt-5/qml-imports.html)来导入所需的模块,例如: ``` import QtQuick.Controls 2.0 import QtQuick.Controls.Calendar 2.0 ``` 然后,我们可以在QML中创建一个日历组件,并指定其属性和行为。例如,我们可以设置最小和最大可选日期、当前选定的日期等: ``` Calendar { id: calendar width: 400 height: 300 minimumDate: new Date(2022, 0, 1) maximumDate: new Date(2022, 11, 31) selectedDate: new Date() } ``` 除了上述属性之外,我们还可以在日历组件上添加信号处理函数来响应特定日期的选择事件。例如: ``` Calendar { id: calendar width: 400 height: 300 onSelectedDateChanged: { console.log("Selected date:", selectedDate) // 在这里可以执行一些自定义的逻辑 } } ``` 通过QML和Qt Quick 2.0提供的日历组件,我们可以轻松地在应用程序中实现日历功能,并根据需要进行定制和扩展。 ### 回答3: QT Quick是一个用于创建现代、高性能用户界面的框架,它提供了一种简单而强大的方式来构建交互式应用程序。QT Quick 2.0是QT Quick的第二个主要版本,它具有更好的性能和更丰富的功能。 QT Quick 2.0中提供了一个日历组件,可以方便地显示和管理日期。该日历组件是使用QML语言编写的,通过使用Qt Quick Controls模块中的QtQuick2.Calendar组件来实现。 使用QT Quick 2.0日历,您可以实现以下功能: 1. 显示当前日期:您可以轻松地显示当前日期,并以易于阅读的方式呈现在界面上。 2. 导航功能:日历组件提供了导航按钮,您可以使用它们来在不同的日期之间进行切换,例如前一天、后一天、前一周、后一周等等。 3. 选择日期:您可以单击特定日期以选择它。一旦您选择了日期,日历组件会触发相应的信号,您可以在应用程序中执行相应的操作。 4. 自定义外观:QT Quick 2.0允许您自定义日历组件的外观。您可以改变字体、背景颜色、日期文本颜色等等,以满足您的应用程序的需求。 5. 事件处理:您可以添加事件处理程序来处理特定日期的事件,例如单击、双击等等。这样,您可以根据日期的特定事件来执行相应的操作。 6. 本地化:QT Quick 2.0日历支持本地化,您可以根据不同的语言和地区来显示日期和月份名称。这使得在不同的环境中更容易使用日历组件。 总之,QT Quick 2.0日历是一个功能强大且易于使用的组件,可以帮助您方便地管理和显示日期。它具有良好的性能和灵活性,可以轻松地集成到您的QT Quick应用程序中。无论是创建个人应用程序还是商业应用程序,QT Quick 2.0日历都是一个很好的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值