欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android自定義view實現(xiàn)電影票在線選座功能

 更新時間:2021年08月31日 11:49:18   作者:_solary  
這篇文章主要為大家詳細(xì)介紹了Android自定義view實現(xiàn)選座功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

先看看電影票在線選座功能實現(xiàn)的效果圖:

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

這個界面主要包括以下幾部分

1、座位
2、左邊的排數(shù)
3、左上方的縮略圖
4、縮略圖中的紅色區(qū)域
5、手指移動時跟隨移動
6、兩個手指縮放時跟隨縮放

主要技術(shù)點

1、矩陣Matrix
2、GestureDetector與ScaleGestureDetector
3、Bitmap的一下基本用法
4、這里只需要重寫view的onDraw就可實現(xiàn)全部功能

可以發(fā)現(xiàn)這個其實沒什么難度,主要就是一些位置的計算。

為了能便于理解首先把要用到的知識點進(jìn)行一下梳理

1、矩陣Matrix

Matrix由3*3矩陣中9個值來決定,我們對Matrix的所有設(shè)置, 就是對這9個值的操作。

{MSCALE_X,MSKEW_X,MTRANS_X,
MSKEW_Y,MSCALE_Y,MTRANS_Y,
MPERSP_0,MPERSP_1,MPERSP_2}

這是矩陣的9個值,看名字也知道他們是什么意思了。

這里主要用到縮放和平移,下面以縮放為例來了解一下縮放的控制
通過Android提供的api我們可以調(diào)用setScale、preScale、postScale來改變MSCALE_X和MSCALE_Y的值達(dá)到縮放的效果

所以只要理解setScale、preScale、postScale這三個方法的區(qū)別我們就可以簡單的進(jìn)行縮放控制了

1、setScale(sx,sy),首先會將該Matrix設(shè)置為對角矩陣,即相當(dāng)于調(diào)用reset()方法,然后在設(shè)置該Matrix的MSCALE_X和MSCALE_Y直接設(shè)置為sx,sy的值
2、preScale(sx,sy),不會重置Matrix,而是直接與Matrix之前的MSCALE_X和MSCALE_Y值結(jié)合起來(相乘),M' = M * S(sx, sy)。
3、postScale(sx,sy),不會重置Matrix,而是直接與Matrix之前的MSCALE_X和MSCALE_Y值結(jié)合起來(相乘),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]+"");

結(jié)果為點坐標(biāo)為(36.0,51.0)
可以得出結(jié)論,進(jìn)行變換的順序是先執(zhí)行preTranslate(8.0f,7.0f),在執(zhí)行的preScale(2.0f,3.0f)。即對于一個Matrix的設(shè)置中,所有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]+"");

結(jié)果為點坐標(biāo)為(28.0,37.0)
可以得出結(jié)論,進(jìn)行變換的順序是先執(zhí)行postScale(2.0f,3.0f),在執(zhí)行的postTranslate(8.0f,7.0f)。即對于一個Matrix的設(shè)置中,所有post….是順著向前執(zhí)行的。

這里主要知道set…和post…方法就行,因為只用到了這兩個。
自我理解其實和scrollTo、scrollBy類似。

2、GestureDetector與ScaleGestureDetector

GestureDetector主要用于識別一些特定手勢,只要調(diào)用GestureDetector.onTouchEvent()把MotionEvent傳遞進(jìn)去就可以了
ScaleGestureDetector用于處理縮放的攻擊類用法和GestureDetector類似

3、Bitmap的一下基本用法
參考: 關(guān)于bitmap你不知道的一些事

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

下面開始正式畫這個選座的功能了

1、畫座位:

@Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 /**
 * 如果第一次進(jìn)入 使座位圖居中
 */
 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),一層畫行,一層畫列

另外要注意的就是當(dāng)前位置的計算 top = (當(dāng)前位置i)(座位圖標(biāo)大小SeatHight 它本身的縮放比scale*縮放時的縮放比scale1)+(當(dāng)前位置i* 垂直方向的間距mSpaceY*縮放時的縮放比scale1)+垂直方向移動是的移動距離

