先看看电影票在线选座功能实现的效果图:

界面比较粗糙,主要看原理。

这个界面主要包括以下几部分

1、座位

2、左边的排数

3、左上方的缩略图

4、缩略图中的红色区域

5、手指移动时跟随移动

6、两个手指缩放时跟随缩放

主要技术点

1、矩阵Matrix

2、GestureDetector与ScaleGestureDetector

3、Bitmap的一下基本用法

4、这里只需要重写view的onDraw就可实现全部功能

可以发现这个其实没什么难度,主要就是一些位置的计算。

为了能便于理解首先把要用到的知识点进行一下梳理

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这三个方法的区别我们就可以简单的进行缩放控制了

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….的执行顺序

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)

可以得出结论,进行变换的顺序是先执行preTranslate(8.0f,7.0f),在执行的preScale(2.0f,3.0f)。即对于一个Matrix的设置中,所有pre….是倒着向后执行的。

2、post…的执行顺序

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)

可以得出结论,进行变换的顺序是先执行postScale(2.0f,3.0f),在执行的postTranslate(8.0f,7.0f)。即对于一个Matrix的设置中,所有post….是顺着向前执行的。

这里主要知道set…和post…方法就行,因为只用到了这两个。

自我理解其实和scrollTo、scrollBy类似。

2、GestureDetector与ScaleGestureDetector

GestureDetector主要用于识别一些特定手势,只要调用GestureDetector.onTouchEvent()把MotionEvent传递进去就可以了

ScaleGestureDetector用于处理缩放的攻击类用法和GestureDetector类似

3、Bitmap的一下基本用法参考: 关于bitmap你不知道的一些事

了解一下bitmap的注意事项即可

下面开始正式画这个选座的功能了

1、画座位:

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

/**

* 如果第一次进入 使座位图居中

*/

if (mViewH != 0 && mViewW != 0&&isFrist) {

isFrist = false;

matrix.setTranslate(-(mViewW-getMeasuredWidth())/2, 0);

}

/**

* 画座位

*/

drawSeat(canvas);

/**

* 画排数

*/

drawText(canvas);

/**

* 画缩略图

*/

drawOverView(canvas);

/**

* 画缩略图选择区域

*/

drawOvewRect(canvas);

}

private void drawSeat(Canvas canvas) {

float zoom = getMatrixScaleX();

scale1 = zoom;

tranlateX = getTranslateX();

tranlateY = getTranslateY();

/**

* 使用两层for循环来画出所有座位

*/

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);

/**

* 根据位置信息画不同的位置图片

*/

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循环,一层画行,一层画列

另外要注意的就是当前位置的计算 top = (当前位置i)(座位图标大小SeatHight 它本身的缩放比scale*缩放时的缩放比scale1)+(当前位置i* 垂直方向的间距mSpaceY*缩放时的缩放比scale1)+垂直方向移动是的移动距离

2、画排数

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、缩略图中的红色区域

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()

tempViewW = getMeasuredWidth();

}else{

tempViewW = mViewW;

}

if(getMeasuredHeight()

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、手指移动时跟随移动

@Override

public boolean onTouchEvent(MotionEvent event) {

/**

* 缩放事件交由ScaleGestureDetector处理

*/

scaleGestureDetector.onTouchEvent(event);

/**

* 移动和点击事件交由GestureDetector处理

*/

gestureDetector.onTouchEvent(event);

return true;

}

/**

* 移动事件 这里只是简单判断了一下,需要更细致的进行条件判断

*/

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缩放比例继续执行一次从而导致变成2.000001之类的数从而使

//判断条件一直为真从而不会执行缩小动作

//判断相乘大于2 可以是当前获得的缩放比例即使是1.9999之类的数如果继续放大即使乘以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;

}

至此这个比较粗糙的选座功能就实现了,有时间会继续优化下细节问题。

下面两个demo都比较给力我就不上传demo了。

参考资料:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

