用c/c++混合編程方式爲ios/android實現一個自繪日期選擇控件(一)

 本文爲原創,若有轉載,請註明出處:http://www.cnblogs.com/jackybujava

 

前言python

章節:linux

    一、需求描述以及c/c++實現日期和月曆的基本操做android

     二、ios實現自繪日期選擇控件ios

     三、android實現自繪日期選擇控件c++

目的:算法

    經過一個相對複雜的自定義自繪控件來分享:數據庫

     一、ios以及android自定義自繪控件的開發流程編程

     二、objc與c/c++混合編程c#

     三、android ndk的環境配置,android studio ndk的編譯模式,swig在android ndk開發中的做用

 

1、需求描述以及c/c++實現日期和月曆的基本操做

一、需求描述:

                             

  圖1  某個月的日曆外觀                                                                                                               圖2  日期區間選中狀態

 

       1) 該日期選擇控件主要用於日期過濾查詢,通常用於近n年的數據查詢。

        2) 假設你點擊某個按鈕彈出該日期選擇控件時,自動定位到當前月,例如本月是4月,則4月份顯示在屏幕最下方。

        3) 當你手指向上滑動時,向前n個月滾動(月份例如:4-3-2-1),手指向下滑動時,向後n個月滾動(月份例如:1-2-3-4)。

        4) 日期區分爲可選擇區或不可選擇區(圖1),當你第一次點擊可選的日期,該日期會被選中(藍色)。

        5) 當你第二次點擊可選的日期,則會造成一個選區(圖2),該選區會跳過全部不可選的日期。

 

二、爲何使用c/c++:

      1) 歷史緣由:該控件是兩年前爲某個項目實現的,當時沒有移動開發經驗,所以最初技術預研時選擇了跨平臺的cocos2d-x來開發,併爲其實現了該控件.可是cocos2d-x存在一些bug,而且其基於遊戲開發模式,不停的循環繪製,cpu佔用高,耗電量大。若是要用cocos2d-x開發通常的app的話,須要爲其加入髒區局部刷新的功能,這樣改動量太大。在研究cocos2d-x時,其所見即所得的cocostudio須要使用swig將c/c++代碼wrap成c#供其進行平臺調用。當時以爲swig真是強大無比,能夠自動wrap爲c#,java,python,lua,js....等進行相互調用。

      2) ios端objc能夠很是容易的與c/c++進行相互調用,而android ndk+swig也能夠大大減輕c/c++代碼在android端的實現和調用難度(具體咱們在第三部分中能夠體會到)。這樣咱們就可以重用爲cocos2d-x所寫的c/c++代碼。

      3) 基於java的android程序,很是容易進行反編譯,所以使用c/c++編譯成.so後,都是二進制代碼。所以若是對運行速度或代碼安全性有比較高的要求的話,可使用c/c++進行實現,由android java jni進行調用。

      4) 還有一個重要緣由就是想深刻了解一下android ndk以及swig的開發方式。

 

