用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)

本文为原创,如有转载,请注明出处:http://www.cnblogs.com/jackybu

前言

章节:

1、需求描述以及c/c++实现日期和月历的基本操作

2、ios实现自绘日期选择控件

3、android实现自绘日期选择控件

目的:

通过一个相对复杂的自定义自绘控件来分享:

1、ios以及android自定义自绘控件的开发流程

2、objc与c/c++混合编程

3、android ndk的环境配置,android studio ndk的编译模式,swig在android ndk开发中的作用

一、需求描述以及c/c++实现日期和月历的基本操作

1、需求描述:

                             

图1  某个月的日历外观                                                                                                               图2  日期区间选中状态

1) 该日期选择控件主要用于日期过滤查询,一般用于近n年的数据查询。

2) 假设你点击某个按钮弹出该日期选择控件时,自动定位到当前月,例如本月是4月,则4月份显示在屏幕最下方。

3) 当你手指向上滑动时,向前n个月滚动(月份例如:4-3-2-1),手指向下滑动时,向后n个月滚动(月份例如:1-2-3-4)。

4) 日期区分为可选择区或不可选择区(图1),当你第一次点击可选的日期,该日期会被选中(蓝色)。

5) 当你第二次点击可选的日期,则会形成一个选区(图2),该选区会跳过所有不可选的日期。

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的开发方式。

3、为什么选择自定义自绘控件方式:

    不管是android还是ios,自定义控件的实现基本上有三种方式:

1) 利用androidStudio或xcode interfaceBuilder中的容器控件以及其他控件组合拼装而成,自定义控件不需要继承自View或子类。你可以进行一些事件的编写就可以完成很多需求。

2) 继承自View或子类,再用现有的控件组合拼装而成。

3) 继承自View或子类,所有该自定义View的显示效果由我们来绘制出来。

这里我们采取第三种方式。相对来说,这种方式内存消耗要小很多,并且速度上也有一定优势吧。要知道每个月都是需要42个cell表示日期,并且加上年月和星期这些区块,都用View组合而成,内存也不算小。不管是ios还是android,每个View的成员变量都不少。而使用自绘控件,只要一个View就解决了。至少内存使用上可以减少40多个View的使用,对吧?

4、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      1、第一版
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      2、第二版 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      3、网上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的实现。

时间: 04-30

用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)的相关文章

[WP8.1UI控件编程]Windows Phone VirtualizingStackPanel、ItemsStackPanel和ItemsWrapGrid虚拟化排列布局控件

11.2.2 VirtualizingStackPanel.ItemsStackPanel和ItemsWrapGrid虚拟化排列布局控件 VirtualizingStackPanel.ItemsStackPanel和ItemsWrapGrid都是虚拟化布局控件,一般情况下在界面的布局上很少会用到这些虚拟化排列的控件,大部分都是封装在列表的布局面板上使用,主要的目的就是为了实现列表上大数据量的虚拟化,从而极大地提高列表的效率.那么其实这3个虚拟化布局控件都是列表控件的默认布局排列的方式,其中Vir

ios 中在容器中移除单个控件的两个方法Subview

我们知道[parentView removeFromSuperView];  会把全部的view都移除.以下我们可以通过给subView设一个tag,然后遍历所有的subView,找到目标subView再删除. ? 1 2 3 4 5 for (UIView *subviews in [self.view subviews]) {         if (subviews.tag==22) {             [subviews removeFromSuperview];        

iOS 根据字符串数目,自定义Label等控件的高度

利用分类,NSString,增加一个方法. #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface NSString (Height) + (CGSize)getRect:(NSString *)textStr andWidth:(CGSize)size andFont:(UIFont *)font; @end #import "NSString+Height.h" @implem

【转】VS2010/MFC编程入门之八(对话框:创建对话框类和添加控件变量)

原文网址:http://www.jizhuomi.com/software/153.html 前两讲中鸡啄米为大家讲解了如何创建对话框资源.创建好对话框资源后要做的就是生成对话框类了.鸡啄米再声明下,生成对话框类主要包括新建对话框类.添加控件变量和控件的消息处理函数等. 因为鸡啄米给大家的例程Addition是基于对话框的程序,所以程序自动创建了对话框模板IDD_ADDITION_DIALOG,并自动生成了对话框类CAdditionDlg,它是从CDialogEx类派生的.大家用过VC++6.0

IOS 开发笔记-基础 UI(8)控件连线的 strong 和 weak 设定

回忆 oc 的内存管理: objective-c 语法快速过(6)内存管理原理,objective-c 语法快速过(7)编译器特性ARC ARC是苹果为了简化程序员对内存的管理,推出的一套内存管理机制,使用ARC机制,对象的申请和释放工作会在运行时,由编译器自动在代码中添加retain和release 1> strong:强指针引用的对象,在生命周期内不会被系统释放,在OC中,对象默认都是强指针 2> weak:弱指针引用的对象,系统会立即释放,弱指针可以指向其他已经被强指针引用的对象 他们都

Android 实现 IOS相机滑动控件

 IOS相比于Android,动画效果是一方面优势,IOS相机切换时滑动的动画很不错,看着是有一个3D的效果,而且变化感觉很自然.Android也可以通过Graphics下面的Camera可以实现3D效果,开始尝试着用这个做了一下,效果不理想,滑动之后各组文字之间的距离就变了,从立体空间来说这是合逻辑的,但是看着很别捏.IOS相机的滑动效果文字之间的间隔在滑动的时候是不变的. 后面通过调整TextView X方向的scale使文字看着紧凑一点,然后通过计算的距离的方式,在滑动的时候保持各组文字之

仿IOS 带字母索引的滑轮控件

效果大概就是这样,右边是字母索引效果 做开发的时候,经常碰到产品经理设计出来的界面是参考IOS控件设计出来的 ,比如上图效果  ios有个控件是UIPickerView  就是可以上下滑动 并有些3d效果,非常炫. 但是android并没有提供这样的原生控件支持,所以需要通过其他方式实现类似效果.上图就是我开发中用到的一个效果. 话不多说 ,直接上代码: Activity package com.example.picscrollview; import java.util.ArrayList;

如何自定义iOS中的控件

本文译自 How to build a custom control in iOS .大家要是有什么问题,可以直接在 twitter 上联系原作者,当然也可以在最后的评论中回复我. 在开发过程中,有时候UIKit的标准控件并不能满足我们的需求,例如你需要一个控件能支持用户方便的选择0-360°之间的一个角度值,此时就需要根据自己的需求自定义控件了. 对于选择角度值的控件可以这样实现:创建一个圆形的滑块,用户通过拖动手柄操作就能选择角度值.实际上这样的控件在别的一些平台中你可能看到过,但是在UIK

026-代码创建控件-iOS笔记

学习目标 1.[理解]代码创建控件过程 2.[理解]代码实现QQ登陆界面 3.[理解]图片浏览器 4.[理解]汤姆猫小游戏 一.代码创建控件过程 所有控件都是类的对象,不同的类创建可以不同类型的控件.也是就说创建一个控件其实就是创建一个对应类的对象. 常用控件类型 UIButton:按钮,界面上可点击的大都是按钮 UILabel:标签,界面上只显示文字不能点击大都是标签 UITextField:文本框,界面上可输入数据的文本框 UIImageView:图片框,界面上不可点击的图片大都是图片框 使