• android 自定义日历控件


    日历控件View:

    1. /** 
    2.  * 日历控件 功能:获得点选的日期区间 
    3.  *  
    4.  */  
    5. public class CalendarView extends View implements View.OnTouchListener {  
    6.     private final static String TAG = "anCalendar";  
    7.     private Date selectedStartDate;  
    8.     private Date selectedEndDate;  
    9.     private Date curDate; // 当前日历显示的月  
    10.     private Date today; // 今天的日期文字显示红色  
    11.     private Date downDate; // 手指按下状态时临时日期  
    12.     private Date showFirstDate, showLastDate; // 日历显示的第一个日期和最后一个日期  
    13.     private int downIndex; // 按下的格子索引  
    14.     private Calendar calendar;  
    15.     private Surface surface;  
    16.     private int[] date = new int[42]; // 日历显示数字  
    17.     private int curStartIndex, curEndIndex; // 当前显示的日历起始的索引  
    18.     //private boolean completed = false; // 为false表示只选择了开始日期,true表示结束日期也选择了  
    19.     //给控件设置监听事件  
    20.     private OnItemClickListener onItemClickListener;  
    21.       
    22.     public CalendarView(Context context) {  
    23.         super(context);  
    24.         init();  
    25.     }  
    26.   
    27.     public CalendarView(Context context, AttributeSet attrs) {  
    28.         super(context, attrs);  
    29.         init();  
    30.     }  
    31.   
    32.     private void init() {  
    33.         curDate = selectedStartDate = selectedEndDate = today = new Date();  
    34.         calendar = Calendar.getInstance();  
    35.         calendar.setTime(curDate);  
    36.         surface = new Surface();  
    37.         surface.density = getResources().getDisplayMetrics().density;  
    38.         setBackgroundColor(surface.bgColor);  
    39.         setOnTouchListener(this);  
    40.     }  
    41.   
    42.     @Override  
    43.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    44.         surface.width = getResources().getDisplayMetrics().widthPixels;  
    45.         surface.height = (int) (getResources().getDisplayMetrics().heightPixels*2/5);  
    46. //      if (View.MeasureSpec.getMode(widthMeasureSpec) == View.MeasureSpec.EXACTLY) {  
    47. //          surface.width = View.MeasureSpec.getSize(widthMeasureSpec);  
    48. //      }  
    49. //      if (View.MeasureSpec.getMode(heightMeasureSpec) == View.MeasureSpec.EXACTLY) {  
    50. //          surface.height = View.MeasureSpec.getSize(heightMeasureSpec);  
    51. //      }  
    52.         widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(surface.width,  
    53.                 View.MeasureSpec.EXACTLY);  
    54.         heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(surface.height,  
    55.                 View.MeasureSpec.EXACTLY);  
    56.         setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);  
    57.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    58.     }  
    59.   
    60.     @Override  
    61.     protected void onLayout(boolean changed, int left, int top, int right,  
    62.             int bottom) {  
    63.         Log.d(TAG, "[onLayout] changed:"  
    64.                 + (changed ? "new size" : "not change") + " left:" + left  
    65.                 + " top:" + top + " right:" + right + " bottom:" + bottom);  
    66.         if (changed) {  
    67.             surface.init();  
    68.         }  
    69.         super.onLayout(changed, left, top, right, bottom);  
    70.     }  
    71.   
    72.     @Override  
    73.     protected void onDraw(Canvas canvas) {  
    74.         Log.d(TAG, "onDraw");  
    75.         // 画框  
    76.         canvas.drawPath(surface.boxPath, surface.borderPaint);  
    77.         // 年月  
    78.         //String monthText = getYearAndmonth();  
    79.         //float textWidth = surface.monthPaint.measureText(monthText);  
    80.         //canvas.drawText(monthText, (surface.width - textWidth) / 2f,  
    81.         //      surface.monthHeight * 3 / 4f, surface.monthPaint);  
    82.         // 上一月/下一月  
    83.         //canvas.drawPath(surface.preMonthBtnPath, surface.monthChangeBtnPaint);  
    84.         //canvas.drawPath(surface.nextMonthBtnPath, surface.monthChangeBtnPaint);  
    85.         // 星期  
    86.         float weekTextY = surface.monthHeight + surface.weekHeight * 3 / 4f;  
    87.         // 星期背景  
    88. //      surface.cellBgPaint.setColor(surface.textColor);  
    89. //      canvas.drawRect(surface.weekHeight, surface.width, surface.weekHeight, surface.width, surface.cellBgPaint);  
    90.         for (int i = 0; i < surface.weekText.length; i++) {  
    91.             float weekTextX = i  
    92.                     * surface.cellWidth  
    93.                     + (surface.cellWidth - surface.weekPaint  
    94.                             .measureText(surface.weekText[i])) / 2f;  
    95.             canvas.drawText(surface.weekText[i], weekTextX, weekTextY,  
    96.                     surface.weekPaint);  
    97.         }  
    98.           
    99.         // 计算日期  
    100.         calculateDate();  
    101.         // 按下状态,选择状态背景色  
    102.         drawDownOrSelectedBg(canvas);  
    103.         // write date number  
    104.         // today index  
    105.         int todayIndex = -1;  
    106.         calendar.setTime(curDate);  
    107.         String curYearAndMonth = calendar.get(Calendar.YEAR) + ""  
    108.                 + calendar.get(Calendar.MONTH);  
    109.         calendar.setTime(today);  
    110.         String todayYearAndMonth = calendar.get(Calendar.YEAR) + ""  
    111.                 + calendar.get(Calendar.MONTH);  
    112.         if (curYearAndMonth.equals(todayYearAndMonth)) {  
    113.             int todayNumber = calendar.get(Calendar.DAY_OF_MONTH);  
    114.             todayIndex = curStartIndex + todayNumber - 1;  
    115.         }  
    116.         for (int i = 0; i < 42; i++) {  
    117.             int color = surface.textColor;  
    118.             if (isLastMonth(i)) {  
    119.                 color = surface.borderColor;  
    120.             } else if (isNextMonth(i)) {  
    121.                 color = surface.borderColor;  
    122.             }  
    123.             if (todayIndex != -1 && i == todayIndex) {  
    124.                 color = surface.todayNumberColor;  
    125.             }  
    126.             drawCellText(canvas, i, date[i] + "", color);  
    127.         }  
    128.         super.onDraw(canvas);  
    129.     }  
    130.   
    131.     private void calculateDate() {  
    132.         calendar.setTime(curDate);  
    133.         calendar.set(Calendar.DAY_OF_MONTH, 1);  
    134.         int dayInWeek = calendar.get(Calendar.DAY_OF_WEEK);  
    135.         Log.d(TAG, "day in week:" + dayInWeek);  
    136.         int monthStart = dayInWeek;  
    137.         if (monthStart == 1) {  
    138.             monthStart = 8;  
    139.         }  
    140.         monthStart -= 1;  //以日为开头-1,以星期一为开头-2  
    141.         curStartIndex = monthStart;  
    142.         date[monthStart] = 1;  
    143.         // last month  
    144.         if (monthStart > 0) {  
    145.             calendar.set(Calendar.DAY_OF_MONTH, 0);  
    146.             int dayInmonth = calendar.get(Calendar.DAY_OF_MONTH);  
    147.             for (int i = monthStart - 1; i >= 0; i--) {  
    148.                 date[i] = dayInmonth;  
    149.                 dayInmonth--;  
    150.             }  
    151.             calendar.set(Calendar.DAY_OF_MONTH, date[0]);  
    152.         }  
    153.         showFirstDate = calendar.getTime();  
    154.         // this month  
    155.         calendar.setTime(curDate);  
    156.         calendar.add(Calendar.MONTH, 1);  
    157.         calendar.set(Calendar.DAY_OF_MONTH, 0);  
    158.         // Log.d(TAG, "m:" + calendar.get(Calendar.MONTH) + " d:" +  
    159.         // calendar.get(Calendar.DAY_OF_MONTH));  
    160.         int monthDay = calendar.get(Calendar.DAY_OF_MONTH);  
    161.         for (int i = 1; i < monthDay; i++) {  
    162.             date[monthStart + i] = i + 1;  
    163.         }  
    164.         curEndIndex = monthStart + monthDay;  
    165.         // next month  
    166.         for (int i = monthStart + monthDay; i < 42; i++) {  
    167.             date[i] = i - (monthStart + monthDay) + 1;  
    168.         }  
    169.         if (curEndIndex < 42) {  
    170.             // 显示了下一月的  
    171.             calendar.add(Calendar.DAY_OF_MONTH, 1);  
    172.         }  
    173.         calendar.set(Calendar.DAY_OF_MONTH, date[41]);  
    174.         showLastDate = calendar.getTime();  
    175.     }  
    176.   
    177.     /** 
    178.      *  
    179.      * @param canvas 
    180.      * @param index 
    181.      * @param text 
    182.      */  
    183.     private void drawCellText(Canvas canvas, int index, String text, int color) {  
    184.         int x = getXByIndex(index);  
    185.         int y = getYByIndex(index);  
    186.         surface.datePaint.setColor(color);  
    187.         float cellY = surface.monthHeight + surface.weekHeight + (y - 1)  
    188.                 * surface.cellHeight + surface.cellHeight * 3 / 4f;  
    189.         float cellX = (surface.cellWidth * (x - 1))  
    190.                 + (surface.cellWidth - surface.datePaint.measureText(text))  
    191.                 / 2f;  
    192.         canvas.drawText(text, cellX, cellY, surface.datePaint);  
    193.     }  
    194.   
    195.     /** 
    196.      *  
    197.      * @param canvas 
    198.      * @param index 
    199.      * @param color 
    200.      */  
    201.     private void drawCellBg(Canvas canvas, int index, int color) {  
    202.         int x = getXByIndex(index);  
    203.         int y = getYByIndex(index);  
    204.         surface.cellBgPaint.setColor(color);  
    205.         float left = surface.cellWidth * (x - 1) + surface.borderWidth;  
    206.         float top = surface.monthHeight + surface.weekHeight + (y - 1)  
    207.                 * surface.cellHeight + surface.borderWidth;  
    208.         canvas.drawRect(left, top, left + surface.cellWidth  
    209.                 - surface.borderWidth, top + surface.cellHeight  
    210.                 - surface.borderWidth, surface.cellBgPaint);  
    211.     }  
    212.   
    213.     private void drawDownOrSelectedBg(Canvas canvas) {  
    214.         // down and not up  
    215.         if (downDate != null) {  
    216.             drawCellBg(canvas, downIndex, surface.cellDownColor);  
    217.         }  
    218.         // selected bg color  
    219.         if (!selectedEndDate.before(showFirstDate)  
    220.                 && !selectedStartDate.after(showLastDate)) {  
    221.             int[] section = new int[] { -1, -1 };  
    222.             calendar.setTime(curDate);  
    223.             calendar.add(Calendar.MONTH, -1);  
    224.             findSelectedIndex(0, curStartIndex, calendar, section);  
    225.             if (section[1] == -1) {  
    226.                 calendar.setTime(curDate);  
    227.                 findSelectedIndex(curStartIndex, curEndIndex, calendar, section);  
    228.             }  
    229.             if (section[1] == -1) {  
    230.                 calendar.setTime(curDate);  
    231.                 calendar.add(Calendar.MONTH, 1);  
    232.                 findSelectedIndex(curEndIndex, 42, calendar, section);  
    233.             }  
    234.             if (section[0] == -1) {  
    235.                 section[0] = 0;  
    236.             }  
    237.             if (section[1] == -1) {  
    238.                 section[1] = 41;  
    239.             }  
    240.             for (int i = section[0]; i <= section[1]; i++) {  
    241.                 drawCellBg(canvas, i, surface.cellSelectedColor);  
    242.             }  
    243.         }  
    244.     }  
    245.   
    246.     private void findSelectedIndex(int startIndex, int endIndex,  
    247.             Calendar calendar, int[] section) {  
    248.         for (int i = startIndex; i < endIndex; i++) {  
    249.             calendar.set(Calendar.DAY_OF_MONTH, date[i]);  
    250.             Date temp = calendar.getTime();  
    251.             // Log.d(TAG, "temp:" + temp.toLocaleString());  
    252.             if (temp.compareTo(selectedStartDate) == 0) {  
    253.                 section[0] = i;  
    254.             }  
    255.             if (temp.compareTo(selectedEndDate) == 0) {  
    256.                 section[1] = i;  
    257.                 return;  
    258.             }  
    259.         }  
    260.     }  
    261.   
    262.     public Date getSelectedStartDate() {  
    263.         return selectedStartDate;  
    264.     }  
    265.   
    266.     public Date getSelectedEndDate() {  
    267.         return selectedEndDate;  
    268.     }  
    269.   
    270.     private boolean isLastMonth(int i) {  
    271.         if (i < curStartIndex) {  
    272.             return true;  
    273.         }  
    274.         return false;  
    275.     }  
    276.   
    277.     private boolean isNextMonth(int i) {  
    278.         if (i >= curEndIndex) {  
    279.             return true;  
    280.         }  
    281.         return false;  
    282.     }  
    283.   
    284.     private int getXByIndex(int i) {  
    285.         return i % 7 + 1; // 1 2 3 4 5 6 7  
    286.     }  
    287.   
    288.     private int getYByIndex(int i) {  
    289.         return i / 7 + 1; // 1 2 3 4 5 6  
    290.     }  
    291.   
    292.     // 获得当前应该显示的年月  
    293.     public String getYearAndmonth() {  
    294.         calendar.setTime(curDate);  
    295.         int year = calendar.get(Calendar.YEAR);  
    296.         int month = calendar.get(Calendar.MONTH);  
    297.         return year + "-" + surface.monthText[month];  
    298.     }  
    299.       
    300.     //上一月  
    301.     public String clickLeftMonth(){  
    302.         calendar.setTime(curDate);  
    303.         calendar.add(Calendar.MONTH, -1);  
    304.         curDate = calendar.getTime();  
    305.         invalidate();  
    306.         return getYearAndmonth();  
    307.     }  
    308.     //下一月  
    309.     public String clickRightMonth(){  
    310.         calendar.setTime(curDate);  
    311.         calendar.add(Calendar.MONTH, 1);  
    312.         curDate = calendar.getTime();  
    313.         invalidate();  
    314.         return getYearAndmonth();  
    315.     }  
    316.   
    317.     private void setSelectedDateByCoor(float x, float y) {  
    318.         // change month  
    319. //      if (y < surface.monthHeight) {  
    320. //          // pre month  
    321. //          if (x < surface.monthChangeWidth) {  
    322. //              calendar.setTime(curDate);  
    323. //              calendar.add(Calendar.MONTH, -1);  
    324. //              curDate = calendar.getTime();  
    325. //          }  
    326. //          // next month  
    327. //          else if (x > surface.width - surface.monthChangeWidth) {  
    328. //              calendar.setTime(curDate);  
    329. //              calendar.add(Calendar.MONTH, 1);  
    330. //              curDate = calendar.getTime();  
    331. //          }  
    332. //      }  
    333.         // cell click down  
    334.         if (y > surface.monthHeight + surface.weekHeight) {  
    335.             int m = (int) (Math.floor(x / surface.cellWidth) + 1);  
    336.             int n = (int) (Math  
    337.                     .floor((y - (surface.monthHeight + surface.weekHeight))  
    338.                             / Float.valueOf(surface.cellHeight)) + 1);  
    339.             downIndex = (n - 1) * 7 + m - 1;  
    340.             Log.d(TAG, "downIndex:" + downIndex);  
    341.             calendar.setTime(curDate);  
    342.             if (isLastMonth(downIndex)) {  
    343.                 calendar.add(Calendar.MONTH, -1);  
    344.             } else if (isNextMonth(downIndex)) {  
    345.                 calendar.add(Calendar.MONTH, 1);  
    346.             }  
    347.             calendar.set(Calendar.DAY_OF_MONTH, date[downIndex]);  
    348.             downDate = calendar.getTime();  
    349.         }  
    350.         invalidate();  
    351.     }  
    352.   
    353.     @Override  
    354.     public boolean onTouch(View v, MotionEvent event) {  
    355.         switch (event.getAction()) {  
    356.         case MotionEvent.ACTION_DOWN:  
    357.             setSelectedDateByCoor(event.getX(), event.getY());  
    358.             break;  
    359.         case MotionEvent.ACTION_UP:  
    360.             if (downDate != null) {  
    361. //              if (!completed) {  
    362. //                  if (downDate.before(selectedStartDate)) {  
    363. //                      selectedEndDate = selectedStartDate;  
    364. //                      selectedStartDate = downDate;  
    365. //                  } else {  
    366. //                      selectedEndDate = downDate;  
    367. //                  }  
    368. //                  completed = true;  
    369. //              } else {  
    370. //                  selectedStartDate = selectedEndDate = downDate;  
    371. //                  completed = false;  
    372. //              }  
    373.                 selectedStartDate = selectedEndDate = downDate;  
    374.                 //响应监听事件  
    375.                 onItemClickListener.OnItemClick(selectedStartDate);  
    376.                 // Log.d(TAG, "downdate:" + downDate.toLocaleString());  
    377.                 //Log.d(TAG, "start:" + selectedStartDate.toLocaleString());  
    378.                 //Log.d(TAG, "end:" + selectedEndDate.toLocaleString());  
    379.                 downDate = null;  
    380.                 invalidate();  
    381.             }  
    382.             break;  
    383.         }  
    384.         return true;  
    385.     }  
    386.       
    387.     //给控件设置监听事件  
    388.     public void setOnItemClickListener(OnItemClickListener onItemClickListener){  
    389.         this.onItemClickListener =  onItemClickListener;  
    390.     }  
    391.     //监听接口  
    392.     public interface OnItemClickListener {  
    393.         void OnItemClick(Date date);  
    394.     }  
    395.   
    396.     /** 
    397.      *  
    398.      * 1. 布局尺寸 2. 文字颜色,大小 3. 当前日期的颜色,选择的日期颜色 
    399.      */  
    400.     private class Surface {  
    401.         public float density;  
    402.         public int width; // 整个控件的宽度  
    403.         public int height; // 整个控件的高度  
    404.         public float monthHeight; // 显示月的高度  
    405.         //public float monthChangeWidth; // 上一月、下一月按钮宽度  
    406.         public float weekHeight; // 显示星期的高度  
    407.         public float cellWidth; // 日期方框宽度  
    408.         public float cellHeight; // 日期方框高度    
    409.         public float borderWidth;  
    410.         public int bgColor = Color.parseColor("#FFFFFF");  
    411.         private int textColor = Color.BLACK;  
    412.         //private int textColorUnimportant = Color.parseColor("#666666");  
    413.         private int btnColor = Color.parseColor("#666666");  
    414.         private int borderColor = Color.parseColor("#CCCCCC");  
    415.         public int todayNumberColor = Color.RED;  
    416.         public int cellDownColor = Color.parseColor("#CCFFFF");  
    417.         public int cellSelectedColor = Color.parseColor("#99CCFF");  
    418.         public Paint borderPaint;  
    419.         public Paint monthPaint;  
    420.         public Paint weekPaint;  
    421.         public Paint datePaint;  
    422.         public Paint monthChangeBtnPaint;  
    423.         public Paint cellBgPaint;  
    424.         public Path boxPath; // 边框路径  
    425.         //public Path preMonthBtnPath; // 上一月按钮三角形  
    426.         //public Path nextMonthBtnPath; // 下一月按钮三角形  
    427.         public String[] weekText = { "Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};  
    428.         public String[] monthText = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};  
    429.              
    430.         public void init() {  
    431.             float temp = height / 7f;  
    432.             monthHeight = 0;//(float) ((temp + temp * 0.3f) * 0.6);  
    433.             //monthChangeWidth = monthHeight * 1.5f;  
    434.             weekHeight = (float) ((temp + temp * 0.3f) * 0.7);  
    435.             cellHeight = (height - monthHeight - weekHeight) / 6f;  
    436.             cellWidth = width / 7f;  
    437.             borderPaint = new Paint();  
    438.             borderPaint.setColor(borderColor);  
    439.             borderPaint.setStyle(Paint.Style.STROKE);  
    440.             borderWidth = (float) (0.5 * density);  
    441.             // Log.d(TAG, "border" + borderWidth);  
    442.             borderWidth = borderWidth < 1 ? 1 : borderWidth;  
    443.             borderPaint.setStrokeWidth(borderWidth);  
    444.             monthPaint = new Paint();  
    445.             monthPaint.setColor(textColor);  
    446.             monthPaint.setAntiAlias(true);  
    447.             float textSize = cellHeight * 0.4f;  
    448.             Log.d(TAG, "text size:" + textSize);  
    449.             monthPaint.setTextSize(textSize);  
    450.             monthPaint.setTypeface(Typeface.DEFAULT_BOLD);  
    451.             weekPaint = new Paint();  
    452.             weekPaint.setColor(textColor);  
    453.             weekPaint.setAntiAlias(true);  
    454.             float weekTextSize = weekHeight * 0.6f;  
    455.             weekPaint.setTextSize(weekTextSize);  
    456.             weekPaint.setTypeface(Typeface.DEFAULT_BOLD);  
    457.             datePaint = new Paint();  
    458.             datePaint.setColor(textColor);  
    459.             datePaint.setAntiAlias(true);  
    460.             float cellTextSize = cellHeight * 0.5f;  
    461.             datePaint.setTextSize(cellTextSize);  
    462.             datePaint.setTypeface(Typeface.DEFAULT_BOLD);  
    463.             boxPath = new Path();  
    464.             //boxPath.addRect(0, 0, width, height, Direction.CW);  
    465.             //boxPath.moveTo(0, monthHeight);  
    466.             boxPath.rLineTo(width, 0);  
    467.             boxPath.moveTo(0, monthHeight + weekHeight);  
    468.             boxPath.rLineTo(width, 0);  
    469.             for (int i = 1; i < 6; i++) {  
    470.                 boxPath.moveTo(0, monthHeight + weekHeight + i * cellHeight);  
    471.                 boxPath.rLineTo(width, 0);  
    472.                 boxPath.moveTo(i * cellWidth, monthHeight);  
    473.                 boxPath.rLineTo(0, height - monthHeight);  
    474.             }  
    475.             boxPath.moveTo(6 * cellWidth, monthHeight);  
    476.             boxPath.rLineTo(0, height - monthHeight);  
    477.             //preMonthBtnPath = new Path();  
    478.             //int btnHeight = (int) (monthHeight * 0.6f);  
    479.             //preMonthBtnPath.moveTo(monthChangeWidth / 2f, monthHeight / 2f);  
    480.             //preMonthBtnPath.rLineTo(btnHeight / 2f, -btnHeight / 2f);  
    481.             //preMonthBtnPath.rLineTo(0, btnHeight);  
    482.             //preMonthBtnPath.close();  
    483.             //nextMonthBtnPath = new Path();  
    484.             //nextMonthBtnPath.moveTo(width - monthChangeWidth / 2f,  
    485.             //      monthHeight / 2f);  
    486.             //nextMonthBtnPath.rLineTo(-btnHeight / 2f, -btnHeight / 2f);  
    487.             //nextMonthBtnPath.rLineTo(0, btnHeight);  
    488.             //nextMonthBtnPath.close();  
    489.             monthChangeBtnPaint = new Paint();  
    490.             monthChangeBtnPaint.setAntiAlias(true);  
    491.             monthChangeBtnPaint.setStyle(Paint.Style.FILL_AND_STROKE);  
    492.             monthChangeBtnPaint.setColor(btnColor);  
    493.             cellBgPaint = new Paint();  
    494.             cellBgPaint.setAntiAlias(true);  
    495.             cellBgPaint.setStyle(Paint.Style.FILL);  
    496.             cellBgPaint.setColor(cellSelectedColor);  
    497.         }  
    498.     }  
    499. }  
    /**
     * 日历控件 功能:获得点选的日期区间
     * 
     */
    public class CalendarView extends View implements View.OnTouchListener {
    	private final static String TAG = "anCalendar";
    	private Date selectedStartDate;
    	private Date selectedEndDate;
    	private Date curDate; // 当前日历显示的月
    	private Date today; // 今天的日期文字显示红色
    	private Date downDate; // 手指按下状态时临时日期
    	private Date showFirstDate, showLastDate; // 日历显示的第一个日期和最后一个日期
    	private int downIndex; // 按下的格子索引
    	private Calendar calendar;
    	private Surface surface;
    	private int[] date = new int[42]; // 日历显示数字
    	private int curStartIndex, curEndIndex; // 当前显示的日历起始的索引
    	//private boolean completed = false; // 为false表示只选择了开始日期,true表示结束日期也选择了
    	//给控件设置监听事件
    	private OnItemClickListener onItemClickListener;
    	
    	public CalendarView(Context context) {
    		super(context);
    		init();
    	}
    
    	public CalendarView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		init();
    	}
    
    	private void init() {
    		curDate = selectedStartDate = selectedEndDate = today = new Date();
    		calendar = Calendar.getInstance();
    		calendar.setTime(curDate);
    		surface = new Surface();
    		surface.density = getResources().getDisplayMetrics().density;
    		setBackgroundColor(surface.bgColor);
    		setOnTouchListener(this);
    	}
    
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		surface.width = getResources().getDisplayMetrics().widthPixels;
    		surface.height = (int) (getResources().getDisplayMetrics().heightPixels*2/5);
    //		if (View.MeasureSpec.getMode(widthMeasureSpec) == View.MeasureSpec.EXACTLY) {
    //			surface.width = View.MeasureSpec.getSize(widthMeasureSpec);
    //		}
    //		if (View.MeasureSpec.getMode(heightMeasureSpec) == View.MeasureSpec.EXACTLY) {
    //			surface.height = View.MeasureSpec.getSize(heightMeasureSpec);
    //		}
    		widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(surface.width,
    				View.MeasureSpec.EXACTLY);
    		heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(surface.height,
    				View.MeasureSpec.EXACTLY);
    		setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
    		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    	}
    
    	@Override
    	protected void onLayout(boolean changed, int left, int top, int right,
    			int bottom) {
    		Log.d(TAG, "[onLayout] changed:"
    				+ (changed ? "new size" : "not change") + " left:" + left
    				+ " top:" + top + " right:" + right + " bottom:" + bottom);
    		if (changed) {
    			surface.init();
    		}
    		super.onLayout(changed, left, top, right, bottom);
    	}
    
    	@Override
    	protected void onDraw(Canvas canvas) {
    		Log.d(TAG, "onDraw");
    		// 画框
    		canvas.drawPath(surface.boxPath, surface.borderPaint);
    		// 年月
    		//String monthText = getYearAndmonth();
    		//float textWidth = surface.monthPaint.measureText(monthText);
    		//canvas.drawText(monthText, (surface.width - textWidth) / 2f,
    		//		surface.monthHeight * 3 / 4f, surface.monthPaint);
    		// 上一月/下一月
    		//canvas.drawPath(surface.preMonthBtnPath, surface.monthChangeBtnPaint);
    		//canvas.drawPath(surface.nextMonthBtnPath, surface.monthChangeBtnPaint);
    		// 星期
    		float weekTextY = surface.monthHeight + surface.weekHeight * 3 / 4f;
    		// 星期背景
    //		surface.cellBgPaint.setColor(surface.textColor);
    //		canvas.drawRect(surface.weekHeight, surface.width, surface.weekHeight, surface.width, surface.cellBgPaint);
    		for (int i = 0; i < surface.weekText.length; i++) {
    			float weekTextX = i
    					* surface.cellWidth
    					+ (surface.cellWidth - surface.weekPaint
    							.measureText(surface.weekText[i])) / 2f;
    			canvas.drawText(surface.weekText[i], weekTextX, weekTextY,
    					surface.weekPaint);
    		}
    		
    		// 计算日期
    		calculateDate();
    		// 按下状态,选择状态背景色
    		drawDownOrSelectedBg(canvas);
    		// write date number
    		// today index
    		int todayIndex = -1;
    		calendar.setTime(curDate);
    		String curYearAndMonth = calendar.get(Calendar.YEAR) + ""
    				+ calendar.get(Calendar.MONTH);
    		calendar.setTime(today);
    		String todayYearAndMonth = calendar.get(Calendar.YEAR) + ""
    				+ calendar.get(Calendar.MONTH);
    		if (curYearAndMonth.equals(todayYearAndMonth)) {
    			int todayNumber = calendar.get(Calendar.DAY_OF_MONTH);
    			todayIndex = curStartIndex + todayNumber - 1;
    		}
    		for (int i = 0; i < 42; i++) {
    			int color = surface.textColor;
    			if (isLastMonth(i)) {
    				color = surface.borderColor;
    			} else if (isNextMonth(i)) {
    				color = surface.borderColor;
    			}
    			if (todayIndex != -1 && i == todayIndex) {
    				color = surface.todayNumberColor;
    			}
    			drawCellText(canvas, i, date[i] + "", color);
    		}
    		super.onDraw(canvas);
    	}
    
    	private void calculateDate() {
    		calendar.setTime(curDate);
    		calendar.set(Calendar.DAY_OF_MONTH, 1);
    		int dayInWeek = calendar.get(Calendar.DAY_OF_WEEK);
    		Log.d(TAG, "day in week:" + dayInWeek);
    		int monthStart = dayInWeek;
    		if (monthStart == 1) {
    			monthStart = 8;
    		}
    		monthStart -= 1;  //以日为开头-1,以星期一为开头-2
    		curStartIndex = monthStart;
    		date[monthStart] = 1;
    		// last month
    		if (monthStart > 0) {
    			calendar.set(Calendar.DAY_OF_MONTH, 0);
    			int dayInmonth = calendar.get(Calendar.DAY_OF_MONTH);
    			for (int i = monthStart - 1; i >= 0; i--) {
    				date[i] = dayInmonth;
    				dayInmonth--;
    			}
    			calendar.set(Calendar.DAY_OF_MONTH, date[0]);
    		}
    		showFirstDate = calendar.getTime();
    		// this month
    		calendar.setTime(curDate);
    		calendar.add(Calendar.MONTH, 1);
    		calendar.set(Calendar.DAY_OF_MONTH, 0);
    		// Log.d(TAG, "m:" + calendar.get(Calendar.MONTH) + " d:" +
    		// calendar.get(Calendar.DAY_OF_MONTH));
    		int monthDay = calendar.get(Calendar.DAY_OF_MONTH);
    		for (int i = 1; i < monthDay; i++) {
    			date[monthStart + i] = i + 1;
    		}
    		curEndIndex = monthStart + monthDay;
    		// next month
    		for (int i = monthStart + monthDay; i < 42; i++) {
    			date[i] = i - (monthStart + monthDay) + 1;
    		}
    		if (curEndIndex < 42) {
    			// 显示了下一月的
    			calendar.add(Calendar.DAY_OF_MONTH, 1);
    		}
    		calendar.set(Calendar.DAY_OF_MONTH, date[41]);
    		showLastDate = calendar.getTime();
    	}
    
    	/**
    	 * 
    	 * @param canvas
    	 * @param index
    	 * @param text
    	 */
    	private void drawCellText(Canvas canvas, int index, String text, int color) {
    		int x = getXByIndex(index);
    		int y = getYByIndex(index);
    		surface.datePaint.setColor(color);
    		float cellY = surface.monthHeight + surface.weekHeight + (y - 1)
    				* surface.cellHeight + surface.cellHeight * 3 / 4f;
    		float cellX = (surface.cellWidth * (x - 1))
    				+ (surface.cellWidth - surface.datePaint.measureText(text))
    				/ 2f;
    		canvas.drawText(text, cellX, cellY, surface.datePaint);
    	}
    
    	/**
    	 * 
    	 * @param canvas
    	 * @param index
    	 * @param color
    	 */
    	private void drawCellBg(Canvas canvas, int index, int color) {
    		int x = getXByIndex(index);
    		int y = getYByIndex(index);
    		surface.cellBgPaint.setColor(color);
    		float left = surface.cellWidth * (x - 1) + surface.borderWidth;
    		float top = surface.monthHeight + surface.weekHeight + (y - 1)
    				* surface.cellHeight + surface.borderWidth;
    		canvas.drawRect(left, top, left + surface.cellWidth
    				- surface.borderWidth, top + surface.cellHeight
    				- surface.borderWidth, surface.cellBgPaint);
    	}
    
    	private void drawDownOrSelectedBg(Canvas canvas) {
    		// down and not up
    		if (downDate != null) {
    			drawCellBg(canvas, downIndex, surface.cellDownColor);
    		}
    		// selected bg color
    		if (!selectedEndDate.before(showFirstDate)
    				&& !selectedStartDate.after(showLastDate)) {
    			int[] section = new int[] { -1, -1 };
    			calendar.setTime(curDate);
    			calendar.add(Calendar.MONTH, -1);
    			findSelectedIndex(0, curStartIndex, calendar, section);
    			if (section[1] == -1) {
    				calendar.setTime(curDate);
    				findSelectedIndex(curStartIndex, curEndIndex, calendar, section);
    			}
    			if (section[1] == -1) {
    				calendar.setTime(curDate);
    				calendar.add(Calendar.MONTH, 1);
    				findSelectedIndex(curEndIndex, 42, calendar, section);
    			}
    			if (section[0] == -1) {
    				section[0] = 0;
    			}
    			if (section[1] == -1) {
    				section[1] = 41;
    			}
    			for (int i = section[0]; i <= section[1]; i++) {
    				drawCellBg(canvas, i, surface.cellSelectedColor);
    			}
    		}
    	}
    
    	private void findSelectedIndex(int startIndex, int endIndex,
    			Calendar calendar, int[] section) {
    		for (int i = startIndex; i < endIndex; i++) {
    			calendar.set(Calendar.DAY_OF_MONTH, date[i]);
    			Date temp = calendar.getTime();
    			// Log.d(TAG, "temp:" + temp.toLocaleString());
    			if (temp.compareTo(selectedStartDate) == 0) {
    				section[0] = i;
    			}
    			if (temp.compareTo(selectedEndDate) == 0) {
    				section[1] = i;
    				return;
    			}
    		}
    	}
    
    	public Date getSelectedStartDate() {
    		return selectedStartDate;
    	}
    
    	public Date getSelectedEndDate() {
    		return selectedEndDate;
    	}
    
    	private boolean isLastMonth(int i) {
    		if (i < curStartIndex) {
    			return true;
    		}
    		return false;
    	}
    
    	private boolean isNextMonth(int i) {
    		if (i >= curEndIndex) {
    			return true;
    		}
    		return false;
    	}
    
    	private int getXByIndex(int i) {
    		return i % 7 + 1; // 1 2 3 4 5 6 7
    	}
    
    	private int getYByIndex(int i) {
    		return i / 7 + 1; // 1 2 3 4 5 6
    	}
    
    	// 获得当前应该显示的年月
    	public String getYearAndmonth() {
    		calendar.setTime(curDate);
    		int year = calendar.get(Calendar.YEAR);
    		int month = calendar.get(Calendar.MONTH);
    		return year + "-" + surface.monthText[month];
    	}
    	
    	//上一月
    	public String clickLeftMonth(){
    		calendar.setTime(curDate);
    		calendar.add(Calendar.MONTH, -1);
    		curDate = calendar.getTime();
    		invalidate();
    		return getYearAndmonth();
    	}
    	//下一月
    	public String clickRightMonth(){
    		calendar.setTime(curDate);
    		calendar.add(Calendar.MONTH, 1);
    		curDate = calendar.getTime();
    		invalidate();
    		return getYearAndmonth();
    	}
    
    	private void setSelectedDateByCoor(float x, float y) {
    		// change month
    //		if (y < surface.monthHeight) {
    //			// pre month
    //			if (x < surface.monthChangeWidth) {
    //				calendar.setTime(curDate);
    //				calendar.add(Calendar.MONTH, -1);
    //				curDate = calendar.getTime();
    //			}
    //			// next month
    //			else if (x > surface.width - surface.monthChangeWidth) {
    //				calendar.setTime(curDate);
    //				calendar.add(Calendar.MONTH, 1);
    //				curDate = calendar.getTime();
    //			}
    //		}
    		// cell click down
    		if (y > surface.monthHeight + surface.weekHeight) {
    			int m = (int) (Math.floor(x / surface.cellWidth) + 1);
    			int n = (int) (Math
    					.floor((y - (surface.monthHeight + surface.weekHeight))
    							/ Float.valueOf(surface.cellHeight)) + 1);
    			downIndex = (n - 1) * 7 + m - 1;
    			Log.d(TAG, "downIndex:" + downIndex);
    			calendar.setTime(curDate);
    			if (isLastMonth(downIndex)) {
    				calendar.add(Calendar.MONTH, -1);
    			} else if (isNextMonth(downIndex)) {
    				calendar.add(Calendar.MONTH, 1);
    			}
    			calendar.set(Calendar.DAY_OF_MONTH, date[downIndex]);
    			downDate = calendar.getTime();
    		}
    		invalidate();
    	}
    
    	@Override
    	public boolean onTouch(View v, MotionEvent event) {
    		switch (event.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			setSelectedDateByCoor(event.getX(), event.getY());
    			break;
    		case MotionEvent.ACTION_UP:
    			if (downDate != null) {
    //				if (!completed) {
    //					if (downDate.before(selectedStartDate)) {
    //						selectedEndDate = selectedStartDate;
    //						selectedStartDate = downDate;
    //					} else {
    //						selectedEndDate = downDate;
    //					}
    //					completed = true;
    //				} else {
    //					selectedStartDate = selectedEndDate = downDate;
    //					completed = false;
    //				}
    				selectedStartDate = selectedEndDate = downDate;
    				//响应监听事件
    				onItemClickListener.OnItemClick(selectedStartDate);
    				// Log.d(TAG, "downdate:" + downDate.toLocaleString());
    				//Log.d(TAG, "start:" + selectedStartDate.toLocaleString());
    				//Log.d(TAG, "end:" + selectedEndDate.toLocaleString());
    				downDate = null;
    				invalidate();
    			}
    			break;
    		}
    		return true;
    	}
    	
    	//给控件设置监听事件
    	public void setOnItemClickListener(OnItemClickListener onItemClickListener){
    		this.onItemClickListener =  onItemClickListener;
    	}
    	//监听接口
    	public interface OnItemClickListener {
    		void OnItemClick(Date date);
    	}
    
    	/**
    	 * 
    	 * 1. 布局尺寸 2. 文字颜色,大小 3. 当前日期的颜色,选择的日期颜色
    	 */
    	private class Surface {
    		public float density;
    		public int width; // 整个控件的宽度
    		public int height; // 整个控件的高度
    		public float monthHeight; // 显示月的高度
    		//public float monthChangeWidth; // 上一月、下一月按钮宽度
    		public float weekHeight; // 显示星期的高度
    		public float cellWidth; // 日期方框宽度
    		public float cellHeight; // 日期方框高度	
    		public float borderWidth;
    		public int bgColor = Color.parseColor("#FFFFFF");
    		private int textColor = Color.BLACK;
    		//private int textColorUnimportant = Color.parseColor("#666666");
    		private int btnColor = Color.parseColor("#666666");
    		private int borderColor = Color.parseColor("#CCCCCC");
    		public int todayNumberColor = Color.RED;
    		public int cellDownColor = Color.parseColor("#CCFFFF");
    		public int cellSelectedColor = Color.parseColor("#99CCFF");
    		public Paint borderPaint;
    		public Paint monthPaint;
    		public Paint weekPaint;
    		public Paint datePaint;
    		public Paint monthChangeBtnPaint;
    		public Paint cellBgPaint;
    		public Path boxPath; // 边框路径
    		//public Path preMonthBtnPath; // 上一月按钮三角形
    		//public Path nextMonthBtnPath; // 下一月按钮三角形
    		public String[] weekText = { "Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    		public String[] monthText = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
    		   
    		public void init() {
    			float temp = height / 7f;
    			monthHeight = 0;//(float) ((temp + temp * 0.3f) * 0.6);
    			//monthChangeWidth = monthHeight * 1.5f;
    			weekHeight = (float) ((temp + temp * 0.3f) * 0.7);
    			cellHeight = (height - monthHeight - weekHeight) / 6f;
    			cellWidth = width / 7f;
    			borderPaint = new Paint();
    			borderPaint.setColor(borderColor);
    			borderPaint.setStyle(Paint.Style.STROKE);
    			borderWidth = (float) (0.5 * density);
    			// Log.d(TAG, "border" + borderWidth);
    			borderWidth = borderWidth < 1 ? 1 : borderWidth;
    			borderPaint.setStrokeWidth(borderWidth);
    			monthPaint = new Paint();
    			monthPaint.setColor(textColor);
    			monthPaint.setAntiAlias(true);
    			float textSize = cellHeight * 0.4f;
    			Log.d(TAG, "text size:" + textSize);
    			monthPaint.setTextSize(textSize);
    			monthPaint.setTypeface(Typeface.DEFAULT_BOLD);
    			weekPaint = new Paint();
    			weekPaint.setColor(textColor);
    			weekPaint.setAntiAlias(true);
    			float weekTextSize = weekHeight * 0.6f;
    			weekPaint.setTextSize(weekTextSize);
    			weekPaint.setTypeface(Typeface.DEFAULT_BOLD);
    			datePaint = new Paint();
    			datePaint.setColor(textColor);
    			datePaint.setAntiAlias(true);
    			float cellTextSize = cellHeight * 0.5f;
    			datePaint.setTextSize(cellTextSize);
    			datePaint.setTypeface(Typeface.DEFAULT_BOLD);
    			boxPath = new Path();
    			//boxPath.addRect(0, 0, width, height, Direction.CW);
    			//boxPath.moveTo(0, monthHeight);
    			boxPath.rLineTo(width, 0);
    			boxPath.moveTo(0, monthHeight + weekHeight);
    			boxPath.rLineTo(width, 0);
    			for (int i = 1; i < 6; i++) {
    				boxPath.moveTo(0, monthHeight + weekHeight + i * cellHeight);
    				boxPath.rLineTo(width, 0);
    				boxPath.moveTo(i * cellWidth, monthHeight);
    				boxPath.rLineTo(0, height - monthHeight);
    			}
    			boxPath.moveTo(6 * cellWidth, monthHeight);
    			boxPath.rLineTo(0, height - monthHeight);
    			//preMonthBtnPath = new Path();
    			//int btnHeight = (int) (monthHeight * 0.6f);
    			//preMonthBtnPath.moveTo(monthChangeWidth / 2f, monthHeight / 2f);
    			//preMonthBtnPath.rLineTo(btnHeight / 2f, -btnHeight / 2f);
    			//preMonthBtnPath.rLineTo(0, btnHeight);
    			//preMonthBtnPath.close();
    			//nextMonthBtnPath = new Path();
    			//nextMonthBtnPath.moveTo(width - monthChangeWidth / 2f,
    			//		monthHeight / 2f);
    			//nextMonthBtnPath.rLineTo(-btnHeight / 2f, -btnHeight / 2f);
    			//nextMonthBtnPath.rLineTo(0, btnHeight);
    			//nextMonthBtnPath.close();
    			monthChangeBtnPaint = new Paint();
    			monthChangeBtnPaint.setAntiAlias(true);
    			monthChangeBtnPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    			monthChangeBtnPaint.setColor(btnColor);
    			cellBgPaint = new Paint();
    			cellBgPaint.setAntiAlias(true);
    			cellBgPaint.setStyle(Paint.Style.FILL);
    			cellBgPaint.setColor(cellSelectedColor);
    		}
    	}
    }
    实现日历控件:
    1. <RelativeLayout  
    2.     android:id="@+id/layout_calendar"  
    3.     android:layout_width="fill_parent"  
    4.     android:layout_height="wrap_content"  
    5.     android:visibility="visible" >  
    6.   
    7.     <TextView  
    8.         android:id="@+id/calendarCenter"  
    9.         style="@style/main_bar_text_style"  
    10.         android:layout_width="wrap_content"  
    11.         android:layout_height="wrap_content"  
    12.         android:layout_centerHorizontal="true"  
    13.         android:layout_margin="8dp" />  
    14.   
    15.     <ImageButton  
    16.         android:id="@+id/calendarLeft"  
    17.         android:layout_width="wrap_content"  
    18.         android:layout_height="wrap_content"  
    19.         android:layout_alignParentLeft="true"  
    20.         android:padding="8dp"  
    21.         android:contentDescription="@null"  
    22.         android:src="@drawable/calendar_month_left"   
    23.         android:background="@null"/>  
    24.   
    25.     <ImageButton  
    26.         android:id="@+id/calendarRight"  
    27.         android:layout_width="wrap_content"  
    28.         android:layout_height="wrap_content"  
    29.         android:layout_alignParentRight="true"  
    30.         android:padding="8dp"  
    31.         android:contentDescription="@null"  
    32.         android:src="@drawable/calendar_month_right"   
    33.         android:background="@null"/>  
    34.   
    35.     <com.techrare.view.CalendarView  
    36.         android:id="@+id/calendar"  
    37.         android:layout_width="fill_parent"  
    38.         android:layout_height="wrap_content"  
    39.         android:layout_alignParentLeft="true"  
    40.         android:layout_below="@+id/calendarCenter" />  
    41. </RelativeLayout>  
            <RelativeLayout
                android:id="@+id/layout_calendar"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:visibility="visible" >
    
                <TextView
                    android:id="@+id/calendarCenter"
                    style="@style/main_bar_text_style"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerHorizontal="true"
                    android:layout_margin="8dp" />
    
                <ImageButton
                    android:id="@+id/calendarLeft"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentLeft="true"
                    android:padding="8dp"
                    android:contentDescription="@null"
                    android:src="@drawable/calendar_month_left" 
                    android:background="@null"/>
    
                <ImageButton
                    android:id="@+id/calendarRight"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:padding="8dp"
                    android:contentDescription="@null"
                    android:src="@drawable/calendar_month_right" 
                    android:background="@null"/>
    
                <com.techrare.view.CalendarView
                    android:id="@+id/calendar"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentLeft="true"
                    android:layout_below="@+id/calendarCenter" />
            </RelativeLayout>
    1. <style name="main_bar_text_style">  
    2.     <item name="android:textColor">@color/white</item>  
    3.     <item name="android:textStyle">bold</item>  
    4.     <item name="android:textSize">18sp</item>  
    5. </style>  
         <style name="main_bar_text_style">
             <item name="android:textColor">@color/white</item>
             <item name="android:textStyle">bold</item>
             <item name="android:textSize">18sp</item>
         </style>
    上一月图片:

    1. 调用 calendar.clickLeftMonth();   
    调用 calendar.clickLeftMonth(); 
         

    下一月:

    1. 调用 calendar.clickRightMonth();   
    调用 calendar.clickRightMonth(); 
    日历控件中的一些功能(可以自己加):
    1. //获取日历控件对象  
    2. calendar = (CalendarView)findViewById(R.id.calendar);  
    3. //获取日历中年月 ya[0]为年,ya[1]为月(格式大家可以自行在日历控件中改)  
    4. String[] ya = calendar.getYearAndmonth().split("-");   
    5. //点击上一月 同样返回年月   
    6. String leftYearAndmonth = calendar.clickLeftMonth();   
    7. String[] lya = leftYearAndmonth.split("-");  
    8. //点击下一月  
    9. String rightYearAndmonth = calendar.clickRightMonth();   
    10. String[] rya = rightYearAndmonth.split("-");  
    11. //设置控件监听,可以监听到点击的每一天(大家也可以在控件中自行设定)  
    12. calendar.setOnItemClickListener(new calendarItemClickListener());  
    13. class calendarItemClickListener implements OnItemClickListener{  
    14.     @Override  
    15.     public void OnItemClick(Date date) {  
    16.         Toast.makeText(getApplicationContext(), date+"", Toast.LENGTH_SHORT).show();  
    17.     }  
    18.  }  
  • 相关阅读:
    XML(学习笔记)
    css样式学习笔记
    Request(对象)
    sql一些错误修改的总结
    转载(如何学习C#)
    sql server(学习笔记2 W3Cschool)
    sql sqrver(学习笔记1 W3Cschool)
    关于 flutter开发碰到的各种问题,有的已经解决有的一直没解决或者用其他方法替代
    关于 Flutter IOS build It appears that your application still contains the default signing identifier.
    关于 flutter本地化问题 The getter 'pasteButtonLabel' was called on null
  • 原文地址:https://www.cnblogs.com/lzjsky/p/5068799.html
Copyright © 2020-2023  润新知