三、爲何選擇自定義自繪控件方式:

    不論是android仍是ios,自定義控件的實現基本上有三種方式:

     1) 利用androidStudio或xcode interfaceBuilder中的容器控件以及其餘控件組合拼裝而成,自定義控件不須要繼承自View或子類。你能夠進行一些事件的編寫就能夠完成不少需求。

     2) 繼承自View或子類,再用現有的控件組合拼裝而成。

     3) 繼承自View或子類,全部該自定義View的顯示效果由咱們來繪製出來。

     這裏咱們採起第三種方式。相對來講,這種方式內存消耗要小不少,而且速度上也有必定優點吧。要知道每月都是須要42個cell表示日期,而且加上年月和星期這些區塊,都用View組合而成,內存也不算小。不論是ios仍是android,每一個View的成員變量都很多。而使用自繪控件,只要一個View就解決了。至少內存使用上能夠減小40多個View的使用,對吧?

 

 四、c/c++實現細節:

  1) android中的一些適配結構和函數:

         由於使用了ios內置的例如CGRect,CGPoint,CGSize等c語言結構,而android ndk中沒有這些結構,所以對於android來講,須要實現這些結構以及在整個程序中用到的一些函數。c/c++中要作到這些,可使用宏來判斷和切換當前的環境,具體見代碼:

 1 /*
 2 blf: 使用ios中的一些基礎數據結構,android中須要移植過來
 3      ios的話,請將下面 #define ANDROID_NDK_IMP這句代碼註釋掉
 4 */
 5 #define ANDROID_NDK_IMP
 6 #ifdef ANDROID_NDK_IMP
 7 typedef struct _CGPoint {
 8     float x;
 9     float y;
10 }CGPoint;
11 
12 /* Sizes. */
13 typedef struct _CGSize {
14     float width;
15     float height;
16 }CGSize;
17 
18 /* Rectangles. */
19 typedef struct _CGRect {
20     CGPoint origin;
21     CGSize size;
22 }CGRect;
23 #endif

 

 1 /*
 2 blf: 使用ios中的一些基礎數據結構,android中須要移植過來
 3      下面是實現代碼
 4 */
 5 #ifdef ANDROID_NDK_IMP
 6 static float GetRectMaxX(CGRect rc) { return rc.origin.x + rc.size.width;  }
 7 static float GetRectMaxY(CGRect rc) { return rc.origin.y + rc.size.height; }
 8 static bool CGRectContainsPoint(CGRect rc, CGPoint pt)
 9 {
10    return(pt.x >= rc.origin.x) && (pt.x <= GetRectMaxX(rc)) && (pt.y >= rc.origin.y) && (pt.y <= GetRectMaxY(rc));
11 }
12 #endif

 

  2) 日期操做函數:

     這些函數都和日期操做相關,具體請參考代碼,註釋應該比較清楚的。

  1 /*
  2 blf: 函數參數都是以指針方式傳入(java或c#中就爲傳引用,swig將指針會轉換爲類對象,全部類對象在java和c#中都是傳引用的.
  3      c#支持struct,是值類型
  4 
  5     c#還支持參數的ref和out方式,能夠將值類型以傳引用方式輸出到參數中,至關於c中的指針
  6 
  7     經驗之談:除非在c/c++中你使用shared_ptr等智能指針,不然千萬不要在函數或成員方法中malloc或new一個新對象而後return出來。
  8     比較好的方式仍是經過參數傳指針或引用方式來返回更新的數據。
  9 */
 10 void date_set(SDate* ret,int year,int month,int day)
 11 {
 12    assert(ret);
 13    ret->year = year;
 14    ret->month = month;
 15    ret->day = day;
 16 }
 17 
 18 /*
 19 blf: 獲取當前的年月日
 20 */
 21 void date_get_now(SDate* ret)
 22 {
 23    assert(ret);
 24 
 25    //time()此函數會返回從公元 1970 年1 月1 日的UTC 時間從0 時0 分0 秒算起到如今所通過的秒數。
 26    //記住:是秒數,而不是毫秒數(不少語言返回的是毫秒數,crt中是以秒爲單位的)
 27    //若是t 並不是空指針的話,此函數也會將返回值存到t指針所指的內存
 28    time_t t;
 29    time(&t);
 30 
 31    //轉換到當前系統的本地時間
 32    struct tm* timeInfo;
 33    timeInfo = localtime(&t);
 34 
 35    //tm結構中的年份是從1900開始到今天的年數,所以須要加上1900
 36    ret->year  =  timeInfo->tm_year + 1900;
 37 
 38    //月份是 0 base的,咱們按照1-12的方式來計算,所以加1
 39    ret->month =  timeInfo->tm_mon + 1;
 40 
 41    ret->day   =  timeInfo->tm_mday;
 42 }
 43 
 44 /*
 45 blf: 是否相等
 46 */
 47 bool date_is_equal(const SDate* left,const SDate* right)
 48 {
 49    assert(left&&right);
 50    return (left->year == right->year &&
 51            left->month == right->month &&
 52            left->day == right->day);
 53 }
 54 
 55 /*
 56 blf: 計算兩個年份之間的月數
 57 */
 58 int date_get_month_count_from_year_range(int startYear,int endYear)
 59 {
 60    int diff = endYear - startYear + 1;
 61    return diff * 12;
 62 }
 63 
 64 /*
 65 blf: 將一維的數據表示映射成二維表示(年和月)
 66      startYear表示起始年,例如 2010年
 67      idx表示相對2010年開始的月份偏移量
 68 
 69      咱們會在下面和後面代碼中看到/ 和 %的屢次使用
 70      能夠理解爲,將一維數據映射成二維行列表示的數據時,均可以使用這種方式
 71 
 72      下面這個函數用於月曆區間選擇控件,例如你有個數據庫查詢需求,能夠查詢
 73      當前年月日----五年前的年1月1號之間的數據,此時在UITabelView或ListView時,就須要
 74      調用本函數來顯示年月信息等
 75 */
 76 void date_map_index_to_year_month(SDate* to,int startYear,int idx)
 77 {
 78    assert(to);
 79 
 80    //每一年有12個月,idx/12你能夠當作每12個月進一位,加上startYear基準值,就能夠得到當前年份
 81    to->year = startYear + idx / 12;
 82 
 83    //每一年有12個月,idx%12你能夠當作【0-11】之間循環,加1是由於咱們的SDate結構是1-12表示的
 84    to->month = idx % 12 + 1;
 85 
 86    //至於day,這裏爲-1,咱們在map中忽略該值,能夠設置任意值
 87    to->day = -1;
 88 }
 89 
 90 /*
 91 blf: 下面函數來源於linux實現,計算從某個時間點(年月日時分秒)到1970年0時0分0秒的時間差
 92 參考url: http://blog.csdn.net/axx1611/article/details/1792827
 93 */
 94 long mymktime (unsigned int year, unsigned int mon,
 95                       unsigned int day, unsigned int hour,
 96                       unsigned int min, unsigned int sec)
 97 {
 98    if (0 >= (int) (mon -= 2)) {    /* 1..12 -> 11,12,1..10 */
 99       mon += 12;      /* Puts Feb last since it has leap day */
100       year -= 1;
101    }
102 
103    return (((
104                     (long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +
105                     year*365 - 719499
106             )*24 + hour /* now have hours */
107            )*60 + min /* now have minutes */
108           )*60 + sec; /* finally seconds */
109 }
110 
111 /*
112 blf: 下面函數一共實現了三個版本
113 
114      初版: 不知道是我對c的mktime用法錯誤仍是有bug(理論上不可能,由於ios和android中都存在問題)
115              同一個時間點,例如2016年1月1日0時0分1秒與1970年1月1日0時0分0秒的時間差不同。
116 
117      第二版: 使用ios自身的 NSCalendar對象計算時間差,這個計算是正確的,可是隻能用在ios中
118 
119      第三版: http://blog.csdn.net/axx1611/article/details/1792827中的算法,來自於linux源碼,ios/android中運行的很好
120 
121      爲何不用time_t而是使用long呢?
122      這是由於android中使用swig將c/c++ 代碼轉換成java jni封裝的函數時,time_t被封裝成了對象。
123      由於java不認識c的typedef結構,Swig將其轉換爲SWITGYPT_p_XXXXX類型的包裝(經典的裝箱/拆箱,每次操做都要進行裝箱拆箱,很麻煩).
124      time_t只是64位整型的typedef而已,所以轉換爲long後,經Swig轉換後,對應爲java的整型,這樣操做起來比較簡單
125 
126 */
127 
128 long date_get_time_t(const SDate* d)
129 {
130     assert(d);
131 
132     /*
133      一、初版
134     struct tm date;
135     //crt函數中year是基於1900年的偏移,所以要減去1900
136     date.tm_year = d->year - 1900;
137 
138     //crt函數中月份是[0-11]表示的,咱們使用[1-12]表示,所以要減去1
139     date.tm_mon = d->month - 1;
140 
141     date.tm_mday = d->day;
142     date.tm_hour = 0;
143     date.tm_min = 0;
144     date.tm_sec = 1;
145     time_t seconds = mktime(&date);
146 
147     return (long)seconds;
148     */
149 
150     /*
151      二、第二版 ios NSCalendar對象計算時間差
152      NSDateComponents *components = [[NSDateComponents alloc] init];
153 
154      [components setDay:d->day]; // Monday
155      [components setMonth:d->month]; // May
156      [components setYear:d->year];
157 
158      NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
159 
160      NSDate *date = [gregorian dateFromComponents:components];
161 
162      return (time_t) [date timeIntervalSince1970];
163      */
164 
165      /*
166      三、網上Linux版本
167      */
168      return mymktime(d->year,d->month,d->day,0,0,1);
169 }
170 
171 /*
172 blf: 根據delta計算月份,返回值存儲在date結構中
173      例如:當前年月爲2015年1月份,delta爲2,則返回2014年11月
174 */
175 void date_get_prev_month(SDate* date, int delta)
176 {
177    assert(date);
178 
179    if((date->month - delta) < 1)
180    {
181       //條件: 假設爲2015年1月,delta = 2
182       //由於: 1-2 = -1 < 1
183       //因此: 年數 = 2015 - 1 = 2014 月份 = 12 + 1 - 2 = 11
184       date->year--;
185       date->month = 12 + date->month - delta;
186    }
187    else
188       date->month = date->month - delta;
189 }
190 
191 /*
192 blf: 根據delta計算出月份,返回值存儲在date結構中
193      例如:當前年月爲2015年11月份,delta爲2,則返回2016年1月
194 */
195 void date_get_next_month(SDate* date, int delta)
196 {
197    assert(date);
198    if((date->month + delta) > 12)
199    {
200       //條件: 假設爲2015年11月,delta = 2
201       //由於: 11 + 2 = 13 > 12
202       //因此: 年數 = 2015 + 1 = 2016 月份 = 11 + 2 - 12 = 1
203       date->year++;
204       date->month = date->month + delta - 12;
205    }
206    else
207       date->month = date->month + delta;
208 }
209 
210 /*
211 blf: 根據輸入年份,判斷是不是閏年
212      固定算法:判斷閏年的方法是該年能被4整除而且不能被100整除,或者是能夠被400整除
213 */
214 int date_get_leap(int year)
215 {
216    if(((year % 4 == 0) && (year % 100) != 0) || (year % 400 == 0))
217       return 1;
218    return 0;
219 }
220 
221 /*
222 blf: 輔助函數,用於計算某年某月的某天是星期幾
223 */
224 int date_get_days(const SDate* date)
225 {
226    assert(date);
227    int day_table[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
228    int i = 0, total = 0;
229    for(i = 0; i < date->month; i++)
230       total += day_table[i];
231    return total + date->day + date_get_leap(date->year);
232 }
233 
234 /*
235 blf: 用於計算某年某月的某天是星期幾,調用上面函數
236      這些算法比較固定,具體原理也不須要太瞭解,由於我也不清楚。
237 */
238 int date_get_week(const SDate* date)
239 {
240    assert(date);
241    return ((date->year - 1 + (date->year - 1) / 4 - (date->year - 1) / 100 +
242             (date->year - 1) / 400 + date_get_days(date) )% 7);
243 }
244 
245 /*
246 blf: 用於計算某個月的天數
247 */
248 int date_get_month_of_day(int year, int month)
249 {
250    switch(month)
251    {
252       case 1:
253       case 3:
254       case 5:
255       case 7:
256       case 8:
257       case 10:
258       case 12: return 31;
259       case 4:
260       case 6:
261       case 9:
262       case 11: return 30;
263    }
264    //blf:2月比較特別,要進行閏年判斷
265    return 28 + date_get_leap(year);
266 }

 

  3) 月曆操做函數:

 1 typedef struct _calendar
 2 {
 3     CGSize  size; //大小尺寸
 4     SDate   date; //該月曆表明的年月
 5 
 6     float   yearMonthSectionHeight; //年月區塊的高度
 7     float   weekSectionHegiht;      //星期區塊的高度
 8    
 9     //blf:計算出來的結果,第三方不要設置這些變量
10     float daySectionHeight;  //已知size.height以及yearMonthSectionHeight和weekSectionHegiht,就能計算出日期區塊的高度
11     
12     int   dayBeginIdx; //1號的偏移索引,具體描述參考實現代碼
13     int   dayCount;    //當前月份一共多少天
14 
15 }SCalendar;

 

  1 /*
  2  blf: calendar dayBeginIdx 和 dayCount圖示
  3 
  4    0   1   2   3   4   5   6       week section
  5  ---------------------------
  6  |   |   |   |   |   |   | 1 |     rowIdx = 0
  7  ---------------------------
  8  ---------------------------
  9  | 2 | 3 | 4 | 5 | 6 | 7 | 8 |     rowIdx = 1
 10  ---------------------------
 11  ---------------------------
 12  | 9 | 10| 11| 12| 13| 14| 15|     rowIdx = 2
 13  ---------------------------
 14  ---------------------------
 15  | 16| 17| 18| 19| 20| 21| 22|     rowIdx = 3
 16  ---------------------------
 17  ---------------------------
 18  | 23| 24| 24| 25| 26| 27| 28|     rowIdx = 4
 19  ---------------------------
 20  ---------------------------
 21  | 30| 31|   |   |   |   |   |     rowIdx = 5
 22  ---------------------------
 23 
 24  */
 25 
 26 void calendar_set_year_month(SCalendar* calendar,int year,int month)
 27 {
 28    assert(calendar);
 29    //if(calendar->date.year != year || calendar->date.month != month)
 30    {
 31       calendar->date.year = year;
 32       calendar->date.month = month;
 33       //每一個day設置爲1號
 34       calendar->date.day = 1;
 35 
 36       //blf:
 37       //參考上面圖示,dayBeginIdx得到的是某個月1號相對日期區塊中的索引,例如本例中1號索引爲6
 38       //而dayCount表示當前月的天數
 39       //這樣經過偏移與長度,咱們能夠很容易進行某些重要操做
 40       //例如在碰撞檢測某個cell是否被點中時,能夠跳過沒有日期的cell
 41       //在繪圖時檢測某個cell是否找範圍以外,若是以外就用灰色現實等等
 42       //經過偏移量和count,進行範圍判斷
 43       calendar->dayBeginIdx = date_get_week(&calendar->date);
 44       calendar->dayCount = date_get_month_of_day(calendar->date.year, calendar->date.month);
 45    }
 46 
 47 }
 48 
 49 void calendar_get_year_month(SCalendar* calendar,int* year,int* month)
 50 {
 51    assert(calendar);
 52    if(year)
 53       *year = calendar->date.year;
 54    if(month)
 55       *month = calendar->date.month;
 56 }
 57 
 58 /*
 59  blf: 初始化一個月曆結構,填充全部成員變量的數據
 60 */
 61 void calendar_init(SCalendar* calendar,CGSize ownerSize,float yearMonthHeight,float weekHeight)
 62 {
 63    assert(calendar && calendar);
 64 
 65    //memset(calendar, 0, sizeof(SCalendar));
 66 
 67    calendar->size = ownerSize;
 68    calendar->yearMonthSectionHeight = yearMonthHeight;
 69    calendar->weekSectionHegiht = weekHeight;
 70    //blf:daySectionHeight是計算出來的
 71    calendar->daySectionHeight = ownerSize.height - yearMonthHeight - weekHeight;
 72    //blf:錯誤檢測,簡單期間,所有使用assert在debug時候進行中斷
 73    assert(calendar->daySectionHeight > 0);
 74 
 75    //blf:初始化時顯示本地當前的年月日
 76    //date_get_now(&calendar->date);
 77 
 78    calendar_set_year_month(calendar, calendar->date.year, calendar->date.month);
 79 }
 80 
 81 /*
 82  blf: 計算出年月區塊的rect
 83 */
 84 void calendar_get_year_month_section_rect(const SCalendar* calendar,CGRect* rect)
 85 {
 86    assert(rect);
 87    memset(rect,0,sizeof(CGRect));
 88    rect->size.width = calendar->size.width;
 89    rect->size.height = calendar->yearMonthSectionHeight;
 90 }
 91 
 92 /*
 93  blf: 計算出星期區塊的rect
 94 */
 95 void calendar_get_week_section_rect(const SCalendar* calendar,CGRect* rect)
 96 {
 97    assert(rect);
 98    memset(rect,0,sizeof(CGRect));
 99    rect->origin.y = calendar->yearMonthSectionHeight;
100    rect->size.width = calendar->size.width;
101    rect->size.height = calendar->weekSectionHegiht;
102 }
103 
104 /*
105  blf: 計算出年日期區塊的rect
106 */
107 void calendar_get_day_section_rect(const SCalendar* calendar,CGRect* rect)
108 {
109    assert(calendar && rect);
110    memset(rect,0,sizeof(CGRect));
111    rect->origin.y = calendar->yearMonthSectionHeight + calendar->weekSectionHegiht;
112    rect->size.width = calendar->size.width;
113    rect->size.height = calendar->daySectionHeight;
114 }
115 
116 /*
117 blf: 上面計算出來的是三大總體的區塊(section)
118      下面計算出來的都是某個大區快中的子區(cell)
119 */
120 
121 /*
122  blf:
123  獲取星期區塊中每一個索引指向的子區rect位置與尺寸
124  輸出數據在rect參數中
125  ---------------------------
126  | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
127  ---------------------------
128  idx = 0時表示星期日
129  用於繪圖
130  */
131 
132 void calendar_get_week_cell_rect(const SCalendar* calendar,CGRect* rect,int idx)
133 {
134    assert(calendar && rect && idx >= 0 && idx < 7);
135    //獲取星期區塊
136    calendar_get_week_section_rect(calendar, rect);
137    //計算出cell的寬度
138    float cellWidth = rect->size.width / 7.0F;
139    //計算出x偏移量
140    rect->origin.x = cellWidth * idx;
141    rect->size.width = cellWidth;
142 }
143 
144 
145 /*
146  blf:
147  獲取日期區塊中行列索引指向的子區rect位置與尺寸
148  ---------------------------
149  | 0 | 1 | 2 | 3 | 4 | 5 | 6 |     rowIdx = 0
150  ---------------------------
151  ---------------------------
152  | 0 | 1 | 2 | 3 | 4 | 5 | 6 |     rowIdx = 1
153  ---------------------------
154  ---------------------------
155  | 0 | 1 | 2 | 3 | 4 | 5 | 6 |     rowIdx = 2
156  ---------------------------
157  ---------------------------
158  | 0 | 1 | 2 | 3 | 4 | 5 | 6 |     rowIdx = 3
159  ---------------------------
160  ---------------------------
161  | 0 | 1 | 2 | 3 | 4 | 5 | 6 |     rowIdx = 4
162  ---------------------------
163  ---------------------------
164  | 0 | 1 | 2 | 3 | 4 | 5 | 6 |     rowIdx = 5
165  ---------------------------
166 
167  一個月老是在28--31天之間,因爲星期要縮進,所以6行7列足夠解決因爲星期縮進引發的顯示不全問題
168  用於繪圖以及碰撞檢測,共計42個單元格
169 
170  以二維方式獲取日期區塊中索引指向的子區的rect位置與尺寸
171  */
172 
173 void calendar_get_day_cell_rect(const SCalendar* calendar,CGRect* rect,int rowIdx,int columIdx)
174 {
175    assert(calendar && rect && rowIdx >= 0 && rowIdx < 6 && columIdx >= 0 && columIdx < 7 );
176    float cellWidth = calendar->size.width / 7.0F;
177    float cellHeight = calendar->daySectionHeight / 6.0F;
178    rect->origin.x = cellWidth  * columIdx;
179    rect->origin.y = cellHeight * rowIdx;
180    rect->size.width  = cellWidth;
181    rect->size.height = cellHeight;
182 }
183 
184 /*
185  blf:
186  以一維方式方式獲取日期區塊中索引指向的子區的rect位置與尺寸
187  */
188 void calendar_get_day_cell_rect_by_index(const SCalendar* calendar,CGRect* rect,int idx)
189 {
190    assert(calendar && rect && idx >= 0 && idx < 42);
191    // (/ 和 %)符號的應用,用於計算出行列索引號
192    int rowIdx   = (idx / 7);
193    int columIdx = (idx % 7);
194    calendar_get_day_cell_rect(calendar, rect, rowIdx, columIdx);
195 
196 }
197 
198 /*
199  blf:
200  檢測touchPoint是否點擊在日期區塊的某一個cell中
201  若是檢測到有cell被點中,返回索引
202  不然返回-1
203 
204    0   1   2   3   4   5   6       week section
205  ---------------------------
206  |   |   |   |   |   |   | 1 |     rowIdx = 0
207  ---------------------------
208  ---------------------------
209  | 2 | 3 | 4 | 5 | 6 | 7 | 8 |     rowIdx = 1
210  ---------------------------
211  ---------------------------
212  | 9 | 10| 11| 12| 13| 14| 15|     rowIdx = 2
213  ---------------------------
214  ---------------------------
215  | 16| 17| 18| 19| 20| 21| 22|     rowIdx = 3
216  ---------------------------
217  ---------------------------
218  | 23| 24| 24| 25| 26| 27| 28|     rowIdx = 4
219  ---------------------------
220  ---------------------------
221  | 30| 31|   |   |   |   |   |     rowIdx = 5
222  ---------------------------
223 注意: 參數localPt是相對於你所繼承的View的左上角[0,0]的偏移量,是定義在View空間座標系的
224  */
225 int calendar_get_hitted_day_cell_index(const SCalendar* calendar, CGPoint localPt)
226 {
227    //優化1: 若是一個點不在日期區塊中,那麼確定沒點中,當即返回
228    CGRect daySec;
229    calendar_get_day_section_rect(calendar, &daySec);
230 
231    if(!CGRectContainsPoint(daySec,localPt))
232       return -1;
233 
234    localPt.y -= daySec.origin.y;
235 
236    //觸摸點確定會會點中日期區塊中的某個cell
237 
238    //優化2: 避免使用循環6*7次遍歷整個cell,檢測是否一點在該cell中,經過下面算法,能夠馬上得到當前點所在的cell行列索引號
239 
240    float cellWidth  =   daySec.size.width  / 7.0F;
241    float cellHeight =   daySec.size.height / 6.0F;
242    int   columIdx   =   localPt.x / cellWidth;
243    int   rowIdx     =   localPt.y / cellHeight;
244 
245    //檢測當前被點中的cell是否容許被選中,具體原理請參考
246    //函數void calendar_set_year_month(SCalendar* calendar,int year,int month)的註釋
247 
248    int idx  =  rowIdx * 7 + columIdx;
249    if(idx < calendar->dayBeginIdx || idx > calendar->dayBeginIdx  + calendar->dayCount - 1)
250       return -1;
251 
252    //到此說明確定有點中的cell,返回該cell的索引號
253    return idx;
254 }

 

         第一部分完畢,下一篇咱們關注ios的實現。

相關文章
相關標籤/搜索