国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android單點(diǎn)觸控實(shí)現(xiàn)圖片平移、縮放、旋轉(zhuǎn)功能

2020-04-11 10:48:18
字體:
供稿:網(wǎng)友

相信大家使用多點(diǎn)對(duì)圖片進(jìn)行縮放,平移的操作很熟悉了,大部分大圖的瀏覽都具有此功能,有些app還可以對(duì)圖片進(jìn)行旋轉(zhuǎn)操作,QQ的大圖瀏覽就可以對(duì)圖片進(jìn)行旋轉(zhuǎn)操作,大家都知道對(duì)圖片進(jìn)行縮放,平移,旋轉(zhuǎn)等操作可以使用Matrix來實(shí)現(xiàn),Matrix就是一個(gè)3X3的矩陣,對(duì)圖片的處理可分為四個(gè)基礎(chǔ)變換操作,Translate(平移變換)、Rotate(旋轉(zhuǎn)變換)、Scale (縮放變換)、Skew(錯(cuò)切變換),如果大家對(duì)Matrix不太了解的話可以看看這篇文章(點(diǎn)擊查看),作者對(duì)每一種Matrix的變換寫的很清楚,但是如果使用一個(gè)手指對(duì)圖片進(jìn)行縮放,平移,旋轉(zhuǎn)等操作大家是否了解呢,其實(shí)單手指操作跟多手指操作差不多,當(dāng)然也是使用Matrix來實(shí)現(xiàn)的,無非是在縮放比例和旋轉(zhuǎn)角度的計(jì)算上面有些不一樣,也許你會(huì)有疑問,多點(diǎn)操作圖片縮放旋轉(zhuǎn)是兩個(gè)手指操作,平移的時(shí)候是一個(gè)手指操作,那么你單手在圖片即平移,又縮放旋轉(zhuǎn)難道不會(huì)有沖突嗎?是的,這樣子肯定是不行的,我們必須將平移和縮放旋轉(zhuǎn)進(jìn)行分開。如下圖


圖片外面的框是一個(gè)邊框,如果我們手指觸摸的是上面的藍(lán)色小圖標(biāo)我們就對(duì)其進(jìn)行縮放旋轉(zhuǎn)操作,如果是觸摸到其他的區(qū)域我們就對(duì)其進(jìn)行平移操作,這樣就避免了上面所說的沖突問題,這里對(duì)圖片的平移操作并沒有使用Matrix來實(shí)現(xiàn),而是使用layout()方法來對(duì)其進(jìn)行位置的變換。

計(jì)算縮放比例比較簡單,使用手指移動(dòng)的點(diǎn)到圖片所在中心點(diǎn)的距離除以圖片對(duì)角線的一半就是縮放比例了,接下來就計(jì)算旋轉(zhuǎn)角度,如下圖


preMove是手指移動(dòng)前一個(gè)點(diǎn),curMove就是當(dāng)前手指所在的點(diǎn),還有一個(gè)中心點(diǎn)center,知道三個(gè)點(diǎn)求旋轉(zhuǎn)的夾角是不是很簡單呢,就是線段a和線段c的一個(gè)夾角,假設(shè)夾角為o,  o的余弦值 cos o = (a * a + c * c - b * b) / (2 * a * c), 知道余弦值夾角就出來了,但是這里還有一個(gè)問題,我們?cè)谑褂肕atrix對(duì)圖片進(jìn)行旋轉(zhuǎn)的時(shí)候需要區(qū)別順時(shí)針旋轉(zhuǎn)還是逆時(shí)針旋轉(zhuǎn),順時(shí)針旋轉(zhuǎn)角度為正,所以上面我們只求出了旋轉(zhuǎn)的角度,并不知道是順時(shí)針還是逆時(shí)針。

具體怎么求是順時(shí)針角度還是逆時(shí)針角度呢?有些同學(xué)可能會(huì)根據(jù)curMove和ProMove的x ,y 的大小來判斷,比如上面的圖中,如果curMove.x > proMove.x則為順時(shí)針,否則為逆時(shí)針,這當(dāng)然是一種辦法,可是你想過這種方法只適合在第二象限,在第一,第三,第四象限這樣子判斷就不行了,當(dāng)然你可以判斷當(dāng)前的點(diǎn)在第幾象限,然后在不同的象限采用不同的判斷,這樣子判斷起來會(huì)很復(fù)雜。

