2011年底原本想做一个课表程序,可以及其方便快速的设置课程,查看课程,可是回家过年来学校就没有力气写完了,360也出了一个类似的软件了。
实现了滑动滚动效果,如同listview滚动时手指离开界面后,其仍在缓慢减速滚动的效果,
这个可以同时水平竖直滚动,并且减速的“阻力”可以设置的不一样,
所以。当初的设想还是不错的,只是遗憾没有把它完成,以后有时间又做吧。
主要要点:1,android的paint画超细的线条方法
2,listview那种滑动滚动效果,并且是两个方向的
3,绘制文字的位置控制
目前存在的问题:
滚动一段距离后偶有卡顿感,不知道是手机性能太低了还是怎么的,虽然我只绘制出需要显示的一块画布区域(脏矩形法,减小绘制工作量)
建立一个继承Activity的虚类BaseActivity:
package zcw.soft;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
public abstract class BaseActivity extends Activity {
View layoutView;
static int screenH;
static int screenW;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
screenH = dm.heightPixels;
screenW = dm.widthPixels;
setContentView(getLayoutView());
}
View getLayoutView(){return null;};
}
接下来创建一个课表展示界面的MonthActivity :
package zcw.soft;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
public class MonthActivity extends BaseActivity {
BaseView myView;//一个View类
@Override
View getLayoutView() {
myView = new BaseView(this, screenW, screenH);
return myView;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (myView.timer != null) {
myView.timer.cancel();
myView.timer = null;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, 1, 1, "编辑");
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 1:
myView.setEditMode();
break;
}
return true;
}
}
BaseView:
package zcw.soft;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Point;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class BaseView extends View implements OnTouchListener,
OnGestureListener {
GestureDetector detector;
float worldX, worldY;
Calendar c;
int startYear = 1990, startMonth = 1, startDay = 1;
int weeks = 20;
int height, width;
Paint LinePaint, TextPaint;
float DatePanelWidth = 30;
float WeekPanelHeight = 40;
float scale = 1f;
int lessons = 11;
float LessonWidth = 50;
float WeekHeight = 90;
float velocityX, velocityY;
static Bitmap checked;
Bitmap unchecked;
boolean editMode = true;
Point point;
ArrayList<ArrayList<Node>> data;
ArrayList<String> classType;
int dateIndex = 0, lessonIndex = 0, dateNum, lessonNum;
public BaseView(Context context, int w, int h) {
super(context);
height = h;
width = w;
this.setKeepScreenOn(true);
c = Calendar.getInstance();
c.set(Calendar.YEAR, startYear);
c.set(Calendar.MONTH, startMonth + 1);
c.set(Calendar.DAY_OF_MONTH, startDay);
LinePaint = new Paint();
LinePaint.setFakeBoldText(false);
LinePaint.setAntiAlias(false);
LinePaint.setStrokeWidth(0);
LinePaint.setColor(Color.rgb(80, 80, 80));
TextPaint = new Paint();
TextPaint.setFakeBoldText(true);
TextPaint.setAntiAlias(true);
TextPaint.setColor(Color.BLACK);
TextPaint.setTextSize(13);
TextPaint.setTextAlign(Align.CENTER);
detector = new GestureDetector(this);
detector.setIsLongpressEnabled(true);
setOnTouchListener(this);
setLongClickable(true);//词句可让触摸事件响应滚动,单击等事件。
checked = BitmapFactory.decodeResource(getResources(),
R.drawable.btn_check_buttonless_on);
unchecked = BitmapFactory.decodeResource(getResources(),
R.drawable.btn_check_buttonless_off);
lessonNum = (int) (width / LessonWidth) + 2;
dateNum = (int) (height / WeekHeight) + 2;
data = new ArrayList<ArrayList<Node>>();
classType = new ArrayList<String>();
LoadData();
}
public void LoadData() {
classType.add("数学");
classType.add("外交英语");
for (int i = 0; i < weeks; i++) {
ArrayList temp = new ArrayList<Node>();
for (int j = 0; j < 7 * lessons; j++) {
temp.add(new Node());
}
data.add(temp);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
dateIndex = (int) ((-WeekPanelHeight - worldY) / WeekHeight);
lessonIndex = (int) ((-worldX - DatePanelWidth) / LessonWidth);
drawData(canvas);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawRect(DatePanelWidth, 0, width, WeekPanelHeight, paint);
canvas.drawRect(0, WeekPanelHeight, DatePanelWidth, height, paint);
drawDatePanel(canvas);
drawWeekPanel(canvas);
paint.setColor(Color.parseColor("#FF6666"));
canvas.drawRect(0, 0, DatePanelWidth, WeekPanelHeight, paint);
drawText(canvas, "周", 0, 0, DatePanelWidth, WeekPanelHeight);
this.postInvalidate(0, 0, width, height);
}
private void drawText(Canvas c, String text, int bg, float left, float top,
float right, float bottom) {
Paint paint = new Paint();
paint.setColor(bg);
c.drawRect(left, top, right, bottom, paint);
c.drawLine(right, top, right, bottom, LinePaint);
c.drawLine(left, top, left, bottom, LinePaint);
c.drawLine(left, top, right, top, LinePaint);
c.drawLine(left, bottom, right + 1, bottom, LinePaint);
if (text != "")
c.drawText(text, (right + left) / 2,
(top + bottom + TextPaint.getTextSize()) / 2, TextPaint);
}
private void drawText(Canvas c, String text1,String text2,String text3,RectF rect) {
// Paint paint = new Paint();
// paint.setColor(bg);
// c.drawRect(rect, paint);
if (text1 != "")
c.drawText(text1, (rect.right + rect.left) / 2,
rect.top + rect.height()/3, TextPaint);
if(text2!="")
c.drawText(text2, (rect.right + rect.left) / 2,
rect.top + 2*rect.height()/3, TextPaint);
if(text3!="")
c.drawText(text3, (rect.right + rect.left) / 2,
rect.top + rect.height()-6, TextPaint);
}
private void drawText(Canvas c, String text1,String text2,String text3,RectF rect,int len) {
// Paint paint = new Paint();
// paint.setColor(bg);
// c.drawRect(rect, paint);
if (text1 != "")
c.drawText(text1, rect.left+(len*LessonWidth) / 2,
rect.top + rect.height()/3, TextPaint);
if(text2!="")
c.drawText(text2, rect.left+(len*LessonWidth) / 2,
rect.top + 2*rect.height()/3, TextPaint);
if(text3!="")
c.drawText(text3, rect.left+(len*LessonWidth) / 2,
rect.top + rect.height()-6, TextPaint);
c.drawLine(rect.left, rect.top, rect.left+len*LessonWidth, rect.top, LinePaint);
c.drawLine(rect.left, rect.bottom, rect.left+len*LessonWidth, rect.bottom, LinePaint);
c.drawLine(rect.left, rect.top, rect.left, rect.bottom, LinePaint);
c.drawLine(rect.left+len*LessonWidth, rect.top, rect.left+len*LessonWidth, rect.bottom, LinePaint);
}
private void drawText(Canvas c, String text, float left, float top,
float right, float bottom) {
c.drawLine(right, top, right, bottom, LinePaint);
c.drawLine(left, top, left, bottom, LinePaint);
c.drawLine(left, top, right, top, LinePaint);
c.drawLine(left, bottom, right + 1, bottom, LinePaint);
if (text != "")
c.drawText(text, (right + left) / 2,
(top + bottom + TextPaint.getTextSize()) / 2, TextPaint);
}
private void drawSmallText(Canvas c, String text1, String text2,
float left, float top, float right, float bottom) {
TextPaint.setTextSize(10);
if (text1 != "")
c.drawText(text1, (right + left) / 2,
top + 10 + (bottom - top) / 6, TextPaint);
if (text2 != "")
c.drawText(text2, (right + left) / 2, bottom - (bottom - top) / 6,
TextPaint);
TextPaint.setTextSize(13);
}
private void drawData(Canvas c) {
LinePaint.setColor(Color.LTGRAY);
// float tx = worldX + DatePanelWidth + 7 * lessons * LessonWidth;
int max1=dateIndex + dateNum;
if(max1>weeks)
max1=weeks;
for (int i = dateIndex; i <= max1; i++) {
float ty = worldY + WeekPanelHeight + i * WeekHeight;
c.drawLine(0, ty, width, ty, LinePaint);
}
int max2=lessonIndex + lessonNum;
if(max2>lessons*7)
max2=lessons*7;
for (int j = lessonIndex; j <= max2; j++) {
float tx = worldX + DatePanelWidth + j * LessonWidth;
if(j%lessons==0)
LinePaint.setColor(Color.BLACK);
else LinePaint.setColor(Color.LTGRAY);
c.drawLine(tx, 0, tx, height, LinePaint);
}
LinePaint.setColor(Color.BLACK);
for (int i = dateIndex; i < max1; i++) {
ArrayList temp = data.get(i);
float tx = worldX + DatePanelWidth+lessonIndex * LessonWidth;
float ty = worldY + WeekPanelHeight + i * WeekHeight;
for (int j = lessonIndex; j < max2; j++) {
RectF rf = new RectF(tx, ty, tx + LessonWidth, ty + WeekHeight);
int k=((Node) temp.get(j)).draw(c, editMode, rf);
j+=(k-1);
tx+=k*LessonWidth;
}
}
}
private void drawDatePanel(Canvas c) {
// float ty=worldY+WeekPanelHeight;
int max1=dateIndex + dateNum;
if(max1>weeks)
max1=weeks;
for (int i = dateIndex; i <= max1; i++) {
drawText(c, (i + 1) + "周", 0, (worldY + i * WeekHeight) * scale
+ WeekPanelHeight, DatePanelWidth, (worldY + (i + 1)
* WeekHeight)
* scale + WeekPanelHeight);
drawSmallText(c, "09/01", "12/26", 0, (worldY + i * WeekHeight)
* scale + WeekPanelHeight, DatePanelWidth,
(worldY + (i + 1) * WeekHeight) * scale + WeekPanelHeight);
}
}
private void drawWeekPanel(Canvas c) {
// float tx=worldX+DatePanelWidth;
float th = WeekPanelHeight / 2;
for (int i = lessonIndex / lessons; i <= lessonIndex / lessons
+ lessonNum / lessons + 1; i++) {
float tw = (worldX + i * LessonWidth * lessons) * scale
+ DatePanelWidth;
drawText(c, "星期" + (i + 1), tw, 0, tw + LessonWidth * lessons
* scale, th);
int color = Color.parseColor("#CCFF99");
for (int j = 0; j < lessons; j++) {
if (j == 4)
color = Color.parseColor("#CCFFFF");
if (j == 8)
color = Color.rgb(255, 253, 222);
drawText(c, "" + (j + 1), color, tw + j * LessonWidth * scale,
th, tw + (j + 1) * LessonWidth * scale, 2 * th);
}
}
}
@Override
public boolean onDown(MotionEvent e) {
if (timer != null) {
timer.cancel();
timer = null;
}
return false;
}
public Timer timer;
private class MyTimerTask extends TimerTask {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
// Log.i("","handleMessage=====velocityX:"+velocityX+",velocityY:"+velocityY);
if (Math.abs(velocityX) <= 0.5f && Math.abs(velocityY) <= 0.5f
&& timer != null) {
timer.cancel();
timer = null;
}
move(-velocityX, -velocityY);
velocityX *= 0.95f;
velocityY *= 0.80f;
break;
}
super.handleMessage(msg);
}
};
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// Log.i("","handleMessage=====velocityX:"+velocityX+",velocityY:"+velocityY);
if (Math.abs(velocityX) >= 200)
this.velocityX = velocityX / 25;
else
this.velocityX = 0;
if (Math.abs(velocityY) >= 200)
this.velocityY = velocityY / 25;
else
this.velocityY = 0;
// Log.i("", "fling=====");
if (timer == null) {
// Log.i("", "starting timer=====");
timer = new Timer();
timer.schedule(new MyTimerTask(), 0, 40);
}
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if (Math.abs(distanceX) - Math.abs(distanceY) > 4)
move(distanceX, 0);
else if (Math.abs(distanceY) - Math.abs(distanceX) > 4)
move(0, distanceY);
else
move(distanceX, distanceY);
return false;
}
private void move(float distanceX, float distanceY) {
worldX -= distanceX / scale;
if ((worldX + 7 * lessons * LessonWidth) * scale + DatePanelWidth < width) {
worldX = (width - DatePanelWidth) / scale - 7 * lessons
* LessonWidth;
velocityX = 0;
}
if (worldX > 0) {
worldX = 0;
velocityX = 0;
}
worldY -= distanceY / scale;
if ((worldY + weeks * WeekHeight) * scale + WeekPanelHeight < height) {
worldY = (height - WeekPanelHeight) / scale - weeks * WeekHeight;
velocityY = 0;
}
if (worldY > 0) {
worldY = 0;
velocityY = 0;
}
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// Log.i("","onSingleTapUp");
if(point==null)
point=new Point();
if(e.getX()<=DatePanelWidth)
point.x=-1;
else
point.x=(int) ((e.getX()-worldX-DatePanelWidth)/LessonWidth);
if(e.getY()<=WeekPanelHeight)
point.y=-1;
else point.y=(int) ((e.getY()-worldY-WeekPanelHeight)/WeekHeight);
if(editMode && point.x!=-1 && point.y!=-1)
{
((Node)data.get(point.y).get(point.x)).clicked(null);
}
return false;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return detector.onTouchEvent(event);
}
class Node {
public Node(){};
public Node(int classI) {
classIndex = classI;
empty=false;
}
public Node(int classI,int classT) {
classIndex = classI;
classTag=classT;
empty=false;
}
public void copy(Node node)
{
if(node==null)
{
empty=true;
checked=false;
}else{
empty=false;
checked=true;
classTag=node.classTag;
placeTag=node.placeTag;
teacherTag=node.teacherTag;
}
}
public int draw(Canvas c,boolean mode,RectF rect)
{
if(empty)
{
if(mode)
c.drawBitmap(unchecked, rect.left,rect.top, new Paint());
}else {
if(mode)
{
c.drawBitmap(checked?BaseView.checked:unchecked, rect.left,rect.top, new Paint());
drawText(c,classType.get(classTag),"6c209","张老师",rect);
}
else {
drawText(c,classType.get(classTag),"6c209","张老师",rect,classLength);
}
}
return classLength;
}
public void clicked(Node node)
{
this.checked=!checked;
if(checked)
copy(node);
else
{
empty=true;
checked=false;
}
}
public boolean empty=true;
public boolean checked=false;
public int classIndex;
public int classLength = 1;
public int classTag;
public int placeTag;
public int teacherTag;
}
public void setEditMode(boolean mode)
{
if(mode==editMode)
return ;
editMode=mode;
}
public void setEditMode()
{
editMode=!editMode;
}
}
package zcw.soft;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
public class TableView extends BaseSurfaceView {
float worldX, worldY;
Calendar c;
int startYear = 1990, startMonth = 1, startDay = 1;
int weeks = 20;
Paint LinePaint, TextPaint;
float DatePanelWidth = 30;
float WeekPanelHeight = 40;
float scale = 2f;
int lessons = 11;
float LessonWidth = 25;
float WeekHeight = 30;
float velocityX, velocityY;
public TableView(Context context, boolean isTransparent, int w, int h) {
super(context, isTransparent);
thread.setDaemon(true);
height = h;
width = w;
this.setKeepScreenOn(true);
c = Calendar.getInstance();
c.set(Calendar.YEAR, startYear);
c.set(Calendar.MONTH, startMonth + 1);
c.set(Calendar.DAY_OF_MONTH, startDay);
LinePaint = new Paint();
LinePaint.setFakeBoldText(false);
LinePaint.setAntiAlias(false);
LinePaint.setStrokeWidth(0);
LinePaint.setColor(Color.rgb(80, 80, 80));
TextPaint = new Paint();
TextPaint.setFakeBoldText(true);
TextPaint.setAntiAlias(true);
TextPaint.setColor(Color.BLACK);
TextPaint.setTextSize(13);
TextPaint.setTextAlign(Align.CENTER);
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch(msg.what)
{
case 2:
// Log.i("", "handleMessage=====velocityX:"+velocityX+",velocityY:"+velocityY);
if(Math.abs(velocityX)<=0.1f && Math.abs(velocityY)<=0.1f && timer!=null)
{
timer.cancel();
timer=null;
}
else {
move(-velocityX,-velocityY);
velocityX*=0.9f;
velocityY*=0.9f;
}
break;
}
super.handleMessage(msg);
}
};
@Override
public void surfaceCreated(SurfaceHolder arg0) {
super.surfaceCreated(arg0);
thread.start();
}
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
long startTime = System.currentTimeMillis();
synchronized(holder)
{
Canvas canvas=holder.lockCanvas();
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawRect(DatePanelWidth, 0, width, WeekPanelHeight, paint);
canvas.drawRect(0, WeekPanelHeight, DatePanelWidth, height, paint);
drawDatePanel(canvas);
drawWeekPanel(canvas);
canvas.drawRect(0, 0, DatePanelWidth, WeekPanelHeight, paint);
drawText(canvas, "周", 0, 0, DatePanelWidth, WeekPanelHeight);
holder.unlockCanvasAndPost(canvas);
}
long endTime = System.currentTimeMillis();
int diffTime = (int)(endTime - startTime);
while(diffTime <=33) {
diffTime = (int)(System.currentTimeMillis() - startTime);
Thread.yield();
}
}
}
});
private void drawText(Canvas c, String text, float left, float top,
float right, float bottom) {
c.drawLine(right, top, right, bottom, LinePaint);
c.drawLine(left, top, left, bottom, LinePaint);
c.drawLine(left, top, right, top, LinePaint);
c.drawLine(left, bottom, right + 1, bottom, LinePaint);
if (text != "")
c.drawText(text, (right + left) / 2,
(top + bottom + TextPaint.getTextSize()) / 2, TextPaint);
}
private void drawDatePanel(Canvas c) {
// float ty=worldY+WeekPanelHeight;
for (int i = 0; i < weeks; i++)
drawText(c, (i + 1) + "周", 0, (worldY + i * WeekHeight) * scale
+ WeekPanelHeight, DatePanelWidth, (worldY + (i + 1)
* WeekHeight)
* scale + WeekPanelHeight);
}
private void drawWeekPanel(Canvas c) {
// float tx=worldX+DatePanelWidth;
float th = WeekPanelHeight / 2;
for (int i = 0; i < 7; i++) {
float tw = (worldX + i * LessonWidth * lessons) * scale
+ DatePanelWidth;
drawText(c, "星期" + (i + 1), tw, 0, tw + LessonWidth * lessons
* scale, th);
for (int j = 0; j < 11; j++) {
drawText(c, "" + (j + 1), tw + j * LessonWidth * scale, th, tw
+ (j + 1) * LessonWidth * scale, 2 * th);
}
}
}
@Override
public boolean onDown(MotionEvent e) {
if (timer != null) {
timer.cancel();
timer = null;
}
return false;
}
private Timer timer;
private class MyTimerTask extends TimerTask {
@Override
public void run() {
Message message = new Message();
message.what = 2;
handler.sendMessage(message);
}
};
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
this.velocityX = velocityX / 100;
this.velocityY = velocityY / 100;
// Log.i("", "fling=====");
if (timer == null) {
// Log.i("", "starting timer=====");
timer = new Timer();
timer.schedule(new MyTimerTask(), 0, 10);
}
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
move(distanceX, distanceY);
return false;
}
private void move(float distanceX, float distanceY) {
worldX -= distanceX / scale;
worldY -= distanceY / scale;
if ((worldX + 7 * lessons * LessonWidth) * scale + DatePanelWidth < width) {
worldX = (width - DatePanelWidth) / scale - 7 * lessons
* LessonWidth;
velocityX = 0;
}
if ((worldY + weeks * WeekHeight) * scale + WeekPanelHeight < height) {
worldY = (height - WeekPanelHeight) / scale - weeks * WeekHeight;
velocityY = 0;
}
if (worldX > 0) {
worldX = 0;
velocityX = 0;
}
if (worldY > 0) {
worldY = 0;
velocityY = 0;
}
}
}
目前的效果: