2

Android自定义控件之日历控件

 2 years ago
source link: http://www.androidchina.net/3641.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Android自定义控件之日历控件 – Android开发中文站

Android自定义控件之日历控件

三月份学习android,至今也有半年有余,中间也做过两个项目,但是依然感觉自己做的应用不是很有新意,比不上应用市场上那些应用如此绚丽。所以自己仍需继续努力。学习至今,仍感觉自定义控件是一块硬骨头,还没修炼到身后的内功,下面就切入正题,以一次项目的需求,来实现一个自定义的日历控件。效果图先来一发。

blob.png

我们分析下效果图,然后确定我们的需求。
(1)、绘制星期的自定义View,用于标识日期的礼拜。
(2)、绘制日期的自定义View。
(3)、绘制事务圆圈,从效果图中我们以红圈标识今日有事务。
(4)、绘制选中日期的颜色。
(5)、对选中日期进行点击事件的处理。

通过对效果图的分析,得出了我们的需求,我们在仔细分析效果图,发现里面就是绘制文字和绘制线条,所以我们只要回Canvas的这两个功能即可,主要的难点是如何将这些日期进行位置的安排,接下来我们就来逐个分析如何实现一个自定义View。

实现Week的自定义View

blob.png

分析下效果图,我们需要绘制上下两条线、然后绘制描述文字(日、一、二、三、四、五、六)。下面就讲解下我们的实现。先看着部分的源码,然后在分开讲解。

