章节:
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) 该日期选择控件主要用于日期过滤查询,一般用于近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++中要做到这些,可以使用宏来判断和切换当前的环境,具体见代码:
/*blf: 使用ios中的一些基础数据结构,android中需要移植过来 。
ios的话,请将下面 #define ANDROID_NDK_IMP这句代码注释掉!
*/
#define ANDROID_NDK_IMP
#ifdef ANDROID_NDK_IMP
typedef struct _CGPoint { float x; float y;}CGPoint;
typedef struct _CGSize { float width; float height;}CGSize;
typedef struct _CGRect { CGPoint origin; CGSize size;}CGRect;
#endif
/*blf: 使用ios中的一些基础数据结构,android中需要移植过来
下面是实现代码
*/
#ifdef ANDROID_NDK_IMP
static float GetRectMaxX(CGRect rc) { return rc.origin.x + rc.size.width; }
static float GetRectMaxY(CGRect rc) { return rc.origin.y + rc.size.height; }
static bool CGRectContainsPoint(CGRect rc, CGPoint pt){return(pt.x >= rc.origin.x) && (pt.x <= GetRectMaxX(rc)) && (pt.y >= rc.origin.y) && (pt.y <= GetRectMaxY(rc));}
#endif
热门评论
再次修正一个bug,已上传github
-(void) setYearMonth:(int)year month:(int)month
{
calendar_set_year_month(&_calendar, year, month);
if (_calendar.date.month > 1) {
_lastMonthDayCount = date_get_month_of_day(_calendar.date.year,_calendar.date.month-1);
}else{
_lastMonthDayCount = date_get_month_of_day(_calendar.date.year-1,12);
}
[self setNeedsDisplay];
}
修正一个bug,已上传github
void date_map_index_to_year_month(SDate* to,int startYear,int idx)
{
assert(to);
to->year = startYear + idx / 12;
to->month = idx % 12 + 1;
//to->day = -1;
//修改成如下代码。应该为1而不是-1
to->day = 1;
}