有沒有更加簡單的方法來判斷呢?答案是肯定的,我們可以使用數(shù)學(xué)中的向量叉乘來判斷。假如向量A(x1, y1)和向量B(x2, y2),我們可以使用向量叉乘 |A X B| = x1*y2 - x2*y1 = |A|×|B|×sin(向量A到B的夾角), 所以這個(gè)值的正負(fù)也就是A到B旋轉(zhuǎn)角sin值的正負(fù), 順時(shí)針旋轉(zhuǎn)角度0~180,sin>0, 順時(shí)針旋轉(zhuǎn)角度180~360或者說逆時(shí)針旋轉(zhuǎn)0~180,sin<0, 所以我們可以用個(gè)center到proMove的向量 叉乘 center到curMove的向量來判斷是順時(shí)針旋轉(zhuǎn)還是逆時(shí)針旋轉(zhuǎn)。

接下來我們就開始動(dòng)手實(shí)現(xiàn)此功能,我們采用一個(gè)自定義的View來實(shí)現(xiàn),這里就叫SingleTouchView,直接繼承View, 從上面的圖中我們可以定義出一些自定義的屬性,比如用于縮放的圖片,控制縮放旋轉(zhuǎn)的小圖標(biāo),圖片邊框的顏色等,我定義了如下的屬性

<declare-styleable name="SingleTouchView">   <attr name="src" format="reference" />      <!-- 用于縮放旋轉(zhuǎn)的圖標(biāo) -->   <attr name="editable" format="boolean"/>     <!-- 是否處于可編輯狀態(tài) -->   <attr name="frameColor" format="color" />     <!-- 邊框顏色 -->   <attr name="frameWidth" format="dimension" />   <!-- 邊框線寬度 -->   <attr name="framePadding" format="dimension" />  <!-- 邊框與圖片的間距 -->   <attr name="degree" format="float" />       <!-- 旋轉(zhuǎn)角度 -->   <attr name="scale" format="float" />       <!-- 縮放比例 -->   <attr name="controlDrawable" format="reference"/> <!-- 控制圖標(biāo) -->   <attr name="controlLocation">           <!-- 控制圖標(biāo)的位置 -->     <enum name="left_top" value="0" />     <enum name="right_top" value="1" />     <enum name="right_bottom" value="2" />     <enum name="left_bottom" value="3" />   </attr> </declare-styleable> 

接下來就是自定義SingleTouchView的代碼,代碼有點(diǎn)長,注釋還是蠻詳細(xì)的