public class WeekDayView extends View {
//上横线颜色
private int mTopLineColor = Color.parseColor("#CCE4F2");
//下横线颜色
private int mBottomLineColor = Color.parseColor("#CCE4F2");
//周一到周五的颜色
private int mWeedayColor = Color.parseColor("#1FC2F3");
//周六、周日的颜色
private int mWeekendColor = Color.parseColor("#fa4451");
//线的宽度
private int mStrokeWidth = 4;
private int mWeekSize = 14;
private Paint paint;
private DisplayMetrics mDisplayMetrics;
private String[] weekString = new String[]{"日","一","二","三","四","五","六"};
public WeekDayView(Context context, AttributeSet attrs) {
super(context, attrs);
mDisplayMetrics = getResources().getDisplayMetrics();
paint = new Paint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if(heightMode == MeasureSpec.AT_MOST){
heightSize = mDisplayMetrics.densityDpi * 30;
}
if(widthMode == MeasureSpec.AT_MOST){
widthSize = mDisplayMetrics.densityDpi * 300;
}
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onDraw(Canvas canvas) {
int width = getWidth();
int height = getHeight();
//进行画上下线
paint.setStyle(Style.STROKE);
paint.setColor(mTopLineColor);
paint.setStrokeWidth(mStrokeWidth);
canvas.drawLine(0, 0, width, 0, paint);
//画下横线
paint.setColor(mBottomLineColor);
canvas.drawLine(0, height, width, height, paint);
paint.setStyle(Style.FILL);
paint.setTextSize(mWeekSize * mDisplayMetrics.scaledDensity);
int columnWidth = width / 7;
for(int i=0;i < weekString.length;i++){
String text = weekString[i];
int fontWidth = (int) paint.measureText(text);
int startX = columnWidth * i + (columnWidth - fontWidth)/2;
int startY = (int) (height/2 - (paint.ascent() + paint.descent())/2);
if(text.indexOf("日") > -1|| text.indexOf("六") > -1){
paint.setColor(mWeekendColor);
}else{
paint.setColor(mWeedayColor);
}
canvas.drawText(text, startX, startY, paint);
}
}
/**
* 设置顶线的颜色
* @param mTopLineColor
*/
public void setmTopLineColor(int mTopLineColor) {
this.mTopLineColor = mTopLineColor;
}
/**
* 设置底线的颜色
* @param mBottomLineColor
*/
public void setmBottomLineColor(int mBottomLineColor) {
this.mBottomLineColor = mBottomLineColor;
}
/**
* 设置周一-五的颜色
* @return
*/
public void setmWeedayColor(int mWeedayColor) {
this.mWeedayColor = mWeedayColor;
}
/**
* 设置周六、周日的颜色
* @param mWeekendColor
*/
public void setmWeekendColor(int mWeekendColor) {
this.mWeekendColor = mWeekendColor;
}
/**
* 设置边线的宽度
* @param mStrokeWidth
*/
public void setmStrokeWidth(int mStrokeWidth) {
this.mStrokeWidth = mStrokeWidth;
}
/**
* 设置字体的大小
* @param mWeekSize
*/
public void setmWeekSize(int mWeekSize) {
this.mWeekSize = mWeekSize;
}
/**
* 设置星期的形式
* @param weekString
* 默认值 "日","一","二","三","四","五","六"
*/
public void setWeekString(String[] weekString) {
this.weekString = weekString;
}
}

(1)、首先我们定义了我们需要的成员变量,比如上下线条的颜色、宽度、字体的大小、周期的表现形式。这些都是为了灵活定制而需要的。方便使用。
(2)、现在来看看onMeasure方法,我们知道在自定义view中,我们遇到wrap_content属性,这是view的大小可能就不是我们想要的了,所以我们在onMeasure方法中,指定此条件下的大小,即默认大小为300*30。
(3)、onDraw方法,我们在onDraw方法中进行我们需要内容的绘制。我们使用drawLine方法,进行上下横线的绘制,然后int columnWidth = width / 7;计算每列的宽度,为什么计算宽度呢?因为我们要将”日”,”一”,”二”,”三”,”四”,”五”,”六”这七个字放在对应格子的居中位置。通过drawText方法进行绘制文字,我们需要指定绘制文字的起始位置,为了达到居中的位置,我们需要进行计算。

int startX = columnWidth * i + (columnWidth - fontWidth)/2;
int startY = (int) (height/2 - (paint.ascent() + paint.descent())/2);

此处不是很了解的,可以参照下爱哥的文章。后面就是一些设置属性,没什么讲头。

至此很简单的实现了我们的week的自定义view。下面我们来分析下日期的实现。

实现日期Date的自定义View

类似WeekView的实现,我们在DateView中的难点也是如何放置这些日期date。先上源码,然后我们在具体分析:

public class MonthDateView extends View {
private static final int NUM_COLUMNS = 7;
private static final int NUM_ROWS = 6;
private Paint mPaint;
private int mDayColor = Color.parseColor("#000000");
private int mSelectDayColor = Color.parseColor("#ffffff");
private int mSelectBGColor = Color.parseColor("#1FC2F3");
private int mCurrentColor = Color.parseColor("#ff0000");
private int mCurrYear,mCurrMonth,mCurrDay;
private int mSelYear,mSelMonth,mSelDay;
private int mColumnSize,mRowSize;
private DisplayMetrics mDisplayMetrics;
private int mDaySize = 18;
private TextView tv_date,tv_week;
private int weekRow;
private int [][] daysString;
private int mCircleRadius = 6;
private DateClick dateClick;
private int mCircleColor = Color.parseColor("#ff0000");
private List<Integer> daysHasThingList;
public MonthDateView(Context context, AttributeSet attrs) {
super(context, attrs);
mDisplayMetrics = getResources().getDisplayMetrics();
Calendar calendar = Calendar.getInstance();
mPaint = new Paint();
mCurrYear = calendar.get(Calendar.YEAR);
mCurrMonth = calendar.get(Calendar.MONTH);
mCurrDay = calendar.get(Calendar.DATE);
setSelectYearMonth(mCurrYear,mCurrMonth,mCurrDay);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if(heightMode == MeasureSpec.AT_MOST){
heightSize = mDisplayMetrics.densityDpi * 200;
}
if(widthMode == MeasureSpec.AT_MOST){
widthSize = mDisplayMetrics.densityDpi * 300;
}
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onDraw(Canvas canvas) {
initSize();
daysString = new int[6][7];
mPaint.setTextSize(mDaySize*mDisplayMetrics.scaledDensity);
String dayString;
int mMonthDays = DateUtils.getMonthDays(mSelYear, mSelMonth);
int weekNumber = DateUtils.getFirstDayWeek(mSelYear, mSelMonth);
Log.d("DateView", "DateView:" + mSelMonth+"月1号周" + weekNumber);
for(int day = 0;day < mMonthDays;day++){
dayString = (day + 1) + "";
int column = (day+weekNumber - 1) % 7;
int row = (day+weekNumber - 1) / 7;
daysString[row][column]=day + 1;
int startX = (int) (mColumnSize * column + (mColumnSize - mPaint.measureText(dayString))/2);
int startY = (int) (mRowSize * row + mRowSize/2 - (mPaint.ascent() + mPaint.descent())/2);
if(dayString.equals(mSelDay+"")){
//绘制背景色矩形
int startRecX = mColumnSize * column;
int startRecY = mRowSize * row;
int endRecX = startRecX + mColumnSize;
int endRecY = startRecY + mRowSize;
mPaint.setColor(mSelectBGColor);
canvas.drawRect(startRecX, startRecY, endRecX, endRecY, mPaint);
//记录第几行,即第几周
weekRow = row + 1;
}
//绘制事务圆形标志
drawCircle(row,column,day + 1,canvas);
if(dayString.equals(mSelDay+"")){
mPaint.setColor(mSelectDayColor);
}else if(dayString.equals(mCurrDay+"") && mCurrDay != mSelDay && mCurrMonth == mSelMonth){
//正常月,选中其他日期,则今日为红色
mPaint.setColor(mCurrentColor);
}else{
mPaint.setColor(mDayColor);
}
canvas.drawText(dayString, startX, startY, mPaint);
if(tv_date != null){
tv_date.setText(mSelYear + "年" + (mSelMonth + 1) + "月");
}
if(tv_week != null){
tv_week.setText("第" + weekRow +"周");
}
}
}
private void drawCircle(int row,int column,int day,Canvas canvas){
if(daysHasThingList != null && daysHasThingList.size() >0){
if(!daysHasThingList.contains(day))return;
mPaint.setColor(mCircleColor);
float circleX = (float) (mColumnSize * column + mColumnSize*0.8);
float circley = (float) (mRowSize * row + mRowSize*0.2);
canvas.drawCircle(circleX, circley, mCircleRadius, mPaint);
}
}
@Override
public boolean performClick() {
return super.performClick();
}
private int downX = 0,downY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
int eventCode= event.getAction();
switch(eventCode){
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
int upX = (int) event.getX();
int upY = (int) event.getY();
if(Math.abs(upX-downX) < 10 && Math.abs(upY - downY) < 10){//点击事件
performClick();
doClickAction((upX + downX)/2,(upY + downY)/2);
}
break;
}
return true;
}
/**
* 初始化列宽行高
*/
private void initSize(){
mColumnSize = getWidth() / NUM_COLUMNS;
mRowSize = getHeight() / NUM_ROWS;
}
/**
* 设置年月
* @param year
* @param month
*/
private void setSelectYearMonth(int year,int month,int day){
mSelYear = year;
mSelMonth = month;
mSelDay = day;
}
/**
* 执行点击事件
* @param x
* @param y
*/
private void doClickAction(int x,int y){
int row = y / mRowSize;
int column = x / mColumnSize;
setSelectYearMonth(mSelYear,mSelMonth,daysString[row][column]);
invalidate();
//执行activity发送过来的点击处理事件
if(dateClick != null){
dateClick.onClickOnDate();
}
}
/**
* 左点击,日历向后翻页
*/
public void onLeftClick(){
int year = mSelYear;
int month = mSelMonth;
int day = mSelDay;
if(month == 0){//若果是1月份,则变成12月份
year = mSelYear-1;
month = 11;
}else if(DateUtils.getMonthDays(year, month) == day){
//如果当前日期为该月最后一点,当向前推的时候,就需要改变选中的日期
month = month-1;
day = DateUtils.getMonthDays(year, month);
}else{
month = month-1;
}
setSelectYearMonth(year,month,day);
invalidate();
}
/**
* 右点击,日历向前翻页
*/
public void onRightClick(){
int year = mSelYear;
int month = mSelMonth;
int day = mSelDay;
if(month == 11){//若果是12月份,则变成1月份
year = mSelYear+1;
month = 0;
}else if(DateUtils.getMonthDays(year, month) == day){
//如果当前日期为该月最后一点,当向前推的时候,就需要改变选中的日期
month = month + 1;
day = DateUtils.getMonthDays(year, month);
}else{
month = month + 1;
}
setSelectYearMonth(year,month,day);
invalidate();
}
/**
* 获取选择的年份
* @return
*/
public int getmSelYear() {
return mSelYear;
}
/**
* 获取选择的月份
* @return
*/
public int getmSelMonth() {
return mSelMonth;
}
/**
* 获取选择的日期
* @param mSelDay
*/
public int getmSelDay() {
return this.mSelDay;
}
/**
* 普通日期的字体颜色,默认黑色
* @param mDayColor
*/
public void setmDayColor(int mDayColor) {
this.mDayColor = mDayColor;
}
/**
* 选择日期的颜色,默认为白色
* @param mSelectDayColor
*/
public void setmSelectDayColor(int mSelectDayColor) {
this.mSelectDayColor = mSelectDayColor;
}
/**
* 选中日期的背景颜色,默认蓝色
* @param mSelectBGColor
*/
public void setmSelectBGColor(int mSelectBGColor) {
this.mSelectBGColor = mSelectBGColor;
}
/**
* 当前日期不是选中的颜色,默认红色
* @param mCurrentColor
*/
public void setmCurrentColor(int mCurrentColor) {
this.mCurrentColor = mCurrentColor;
}
/**
* 日期的大小,默认18sp
* @param mDaySize
*/
public void setmDaySize(int mDaySize) {
this.mDaySize = mDaySize;
}
/**
* 设置显示当前日期的控件
* @param tv_date
* 显示日期
* @param tv_week
* 显示周
*/
public void setTextView(TextView tv_date,TextView tv_week){
this.tv_date = tv_date;
this.tv_week = tv_week;
invalidate();
}
/**
* 设置事务天数
* @param daysHasThingList
*/
public void setDaysHasThingList(List<Integer> daysHasThingList) {
this.daysHasThingList = daysHasThingList;
}
/***
* 设置圆圈的半径,默认为6
* @param mCircleRadius
*/
public void setmCircleRadius(int mCircleRadius) {
this.mCircleRadius = mCircleRadius;
}
/**
* 设置圆圈的半径
* @param mCircleColor
*/
public void setmCircleColor(int mCircleColor) {
this.mCircleColor = mCircleColor;
}
/**
* 设置日期的点击回调事件
* @author shiwei.deng
*
*/
public interface DateClick{
public void onClickOnDate();
}
/**
* 设置日期点击事件
* @param dateClick
*/
public void setDateClick(DateClick dateClick) {
this.dateClick = dateClick;
}
/**
* 跳转至今天
*/
public void setTodayToView(){
setSelectYearMonth(mCurrYear,mCurrMonth,mCurrDay);
invalidate();
}
}

(1)、首先我们还是定义了一些我们需要的成员变量,比如,字体的颜色、圆圈的颜色、选中的背景色、同样我们需要记录下我们正确的年月日、以及选中的年月日来进行区分,主要就这么多。
(2)、然后进行重写onMeasure方法,类似于WeekView,不做过多解释,差不多。
(3)、在onDraw方法中进行绘制,绘制的原理,我们根据Calendar获取当前月份的天数,以及第一天是礼拜几,只有计算出礼拜几,我们才知道我们的日历从哪列开始,这样我们就可以计算出每次绘制日期的位置:

int column = (day+weekNumber - 1) % 7;
int row = (day+weekNumber - 1) / 7;
daysString[row][column]=day + 1;
int startX = (int) (mColumnSize * column + (mColumnSize - mPaint.measureText(dayString))/2);
int startY = (int) (mRowSize * row + mRowSize/2 - (mPaint.ascent() + mPaint.descent())/2);

一个礼拜有七天,我们根据日期号和起始计算出日期的对应行列,然后在乘以行列宽,就可以计算出每个日期号的其实位置。这样我们就可以通过drawText进行日期的绘制。我们有一个成员变量记录选中的日期号,然后进行绘制选中的背景色,如下代码:

if(dayString.equals(mSelDay+"")){
//绘制背景色矩形
int startRecX = mColumnSize * column;
int startRecY = mRowSize * row;
int endRecX = startRecX + mColumnSize;
int endRecY = startRecY + mRowSize;
mPaint.setColor(mSelectBGColor);
canvas.drawRect(startRecX, startRecY, endRecX, endRecY, mPaint);
//记录第几行,即第几周
weekRow = row + 1;
}

(4)、我们还有一个需求,就是绘制事务标志,我们定义了List daysHasThingList的list对象,这个对象我们用来’装’事务的日期号。然后我们在onDraw方法中判断日期是否包含在这个list中,然后绘制对应的圆圈。

private void drawCircle(int row,int column,int day,Canvas canvas){
if(daysHasThingList != null && daysHasThingList.size() >0){
if(!daysHasThingList.contains(day))return;
mPaint.setColor(mCircleColor);
float circleX = (float) (mColumnSize * column + mColumnSize*0.8);
float circley = (float) (mRowSize * row + mRowSize*0.2);
canvas.drawCircle(circleX, circley, mCircleRadius, mPaint);
}
}
}