java电影票选座_Android自定义view实现电影票在线选座功能相关推荐

  1. java 手写签名_Android 自定义View手写签名并保存图片

    GIF压缩有问题,运行很顺滑!!! 1.自定义View--支持设置画笔颜色,画笔宽度,画板颜色,清除画板,检查是否有签名,保存画板图片(复制粘贴可直接使用) /*** Created by YyyyQ ...

  2. Android仿支付宝UI功能开发,Android 自定义view仿支付宝咻一咻功能

    支付宝上有一个咻一咻的功能,就是点击图片后四周有水波纹的这种效果,今天也写一个类似的功能. 效果如下所示: 思路: 就是几个圆的半径不断在变大,这个可以使用动画缩放实现,还有透明动画 还有就是这是好几 ...

  3. android wear支付宝6,Android自定义View仿支付宝输入六位密码功能

    跟选择银行卡界面类似,也是用一个PopupWindow,不过输入密码界面是一个自定义view,当输入六位密码完成后用回调在Activity中获取到输入的密码并以Toast显示密码.效果图如下: 自定义 ...

  4. android java 圆角_Android自定义View实现带4圆角或者2圆角的效果

    1 问题 实现任意view经过自定义带4圆角或者2圆角的效果 2 原理 1) 实现view 4圆角 我们只需要把左边的图嵌入到右边里面去,最终显示左边的图就行. 2) 实现view上2圆角 我们只需要 ...

  5. android绘制心形_Android自定义View系列(一)——打造一个爱心进度条

    写作原因:Android进阶过程中有一个绕不开的话题--自定义View.这一块是安卓程序员更好地实现功能自主化必须迈出的一步.下面这个系列博主将通过实现几个例子来认识安卓自定义View的方法.从自定义 ...

  6. android app自动更新界面_Android自定义view之模仿登录界面文本输入框(华为云APP)...

    好久不见!!!!!,最近终于挤出时间来更新文章了,废话不多说,直接开始. 效果图如下: 01 分析 1.组合多个控件完成此输入框静态效果 2.hint值上浮下潜动画 3.一些功能 02 步骤 01 自 ...

  7. 安卓仿苹果音量调节_android自定义view仿照MIUI中音量控制效果

    先看效果图: 这就是miui中的音量效果图,实现思路是自定义视图,绘制圆环,然后设置进度显示. 核心代码在onDraw中实现如下: @Override protected void onDraw(Ca ...

  8. java雪花纷飞_分析自定义view的实现过程-实现雪花飞舞效果(转载有改动)

    声明:本文源码出自实现雪花飞舞效果(有改动)主要通过这篇文来分析自定义view的实现过程. 没事时,比较喜欢上网看看一些新的东西,泡在网上的日子就是一个很不错的网站. 下面开始了,哈哈.^_^ 大家都 ...

  9. android canvas绘制圆角_Android自定义View撸一个渐变的温度指示器(TmepView)

    秦子帅明确目标,每天进步一点点..... 作者 |  andy 地址 |  blog.csdn.net/Andy_l1/article/details/82910061 1.概述 自定义View对需要 ...

最新文章

  1. 一个同步github上fork过来的项目的批处理脚本
  2. Java数据结构——解析算术表达式
  3. 美媒评全球十家增速最快IT办事公司 当当网居首
  4. Ubuntu 12.04 下编译Android 4.0.3
  5. VTK:创建字母频率的条形图用法实战
  6. 电脑桌面便签小工具_iPhone12系列售价曝光,苹果手机上有什么好用的便签记事本软件推荐吗...
  7. scala中处理json数据
  8. [渝粤教育] 西南科技大学 理论力学 在线考试复习资料
  9. python时间模块的使用
  10. 2db多少功率_话筒的灵敏度:-58dB+(-)2dB表示什么意思,数字大的好,还是小的好呢?...
  11. c语言标准库函数system,C 库函数
  12. winform根据字符串生成HTML静态页
  13. Django 开发收银系统六
  14. HTML 语法练习---常见标签
  15. 再见丑陋的 SwaggerUI,这款开源的API文档生成神器界面更炫酷,逼格更高
  16. 关于联想小新16pro无网络问题_雪雪专享篇(安装网卡驱动篇)
  17. IO编程,真的很简单
  18. 产品经理的工作职责是什么,需要具备什么能力?
  19. 嵌入式数据库BERKELEY DB 之dubbo实战
  20. Mac 搭建本地SVN,并使用Cornerstone管理svn

热门文章

  1. MySQL设置数据库隔离级别
  2. django入门学习
  3. 07图示围墙lisp_大神门有没有CASSlisp程序将房屋注记放在右上角?大神门有没 爱问知识人...
  4. c语言485通讯源程序,51单片机485通信实验C语言源代码实现
  5. vc不能添加文件的小工具
  6. IDEA setup 选择目录安装不了_在 Ubuntu 18.04 中安装几个常用软件(一)
  7. 终极算法——第三章:符号学派:休谟的归纳问题
  8. mysql sql注入工具_SQL注入工具实践
  9. tensorflow2.0 新特性 + kaggle练习
  10. 用 WPF 写的颜色拾取器