package com.example.singletouchview;  import java.util.Arrays; import java.util.Collections; import java.util.List;  import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.Point; import android.graphics.PointF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.FloatMath; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup;  /**  * 單手對(duì)圖片進(jìn)行縮放,旋轉(zhuǎn),平移操作,詳情請(qǐng)查看  *  * @blog http://blog.csdn.net/xiaanming/article/details/42833893  *  * @author xiaanming  *  */ public class SingleTouchView extends View {   /**    * 圖片的最大縮放比例    */   public static final float MAX_SCALE = 4.0f;      /**    * 圖片的最小縮放比例    */   public static final float MIN_SCALE = 0.3f;      /**    * 控制縮放,旋轉(zhuǎn)圖標(biāo)所在四個(gè)點(diǎn)得位置    */   public static final int LEFT_TOP = 0;   public static final int RIGHT_TOP = 1;   public static final int RIGHT_BOTTOM = 2;   public static final int LEFT_BOTTOM = 3;      /**    * 一些默認(rèn)的常量    */   public static final int DEFAULT_FRAME_PADDING = 8;   public static final int DEFAULT_FRAME_WIDTH = 2;   public static final int DEFAULT_FRAME_COLOR = Color.WHITE;   public static final float DEFAULT_SCALE = 1.0f;   public static final float DEFAULT_DEGREE = 0;   public static final int DEFAULT_CONTROL_LOCATION = RIGHT_TOP;   public static final boolean DEFAULT_EDITABLE = true;   public static final int DEFAULT_OTHER_DRAWABLE_WIDTH = 50;   public static final int DEFAULT_OTHER_DRAWABLE_HEIGHT = 50;            /**    * 用于旋轉(zhuǎn)縮放的Bitmap    */   private Bitmap mBitmap;      /**    * SingleTouchView的中心點(diǎn)坐標(biāo),相對(duì)于其父類布局而言的    */   private PointF mCenterPoint = new PointF();      /**    * View的寬度和高度,隨著圖片的旋轉(zhuǎn)而變化(不包括控制旋轉(zhuǎn),縮放圖片的寬高)    */   private int mViewWidth, mViewHeight;      /**    * 圖片的旋轉(zhuǎn)角度    */   private float mDegree = DEFAULT_DEGREE;      /**    * 圖片的縮放比例    */   private float mScale = DEFAULT_SCALE;      /**    * 用于縮放,旋轉(zhuǎn),平移的矩陣    */   private Matrix matrix = new Matrix();      /**    * SingleTouchView距離父類布局的左間距    */   private int mViewPaddingLeft;      /**    * SingleTouchView距離父類布局的上間距    */   private int mViewPaddingTop;      /**    * 圖片四個(gè)點(diǎn)坐標(biāo)    */   private Point mLTPoint;   private Point mRTPoint;   private Point mRBPoint;   private Point mLBPoint;      /**    * 用于縮放,旋轉(zhuǎn)的控制點(diǎn)的坐標(biāo)    */   private Point mControlPoint = new Point();      /**    * 用于縮放,旋轉(zhuǎn)的圖標(biāo)    */   private Drawable controlDrawable;      /**    * 縮放,旋轉(zhuǎn)圖標(biāo)的寬和高    */   private int mDrawableWidth, mDrawableHeight;      /**    * 畫外圍框的Path    */   private Path mPath = new Path();      /**    * 畫外圍框的畫筆    */   private Paint mPaint ;      /**    * 初始狀態(tài)    */   public static final int STATUS_INIT = 0;      /**    * 拖動(dòng)狀態(tài)    */   public static final int STATUS_DRAG = 1;      /**    * 旋轉(zhuǎn)或者放大狀態(tài)    */   public static final int STATUS_ROTATE_ZOOM = 2;       /**    * 當(dāng)前所處的狀態(tài)    */   private int mStatus = STATUS_INIT;      /**    * 外邊框與圖片之間的間距, 單位是dip    */   private int framePadding = DEFAULT_FRAME_PADDING;      /**    * 外邊框顏色    */   private int frameColor = DEFAULT_FRAME_COLOR;      /**    * 外邊框線條粗細(xì), 單位是 dip    */   private int frameWidth = DEFAULT_FRAME_WIDTH;      /**    * 是否處于可以縮放,平移,旋轉(zhuǎn)狀態(tài)    */   private boolean isEditable = DEFAULT_EDITABLE;      private DisplayMetrics metrics;         private PointF mPreMovePointF = new PointF();   private PointF mCurMovePointF = new PointF();      /**    * 圖片在旋轉(zhuǎn)時(shí)x方向的偏移量    */   private int offsetX;   /**    * 圖片在旋轉(zhuǎn)時(shí)y方向的偏移量    */   private int offsetY;      /**    * 控制圖標(biāo)所在的位置(比如左上,右上,左下,右下)    */   private int controlLocation = DEFAULT_CONTROL_LOCATION;       public SingleTouchView(Context context, AttributeSet attrs) {     this(context, attrs, 0);   }    public SingleTouchView(Context context) {     this(context, null);   }    public SingleTouchView(Context context, AttributeSet attrs, int defStyle) {     super(context, attrs, defStyle);     obtainStyledAttributes(attrs);     init();   }      /**    * 獲取自定義屬性    * @param attrs    */   private void obtainStyledAttributes(AttributeSet attrs){     metrics = getContext().getResources().getDisplayMetrics();     framePadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_FRAME_PADDING, metrics);     frameWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_FRAME_WIDTH, metrics);          TypedArray mTypedArray = getContext().obtainStyledAttributes(attrs,         R.styleable.SingleTouchView);          Drawable srcDrawble = mTypedArray.getDrawable(R.styleable.SingleTouchView_src);     mBitmap = drawable2Bitmap(srcDrawble);          framePadding = mTypedArray.getDimensionPixelSize(R.styleable.SingleTouchView_framePadding, framePadding);     frameWidth = mTypedArray.getDimensionPixelSize(R.styleable.SingleTouchView_frameWidth, frameWidth);     frameColor = mTypedArray.getColor(R.styleable.SingleTouchView_frameColor, DEFAULT_FRAME_COLOR);     mScale = mTypedArray.getFloat(R.styleable.SingleTouchView_scale, DEFAULT_SCALE);     mDegree = mTypedArray.getFloat(R.styleable.SingleTouchView_degree, DEFAULT_DEGREE);     controlDrawable = mTypedArray.getDrawable(R.styleable.SingleTouchView_controlDrawable);     controlLocation = mTypedArray.getInt(R.styleable.SingleTouchView_controlLocation, DEFAULT_CONTROL_LOCATION);     isEditable = mTypedArray.getBoolean(R.styleable.SingleTouchView_editable, DEFAULT_EDITABLE);          mTypedArray.recycle();        }         private void init(){     mPaint = new Paint();     mPaint.setAntiAlias(true);     mPaint.setColor(frameColor);     mPaint.setStrokeWidth(frameWidth);     mPaint.setStyle(Style.STROKE);          if(controlDrawable == null){       controlDrawable = getContext().getResources().getDrawable(R.drawable.st_rotate_icon);     }          mDrawableWidth = controlDrawable.getIntrinsicWidth();     mDrawableHeight = controlDrawable.getIntrinsicHeight();          transformDraw();    }         @Override   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {     super.onMeasure(widthMeasureSpec, heightMeasureSpec);          //獲取SingleTouchView所在父布局的中心點(diǎn)     ViewGroup mViewGroup = (ViewGroup) getParent();     if(null != mViewGroup){       int parentWidth = mViewGroup.getWidth();       int parentHeight = mViewGroup.getHeight();       mCenterPoint.set(parentWidth/2, parentHeight/2);     }   }         /**    * 調(diào)整View的大小,位置    */   private void adjustLayout(){     int actualWidth = mViewWidth + mDrawableWidth;     int actualHeight = mViewHeight + mDrawableHeight;          int newPaddingLeft = (int) (mCenterPoint.x - actualWidth /2);     int newPaddingTop = (int) (mCenterPoint.y - actualHeight/2);          if(mViewPaddingLeft != newPaddingLeft || mViewPaddingTop != newPaddingTop){       mViewPaddingLeft = newPaddingLeft;       mViewPaddingTop = newPaddingTop;        //     layout(newPaddingLeft, newPaddingTop, newPaddingLeft + actualWidth, newPaddingTop + actualHeight);     }          layout(newPaddingLeft, newPaddingTop, newPaddingLeft + actualWidth, newPaddingTop + actualHeight);   }         /**    * 設(shè)置旋轉(zhuǎn)圖    * @param bitmap    */   public void setImageBitamp(Bitmap bitmap){     this.mBitmap = bitmap;     transformDraw();   }         /**    * 設(shè)置旋轉(zhuǎn)圖    * @param drawable    */   public void setImageDrawable(Drawable drawable){     this.mBitmap = drawable2Bitmap(drawable);     transformDraw();   }      /**    * 從Drawable中獲取Bitmap對(duì)象    * @param drawable    * @return    */   private Bitmap drawable2Bitmap(Drawable drawable) {     try {       if (drawable == null) {         return null;       }        if (drawable instanceof BitmapDrawable) {         return ((BitmapDrawable) drawable).getBitmap();       }        int intrinsicWidth = drawable.getIntrinsicWidth();       int intrinsicHeight = drawable.getIntrinsicHeight();       Bitmap bitmap = Bitmap.createBitmap(           intrinsicWidth <= 0 ? DEFAULT_OTHER_DRAWABLE_WIDTH               : intrinsicWidth,           intrinsicHeight <= 0 ? DEFAULT_OTHER_DRAWABLE_HEIGHT               : intrinsicHeight, Config.ARGB_8888);        Canvas canvas = new Canvas(bitmap);       drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());       drawable.draw(canvas);       return bitmap;     } catch (OutOfMemoryError e) {       return null;     }    }      /**    * 根據(jù)id設(shè)置旋轉(zhuǎn)圖    * @param resId    */   public void setImageResource(int resId){     Drawable drawable = getContext().getResources().getDrawable(resId);     setImageDrawable(drawable);   }      @Override   protected void onDraw(Canvas canvas) {     //每次draw之前調(diào)整View的位置和大小     super.onDraw(canvas);          if(mBitmap == null) return;     canvas.drawBitmap(mBitmap, matrix, mPaint);               //處于可編輯狀態(tài)才畫邊框和控制圖標(biāo)     if(isEditable){       mPath.reset();       mPath.moveTo(mLTPoint.x, mLTPoint.y);       mPath.lineTo(mRTPoint.x, mRTPoint.y);       mPath.lineTo(mRBPoint.x, mRBPoint.y);       mPath.lineTo(mLBPoint.x, mLBPoint.y);       mPath.lineTo(mLTPoint.x, mLTPoint.y);       mPath.lineTo(mRTPoint.x, mRTPoint.y);       canvas.drawPath(mPath, mPaint);       //畫旋轉(zhuǎn), 縮放圖標(biāo)              controlDrawable.setBounds(mControlPoint.x - mDrawableWidth / 2,           mControlPoint.y - mDrawableHeight / 2, mControlPoint.x + mDrawableWidth               / 2, mControlPoint.y + mDrawableHeight / 2);       controlDrawable.draw(canvas);     }          adjustLayout();             }            /**    * 設(shè)置Matrix, 強(qiáng)制刷新    */   private void transformDraw(){     if(mBitmap == null) return;     int bitmapWidth = (int)(mBitmap.getWidth() * mScale);     int bitmapHeight = (int)(mBitmap.getHeight()* mScale);     computeRect(-framePadding, -framePadding, bitmapWidth + framePadding, bitmapHeight + framePadding, mDegree);          //設(shè)置縮放比例     matrix.setScale(mScale, mScale);     //繞著圖片中心進(jìn)行旋轉(zhuǎn)     matrix.postRotate(mDegree % 360, bitmapWidth/2, bitmapHeight/2);     //設(shè)置畫該圖片的起始點(diǎn)     matrix.postTranslate(offsetX + mDrawableWidth/2, offsetY + mDrawableHeight/2);          adjustLayout();   }         public boolean onTouchEvent(MotionEvent event) {     if(!isEditable){       return super.onTouchEvent(event);     }     switch (event.getAction() ) {     case MotionEvent.ACTION_DOWN:       mPreMovePointF.set(event.getX() + mViewPaddingLeft, event.getY() + mViewPaddingTop);              mStatus = JudgeStatus(event.getX(), event.getY());        break;     case MotionEvent.ACTION_UP:       mStatus = STATUS_INIT;       break;     case MotionEvent.ACTION_MOVE:       mCurMovePointF.set(event.getX() + mViewPaddingLeft, event.getY() + mViewPaddingTop);       if (mStatus == STATUS_ROTATE_ZOOM) {         float scale = 1f;                  int halfBitmapWidth = mBitmap.getWidth() / 2;         int halfBitmapHeight = mBitmap.getHeight() /2 ;                  //圖片某個(gè)點(diǎn)到圖片中心的距離         float bitmapToCenterDistance = FloatMath.sqrt(halfBitmapWidth * halfBitmapWidth + halfBitmapHeight * halfBitmapHeight);                  //移動(dòng)的點(diǎn)到圖片中心的距離         float moveToCenterDistance = distance4PointF(mCenterPoint, mCurMovePointF);                  //計(jì)算縮放比例         scale = moveToCenterDistance / bitmapToCenterDistance;                           //縮放比例的界限判斷         if (scale <= MIN_SCALE) {           scale = MIN_SCALE;         } else if (scale >= MAX_SCALE) {           scale = MAX_SCALE;         }                           // 角度         double a = distance4PointF(mCenterPoint, mPreMovePointF);         double b = distance4PointF(mPreMovePointF, mCurMovePointF);         double c = distance4PointF(mCenterPoint, mCurMovePointF);                  double cosb = (a * a + c * c - b * b) / (2 * a * c);                  if (cosb >= 1) {           cosb = 1f;         }                  double radian = Math.acos(cosb);         float newDegree = (float) radianToDegree(radian);                  //center -> proMove的向量, 我們使用PointF來實(shí)現(xiàn)         PointF centerToProMove = new PointF((mPreMovePointF.x - mCenterPoint.x), (mPreMovePointF.y - mCenterPoint.y));                  //center -> curMove 的向量          PointF centerToCurMove = new PointF((mCurMovePointF.x - mCenterPoint.x), (mCurMovePointF.y - mCenterPoint.y));                  //向量叉乘結(jié)果, 如果結(jié)果為負(fù)數(shù), 表示為逆時(shí)針, 結(jié)果為正數(shù)表示順時(shí)針         float result = centerToProMove.x * centerToCurMove.y - centerToProMove.y * centerToCurMove.x;          if (result < 0) {           newDegree = -newDegree;         }                   mDegree = mDegree + newDegree;         mScale = scale;                  transformDraw();       }       else if (mStatus == STATUS_DRAG) {         // 修改中心點(diǎn)         mCenterPoint.x += mCurMovePointF.x - mPreMovePointF.x;         mCenterPoint.y += mCurMovePointF.y - mPreMovePointF.y;                  System.out.println(this + "move = " + mCenterPoint);                  adjustLayout();       }              mPreMovePointF.set(mCurMovePointF);       break;     }     return true;   }            /**    * 獲取四個(gè)點(diǎn)和View的大小    * @param left    * @param top    * @param right    * @param bottom    * @param degree    */   private void computeRect(int left, int top, int right, int bottom, float degree){     Point lt = new Point(left, top);     Point rt = new Point(right, top);     Point rb = new Point(right, bottom);     Point lb = new Point(left, bottom);     Point cp = new Point((left + right) / 2, (top + bottom) / 2);     mLTPoint = obtainRoationPoint(cp, lt, degree);     mRTPoint = obtainRoationPoint(cp, rt, degree);     mRBPoint = obtainRoationPoint(cp, rb, degree);     mLBPoint = obtainRoationPoint(cp, lb, degree);          //計(jì)算X坐標(biāo)最大的值和最小的值     int maxCoordinateX = getMaxValue(mLTPoint.x, mRTPoint.x, mRBPoint.x, mLBPoint.x);     int minCoordinateX = getMinValue(mLTPoint.x, mRTPoint.x, mRBPoint.x, mLBPoint.x);;          mViewWidth = maxCoordinateX - minCoordinateX ;               //計(jì)算Y坐標(biāo)最大的值和最小的值     int maxCoordinateY = getMaxValue(mLTPoint.y, mRTPoint.y, mRBPoint.y, mLBPoint.y);     int minCoordinateY = getMinValue(mLTPoint.y, mRTPoint.y, mRBPoint.y, mLBPoint.y);      mViewHeight = maxCoordinateY - minCoordinateY ;               //View中心點(diǎn)的坐標(biāo)     Point viewCenterPoint = new Point((maxCoordinateX + minCoordinateX) / 2, (maxCoordinateY + minCoordinateY) / 2);          offsetX = mViewWidth / 2 - viewCenterPoint.x;     offsetY = mViewHeight / 2 - viewCenterPoint.y;                    int halfDrawableWidth = mDrawableWidth / 2;     int halfDrawableHeight = mDrawableHeight /2;          //將Bitmap的四個(gè)點(diǎn)的X的坐標(biāo)移動(dòng)offsetX + halfDrawableWidth     mLTPoint.x += (offsetX + halfDrawableWidth);     mRTPoint.x += (offsetX + halfDrawableWidth);     mRBPoint.x += (offsetX + halfDrawableWidth);     mLBPoint.x += (offsetX + halfDrawableWidth);      //將Bitmap的四個(gè)點(diǎn)的Y坐標(biāo)移動(dòng)offsetY + halfDrawableHeight     mLTPoint.y += (offsetY + halfDrawableHeight);     mRTPoint.y += (offsetY + halfDrawableHeight);     mRBPoint.y += (offsetY + halfDrawableHeight);     mLBPoint.y += (offsetY + halfDrawableHeight);          mControlPoint = LocationToPoint(controlLocation);   }         /**    * 根據(jù)位置判斷控制圖標(biāo)處于那個(gè)點(diǎn)    * @return    */   private Point LocationToPoint(int location){     switch(location){     case LEFT_TOP:       return mLTPoint;     case RIGHT_TOP:       return mRTPoint;     case RIGHT_BOTTOM:       return mRBPoint;     case LEFT_BOTTOM:       return mLBPoint;     }     return mLTPoint;   }         /**    * 獲取變長參數(shù)最大的值    * @param array    * @return    */   public int getMaxValue(Integer...array){     List<Integer> list = Arrays.asList(array);     Collections.sort(list);     return list.get(list.size() -1);   }         /**    * 獲取變長參數(shù)最大的值    * @param array    * @return    */   public int getMinValue(Integer...array){     List<Integer> list = Arrays.asList(array);     Collections.sort(list);     return list.get(0);   }            /**    * 獲取旋轉(zhuǎn)某個(gè)角度之后的點(diǎn)    * @param viewCenter    * @param source    * @param degree    * @return    */   public static Point obtainRoationPoint(Point center, Point source, float degree) {     //兩者之間的距離     Point disPoint = new Point();     disPoint.x = source.x - center.x;     disPoint.y = source.y - center.y;          //沒旋轉(zhuǎn)之前的弧度     double originRadian = 0;      //沒旋轉(zhuǎn)之前的角度     double originDegree = 0;          //旋轉(zhuǎn)之后的角度     double resultDegree = 0;          //旋轉(zhuǎn)之后的弧度     double resultRadian = 0;          //經(jīng)過旋轉(zhuǎn)之后點(diǎn)的坐標(biāo)     Point resultPoint = new Point();          double distance = Math.sqrt(disPoint.x * disPoint.x + disPoint.y * disPoint.y);     if (disPoint.x == 0 && disPoint.y == 0) {       return center;       // 第一象限     } else if (disPoint.x >= 0 && disPoint.y >= 0) {       // 計(jì)算與x正方向的夾角       originRadian = Math.asin(disPoint.y / distance);              // 第二象限     } else if (disPoint.x < 0 && disPoint.y >= 0) {       // 計(jì)算與x正方向的夾角       originRadian = Math.asin(Math.abs(disPoint.x) / distance);       originRadian = originRadian + Math.PI / 2;              // 第三象限     } else if (disPoint.x < 0 && disPoint.y < 0) {       // 計(jì)算與x正方向的夾角       originRadian = Math.asin(Math.abs(disPoint.y) / distance);       originRadian = originRadian + Math.PI;     } else if (disPoint.x >= 0 && disPoint.y < 0) {       // 計(jì)算與x正方向的夾角       originRadian = Math.asin(disPoint.x / distance);       originRadian = originRadian + Math.PI * 3 / 2;     }          // 弧度換算成角度     originDegree = radianToDegree(originRadian);     resultDegree = originDegree + degree;          // 角度轉(zhuǎn)弧度     resultRadian = degreeToRadian(resultDegree);          resultPoint.x = (int) Math.round(distance * Math.cos(resultRadian));     resultPoint.y = (int) Math.round(distance * Math.sin(resultRadian));     resultPoint.x += center.x;     resultPoint.y += center.y;      return resultPoint;   }    /**    * 弧度換算成角度    *    * @return    */   public static double radianToDegree(double radian) {     return radian * 180 / Math.PI;   }       /**    * 角度換算成弧度    * @param degree    * @return    */   public static double degreeToRadian(double degree) {     return degree * Math.PI / 180;   }      /**    * 根據(jù)點(diǎn)擊的位置判斷是否點(diǎn)中控制旋轉(zhuǎn),縮放的圖片, 初略的計(jì)算    * @param x    * @param y    * @return    */   private int JudgeStatus(float x, float y){     PointF touchPoint = new PointF(x, y);     PointF controlPointF = new PointF(mControlPoint);          //點(diǎn)擊的點(diǎn)到控制旋轉(zhuǎn),縮放點(diǎn)的距離     float distanceToControl = distance4PointF(touchPoint, controlPointF);          //如果兩者之間的距離小于 控制圖標(biāo)的寬度,高度的最小值,則認(rèn)為點(diǎn)中了控制圖標(biāo)     if(distanceToControl < Math.min(mDrawableWidth/2, mDrawableHeight/2)){       return STATUS_ROTATE_ZOOM;     }          return STATUS_DRAG;        }         public float getImageDegree() {     return mDegree;   }    /**    * 設(shè)置圖片旋轉(zhuǎn)角度    * @param degree    */   public void setImageDegree(float degree) {     if(this.mDegree != degree){       this.mDegree = degree;       transformDraw();     }   }    public float getImageScale() {     return mScale;   }    /**    * 設(shè)置圖片縮放比例    * @param scale    */   public void setImageScale(float scale) {     if(this.mScale != scale){       this.mScale = scale;       transformDraw();     };   }       public Drawable getControlDrawable() {     return controlDrawable;   }    /**    * 設(shè)置控制圖標(biāo)    * @param drawable    */   public void setControlDrawable(Drawable drawable) {     this.controlDrawable = drawable;     mDrawableWidth = drawable.getIntrinsicWidth();     mDrawableHeight = drawable.getIntrinsicHeight();     transformDraw();   }    public int getFramePadding() {     return framePadding;   }    public void setFramePadding(int framePadding) {     if(this.framePadding == framePadding)       return;     this.framePadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, framePadding, metrics);     transformDraw();   }    public int getFrameColor() {     return frameColor;   }    public void setFrameColor(int frameColor) {     if(this.frameColor == frameColor)       return;     this.frameColor = frameColor;     mPaint.setColor(frameColor);     invalidate();   }    public int getFrameWidth() {     return frameWidth;   }    public void setFrameWidth(int frameWidth) {     if(this.frameWidth == frameWidth)        return;     this.frameWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, frameWidth, metrics);     mPaint.setStrokeWidth(frameWidth);     invalidate();   }      /**    * 設(shè)置控制圖標(biāo)的位置, 設(shè)置的值只能選擇LEFT_TOP ,RIGHT_TOP, RIGHT_BOTTOM,LEFT_BOTTOM    * @param controlLocation    */   public void setControlLocation(int location) {     if(this.controlLocation == location)       return;     this.controlLocation = location;     transformDraw();   }    public int getControlLocation() {     return controlLocation;   }          public PointF getCenterPoint() {     return mCenterPoint;   }    /**    * 設(shè)置圖片中心點(diǎn)位置,相對(duì)于父布局而言    * @param mCenterPoint    */   public void setCenterPoint(PointF mCenterPoint) {     this.mCenterPoint = mCenterPoint;     adjustLayout();   }       public boolean isEditable() {     return isEditable;   }    /**    * 設(shè)置是否處于可縮放,平移,旋轉(zhuǎn)狀態(tài)    * @param isEditable    */   public void setEditable(boolean isEditable) {     this.isEditable = isEditable;     invalidate();   }    /**    * 兩個(gè)點(diǎn)之間的距離    * @param x1    * @param y1    * @param x2    * @param y2    * @return    */   private float distance4PointF(PointF pf1, PointF pf2) {     float disX = pf2.x - pf1.x;     float disY = pf2.y - pf1.y;     return FloatMath.sqrt(disX * disX + disY * disY);   }     } 

為了讓SingleTouchView居中,我們需要獲取父布局的長和寬,我們?cè)趏nMeasure()中來獲取,當(dāng)然如果我們不需要居中顯示我們也可以調(diào)用setCenterPoint方法來設(shè)置其位置.

onTouchEvent()方法中,mPreMovePointF和mCurMovePointF點(diǎn)的坐標(biāo)不是相對(duì)View來的,首先如果采用相對(duì)于View本身(getX(), getY())肯定是不行的,假如你往x軸方向移動(dòng)一段距離,這個(gè)SingleTouchView也會(huì)移動(dòng)一段距離,mPreMovePointF和mCurMovePointF點(diǎn)和SingleTouchView的中心點(diǎn)都是會(huì)變化的,所以在移動(dòng)的時(shí)候會(huì)不停的閃爍,相對(duì)于屏幕左上角(getRawX(), getRawY())是可以的,但是由于mCenterPointF并不是相對(duì)于屏幕的坐標(biāo),而是相對(duì)于父類布局的,所以將需要將mPreMovePointF和mCurMovePointF的坐標(biāo)換算成相對(duì)于父類布局。

這里面最重要的方法就是transformDraw()方法,它主要做的是調(diào)用computeRect()方法求出圖片的四個(gè)角的坐標(biāo)點(diǎn)mLTPoint,mRTPoint,mRBPoint,mLBPoint(這幾點(diǎn)的坐標(biāo)是相對(duì)于SingleTouchView本身)和SingleTouchView的寬度和高度,以及控制圖標(biāo)所在圖標(biāo)四個(gè)點(diǎn)中的哪個(gè)點(diǎn)。如下圖


