1.触摸事件的处理
当手指触摸屏幕时获取触摸点的个数,当触摸点只有一个时,当做平移操作处理,当触摸点为两个时则为放大缩小操作。平移操作记录下当前按下的点的坐标就行,如果是放大缩小操作,则计算出两点间的中点,后续放大和缩小以改点为中点进行.代码片段:
if(ev.getActionMasked()==MotionEvent.ACTION_DOWN||ev.getActionMasked()==MotionEvent.ACTION_POINTER_DOWN){
if(!scroller.isFinished())
scroller.abortAnimation();
if(ev.getPointerCount()==1){
downX=ev.getX();
downY=ev.getY();
isZooming=false;
}else if(ev.getPointerCount()==2){
preScale=scale;
preTranslationX=translationX;//记录之前的x平移值
preTranslationY=translationY;//记录前一次的y平移值
touchDistance=(float) Math.hypot(ev.getX(1) - ev.getX(0), ev.getY(1) - ev.getY(0));//计算两个触摸点之间的距离
touchCenterX=(ev.getX(0) + ev.getX(1)) / 2.0f;//计算中点x 坐标
touchCenterY=(ev.getY(0) + ev.getY(1)) / 2.0f;//计算y的中点坐标
isZooming=true;
isMoving=false;
}
}
2.平移后计算X和Y的偏移量计算
上图中缩放点为x,y对应代码中的touchCenterX和touchCenterY,x1和Y2对应之前的边界坐标(左边和上边,上图标的有问题)在程序中对应preTranslationX和,preTranslationY。x2和Y2为缩放后新的边界坐标(左边和上边)对应代码中的translationX和translationY。代码如下
if(ev.getPointerCount()==2){
//计算新的放大系数和移动的边界值
scale = (float) Math.hypot(ev.getX(1) - ev.getX(0), ev.getY(1) - ev.getY(0)) / touchDistance *preScale;
translationX = touchCenterX - (touchCenterX - preTranslationX) * (scale / preScale);
translationY = touchCenterY - (touchCenterY - preTranslationY) * (scale / preScale);
updateMinMax(scale);
this.invalidate();
}
3.每次放大缩小后边界值计算
放大缩小后要重新计算最大最下边界值,用于在拖动图片时防止图片被拖出边界
private void updateMinMax(float scale) {
int dw = (int) (getWidth()-scaleImgW)/2;
int dh = (int) (getHeight()-scaleImgH)/2;
if(scaleImgW*scale<getWidth()){
minX=maxX=-(int) (dw*scale-(int) (getWidth()-scaleImgW*scale)/2);
}else{
if(scaleImgW<getWidth()){
maxX=-dw*scale;
minX=getWidth()-scaleImgW*scale+maxX;
}else{
maxX=0;
minX=getWidth()-scaleImgW*scale;
}
}
if(scaleImgH*scale<getHeight()){
minY=maxY=-(int) (dh*scale-(int) (getHeight()-scaleImgH*scale)/2);
}else{
if(scaleImgH<getHeight()){
maxY=-dh*scale;
minY=getHeight()-scaleImgH*scale+maxY;
}else{
maxY=0;
minY=getHeight()-scaleImgH*scale;
}
}
}
4.拖动过程中边界值得检查
private void checkMinMax(boolean zoom) {
float moveToX = translationX;
float moveToY = translationY;
updateMinMax(scale);
if (translationX < minX) {
moveToX = minX;
} else if (translationX > maxX) {
moveToX = maxX;
}
if (translationY < minY) {
moveToY = minY;
} else if (translationY > maxY) {
moveToY = maxY;
}
animateTo(scale, moveToX, moveToY);
}
5.拖出边界后要进行还原
<span style="font-size:14px;">//放大缩小和平移动画
private void animateTo(float newScale, float newTx, float newTy, int duration) {
if (scale == newScale && translationX == newTx && translationY == newTy) {
return;
}
animateToScale = newScale;
animateToX = newTx;
animateToY = newTy;
animationStartTime = System.currentTimeMillis();
imageMoveAnimation = new AnimatorSet();
imageMoveAnimation.playTogether(ObjectAnimator.ofFloat(this, "animationValue", 0, 1));
imageMoveAnimation.setInterpolator(interpolator);
imageMoveAnimation.setDuration(duration);
imageMoveAnimation.addListener(new AnimatorListenerAdapterProxy() {
@Override
public void onAnimationEnd(Animator animation) {
imageMoveAnimation = null;
ZoomImageView.this.invalidate();
}
});
imageMoveAnimation.start();
}</span>
6.重新进行绘制
<span style="font-size:14px;">//保存画布
canvas.save();
//先进行平移操作
canvas.translate(translationX, translationY);
//再进行缩放
canvas.scale(scale,scale);
//进行旋转
canvas.rotate(rolateDegree, getWidth()/2, getHeight()/2);
//根据图片的长和宽计算绘制区域
float scaleX=(float)imgW/getWidth();
float scaleY=(float)imgH/getHeight();
float scale=scaleX>scaleY?scaleX:scaleY;
scaleImgW=(int) (imgW/scale);
scaleImgH=(int) (imgH/scale);
imgDrawable.setBounds(caculateDrawRect(scaleImgW, scaleImgH));
imgDrawable.draw(canvas);
canvas.restore();</span>
效果图: