Android自定义控件篇 图片进行平移,缩放,旋转

一、自定义属性

    <declare-styleable name="SingleTouchView">
        <attr name="src" format="reference" />            <!-- 用于缩放旋转的图标 -->
        <attr name="editable" format="boolean"/>          <!-- 是否处于可编辑状态 -->
        <attr name="frameColor" format="color" />         <!-- 边框颜色 -->
        <attr name="frameWidth" format="dimension" />     <!-- 边框线宽度 -->
        <attr name="framePadding" format="dimension" />   <!-- 边框与图片的间距 -->
        <attr name="degree" format="float" />             <!-- 旋转角度 -->
        <attr name="scale" format="float" />              <!-- 缩放比例 -->
        <attr name="controlDrawable" format="reference"/> <!-- 控制图标 -->
        <attr name="controlLocation">                     <!-- 控制图标的位置 -->
            <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>

二、自定义控件代码

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;
 
/**
 * 单手对图片进行缩放,旋转,平移操作,详情请查看
 */
public class SingleTouchView extends View {
	/**
	 * 图片的最大缩放比例
	 */
	public static final float MAX_SCALE = 4.0f;
	
	/**
	 * 图片的最小缩放比例
	 */
	public static final float MIN_SCALE = 0.3f;
	
	/**
	 * 控制缩放,旋转图标所在四个点得位置
	 */
	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;
	
	/**
	 * 一些默认的常量
	 */
	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;
	
	
	
	/**
	 * 用于旋转缩放的Bitmap
	 */
	private Bitmap mBitmap;
	
	/**
	 * SingleTouchView的中心点坐标,相对于其父类布局而言的
	 */
	private PointF mCenterPoint = new PointF();
	
	/**
	 * View的宽度和高度,随着图片的旋转而变化(不包括控制旋转,缩放图片的宽高)
	 */
	private int mViewWidth, mViewHeight;
	
	/**
	 * 图片的旋转角度
	 */
	private float mDegree = DEFAULT_DEGREE;
	
	/**
	 * 图片的缩放比例
	 */
	private float mScale = DEFAULT_SCALE;
	
	/**
	 * 用于缩放,旋转,平移的矩阵
	 */
	private Matrix matrix = new Matrix();
	
	/**
	 * SingleTouchView距离父类布局的左间距
	 */
	private int mViewPaddingLeft;
	
	/**
	 * SingleTouchView距离父类布局的上间距
	 */
	private int mViewPaddingTop;
	
	/**
	 * 图片四个点坐标
	 */
	private Point mLTPoint;
	private Point mRTPoint;
	private Point mRBPoint;
	private Point mLBPoint;
	
	/**
	 * 用于缩放,旋转的控制点的坐标
	 */
	private Point mControlPoint = new Point();
	
	/**
	 * 用于缩放,旋转的图标
	 */
	private Drawable controlDrawable;
	
	/**
	 * 缩放,旋转图标的宽和高
	 */
	private int mDrawableWidth, mDrawableHeight;
	
	/**
	 * 画外围框的Path
	 */
	private Path mPath = new Path();
	
	/**
	 * 画外围框的画笔
	 */
	private Paint mPaint ;
	
	/**
	 * 初始状态
	 */
	public static final int STATUS_INIT = 0;
	
	/**
	 * 拖动状态
	 */
	public static final int STATUS_DRAG = 1;
	
	/**
	 * 旋转或者放大状态
	 */
	public static final int STATUS_ROTATE_ZOOM = 2; 
	
	/**
	 * 当前所处的状态
	 */
	private int mStatus = STATUS_INIT;
	
	/**
	 * 外边框与图片之间的间距, 单位是dip
	 */
	private int framePadding = DEFAULT_FRAME_PADDING;
	
	/**
	 * 外边框颜色
	 */
	private int frameColor = DEFAULT_FRAME_COLOR;
	
	/**
	 * 外边框线条粗细, 单位是 dip
	 */
	private int frameWidth = DEFAULT_FRAME_WIDTH;
	
	/**
	 * 是否处于可以缩放,平移,旋转状态
	 */
	private boolean isEditable = DEFAULT_EDITABLE;
	
