在自己项目中有这样一个需求,要把一天分为上午下午晚上三个时间段,把一周分为7天,进行时间选择的一个需求,这样可以比较直观的选择,不必像原来程序那样,手动直接输入,回便捷很多,我先上图,后续慢慢道来!
图片所示的就是我们代码所产生的效果了!
那么我来说明一下我自己的实现思路,核心的问题就是自定义View并绘制!
首先要建立实体类:
package cn.sdut.lsy.time.chooser;
/**
* Created by lsy on 15-8-10.
*/
public class TimerChoosen {
private int fromX;
private int fromY;
private int toX;
private int toY;
private int timeId; // 时间选择位置
private String timeFree; // 存储的时间状态
private boolean isChoosen = false; // 是否被选中
private int fromTimeNum; // 设置是哪个时间段开始
// 划分三个时间段,早上,下午,晚上,对应1,2,3三个位置
private int weekday; // 属于周几
public void setPoint(int fromX, int fromY, int toX, int toY) {
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
}
public int getFromX() {
return fromX;
}
public void setFromX(int fromX) {
this.fromX = fromX;
}
public int getFromY() {
return fromY;
}
public void setFromY(int fromY) {
this.fromY = fromY;
}
public int getToX() {
return toX;
}
public void setToX(int toX) {
this.toX = toX;
}
public int getToY() {
return toY;
}
public void setToY(int toY) {
this.toY = toY;
}
public int getWeekday() {
return weekday;
}
public void setWeekday(int weekday) {
this.weekday = weekday;
}
public int getTimeId() {
return timeId;
}
public void setTimeId(int timeId) {
this.timeId = timeId;
}
public String getTimeFree() {
return timeFree;
}
public void setTimeFree(String timeFree) {
this.timeFree = timeFree;
}
public int getFromTimeNum() {
return fromTimeNum;
}
public void setFromTimeNum(int fromTimeNum) {
this.fromTimeNum = fromTimeNum;
}
public boolean isChoosen() {
return isChoosen;
}
public void setIsChoosen(boolean isChoosen) {
this.isChoosen = isChoosen;
}
@Override
public String toString() {
return "+++"+timeFree+"---"+fromTimeNum+"***"+weekday;
}
}
fromX,fromY,toX,toY是确定位置的主要因素,其他的不多说了,下面是这一个项目中最核心的代码。
/**
* @author lsy
* date : 2015年8月10日
*/
package cn.sdut.lsy.time.chooser;
import com.yxw.schedule.ClassInfo;
import com.yxw.schedule.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import java.util.List;
public class ScheduleViewTime extends View implements OnTouchListener {
private Paint mPaint; // 画笔,包含了画几何图形、文本等的样式和颜色信息
private int startX = 0;//画布的原点X(所有的画图操作,都是基于这个原点的,touch中只要修改这个值)
private int startY = 0;//画布的原点Y(所有的画图操作,都是基于这个原点的,touch中只要修改这个值)
private static final int sidewidth = 100;//左边,上面bar的宽度
private static final int eachBoxH = 120;//每个格子的高度
private static int eachBoxW = 120;//每个格子的宽度,后面根据屏幕对它做了均分
private int focusX = -1;//当前手指焦点的位置坐标
private int focusY = -1;//当前手指焦点的位置坐标
//private static int classTotal = 12;//左边栏总格子数
private static int timeTotal = 3; // 三个时间段,左边栏总格子数目
private static int dayTotal = 7;//顶部栏总共格子数
private String[] weekdays;//星期
private boolean isMove = false; // 判断是否移动
private Context context;
// 监听器
private OnItemTimeClickListener onItemClassClickListener;
// 数据
private List<TimerChoosen> timeChoosens;
// 颜色
// public static final int contentBg = Color.argb(255, 255, 255, 255);
public static final int barBg = Color.argb(255, 225, 225, 225);
// public static final int bayText = Color.argb(255, 150, 150, 150);
public static final int barBgHrLine = Color.argb(255, 150, 150, 150);
public static final int classBorder = Color.argb(180, 150, 150, 150);
public static final int markerBorder = Color.argb(100, 150, 150, 150);
//预设格子背景颜色数组
public static final int[] classBgColors = {Color.argb(200, 71, 154, 199),
Color.argb(200, 230, 91, 62), Color.argb(200, 50, 178, 93),
Color.argb(200, 255, 225, 0), Color.argb(200, 102, 204, 204),
Color.argb(200, 51, 102, 153), Color.argb(200, 102, 153, 204)
};
public ScheduleViewTime(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
weekdays = context.getResources().getStringArray(R.array.weekdays);
mPaint = new Paint();
setOnTouchListener(this);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
eachBoxW = (getWidth() - sidewidth) / 5;
printMarker(canvas);
printContent(canvas);
printTopBar(canvas);
printLeftBar(canvas);
}
/**
* 区分课间隔,画交线处的十字
*
* @param canvas
*/
private void printMarker(Canvas canvas) {
mPaint.setColor(markerBorder);
for (int i = 0; i < dayTotal - 1; i++) { // 按天
for (int j = 0; j < timeTotal - 1; j++) { // 按上课节数
// 画交线处的十字
mPaint.setStyle(Style.STROKE);
// 横向
canvas.drawRect(startX + sidewidth + eachBoxW * (i + 1)
- eachBoxW / 20, startY + sidewidth + eachBoxH
* (j + 1) - 1, startX + sidewidth + eachBoxW * (i + 1)
+ eachBoxW / 20, startY + sidewidth + eachBoxH
* (j + 1), mPaint);
// 纵向
canvas.drawRect(
startX + sidewidth + eachBoxW * (i + 1) - 1,
startY + sidewidth + eachBoxH * (j + 1) - eachBoxW / 20,
startX + sidewidth + eachBoxW * (i + 1), startY
+ sidewidth + eachBoxH * (j + 1) + eachBoxW
/ 20, mPaint);
}
}
}
/**
* 画中间主体部分
*
* @param canvas
*/
private void printContent(Canvas canvas) {
if (timeChoosens != null && timeChoosens.size() > 0) {
mPaint.setTextSize(25); // 设置字体大小
TimerChoosen timerChoosen;
for (int i = 0; i < timeChoosens.size(); i++) {
timerChoosen = timeChoosens.get(i);
// Log.d("TAG",timerChoosen.toString());
int fromX = startX + sidewidth + eachBoxW
* (timerChoosen.getWeekday() - 1);
int fromY = startY + sidewidth + eachBoxH
* (timerChoosen.getFromTimeNum() - 1);
int toX = startX + sidewidth + eachBoxW
* timerChoosen.getWeekday();
int toY = startY + sidewidth + eachBoxH
* (timerChoosen.getFromTimeNum());
timerChoosen.setPoint(fromX, fromY, toX, toY); // 存储范围数据
// 画classbg
mPaint.setStyle(Style.FILL);
// 确定颜色
if(timerChoosen.isChoosen()) {
mPaint.setColor(classBgColors[1]);
} else {
mPaint.setColor(classBgColors[2]);
}
canvas.drawRect(fromX, fromY, toX - 2, toY - 2, mPaint);
// 画文字
mPaint.setColor(Color.WHITE);
String className = timerChoosen.getTimeFree();
Rect textRect1 = new Rect();
mPaint.getTextBounds(className, 0, className.length(),
textRect1);
int th = textRect1.bottom - textRect1.top;
int tw = textRect1.right - textRect1.left;
//计算行数
int row = (int) ((tw + 30) / eachBoxW + 1);
int length = className.length() / row;
//逐行写字
for (int j = 0; j < row - 1; j++) {
canvas.drawText(className, length * j, length * (j + 1),
fromX + 5, fromY + 10 + th * (j + 1), mPaint);
}
//最后一行文字特殊处理
canvas.drawText(className, length * (row - 1),
className.length(), fromX + 15, fromY + 20 + th * row,
mPaint);
// 画边框
mPaint.setColor(classBorder);
mPaint.setStyle(Style.STROKE);
canvas.drawRect(fromX, fromY, toX - 2, toY - 2, mPaint);
}
}
}
/**
* 画左边课时bar
*
* @param canvas
*/
private void printLeftBar(Canvas canvas) {
// =================画左边课时栏=================
mPaint.setColor(barBg);
mPaint.setStyle(Style.FILL);
mPaint.setTextSize(30);
// 课时栏背景
canvas.drawRect(0, startY + sidewidth, sidewidth, sidewidth + startY
+ eachBoxH * timeTotal, mPaint);
mPaint.setColor(barBgHrLine);
// 画第一个边框线
canvas.drawRect(0, startY + sidewidth + eachBoxH - 1, sidewidth, startY
+ eachBoxH + sidewidth, mPaint);
// 居中处理
Rect textRect1 = new Rect();
mPaint.getTextBounds("1", 0, 1, textRect1);
int th = textRect1.bottom - textRect1.top;
int tw1 = textRect1.right - textRect1.left;
mPaint.getTextBounds("10", 0, 2, textRect1);
int tw2 = textRect1.right - textRect1.left;
// 画第一个文字
canvas.drawText("上午", sidewidth / 2 - tw1 - th/2, startY + sidewidth + eachBoxH
/ 2 + th / 2, mPaint);
for (int i = 2; i < timeTotal + 1; i++) {
String textGet = "";
// 画边框
canvas.drawRect(0, startY + sidewidth + eachBoxH * (i-1) - 1,
sidewidth, startY + eachBoxH * (i-1) + sidewidth, mPaint);
// 画文字
int tw = tw1 * 2 + (tw2 - tw1) * (i / 10);
if(i == 2) {
textGet = "下午";
} else {
textGet = "晚上";
}
canvas.drawText(textGet, sidewidth / 2 - tw / 2 - th / 2, startY + sidewidth
+ eachBoxH * (i - 1) + eachBoxH / 2 + th / 2, mPaint);
}
// =========左上角正方形============
canvas.drawRect(0, 0, sidewidth, sidewidth, mPaint);
}
/**
* 画顶部星期bar
*
* @param canvas
*/
private void printTopBar(Canvas canvas) {
// =================画顶部星期栏==================
mPaint.setColor(barBg);
mPaint.setStyle(Style.FILL);
// 星期栏背景
canvas.drawRect(startX + sidewidth, 0, sidewidth + startX + eachBoxW
* dayTotal, sidewidth, mPaint);
mPaint.setColor(barBgHrLine);
// 画第一个边框线
mPaint.setTextSize(25);
canvas.drawRect(startX + sidewidth + eachBoxW - 1, 0, startX + eachBoxW
+ sidewidth, sidewidth, mPaint);
// 居中处理
Rect textBounds = new Rect();
mPaint.getTextBounds(weekdays[0], 0, weekdays[0].length(), textBounds);
int textHeight = textBounds.bottom - textBounds.top;
int textWidth = textBounds.right - textBounds.left;
// 画第一个文字
canvas.drawText(weekdays[0], startX + sidewidth + eachBoxW / 2
- textWidth / 2, sidewidth / 2 + textHeight / 2, mPaint);
for (int i = 2; i < dayTotal + 1; i++) {
// 画边框线
canvas.drawRect(startX + sidewidth + eachBoxW * i - 1, 0, startX
+ eachBoxW * i + sidewidth, sidewidth, mPaint);
// 画文字
canvas.drawText(weekdays[i - 1], startX + sidewidth + eachBoxW
* (i - 1) + eachBoxW / 2 - textWidth / 2, sidewidth / 2
+ textHeight / 2, mPaint);
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
focusX = (int) event.getX();
focusY = (int) event.getY();
isMove = false;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
int dx = (int) (event.getX() - focusX);
int dy = (int) (event.getY() - focusY);
if (!isMove && Math.abs(dx) < 5 && Math.abs(dy) < 5) {
isMove = false;
return false;
}
isMove = true;
//判断是否超出左右边框
if (startX + dx < 0
&& startX + dx + eachBoxW * dayTotal + sidewidth >= getWidth()) {
startX += dx;
}
//判断是否超出上下边框
if (startY + dy < 0
&& startY + dy + eachBoxH * timeTotal + sidewidth >= getHeight()) {
startY += dy;
}
//重新获得焦点坐标
focusX = (int) event.getX();
focusY = (int) event.getY();
//重绘
invalidate();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (!isMove) {
int focusX = (int) event.getX();
int focusY = (int) event.getY();
// 是点击效果,遍历是哪个课程的点击效果
for (int i = 0; i < timeChoosens.size(); i++) {
TimerChoosen classInfo = timeChoosens.get(i);
if (focusX > classInfo.getFromX()
&& focusX < classInfo.getToX()
&& focusY > classInfo.getFromY()
&& focusY < classInfo.getToY()) {
if (onItemClassClickListener != null) {
onItemClassClickListener.onClick(classInfo);
// 重绘
invalidate();
}
break;
}
}
}
}
return true;
}
public interface OnItemTimeClickListener {
public void onClick(TimerChoosen timerChoosen);
}
public OnItemTimeClickListener getOnItemClassClickListener() {
return onItemClassClickListener;
}
public void setOnItemClassClickListener(
OnItemTimeClickListener onItemClassClickListener) {
this.onItemClassClickListener = onItemClassClickListener;
}
public List<TimerChoosen> getTimerChoosens() {
return timeChoosens;
}
public void setTimerChoosens(List<TimerChoosen> timerChoosens) {
this.timeChoosens = timerChoosens;
invalidate();// 刷新页面
}
}
代码中的注释很清楚,想说明的一点是:在设置点击事件的时候,处理相应的点击事件很重要。我们常见的OnClickListener的设置和自定义View的实现大体一致,所有的对自定义View触摸相关操作,都要放在onTouch()中实现。
最后我们来看是如何用在Activity中的,布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<!--<com.yxw.schedule.ScheduleView
android:id="@+id/scheduleView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>-->
<cn.sdut.lsy.time.chooser.ScheduleViewTime
android:id="@+id/svt"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Activity:
package com.yxw.schedule;
import java.util.ArrayList;
import com.yxw.schedule.ScheduleView.OnItemClassClickListener;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.widget.Toast;
import cn.sdut.lsy.time.chooser.ScheduleViewTime;
import cn.sdut.lsy.time.chooser.TimerChoosen;
public class MainActivity extends Activity {
private ScheduleViewTime svt;
private ArrayList<TimerChoosen> timeChoosens;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
svt = (ScheduleViewTime) findViewById(R.id.svt);
getTimeData();
svt.setTimerChoosens(timeChoosens);
svt.setOnItemClassClickListener(new ScheduleViewTime.OnItemTimeClickListener() {
@Override
public void onClick(TimerChoosen timerChoosen) {
Toast.makeText(MainActivity.this,
"您选择的时间为:" + timerChoosen.getTimeFree(),
Toast.LENGTH_SHORT).show();
if(timerChoosen.isChoosen()) {
timerChoosen.setTimeFree("free");
timerChoosen.setIsChoosen(false);
} else {
timerChoosen.setTimeFree("busy");
timerChoosen.setIsChoosen(true);
}
}
});
}
private void getTimeData() {
timeChoosens = new ArrayList<TimerChoosen>();
TimerChoosen classInfo = null;
for(int i = 1; i < 8; i++) {
for(int j = 1; j < 4; j++) {
Log.d("TAG","i = "+i+"::::j="+j);
classInfo = new TimerChoosen();
classInfo.setTimeId(i);
classInfo.setTimeFree("free");
classInfo.setFromTimeNum(j);
classInfo.setWeekday(i);
timeChoosens.add(classInfo);
classInfo = null;
}
}
}
}
这样就完成了我们的需求了。