界面比較粗糙,主要看原理。
這個界面主要包括以下幾部分
1、座位
2、左邊的排數(shù)
3、左上方的縮略圖
4、縮略圖中的紅色區(qū)域
5、手指移動時跟隨移動
6、兩個手指縮放時跟隨縮放
主要技術點
1、矩陣Matrix
2、GestureDetector與ScaleGestureDetector
3、Bitmap的一下基本用法
4、這里只需要重寫view的onDraw就可實現(xiàn)全部功能
可以發(fā)現(xiàn)這個其實沒什么難度,主要就是一些位置的計算。
為了能便于理解首先把要用到的知識點進行一下梳理
1、矩陣Matrix
Matrix由3*3矩陣中9個值來決定,我們對Matrix的所有設置, 就是對這9個值的操作。
{MSCALE_X,MSKEW_X,MTRANS_X,
MSKEW_Y,MSCALE_Y,MTRANS_Y,
MPERSP_0,MPERSP_1,MPERSP_2}
這是矩陣的9個值,看名字也知道他們是什么意思了。
這里主要用到縮放和平移,下面以縮放為例來了解一下縮放的控制
通過Android提供的api我們可以調用setScale、preScale、postScale來改變MSCALE_X和MSCALE_Y的值達到縮放的效果
所以只要理解setScale、preScale、postScale這三個方法的區(qū)別我們就可以簡單的進行縮放控制了
1、setScale(sx,sy),首先會將該Matrix設置為對角矩陣,即相當于調用reset()方法,然后在設置該Matrix的MSCALE_X和MSCALE_Y直接設置為sx,sy的值
2、preScale(sx,sy),不會重置Matrix,而是直接與Matrix之前的MSCALE_X和MSCALE_Y值結合起來(相乘),M’ = M * S(sx, sy)。
3、postScale(sx,sy),不會重置Matrix,而是直接與Matrix之前的MSCALE_X和MSCALE_Y值結合起來(相乘),M’ = S(sx, sy) * M。
這么說其實也有些不好理解,舉個栗子一看就明白
1、pre….的執(zhí)行順序
Matrix matrix=new Matrix();
float[] points=new float[]{10.0f,10.0f};
matrix.preScale(2.0f, 3.0f);
matrix.preTranslate(8.0f,7.0f);
matrix.mapPoints(points);
Log.i("test", points[0]+"");
Log.i("test", points[1]+"");
結果為點坐標為(36.0,51.0) 可以得出結論,進行變換的順序是先執(zhí)行preTranslate(8.0f,7.0f),在執(zhí)行的preScale(2.0f,3.0f)。即對于一個Matrix的設置中,所有pre….是倒著向后執(zhí)行的。
2、post…的執(zhí)行順序
Matrix matrix=new Matrix();
float[] points=new float[]{10.0f,10.0f};
matrix.postScale(2.0f, 3.0f);
matrix.postTranslate(8.0f,7.0f);
matrix.mapPoints(points);
Log.i("test", points[0]+"");
Log.i("test", points[1]+"");
結果為點坐標為(28.0,37.0) 可以得出結論,進行變換的順序是先執(zhí)行postScale(2.0f,3.0f),在執(zhí)行的postTranslate(8.0f,7.0f)。即對于一個Matrix的設置中,所有post….是順著向前執(zhí)行的。
這里主要知道set…和post…方法就行,因為只用到了這兩個。 自我理解其實和scrollTo、scrollBy類似。
3、Bitmap的一下基本用法 參考: 關于bitmap你不知道的一些事
了解一下bitmap的注意事項即可
下面開始正式畫這個選座的功能了
1、畫座位:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 如果第一次進入 使座位圖居中
*/
if (mViewH != 0 && mViewW != 0&&isFrist) {
isFrist = false;
matrix.setTranslate(-(mViewW-getMeasuredWidth())/2, 0);
}
/**
* 畫座位
*/
drawSeat(canvas);
/**
* 畫排數(shù)
*/
drawText(canvas);
/**
* 畫縮略圖
*/
drawOverView(canvas);
/**
* 畫縮略圖選擇區(qū)域
*/
drawOvewRect(canvas);
}
private void drawSeat(Canvas canvas) {
float zoom = getMatrixScaleX();
scale1 = zoom;
tranlateX = getTranslateX();
tranlateY = getTranslateY();
/**
* 使用兩層for循環(huán)來畫出所有座位
*/
for (int i = 0; i < row; i++) {
float top = i * SeatHight * scale * scale1 + i * mSpaceY * scale1
+ tranlateY;
for (int j = 0; j < column; j++) {
float left = j * SeatWidth * scale * scale1 + j * mSpaceX
* scale1 + tranlateX;
tempMatrix.setTranslate(left, top);
tempMatrix.postScale(scale, scale, left, top);
tempMatrix.postScale(scale1, scale1, left, top);
/**
* 獲取每個位置的信息
*/
int state = getSeatType(i, j);
/**
* 根據(jù)位置信息畫不同的位置圖片
*/
switch (state) {
case SEAT_TYPE_SOLD:
canvas.drawBitmap(SeatLock, tempMatrix, null);
break;
case SEAT_TYPE_SELECTED:
canvas.drawBitmap(SeatChecked, tempMatrix, null);
break;
case SEAT_TYPE_AVAILABLE:
canvas.drawBitmap(SeatNormal, tempMatrix, null);
break;
case SEAT_TYPE_NOT_AVAILABLE:
break;
}
}
}
}
這里其實沒什么難度,主要就是使用兩層for循環(huán),一層畫行,一層畫列
另外要注意的就是當前位置的計算 top = (當前位置i)(座位圖標大小SeatHight 它本身的縮放比scale縮放時的縮放比scale1)+(當前位置i 垂直方向的間距mSpaceY*縮放時的縮放比scale1)+垂直方向移動是的移動距離
2、畫排數(shù)
/**
* 畫排數(shù)
*/
private void drawText(Canvas canvas) {
mTextPaint.setColor(bacColor);
RectF rectF = new RectF();
rectF.top = getTranslateY() - mNumberHeight/2;
rectF.bottom = getTranslateY()+ mViewH* getMatrixScaleX() + mNumberHeight/2;
rectF.left = 0;
rectF.right = mTextWidth;
canvas.drawRoundRect(rectF, mTextWidth/2, mTextWidth/2, mTextPaint);
mTextPaint.setColor(Color.WHITE);
for (int i = 0; i < row; i++) {
float top = (i *SeatHight*scale + i * mSpaceY) * getMatrixScaleX() + getTranslateY();
float bottom = (i * SeatHight*scale + i * mSpaceY + SeatHight) * getMatrixScaleX() + getTranslateY();
float baseline = (bottom + top - lineNumberPaintFontMetrics.bottom - lineNumberPaintFontMetrics.top ) / 2-6;
canvas.drawText(lineNumbers.get(i), mTextWidth / 2, baseline, mTextPaint);
}
}
3、畫縮略圖
private void drawOverView(Canvas canvas) {
/**
* 1、先畫張背景圖片
*/
mBitMapOverView = Bitmap.createBitmap((int)mOverViewWidth,(int)mOverViewHight,Bitmap.Config.ARGB_8888);
Canvas OverViewCanvas = new Canvas(mBitMapOverView);
Paint paint = new Paint();
paint.setColor(bacColor);
scaleoverX = mOverViewWidth / mViewW;
scaleoverY = mOverViewHight / mViewH;
float tempX = mViewW * scaleoverX;
float tempY = mViewH * scaleoverY;
OverViewCanvas.drawRect(0, 0, (float)tempX, (float)tempY, paint);
Matrix tempoverMatrix = new Matrix();
/**
* 2、和畫座位圖一樣在縮略圖中畫座位
*/
for (int i = 0; i < row; i++) {
float top = i * SeatHight * scale * scaleoverY+ i * mSpaceY * scaleoverY;
for (int j = 0; j < column; j++) {
float left = j * SeatWidth * scale * scaleoverX + j * mSpaceX * scaleoverX+mTextWidth*scaleoverX;
tempoverMatrix.setTranslate(left, top);
tempoverMatrix.postScale(scale*scaleoverX, scale*scaleoverY, left, top);
int state = getSeatType(i, j);
switch (state) {
case SEAT_TYPE_SOLD:
OverViewCanvas.drawBitmap(SeatLock, tempoverMatrix, null);
break;
case SEAT_TYPE_SELECTED:
OverViewCanvas.drawBitmap(SeatChecked, tempoverMatrix, null);
break;
case SEAT_TYPE_AVAILABLE:
OverViewCanvas.drawBitmap(SeatNormal, tempoverMatrix, null);
break;
case SEAT_TYPE_NOT_AVAILABLE:
break;
}
}
}
canvas.drawBitmap(mBitMapOverView,0,0,null);
}
4、縮略圖中的紅色區(qū)域
private void drawOvewRect(Canvas canvas) {
OverRectPaint = new Paint();
OverRectPaint.setColor(Color.RED);
OverRectPaint.setStyle(Paint.Style.STROKE);
OverRectPaint.setStrokeWidth(overRectLineWidth);
int tempViewW ;
int tempViewH;
if(getMeasuredWidth()<mViewW){
tempViewW = getMeasuredWidth();
}else{
tempViewW = mViewW;
}
if(getMeasuredHeight()<mViewH){
tempViewH = getMeasuredHeight();
}else{
tempViewH = mViewH;
}
try{
Rect rect ;
if(getMatrixScaleX()>= 1.0f){
rect = new Rect((int)(scaleoverX*Math.abs(getTranslateX())/getMatrixScaleX()),
(int)(scaleoverY*Math.abs(getTranslateY())/getMatrixScaleX()),
(int)(scaleoverX*Math.abs(getTranslateX())/getMatrixScaleX()+tempViewW*scaleoverX/getMatrixScaleX()),
(int)(scaleoverY*Math.abs(getTranslateY())/getMatrixScaleX()+tempViewH*scaleoverY/getMatrixScaleX()));
}else{
rect = new Rect((int)(scaleoverX*Math.abs(getTranslateX())),
(int)(scaleoverY*Math.abs(getTranslateY())),
(int)(scaleoverX*Math.abs(getTranslateX())+tempViewW*scaleoverX),
(int)(scaleoverY*Math.abs(getTranslateY())+tempViewH*scaleoverY));
}
canvas.drawRect(rect, OverRectPaint);
}catch(Exception e){
e.printStackTrace();
}
}
5、手指移動時跟隨移動
/**
* 移動事件 這里只是簡單判斷了一下,需要更細致的進行條件判斷
*/
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
float tempMViewW = column * SeatWidth*scale*scale1+(column -1)*mSpaceX*scale1+mTextWidth-getWidth();
float tempmViewH = row * SeatHight * scale * scale1 + (row -1) * mSpaceY * scale1 - getHeight();
if((getTranslateX()>mTextWidth+mSpaceX)&& distanceX<0){
distanceX = 0.0f;
}
if((Math.abs(getTranslateX())>tempMViewW)&&(distanceX>0)){
distanceX = 0.0f;
}
if((getTranslateY()>0)&&distanceY<0){
distanceY=0.0f;
}
if((Math.abs(getTranslateY())>tempmViewH)&&(distanceY>0)){
distanceY = 0.0f;
}
matrix.postTranslate(-distanceX, -distanceY);
invalidate();
return false;
}
/**
* 單擊事件
*/
public boolean onSingleTapConfirmed(MotionEvent e) {
int x = (int) e.getX();
int y = (int) e.getY();
for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
int tempX = (int) ((j * SeatWidth * scale + j * mSpaceX) * getMatrixScaleX() + getTranslateX());
int maxTemX = (int) (tempX + SeatWidth * scale * getMatrixScaleX());
int tempY = (int) ((i * SeatHight * scale + i * mSpaceX) * getMatrixScaleY() + getTranslateY());
int maxTempY = (int) (tempY + SeatHight * scale * getMatrixScaleY());
if (x >= tempX && x <= maxTemX && y >= tempY
&& y <= maxTempY) {
int id = getID(i, j);
int index = isHave(id);
if (index >= 0) {
remove(index);
} else {
addChooseSeat(i, j);
}
float currentScaleY = getMatrixScaleY();
if (currentScaleY < 1.7f) {
scaleX = x;
scaleY = y;
/**
* 選中時進行縮放操作
*/
zoomAnimate(currentScaleY, 1.9f);
}
invalidate();
break;
}
}
}
return super.onSingleTapConfirmed(e);
}
6、兩個手指縮放時跟隨縮放
public boolean onScale(ScaleGestureDetector detector) {
float scaleFactor = detector.getScaleFactor();
//scaleX = detector.getCurrentSpanX();
//scaleY = detector.getCurrentSpanY();
//直接判斷大于2會導致獲取的matrix縮放比例繼續(xù)執(zhí)行一次從而導致變成2.000001之類的數(shù)從而使
//判斷條件一直為真從而不會執(zhí)行縮小動作
//判斷相乘大于2 可以是當前獲得的縮放比例即使是1.9999之類的數(shù)如果繼續(xù)放大即使乘以1.0001也會比2大從而
//避免上述問題。
if (getMatrixScaleY() * scaleFactor > 2) {
scaleFactor = 2 / getMatrixScaleY();
}
if (getMatrixScaleY() * scaleFactor < 0.8) {
scaleFactor = 0.8f / getMatrixScaleY();
}
matrix.postScale(scaleFactor, scaleFactor);
invalidate();
return true;
}
至此這個比較粗糙的選座功能就實現(xiàn)了,有時間會繼續(xù)優(yōu)化下細節(jié)問題。