最近公司有新項目下來了,拿到美工交給我的圖片以後,是這樣的:
剛看到這個界面的時候,我的第一想法是想看有沒有第三方控件什麼,於是乎找來找去,可還是沒能找類似的,主要是看到這個界面也不知道該怎麼去描述功能,大致的功能就是用戶可以選擇一週內的時間段去預約(或者做些別的不可描述的操作),嘿嘿嘿~
玩笑歸玩笑,發現沒能找到和我一樣的,本着嘗試的心態,我決定自己去寫這個功能。終於經過兩天的時間,完成了這個大致類似的功能。老規矩,先來看看我實現的界面效果吧。
首先說一下這個主要的功能點,日曆表上面所展示的日期段是一週內的星期一至星期天,點擊左右兩張圖片可以切換上一週或下一週,在切換的過程中進行刷新表格中的信息,表中週一至週日下面所展示的是當前一週內的每天的日期。點擊對應下面的格子可以選擇預約或者其他的什麼操作。
其實當我第一次做這個控件的時候準備用線性佈局或相對佈局搭配表格佈局來一個一個的複製粘貼,但是呢,作爲一枚“”懶散“”的程序員,這樣做我是不能忍受的呢。這樣做也不夠優雅。不然到時候怎麼出去裝*呢,哈哈哈哈哈,好吧,我又失態了,這樣不好不好。
於是我決定利用android自帶的畫筆Paint來完成基本表格界面的繪製。感興趣的也可以去學習使用Paint(真的很強大呢。。。。)
閒話不多說,現在我們來看看代碼:
首先是繪製下面的表格,已經一些監聽代碼事件都寫在裏面,備註也都寫了。
public class CalendarTableView extends View {
private Paint paint;
private DisplayMetrics mDisplayMetrics;
//星期字體顏色 默認黑色
private int mWeekdayColor = Color.parseColor("#000000");
//未選中佈局顏色 默認白色
private int mUnSelectColor = Color.parseColor("#ffffff");
//選中佈局顏色 默認紅色
private int mSelectColor = Color.parseColor("#FF0000");
private static final int NUM_COLUMNS = 8;
private static final int NUM_ROWS = 4;
//每一格的寬度和高度
private int mColumnSize, mRowSize;
//用戶選中的行和列數
private int checkRow, checkColumn;
//控件的總寬度和總高度
private int width, height;
private String[] weekString = new String[]{ "一", "二", "三", "四", "五", "六", "日"};
private List<Integer> dateList = new ArrayList<>();
private String[] timeString = new String[]{ "上午", "下午", "晚上"};
public CalendarTableView(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) {
initSize();
//設置監測當前行數
int row = 1;
//行
for (int j = 0; j < NUM_ROWS; j++) {
//進行畫線
paint.setColor(Color.parseColor("#EDEDED"));
paint.setStrokeWidth(4);
canvas.drawLine(0, mRowSize * j, width, mRowSize * j, paint);
//因爲第一行有些特殊,單獨拿出來繪製第一行
if (j == 0) {
//繪製列
for (int i = 0; i < NUM_COLUMNS; i++) {
//進行畫格子左邊的豎線
paint.setColor(Color.parseColor("#EDEDED"));
paint.setStrokeWidth(4);
canvas.drawLine(mColumnSize * i, 0, mColumnSize * i, mRowSize, paint);
paint.setColor(Color.parseColor("#f5f5f5"));
//繪製背景色矩形
int startRecX = mColumnSize * i;
int startRecY = 0;
int endRecX = startRecX + mColumnSize;
int endRecY = mRowSize;
canvas.drawRect(startRecX, startRecY, endRecX, endRecY, paint);
//第一格不設置任何信息
if (i != 0) {
String text = weekString[i-1];
int fontWidth = (int) paint.measureText(text);
//設置文字居中
int startX = mColumnSize * i + (mColumnSize - fontWidth) / 2;
int startY = mRowSize / 2;
//設置星期
paint.setColor(mWeekdayColor);
paint.setTextSize(40);
canvas.drawText(text, startX, startY, paint);
//設置底部日期
paint.setTextSize(35);
paint.setColor(Color.parseColor("#6f6f6f"));
int date = dateList.get(i-1);
//獲取文字寬度
fontWidth = (int) paint.measureText(date+"");
//設置文字居中
startX = mColumnSize * i + (mColumnSize - fontWidth) / 2;
canvas.drawText(date+"", startX, startY + mRowSize / 3, paint);
}
}
} else {
//繪製其他行
//繪製列
for (int i = 0; i < NUM_COLUMNS; i++) {
//進行畫格子左邊的豎線
paint.setColor(Color.parseColor("#EDEDED"));
paint.setStrokeWidth(4);
canvas.drawLine(mColumnSize * i, mRowSize * (row - 1), mColumnSize * i, mRowSize * row, paint);
if (checkRow != 0 && checkColumn != 0 && j == checkRow && i == checkColumn) {
paint.setColor(Color.parseColor("#FF0000"));
//繪製背景色矩形
int startRecX = mColumnSize * i;
int startRecY = mRowSize * (row - 1);
int endRecX = startRecX + mColumnSize;
int endRecY = startRecY + mRowSize;
//選中之後繪製文字
canvas.drawRect(startRecX, startRecY, endRecX, endRecY, paint);
paint.setTextSize(40);
paint.setColor(Color.parseColor("#FFFFFF"));
canvas.drawText("預約", (endRecX - startRecX) / 5 + startRecX, (endRecY - startRecY) / 2 + startRecY + 13, paint);
} else {
paint.setColor(mUnSelectColor);
//繪製背景色白色矩形
int startRecX = mColumnSize * i;
int startRecY = mRowSize * (row - 1);
int endRecX = startRecX + mColumnSize;
int endRecY = startRecY + mRowSize;
canvas.drawRect(startRecX, startRecY, endRecX, endRecY, paint);
}
}
paint.setTextSize(40);
paint.setColor(mWeekdayColor);
String text = timeString[j-1];
int fontWidth = (int) paint.measureText(text);
//設置文字居中
int startX = (mColumnSize - fontWidth) / 2;
int startY = (int) (mRowSize / 2 - (paint.ascent() + paint.descent()) / 2);
canvas.drawText(text, startX, startY + mRowSize * j, paint);
}
//繪製完一行之後行數加1
row++;
}
//進行畫邊界線
paint.setColor(Color.parseColor("#44cdc3"));
paint.setStrokeWidth(4);
canvas.drawLine(0, 0, width, 0, paint);
canvas.drawLine(0, 0, 0, height, paint);
canvas.drawLine(width, 0, width, height, paint);
canvas.drawLine(0, height, width, height, paint);
}
/**
* 初始化列寬行高
*/
private void initSize() {
width = getWidth();
height = getHeight();
mColumnSize = width / NUM_COLUMNS;
mRowSize = height / NUM_ROWS;
}
//將日期列表傳遞過來
public void setDateList(List<Integer> dateList){
this.dateList = dateList;
//通知刷新
invalidate();
}
//用來獲取用戶點擊屏幕的座標
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;
}
/**
* 執行點擊事件
*
* @param x
* @param y
*/
private void doClickAction(int x, int y) {
int row = y / mRowSize;
int column = x / mColumnSize;
if (row != 0 && column != 0) {
//保存用戶選中的item
setCheckedItem(row, column);
}
invalidate();
}
private void setCheckedItem(int checkRow, int checkColumn) {
this.checkRow = checkRow;
this.checkColumn = checkColumn;
}
}
繪製好表格之後,就將上面的佈局用xml進行封裝,接下來是整體控件的代碼,裏面也做了一些計算時間的操作,備註都有。
/**
* 簡易的日曆時間表
* Created by Administrator on 2016/11/7.
*/
public class SimpleCalendarView extends RelativeLayout {
private Context mContext;
private String mondayDate, sundayDate;
private TextView currentDateTv;
private ImageView leftImg, rightImg;
private SimpleDateFormat df;
//用戶點擊次數 初始爲0
private int operateNums;
private Calendar mCalendar;
private CalendarTableView calendarView;
public SimpleCalendarView(Context context) {
super(context);
this.mContext = context;
init();
}
private void init() {
View simpleCalendar = LayoutInflater.from(mContext).inflate(R.layout.simple_calendar_view,this);
calendarView = (CalendarTableView) simpleCalendar.findViewById(R.id.main_simple_view);
currentDateTv = (TextView) simpleCalendar.findViewById(R.id.current_time_tv);
leftImg = (ImageView) simpleCalendar.findViewById(R.id.time_left_img);
rightImg = (ImageView) simpleCalendar.findViewById(R.id.time_right_img);
df = new SimpleDateFormat("yyyy-MM-dd");
operateDates();
leftImg.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
operateNums --;
operateDates();
}
});
rightImg.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
operateNums ++;
operateDates();
}
});
}
//計算當前一週日期並顯示在界面
private void operateDates() {
mCalendar = Calendar.getInstance();
mCalendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
mCalendar.add(Calendar.WEEK_OF_YEAR, operateNums);
//本週的星期一的日期
mondayDate = df.format(mCalendar.getTime());
//本週的週末的日期
mCalendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
//增加一個星期,纔是我們中國人理解的本週日的日期
mCalendar.add(Calendar.WEEK_OF_YEAR, 1);
//本週週日日期
sundayDate = df.format(mCalendar.getTime());
currentDateTv.setText(mondayDate + " - " + sundayDate);
try {
Date date = df.parse(mondayDate);
calendarView.setDateList(dateToWeek(date));
} catch (ParseException e) {
e.printStackTrace();
}
}
/**
* 根據日期獲得所在周的日期列表
*
* @param mDate
* @return
*/
@SuppressWarnings("deprecation")
public static List<Integer> dateToWeek(Date mDate) {
int b = mDate.getDay();
Date fDate;
List<Integer> list = new ArrayList<>();
Long fTime = mDate.getTime() - b * 24 * 3600000;
for (int i = 0; i < 7; i++) {
fDate = new Date();
fDate.setTime(fTime + ((i + 1) * 24 * 3600000)); //一週從週日開始算
int dates = fDate.getDate();
list.add(dates);
}
return list;
}
}
這個控件的佈局代碼也很簡單,由於沒有左右的圖片,先用小安卓來替代,後面替換即可。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp">
<ImageView
android:id="@+id/time_left_img"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/time_right_img"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/current_time_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="@color/black"
android:textSize="18sp" />
</RelativeLayout>
<com.kairui.medicaldoctor.ui.views.CalendarTableView
android:id="@+id/main_simple_view"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="10dp" />
</LinearLayout>
</LinearLayout>
以上這些代碼就是整體的封裝,最後就是使用它了。只需要用一個線性佈局將實例化的日曆控件add進去即可。
由於時間緊迫,目前未將表格的記錄事件刷新功能完成,等後期有時間了再來完善吧。
如果你在使用的過程中有問題也可以留言和我交流~