	private DisplayMetrics metrics;
	
	
	private PointF mPreMovePointF = new PointF();
	private PointF mCurMovePointF = new PointF();
	
	/**
	 * 图片在旋转时x方向的偏移量
	 */
	private int offsetX;
	/**
	 * 图片在旋转时y方向的偏移量
	 */
	private int offsetY;
	
	/**
	 * 控制图标所在的位置(比如左上,右上,左下,右下)
	 */
	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所在父布局的中心点
		ViewGroup mViewGroup = (ViewGroup) getParent();
		if(null != mViewGroup){
			int parentWidth = mViewGroup.getWidth();
			int parentHeight = mViewGroup.getHeight();
			mCenterPoint.set(parentWidth/2, parentHeight/2);
		}
	}
	
	
	/**
	 * 调整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);
	}
	
	
	/**
	 * 设置旋转图
	 * @param bitmap
	 */
	public void setImageBitamp(Bitmap bitmap){
		this.mBitmap = bitmap;
		transformDraw();
	}
	
	
	/**
	 * 设置旋转图
	 * @param drawable
	 */
	public void setImageDrawable(Drawable drawable){
		this.mBitmap = drawable2Bitmap(drawable);
		transformDraw();
	}
	
	/**
	 * 从Drawable中获取Bitmap对象
	 * @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;
		}
 
	}
	
	/**
	 * 根据id设置旋转图
	 * @param resId
	 */
	public void setImageResource(int resId){
		Drawable drawable = getContext().getResources().getDrawable(resId);
		setImageDrawable(drawable);
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		//每次draw之前调整View的位置和大小
		super.onDraw(canvas);
		
		if(mBitmap == null) return;
		canvas.drawBitmap(mBitmap, matrix, mPaint);
		
		
		//处于可编辑状态才画边框和控制图标
		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);
			//画旋转, 缩放图标
			
			controlDrawable.setBounds(mControlPoint.x - mDrawableWidth / 2,
					mControlPoint.y - mDrawableHeight / 2, mControlPoint.x + mDrawableWidth
							/ 2, mControlPoint.y + mDrawableHeight / 2);
			controlDrawable.draw(canvas);
		}
		
		adjustLayout();
		
		
	}
	
	
	
	/**
	 * 设置Matrix, 强制刷新
	 */
	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);
		
		//设置缩放比例
		matrix.setScale(mScale, mScale);
		//绕着图片中心进行旋转
		matrix.postRotate(mDegree % 360, bitmapWidth/2, bitmapHeight/2);
		//设置画该图片的起始点
		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 ;
				
				//图片某个点到图片中心的距离
				float bitmapToCenterDistance = FloatMath.sqrt(halfBitmapWidth * halfBitmapWidth + halfBitmapHeight * halfBitmapHeight);
				
				//移动的点到图片中心的距离
				float moveToCenterDistance = distance4PointF(mCenterPoint, mCurMovePointF);
				
				//计算缩放比例
				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来实现
				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));
				
				//向量叉乘结果, 如果结果为负数, 表示为逆时针, 结果为正数表示顺时针
				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) {
				// 修改中心点
				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;
	}
	
	
	
	/**
	 * 获取四个点和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);
		
		//计算X坐标最大的值和最小的值
		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 ;
		
		
		//计算Y坐标最大的值和最小的值
		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中心点的坐标
		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的四个点的X的坐标移动offsetX + halfDrawableWidth
		mLTPoint.x += (offsetX + halfDrawableWidth);
		mRTPoint.x += (offsetX + halfDrawableWidth);
		mRBPoint.x += (offsetX + halfDrawableWidth);
		mLBPoint.x += (offsetX + halfDrawableWidth);
 
		//将Bitmap的四个点的Y坐标移动offsetY + halfDrawableHeight
		mLTPoint.y += (offsetY + halfDrawableHeight);
		mRTPoint.y += (offsetY + halfDrawableHeight);
		mRBPoint.y += (offsetY + halfDrawableHeight);
		mLBPoint.y += (offsetY + halfDrawableHeight);
		
		mControlPoint = LocationToPoint(controlLocation);
	}
	
	
	/**
	 * 根据位置判断控制图标处于那个点
	 * @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;
	}
	
	
	/**
	 * 获取变长参数最大的值
	 * @param array
	 * @return
	 */
	public int getMaxValue(Integer...array){
		List<Integer> list = Arrays.asList(array);
		Collections.sort(list);
		return list.get(list.size() -1);
	}
	
	
	/**
	 * 获取变长参数最大的值
	 * @param array
	 * @return
	 */
	public int getMinValue(Integer...array){
		List<Integer> list = Arrays.asList(array);
		Collections.sort(list);
		return list.get(0);
	}
	
	
	
	/**
	 * 获取旋转某个角度之后的点
	 * @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;
		
		//没旋转之前的弧度
		double originRadian = 0;
 
		//没旋转之前的角度
		double originDegree = 0;
		
		//旋转之后的角度
		double resultDegree = 0;
		
		//旋转之后的弧度
		double resultRadian = 0;
		
		//经过旋转之后点的坐标
		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) {
			// 计算与x正方向的夹角
			originRadian = Math.asin(disPoint.y / distance);
			
			// 第二象限
		} else if (disPoint.x < 0 && disPoint.y >= 0) {
			// 计算与x正方向的夹角
			originRadian = Math.asin(Math.abs(disPoint.x) / distance);
			originRadian = originRadian + Math.PI / 2;
			
			// 第三象限
		} else if (disPoint.x < 0 && disPoint.y < 0) {
			// 计算与x正方向的夹角
			originRadian = Math.asin(Math.abs(disPoint.y) / distance);
			originRadian = originRadian + Math.PI;
		} else if (disPoint.x >= 0 && disPoint.y < 0) {
			// 计算与x正方向的夹角
			originRadian = Math.asin(disPoint.x / distance);
			originRadian = originRadian + Math.PI * 3 / 2;
		}
		
		// 弧度换算成角度
		originDegree = radianToDegree(originRadian);
		resultDegree = originDegree + degree;
		
		// 角度转弧度
		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;
	}
	
	/**
	 * 根据点击的位置判断是否点中控制旋转,缩放的图片, 初略的计算
	 * @param x
	 * @param y
	 * @return
	 */
	private int JudgeStatus(float x, float y){
		PointF touchPoint = new PointF(x, y);
		PointF controlPointF = new PointF(mControlPoint);
		
		//点击的点到控制旋转,缩放点的距离
		float distanceToControl = distance4PointF(touchPoint, controlPointF);
		
		//如果两者之间的距离小于 控制图标的宽度,高度的最小值,则认为点中了控制图标
		if(distanceToControl < Math.min(mDrawableWidth/2, mDrawableHeight/2)){
			return STATUS_ROTATE_ZOOM;
		}
		
		return STATUS_DRAG;
		
	}
	
	
	public float getImageDegree() {
		return mDegree;
	}
 
	/**
	 * 设置图片旋转角度
	 * @param degree
	 */
	public void setImageDegree(float degree) {
		if(this.mDegree != degree){
			this.mDegree = degree;
			transformDraw();
		}
	}
 
	public float getImageScale() {
		return mScale;
	}
 
	/**
	 * 设置图片缩放比例
	 * @param scale
	 */
	public void setImageScale(float scale) {
		if(this.mScale != scale){
			this.mScale = scale;
			transformDraw();
		};
	}
	
 
	public Drawable getControlDrawable() {
		return controlDrawable;
	}
 
	/**
	 * 设置控制图标
	 * @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();
	}
	
	/**
	 * 设置控制图标的位置, 设置的值只能选择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;
	}
 
	/**
	 * 设置图片中心点位置,相对于父布局而言
	 * @param mCenterPoint
	 */
	public void setCenterPoint(PointF mCenterPoint) {
		this.mCenterPoint = mCenterPoint;
		adjustLayout();
	}
	
 
	public boolean isEditable() {
		return isEditable;
	}
 
	/**
	 * 设置是否处于可缩放,平移,旋转状态
	 * @param isEditable
	 */
	public void setEditable(boolean isEditable) {
		this.isEditable = isEditable;
		invalidate();
	}
 
	/**
	 * 两个点之间的距离
	 * @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);
	}
	
 
}

三、使用

<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/photo"
            app:frameColor="#0022ff"
            app:controlLocation="right_top"/>
</merge>

View复用: http://files.cnblogs.com/files/andlp/com.maneater.picscreator.zip

文档:https://www.cnblogs.com/andlp/p/5972260.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在Android平台上,可以使用一些常用的手势操作来实现图片平移、双指缩放和双击放大缩小。 首先,为了实现平移功能,可以使用触摸事件的ACTION_MOVE来处理手指在屏幕上滑动的操作。通过计算滑动距离,可以改变图片的位置从而实现平移效果。 其次,双指缩放可以使用触摸事件的ACTION_POINTER_DOWN和ACTION_POINTER_UP来追踪并处理触摸屏幕上第二个手指按下和松开的操作。在处理时,可以通过计算两个手指触摸点之间的距离来改变图片缩放比例,从而实现双指缩放效果。 最后,双击放大缩小可以通过设置一个双击的监听器来实现。在双击监听器的回调方法中,可以根据当前的图片缩放状态来判断是进行放大操作还是缩小操作。并根据需要更改图片缩放比例,实现双击放大缩小功能。 在实现上述功能时,可以使用Android提供的一些核心类来辅助开发,如View、MotionEvent、GestureDetector等。同时,还可以结合自定义View和动画效果来提升用户体验,使图片平移缩放和双击动作更加流畅和自然。 总之,通过合理运用触摸事件、手势识别、双击监听器等技术,可以在Android上实现图片平移、双指缩放和双击放大缩小功能,从而提升用户与应用程序的交互体验。 ### 回答2: 在Android开发中,可以通过使用ImageView控件来实现对图片平移、双指缩放和双击放大缩小操作。 1. 平移:可以通过使用Matrix类对ImageView进行变换来实现图片平移。首先,我们需要为ImageView设置一个OnTouchListener来监听用户的手势操作。在手势操作的回调方法中,可以通过获取手指按下和移动的位置差来计算出需要平移的距离,然后通过设置Matrix的平移矩阵来实现图片平移效果。 2. 双指缩放:可以通过GestureDetector类来监听双指缩放手势。在手势操作的回调方法中,可以获取到两个手指的起始位置和当前位置,通过计算两个手指之间的距离差来判断缩放的比例。然后,再通过设置Matrix的缩放矩阵来实现图片缩放效果。需要注意的是,在进行缩放操作时,还需要考虑到手指中心点的位置变化。 3. 双击放大缩小:也可以通过GestureDetector类来监听双击手势。在手势操作的回调方法中,可以判断是否发生了双击事件,并获取到双击的位置。当双击事件发生时,可以通过判断当前图片缩放比例来决定是放大还是缩小。通过设置Matrix的缩放矩阵和平移矩阵来实现图片的放大缩小效果。 需要注意的是,为了实现图片平移、双指缩放和双击放大缩小操作,我们需要对ImageView进行一系列的监听和处理操作。同时,为了方便管理和控制这些操作,可以将相关的代码封装成一个自定义的图片控件,并在该控件进行处理。 ### 回答3: Android 提供了多种方法来实现图片平移、双指缩放和双击放大缩小功能。 首先,要实现图片平移,我们可以使用 GestureDetector 类。可以通过重写 onScroll() 方法来检测用户的滑动手势,然后将图片的位置进行相应的调整,从而实现平移效果。 其次,要实现双指缩放功能,我们可以借助 ScaleGestureDetector 类。通过重写 onScale() 方法,我们可以获取用户的缩放手势,并根据手势的缩放比例调整图片的尺寸,从而实现双指缩放的效果。 最后,要实现双击放大缩小功能,我们可以使用 GestureDetector 类。可以通过重写 onDoubleTap() 方法来检测用户的双击手势,然后根据当前图片的尺寸和双击事件的位置,来判断是进行放大还是缩小操作,最后将图片的尺寸进行相应的调整,实现双击放大缩小的效果。 需要注意的是,以上的功能实现是基于 Android 的原生 API,还有其他第三方库也提供了更便捷的方法来实现这些功能,例如 PhotoView 库,它提供了更多灵活的选项和交互效果,可以更加方便地实现图片平移缩放和双击操作。 综上所述,Android 针对图片平移、双指缩放和双击放大缩小,可以通过重写相应的手势监听方法或使用第三方库来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

其子昱舟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值