上面的圖忽略了控制旋轉(zhuǎn),縮放圖標(biāo),黑色的框是開始的View的大小,而經(jīng)過旋轉(zhuǎn)之后,VIew的大小變成最外層的虛線框了,所以我們需要調(diào)用adjustLayout()方法來重新設(shè)置View的位置和大小,接下來就是設(shè)置Matrix了

matrix.setScale(mScale, mScale); matrix.postRotate(mDegree % 360, bitmapWidth/2, bitmapHeight/2); matrix.postTranslate(offsetX + mDrawableWidth/2, offsetY + mDrawableHeight/2); 

先設(shè)置縮放比例, 然后設(shè)置圍繞圖片的中心點(diǎn)旋轉(zhuǎn)mDegree,postTranslate( float dx, float dy)方法是畫該圖片的起始點(diǎn)進(jìn)行平移dx, dy個(gè)單位,而不是移動(dòng)到dx,dy這個(gè)點(diǎn)。
接下來就來使用,定義一個(gè)xml布局文件

<merge xmlns:android="http://schemas.android.com/apk/res/android"   xmlns:tools="http://schemas.android.com/tools">      <com.example.singletouchview.SingleTouchView       xmlns:app="http://schemas.android.com/apk/res-auto"       android:id="@+id/SingleTouchView"       android:layout_width="match_parent"       android:layout_height="match_parent"       app:scale="1.2"       app:src="@drawable/scale"       app:frameColor="#0022ff"       app:controlLocation="right_top"/>  </merge> 