2、畫排數(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、手指移動時跟隨移動

@Override
 public boolean onTouchEvent(MotionEvent event) {

 /**
 * 縮放事件交由ScaleGestureDetector處理
 */
 scaleGestureDetector.onTouchEvent(event);
 /**
 * 移動和點擊事件交由GestureDetector處理
 */
 gestureDetector.onTouchEvent(event);

 return true;
 }
/**
 * 移動事件 這里只是簡單判斷了一下,需要更細(xì)致的進(jìn)行條件判斷
 */
 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;
  /**
  * 選中時進(jìn)行縮放操作
  */
  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會導(dǎo)致獲取的matrix縮放比例繼續(xù)執(zhí)行一次從而導(dǎo)致變成2.000001之類的數(shù)從而使
 //判斷條件一直為真從而不會執(zhí)行縮小動作
 //判斷相乘大于2 可以是當(dāng)前獲得的縮放比例即使是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)化下細(xì)節(jié)問題。

下面兩個demo都比較給力我就不上傳demo了。

參考資料:

Android選座源碼解析

 Andriod 打造炫酷的電影票在線選座控件,1比1還原淘寶電影在線選座功能

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • android實現(xiàn)倒計時功能的方法

    android實現(xiàn)倒計時功能的方法

    這篇文章主要為大家詳細(xì)介紹了兩種android實現(xiàn)倒計時功能的方法,具有一定的實用性,感興趣的小伙伴們可以參考一下
    2016-08-08
  • Android工程:引用另一個Android工程的方法詳解

    Android工程:引用另一個Android工程的方法詳解

    本篇文章是對在Android中引用另一個Android工程的方法進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下
    2013-05-05
  • 關(guān)于RxJava的一些特殊用法小結(jié)

    關(guān)于RxJava的一些特殊用法小結(jié)

    RxJava 是一個響應(yīng)式編程框架,采用觀察者設(shè)計模式。下面這篇文章主要總結(jié)介紹了一些關(guān)于RxJava的特殊用法,需要的朋友可以參考借鑒,下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。
    2017-05-05
  • Android?狀態(tài)管理之Lifecycle淺析

    Android?狀態(tài)管理之Lifecycle淺析

    這篇文章主要介紹了Android?狀態(tài)管理之Lifecycle淺析,Lifecycle主要用于Activity、Fragment這一類具有狀態(tài)的組件的狀態(tài)監(jiān)聽,更多相關(guān)資料介紹需要的小伙伴可以參考下面文章內(nèi)容
    2022-06-06
  • Android實現(xiàn)Recycleview懸浮粘性頭部外加右側(cè)字母導(dǎo)航

    Android實現(xiàn)Recycleview懸浮粘性頭部外加右側(cè)字母導(dǎo)航

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)Recycleview懸浮粘性頭部外加右側(cè)字母導(dǎo)航,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • Android使用ImageView實現(xiàn)支持手勢縮放效果

    Android使用ImageView實現(xiàn)支持手勢縮放效果

    這篇文章主要介紹了Android使用ImageView實現(xiàn)支持手勢縮放效果,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • Android ExpandableListView實現(xiàn)下拉刷新和加載更多效果

    Android ExpandableListView實現(xiàn)下拉刷新和加載更多效果

    這篇文章主要為大家詳細(xì)介紹了Android ExpandableListView實現(xiàn)下拉刷新和加載更多效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • Android中檢查、設(shè)置默認(rèn)程序詳解

    Android中檢查、設(shè)置默認(rèn)程序詳解

    這篇文章主要介紹了Android中檢查、設(shè)置默認(rèn)程序詳解,本文講解了檢測是否有默認(rèn)的程序、如果有默認(rèn)程序、沒有默認(rèn)的程序的情況等內(nèi)容,需要的朋友可以參考下
    2015-01-01
  • Android ANR(Application Not Responding)的分析

    Android ANR(Application Not Responding)的分析

    這篇文章主要介紹了Android ANR(Application Not Responding)的分析的相關(guān)資料,這里說明什么原因出現(xiàn)應(yīng)用程序的強(qiáng)制關(guān)閉,并說明該如何避免,需要的朋友可以參考下
    2017-08-08
  • Android 靜默安裝和智能安裝的實現(xiàn)方法

    Android 靜默安裝和智能安裝的實現(xiàn)方法

    靜默安裝就是無聲無息的在后臺安裝apk,沒有任何界面提示。智能安裝就是有安裝界面,但全部是自動的,不需要用戶去點擊。下面腳本之家小編給大家介紹下Android 靜默安裝和智能安裝的實現(xiàn)方法,感興趣的朋友一起看看吧
    2018-01-01

最新評論