(5)、至此,日期的绘制和事务都完成了,但是还没有点击事件进行切换日期的选择,这怎么办呢?所以我们需要重写View的onTouchEvent方法,然后判断点击事件,根据获取的X、Y值,计算出我们选择行列,然后我们在根据行列在daysString中获取我们选中的日期,设置选中日期,然后刷新视图。

public boolean onTouchEvent(MotionEvent event) {
int eventCode= event.getAction();
switch(eventCode){
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
int upX = (int) event.getX();
int upY = (int) event.getY();
if(Math.abs(upX-downX) < 10 && Math.abs(upY - downY) < 10){//点击事件
performClick();
doClickAction((upX + downX)/2,(upY + downY)/2);
}
break;
}
return true;
}

(5)、有的需求是进行点击事情的处理,这时我们只需要写一个简单的回调,然后在activity中进行处理即可。

private void doClickAction(int x,int y){
int row = y / mRowSize;
int column = x / mColumnSize;
setSelectYearMonth(mSelYear,mSelMonth,daysString[row][column]);
invalidate();
//执行activity发送过来的点击处理事件
if(dateClick != null){
dateClick.onClickOnDate();
}
}
/**
* 设置日期的点击回调事件
* @author shiwei.deng
*
*/
public interface DateClick{
public void onClickOnDate();
}
/**
* 设置日期点击事件
* @param dateClick
*/
public void setDateClick(DateClick dateClick) {
this.dateClick = dateClick;
}