在里面寫了一些自定義的屬性,寫自定義屬性之前需要聲明xmlns:app="http://schemas.android.com/apk/res-auto",Activity只需要設(shè)置這個(gè)布局文件作為ContentView就行了,接下來運(yùn)行程序看下效果。


怎么樣?效果還是不錯(cuò)的吧,如果我們想去掉藍(lán)色的邊框和用于縮放旋轉(zhuǎn)的小圖標(biāo),直接調(diào)用setEditable(false)就可以了,設(shè)置了setEditable(false)該View的點(diǎn)擊事件,長按事件是正常的。

以上就是本文的全部內(nèi)容,希望對(duì)大家學(xué)習(xí)Android軟件編程有所幫助。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 台江县| 肥西县| 新巴尔虎左旗| 昂仁县| 龙州县| 湖北省| 原阳县| 浮山县| 牙克石市| 惠东县| 玉田县| 汉阴县| 文登市| 江华| 龙海市| 图木舒克市| 庄浪县| 尼勒克县| 九江市| 合川市| 阿瓦提县| 翼城县| 喜德县| 玛纳斯县| 新巴尔虎右旗| 定襄县| 镇宁| 保山市| 泸溪县| 定远县| 灵丘县| 密云县| 高台县| 凤台县| 榆林市| 阜城县| 元氏县| 广宁县| 鄱阳县| 莱芜市| 贡山|