Introduction
This series of articles will show you how to implement a custom-draw calendarView for IOS & Android.
We will write C/C++ code for manipulation of Date and Calendar .
For the IOS sytem, Objc will invoke C/C++ function.
For the Android sytem, We will use lib of SWIG(Simplified Wrapper and Interface Generator) which will generate JNI code for accessing C/C++ code from Java.
Contents
- Requirements description and implement of Date&Calendar operation in C/C++
- How to invoke C/C++ code from Objc and make a custom-draw CalendarView in IOS
- How to config android NDK and compile C/C++ code with gradle system
- how to generate JNI code for accessing C/C++ code from Java
- update to android studion 2.2 and use cmake compile C/C++ code
Requirements description
- First click, select one cell in selectable date area
- Second click,select all cells between the last selected and this one
- Repeating these steps
- It will scroll calendar in UITableView/ListView accroding to swipe gesture
Why use C/C++ in mobile development?
- I am familiar with C/C++
- This CalendarView is writed in 2 years aga. It is my first time for mobile development and choose cocos2d-x for cross platform development. I found that cocos2d-x was suit for game rather than app. cocos2d-x is lack of a mechanism of "dirty area" which lead to redraw all scene every 1/60 seconds and do a bad job in power saving. finally, we decide to select native language for logical development and use C/C++ for core operation.
- Android's apk is very easy to decompile . It is very difficult to decompile binary code compiled by C/C++.
- In cocos2d-x , It use SWIG(Simplified Wrapper and Interface Generator) to generate PInvoke code for accessing C/C++ code from C# in CocosStudio. I think this is a great technique and want to master it.we can use it in android app.
Why use custom-draw in this CalendarView(control)?
No matter IOS or Android,I think that There are 3 ways to extend your own view(control):
- You can use existed views in a container by InterfaceBuilder or androidStudio. It is not need to inherit from base view class.All you need to do is write event handler.
- You can inherit from a view or container view and use existed views.You can override some virtual method to extend your function.
- You can inherit from a base view and draw all the things you needs(custom-darw).
We chose the way of custom-draw for this CalendarView.
It is memory savings and fast in run.
C/C++ Base Struct:
<code>
#define ANDROID_NDK_IMP //in IOS,this code is commited out
#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</code>
<code>
#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</code>
C/C++ Date Function:
<code>
void date_set(SDate* ret,int year,int month,int day)
{
assert(ret);
ret->year = year;
ret->month = month;
ret->day = day;
}
void date_get_now(SDate* ret)
{
assert(ret);
time_t t;
time(&t);
struct tm* timeInfo;
timeInfo = localtime(&t);
ret->year = timeInfo->tm_year + 1900;
ret->month = timeInfo->tm_mon + 1;
ret->day = timeInfo->tm_mday;
}
bool date_is_equal(const SDate* left,const SDate* right)
{
assert(left&&right);
return (left->year == right->year &&
left->month == right->month &&
left->day == right->day);
}
int date_get_month_count_from_year_range(int startYear,int endYear)
{
int diff = endYear - startYear + 1;
return diff * 12;
}
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;
}
long mymktime (unsigned int year, unsigned int mon,
unsigned int day, unsigned int hour,
unsigned int min, unsigned int sec)
{
if (0 >= (int) (mon -= 2)) {
mon += 12;
year -= 1;
}
return (((
(long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +
year*365 - 719499
)*24 + hour
)*60 + min
)*60 + sec;
}
long date_get_time_t(const SDate* d)
{
assert(d);
return mymktime(d->year,d->month,d->day,0,0,1);
}
void date_get_prev_month(SDate* date, int delta)
{
assert(date);
if((date->month - delta) < 1)
{
date->year--;
date->month = 12 + date->month - delta;
}
else
date->month = date->month - delta;
}
void date_get_next_month(SDate* date, int delta)
{
assert(date);
if((date->month + delta) > 12)
{
date->year++;
date->month = date->month + delta - 12;
}
else
date->month = date->month + delta;
}
int date_get_leap(int year)
{
if(((year % 4 == 0) && (year % 100) != 0) || (year % 400 == 0))
return 1;
return 0;
}
int date_get_days(const SDate* date)
{
assert(date);
int day_table[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int i = 0, total = 0;
for(i = 0; i < date->month; i++)
total += day_table[i];
return total + date->day + date_get_leap(date->year);
}
int date_get_week(const SDate* date)
{
assert(date);
return ((date->year - 1 + (date->year - 1) / 4 - (date->year - 1) / 100 +
(date->year - 1) / 400 + date_get_days(date) )% 7);
}
int date_get_month_of_day(int year, int month)
{
switch(month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: return 31;
case 4:
case 6:
case 9:
case 11: return 30;
}
return 28 + date_get_leap(year);
}</code>
C/C++ Calendar Function:
<code>
void calendar_set_year_month(SCalendar* calendar,int year,int month)
{
assert(calendar);
{
calendar->date.year = year;
calendar->date.month = month;
calendar->date.day = 1;
calendar->dayBeginIdx = date_get_week(&calendar->date);
calendar->dayCount = date_get_month_of_day(calendar->date.year, calendar->date.month);
}
}
void calendar_get_year_month(SCalendar* calendar,int* year,int* month)
{
assert(calendar);
if(year)
*year = calendar->date.year;
if(month)
*month = calendar->date.month;
}
void calendar_init(SCalendar* calendar,CGSize ownerSize,float yearMonthHeight,float weekHeight)
{
assert(calendar && calendar);
calendar->size = ownerSize;
calendar->yearMonthSectionHeight = yearMonthHeight;
calendar->weekSectionHegiht = weekHeight;
calendar->daySectionHeight = ownerSize.height - yearMonthHeight - weekHeight;
assert(calendar->daySectionHeight > 0);
calendar_set_year_month(calendar, calendar->date.year, calendar->date.month);
}
void calendar_get_year_month_section_rect(const SCalendar* calendar,CGRect* rect)
{
assert(rect);
memset(rect,0,sizeof(CGRect));
rect->size.width = calendar->size.width;
rect->size.height = calendar->yearMonthSectionHeight;
}
void calendar_get_week_section_rect(const SCalendar* calendar,CGRect* rect)
{
assert(rect);
memset(rect,0,sizeof(CGRect));
rect->origin.y = calendar->yearMonthSectionHeight;
rect->size.width = calendar->size.width;
rect->size.height = calendar->weekSectionHegiht;
}
void calendar_get_day_section_rect(const SCalendar* calendar,CGRect* rect)
{
assert(calendar && rect);
memset(rect,0,sizeof(CGRect));
rect->origin.y = calendar->yearMonthSectionHeight + calendar->weekSectionHegiht;
rect->size.width = calendar->size.width;
rect->size.height = calendar->daySectionHeight;
}
void calendar_get_week_cell_rect(const SCalendar* calendar,CGRect* rect,int idx)
{
assert(calendar && rect && idx >= 0 && idx < 7);
calendar_get_week_section_rect(calendar, rect);
float cellWidth = rect->size.width / 7.0F;
rect->origin.x = cellWidth * idx;
rect->size.width = cellWidth;
}
void calendar_get_day_cell_rect(const SCalendar* calendar,CGRect* rect,int rowIdx,int columIdx)
{
assert(calendar && rect && rowIdx >= 0 && rowIdx < 6 && columIdx >= 0 && columIdx < 7 );
float cellWidth = calendar->size.width / 7.0F;
float cellHeight = calendar->daySectionHeight / 6.0F;
rect->origin.x = cellWidth * columIdx;
rect->origin.y = cellHeight * rowIdx;
rect->size.width = cellWidth;
rect->size.height = cellHeight;
}
void calendar_get_day_cell_rect_by_index(const SCalendar* calendar,CGRect* rect,int idx)
{
assert(calendar && rect && idx >= 0 && idx < 42);
int rowIdx = (idx / 7);
int columIdx = (idx % 7);
calendar_get_day_cell_rect(calendar, rect, rowIdx, columIdx);
}
int calendar_get_hitted_day_cell_index(const SCalendar* calendar, CGPoint localPt)
{
CGRect daySec;
calendar_get_day_section_rect(calendar, &daySec);
if(!CGRectContainsPoint(daySec,localPt))
return -1;
localPt.y -= daySec.origin.y;
float cellWidth = daySec.size.width / 7.0F;
float cellHeight = daySec.size.height / 6.0F;
int columIdx = localPt.x / cellWidth;
int rowIdx = localPt.y / cellHeight;
int idx = rowIdx * 7 + columIdx;
if(idx < calendar->dayBeginIdx || idx > calendar->dayBeginIdx + calendar->dayCount - 1)
return -1;
return idx;
}</code>
Note:
This is my first time write articles in english.
I have do my best to make me understood. If any question,let me know.
Sorry for my poor english.
Next:
Part 2 : How to invoke C/C++ code from Objc and make a custom-draw CalendarView in IOS
IOS source code will be provided in part2