(6)主要的处理已经完成,剩下的需要我们获取日期的显示以及显示第几周、点击【今】返回到今天,这些处理的逻辑就是设置选中的日期,然后刷新视图。代码就不贴了,上面的源码注释的挺详细的。

最后就是我们使用自定义View进行显示。如:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"
android:orientation="vertical" >
<!-- 日历时间选择栏 -->
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="#ffffff"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:paddingTop="3dp">
<ImageView
android:id="@+id/iv_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:contentDescription="@null"
android:background="@drawable/left_arrow" />
<ImageView
android:id="@+id/iv_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:contentDescription="@null"
android:background="@drawable/right_arrow" />
<LinearLayout
android:id="@+id/date_operator_ll"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center"
android:layout_centerInParent="true"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_today"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginRight="5dp"
android:text="今"
android:gravity="center"
android:background="#FFD700"
android:textColor="#ffffff"
android:textSize="17sp" />
<TextView
android:id="@+id/date_text"
style="@style/myschedule_current_month_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textColor="#93C73C"
android:textSize="20sp"
android:text="" />
<TextView
android:id="@+id/week_text"
style="@style/myschedule_current_month_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginLeft="10dp"
android:textColor="#93C73C"
android:textSize="20sp"
android:text="" />
</LinearLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="#ffffff"
android:orientation="vertical" >
<com.dsw.datepicker.WeekDayView
android:layout_width="match_parent"
android:layout_height="30dp" />
<com.dsw.datepicker.MonthDateView
android:id="@+id/monthDateView"
android:layout_width="fill_parent"
android:layout_height="200dp" />
</LinearLayout>
</LinearLayout>

