編輯:關於Android編程
前幾天閒來無事,變想做一些小工具玩玩。花了一天多的時間,弄出一個簡單日歷的View。分為月份模式和星期模式。滾動查看,先上圖看看:
上面的是顯示的是月份的模式。下面是星期的模式:
一個很簡單的自定義View,然後通過Viewpager的OnpageChangeListener進行刷新View的數據。Viewpager通過輪回使用View。我默認設置是5個。可以左右無限切換。
下面是自定義CalendarView:
package com.example.calendar; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; public class CalendarView extends View { private static final String TAG = CalendarView; /** * 兩種模式 (月份和星期) */ public static final int MONTH_STYLE = 0; public static final int WEEK_STYLE = 1; private static final int TOTAL_COL = 7; private static final int TOTAL_ROW = 6; private Paint mCirclePaint; private Paint mTextPaint; private int mViewWidth; private int mViewHight; private int mCellSpace; private Row rows[] = new Row[TOTAL_ROW]; private int mShowYear;//view顯示的年份 private int mShowMonth;//view顯示的月份 private int mShowDay;//針對星期樣式 顯示的開始的天 protected int defaultStyle = MONTH_STYLE; private static final int WEEK = 7; public CalendarView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public CalendarView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CalendarView(Context context) { super(context); init(); } public CalendarView(Context context, int style) { super(context); this.defaultStyle = style; init(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); fillDate(); for (int i = 0; i < TOTAL_ROW; i++) { if (rows[i] != null) rows[i].drawCells(canvas, i); } } private void init() { mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCirclePaint.setStyle(Paint.Style.FILL); mCirclePaint.setColor(Color.parseColor(#F24949)); initDate(); } private void initDate(){ if(defaultStyle == MONTH_STYLE){ mShowYear = DateUtil.getYear(); mShowMonth = DateUtil.getMonth(); mShowDay = 1; }else{ int time[] = DateUtil.getPerviousWeekSunday(); mShowYear = time[0]; mShowMonth = time[1]; mShowDay = time[2]; } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mViewWidth = w; mViewHight = h; mCellSpace = Math.min(mViewHight / TOTAL_ROW, mViewWidth / TOTAL_COL); mTextPaint.setTextSize(mCellSpace / 3); } //組 class Row { public Cell[] cells = new Cell[TOTAL_COL]; public void drawCells(Canvas canvas, int j) { for (int i = 0; i < cells.length; i++) { if (cells[i] != null) cells[i].drawSelf(canvas, i, j); } } } //單元格 class Cell { public String text; public State state; public Cell(String text, State state) { super(); this.text = text; this.state = state; } public void setText(String text) { this.text = text; } //繪制一個單元格 如果顏色需要自定義可以修改 public void drawSelf(Canvas canvas, int i, int j) { switch (state) { case CURRENT_MONTH_DAY: mTextPaint.setColor(Color.parseColor(#80000000)); break; case NEXT_MONTH_DAY: case PAST_MONTH_DAY: mTextPaint.setColor(Color.parseColor(#40000000)); break; case TODAY: mTextPaint.setColor(Color.parseColor(#fffffe)); canvas.drawCircle((float) (mCellSpace * (i + 0.45)), (float) ((j + 0.8) * mCellSpace), mCellSpace / 2, mCirclePaint); break; } //繪制文字 canvas.drawText(text, i * mCellSpace + mTextPaint.measureText(11), (j + 1) * mCellSpace - mTextPaint.measureText(text, 0, 1) / 2, mTextPaint); } } enum State { CURRENT_MONTH_DAY, PAST_MONTH_DAY, NEXT_MONTH_DAY, TODAY; } /** * 填充日期的數據 */ private void fillDate() { if (defaultStyle == MONTH_STYLE) { fillMonthDate(); } else { fillWeekDate(); } } /** * 填充星期模式下的數據 * 默認通過當前日期得到所在星期天的日期,然後依次填充日期 */ private void fillWeekDate() { int currentMonthDays = DateUtil.getMonthDays(mShowYear, mShowMonth); rows[0] = new Row(); if(mShowDay + WEEK -1 > currentMonthDays){ mShowMonth += 1; } for (int i = 0; i < TOTAL_COL; i++) { mShowDay += 1; if (mShowDay > currentMonthDays) { mShowDay = 1; } if (mShowDay == DateUtil.getCurrentMonthDays()&& mShowYear == DateUtil.getYear() && mShowMonth == DateUtil.getMonth()) { rows[0].cells[i] = new Cell(mShowDay + , State.TODAY); continue; } rows[0].cells[i] = new Cell(mShowDay + , State.CURRENT_MONTH_DAY); } } /** * 填充月份模式下數據 * 通過getWeekDayFromDate得到一個月第一天是星期幾就可以算出所有的日期的位置 * 然後依次填充 */ private void fillMonthDate() { int monthDay = DateUtil.getCurrentMonthDays(); int lastMonthDays = DateUtil.getMonthDays(mShowYear, mShowMonth - 1); int currentMonthDays = DateUtil.getMonthDays(mShowYear, mShowMonth); int firstDayWeek = DateUtil.getWeekDayFromDate(mShowYear, mShowMonth); boolean isCurrentMonth = false; if (mShowYear == DateUtil.getYear() && mShowMonth == DateUtil.getMonth()) { isCurrentMonth = true; } int time = 0; for (int j = 0; j < TOTAL_ROW; j++) { rows[j] = new Row(); for (int i = 0; i < TOTAL_COL; i++) { int postion = i + j * TOTAL_COL; if (postion >= firstDayWeek && postion < firstDayWeek + currentMonthDays) { time++; if (isCurrentMonth && time == monthDay) { rows[j].cells[i] = new Cell(time + , State.TODAY); continue; } rows[j].cells[i] = new Cell(time + , State.CURRENT_MONTH_DAY); continue; } else if (postion < firstDayWeek) { rows[j].cells[i] = new Cell((lastMonthDays - (firstDayWeek - postion - 1)) + , State.PAST_MONTH_DAY); continue; } else if (postion >= firstDayWeek + currentMonthDays) { rows[j].cells[i] = new Cell((postion - firstDayWeek - currentMonthDays + 1) + , State.NEXT_MONTH_DAY); } } } } //切換pager調用進行刷新 public void update(int year, int month,int day) { this.mShowMonth = month; this.mShowYear = year; this.mShowDay = day; invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = measure(widthMeasureSpec); int height = measure(heightMeasureSpec); int d = Math.min(width, height); setMeasuredDimension(d, d); } protected int measure(int measureSpec) { int size = MeasureSpec.getSize(measureSpec); return size; } //為了方便viewPager生產出多個的CalendarView public static CalendarView[] createCalendarViewsForPager(Context context,int count,int style){ CalendarView[] views = new CalendarView[count]; for(int i = 0; i < count;i++){ views[i] = new CalendarView(context, style); } return views; } public static CalendarView[] createCalendarViewsForPager(Context context,int count){ CalendarView[] views = new CalendarView[count]; for(int i = 0; i < count;i++){ views[i] = new CalendarView(context, CalendarView.MONTH_STYLE); } return views; } }
我重寫了ViewPagerAdapter。CalendarViewPagerAdapter實現了日歷所需無限循環的功能,如果想使用就繼承它。
package com.example.calendar; import android.os.Parcelable; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.View; public class CalendarViewPagerAdapter extends PagerAdapter { private View[] views; public CalendarViewPagerAdapter(View[] views) { super(); this.views = views; } @Override public void finishUpdate(View arg0) { } @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); } @Override public int getCount() { return Integer.MAX_VALUE; } @Override public Object instantiateItem(View arg0, int arg1) { if (((ViewPager) arg0).getChildCount() == views.length) { ((ViewPager) arg0).removeView(views[arg1 % views.length]); } ((ViewPager) arg0).addView(views[arg1 % views.length], 0); return views[arg1 % views.length]; } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == (arg1); } @Override public Parcelable saveState() { return null; } @Override public void destroyItem(View arg0, int arg1, Object arg2) { // TODO Auto-generated method stub } @Override public void startUpdate(View arg0) { } }然後為了實現對CalendarView的滑動時的數據更新,我重寫了OnPageChangeListener的方法。預留一個方法供activity界面進行回調onPageSelected(int year, int month, int day)。
package com.example.calendar; import android.support.v4.view.ViewPager.OnPageChangeListener; public class CalendarViewPagerLisenter implements OnPageChangeListener { private SildeDirection mDirection = SildeDirection.NO_SILDE; int mCurrIndex = 498; private static final int WEEK = 7; private int mShowYear; private int mShowMonth; private int mShowDay = 1; private CalendarView[] mShowViews; private int style; public CalendarViewPagerLisenter(CalendarView[] mShowViews) { super(); this.mShowViews = mShowViews; } public CalendarViewPagerLisenter(CalendarView[] mShowViews, int style) { super(); this.mShowViews = mShowViews; this.style = style; initDate(); } @Override public void onPageSelected(int arg0) { measureDirection(arg0); updateCalendarView(arg0); onPageSelected(mShowYear, mShowMonth, mShowDay); } private void updateCalendarView(int arg0) { if (style == CalendarView.MONTH_STYLE) updateMonthStyleCalendarView(); else if ((style == CalendarView.WEEK_STYLE)) updateWeekStyleCalendarView(); mShowViews[arg0 % mShowViews.length].update(mShowYear, mShowMonth, mShowDay); } private void updateWeekStyleCalendarView() { //int[] time = new int[3]; if (mDirection == SildeDirection.RIGHT) { int currentMonthDays = DateUtil.getMonthDays(mShowYear,mShowMonth); if(mShowDay + WEEK > currentMonthDays){ if(mShowMonth == 12){ mShowMonth = 1; mShowYear += 1; }else{ mShowMonth += 1; } mShowDay = WEEK -currentMonthDays + mShowDay; return; } mShowDay += WEEK; } else if (mDirection == SildeDirection.LEFT) { int lastMonthDays = DateUtil.getMonthDays(mShowYear, mShowMonth); if(mShowDay - WEEK < 1){ if(mShowMonth == 1){ mShowMonth = 12; mShowYear -= 1; }else{ mShowMonth -= 1; } mShowDay = lastMonthDays - WEEK + mShowDay; return; } mShowDay -= WEEK; } mDirection = SildeDirection.NO_SILDE; } private void updateMonthStyleCalendarView() { if (mDirection == SildeDirection.RIGHT) { if (mShowMonth == 12) { mShowMonth = 1; mShowYear += 1; } else { mShowMonth += 1; } } else if (mDirection == SildeDirection.LEFT) { if (mShowMonth == 1) { mShowMonth = 12; mShowYear -= 1; } else { mShowMonth -= 1; } } mDirection = SildeDirection.NO_SILDE; } private void measureDirection(int arg0) { if (arg0 > mCurrIndex) { mDirection = SildeDirection.RIGHT; } else if (arg0 < mCurrIndex) { mDirection = SildeDirection.LEFT; } mCurrIndex = arg0; } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } public void onPageSelected(int year, int month, int day) { } enum SildeDirection { RIGHT, LEFT, NO_SILDE; } private void initDate(){ if(style == CalendarView.MONTH_STYLE){ mShowYear = DateUtil.getYear(); mShowMonth = DateUtil.getMonth(); mShowDay = 1; }else if(style == CalendarView.WEEK_STYLE){ int time[] = DateUtil.getPerviousWeekSunday(); mShowYear = time[0]; mShowMonth = time[1]; mShowDay = time[2]; } onPageSelected(mShowYear, mShowMonth, mShowDay); } public void setData(int style, CalendarView[] mShowViews){ this.style = style; this.mShowViews = mShowViews; } }
這是個簡單的demo,但是我把他封裝起來,直接調用就可以了。如果有需要的同學,可以直接下載就可以了。
以在搜索框搜索時,自動補全為例:其中還涉及到一個詞,Tokenizer:分詞器,分解器。上效果圖:MainActivity.java:package com.joan.t
初看這個博文名,我都蒙蔽了,Activity的啟動模式居然能扯到內存問題,還有內存洩漏問題,WTF!!!不要方,小司機我帶你理解和稍微深入的探討一下Activity的四種
效果:滑動切換;點擊標簽切換。 代碼:https://github.com/ldb-github/Layout_Tab1、使用ViewPager和PagerTa
在Android開發中,事件分發機制是一塊Android比較重要的知識體系,了解並熟悉整套的分發機制有助於更好的分析各種點擊滑動失效問題,更好去擴展控件的事件功能和開發自