这样我们在activity中就能使用了:

public class MainActivity extends FragmentActivity {
private ImageView iv_left;
private ImageView iv_right;
private TextView tv_date;
private TextView tv_week;
private TextView tv_today;
private MonthDateView monthDateView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
List<Integer> list = new ArrayList<Integer>();
list.add(10);
list.add(12);
list.add(15);
list.add(16);
setContentView(R.layout.activity_date);
iv_left = (ImageView) findViewById(R.id.iv_left);
iv_right = (ImageView) findViewById(R.id.iv_right);
monthDateView = (MonthDateView) findViewById(R.id.monthDateView);
tv_date = (TextView) findViewById(R.id.date_text);
tv_week =(TextView) findViewById(R.id.week_text);
tv_today = (TextView) findViewById(R.id.tv_today);
monthDateView.setTextView(tv_date,tv_week);
monthDateView.setDaysHasThingList(list);
monthDateView.setDateClick(new DateClick() {
@Override
public void onClickOnDate() {
Toast.makeText(getApplication(), "点击了:" + monthDateView.getmSelDay(), Toast.LENGTH_SHORT).show();
}
});
setOnlistener();
}
private void setOnlistener(){
iv_left.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
monthDateView.onLeftClick();
}
});
iv_right.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
monthDateView.onRightClick();
}
});
tv_today.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
monthDateView.setTodayToView();
}
});
}
}

至此,全部的内容已经完成,一个简单的自定义view的使用,在实际项目中使用颇多,当然这个例子还有很多完善的地方,比如在onTouchEvent中进行滑动的监视,通过滑动来进行日期的修改,这些有兴趣的同学可以试试。

20150926222104453.gif

欢迎大家留言交流。

源码下载

作者:mr_dsw 欢迎转载,与人分享是进步的源泉!

转载请保留地址:http://blog.csdn.net/mr_dsw

转载请注明:Android开发中文站 » Android自定义控件之日历控件


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK