今天我们来做一个可以根据手势进行缩放的图片查看器。
首先是需要用到的几个东西:
1.Matrix (用来实现图片的缩放)
Matrix可以实现对图形进行平移,缩放,旋转等操作。在这里,我们主要用到的是平移和缩放。
postTranslate(dx,dy) 对图形进行平移
postScale(sx,sy,px,py)对图形进行缩放
2.ScaleGestureDetector (对缩放手势的监听)
ScaleGestureDetector gestureDetector=new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
float factor=scaleGestureDetector.getScaleFactor();//缩放因子,这个是根据两个手指来计算缩放的倍数
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
//缩放开始,这里返回true表示要接收这个事件,必须为true,onScale才能执行
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
//缩放结束
}
});
以上就是实现本文功能的两个重点了。下面我们就开始写代码了
首先要先写一个view继承Imageview
public class PhotoView extends AppCompatImageView {
private Context mContext;
public PhotoView(Context context) {
super(context);
//在代码中new PhotoView()的时候会调用这个初始化
mContext=context;
init();
}
public PhotoView(Context context, AttributeSet attrs) {
super(context, attrs);
//在布局中使用会调用这个初始化
mContext=context;
init();
}
void init(){
//初始化
}
}
这样,自定义photoview的主结构就出来了。接下来就是对图形的处理了。
我们无法保证所加载的图片的宽高,所以我们第一步需要对图片进行放大或缩小。使图片适应view的大小,并将图片移动到屏幕中间。下面是代码实现
public class PhotoView extends AppCompatImageView implements ViewTreeObserver.OnGlobalLayoutListener {
//这里我们需要实现 ViewTreeObserver.OnGlobalLayoutListener,这个接口用于测量大小
...省略构造函数
void init(){
mMatrix=new Matrix();//初始化
setScaleType(ScaleType.MATRIX);//设置类型,使图片能支持Matrix
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
//需要在这里添加监听。
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
}
下面是实现
@Override
public void onGlobalLayout() {
if(isFristLoad){
//因为onGlobalLayout可能会回调多次,所以这里要加个布尔值,控制下面的代码只执行一次,这里也可以改成把监听移除。
isFristLoad=false;
int width=getWidth();//获取view的宽度
int height=getHeight();//获取view的高度
Drawable drawable=getDrawable();//获取加载的图片
if(drawable==null){
return;
}
int imgWidth=drawable.getIntrinsicWidth();//获取图片的宽度
int imgHeight=drawable.getIntrinsicHeight();//获取图片的宽度
//下面是计算出图片的大小和view大小相差的倍数,然后对图片进行缩放,
if(imgWidth>width||imgHeight>height){
//当图片比view大的,需要缩小
//图片宽或高超过view
if(imgWidth>imgHeight){
//乘1f是为了不让scale变成int值
//mScale是一个全局的变量,存放初始的缩放倍数
mScale=width*1f/imgWidth*1f;
}else {
mScale=height*1f/imgHeight*1f;
}
}else if(imgWidth<width&&imgHeight<height) {
//当图片比view小,需要放大
//图片宽高都小于view
if(imgWidth>imgHeight){
mScale=width*1f/imgWidth*1f;
}else {
mScale=height*1f/imgHeight*1f;
}
}
//这个方法文章开头有介绍过,设置移动到view中间
mMatrix.postTranslate(width/2-imgWidth/2,height/2-imgHeight/2);
//根据前面计算出的倍数,对图片进行缩放,使图片适应view大小
mMatrix.postScale(mScale,mScale,width/2,height/2);
//将修改应用到图片上。
setImageMatrix(mMatrix);
}
}
接下来就是对手势的监听了,上面我们已经说过ScaleGestureDetector得基础实现,下面我们就用它来实现手势缩放.
void init(){
mMatrix=new Matrix();
setScaleType(ScaleType.MATRIX);
gestureDetector=new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
float factor=scaleGestureDetector.getScaleFactor();//缩放因子
float scale=getScale();//获取Matrix当前的缩放数值
if(scale*factor>MAX_SCALE||scale*factor<=mScale*0.8){
//MAX_SCALE是一个全局变量,为了防止图片放大太大,我们设置一个值,图片放大到这个值就不能再放大。
//后面那个0.8的作用我们在下面的onScaleBegin里面做说明
}else {
//根据手指获取缩放中心,然后根据缩放因子进行缩放
//原理和我们上面的一样
mMatrix.postScale(factor,factor,scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY());
setImageMatrix(mMatrix);
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
//这里是缩放结束后做的操作,
//上面onScale那个0.8就是配合下面这段代码的
//下面是获取当前缩放值,mScale就是我们第一步里面图片适应了view时候的倍数
//上面的0.8是我们允许在缩放过程中,图片缩小到原来80%的大小
//然后再缩放结束后,回复到100%的大小。这样一来,图片就会又一个回弹的效果(参考微信)
float scale=getScale();//获取当前缩放值
if(scale<mScale){
//恢复到100%大小
float s=mScale/scale;
mMatrix.postScale(s,s,getWidth()/2,getHeight()/2);
setImageMatrix(mMatrix);
}
}
});
}
//需要在onTouchEvent里面改成调用gestureDetector.onTouchEvent(event);
@Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
这样,我们就实现了一个手势缩放的图片查看器。下面是完整代码
public class PhotoView extends AppCompatImageView implements ViewTreeObserver.OnGlobalLayoutListener {
//最大缩放倍数
private static int MAX_SCALE=5;
//默认缩放倍数,初始化后会根据图片大小改变这个值
private static float mScale=1f;
private Context mContext;
private ScaleGestureDetector gestureDetector;
private Matrix mMatrix;
//首次加载,避免onGlobalLayout多次执行
private boolean isFristLoad=true;
public PhotoView(Context context) {
super(context);
mContext=context;
init();
}
public PhotoView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext=context;
init();
}
void init(){
mMatrix=new Matrix();
setScaleType(ScaleType.MATRIX);
gestureDetector=new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
float factor=scaleGestureDetector.getScaleFactor();
float scale=getScale();
if(scale*factor>MAX_SCALE||scale*factor<=mScale*0.8){
}else {
mMatrix.postScale(factor,factor,getWidth()/2,getHeight()/2);
setImageMatrix(mMatrix);
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
float scale=getScale();
if(scale<mScale){
float s=mScale/scale;
mMatrix.postScale(s,s,getWidth()/2,getHeight()/2);
setImageMatrix(mMatrix);
}
}
});
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
//获取当前缩放值
private float getScale() {
float[] values = new float[9];
mMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
@Override
public void onGlobalLayout() {
if(isFristLoad){
isFristLoad=false;
int width=getWidth();
int height=getHeight();
Drawable drawable=getDrawable();
if(drawable==null){
return;
}
int imgWidth=drawable.getIntrinsicWidth();
int imgHeight=drawable.getIntrinsicHeight();
if(imgWidth>width||imgHeight>height){
//图片宽或高超过view
if(imgWidth>imgHeight){
//乘1f是为了不让scale变成int值
mScale=width*1f/imgWidth*1f;
}else {
mScale=height*1f/imgHeight*1f;
}
}else if(imgWidth<width&&imgHeight<height) {
//图片宽高都小于view
if(imgWidth>imgHeight){
mScale=width*1f/imgWidth*1f;
}else {
mScale=height*1f/imgHeight*1f;
}
}
mMatrix.postTranslate(width/2-imgWidth/2,height/2-imgHeight/2);
mMatrix.postScale(mScale,mScale,width/2,height/2);
setImageMatrix(mMatrix);
}
}
}
网友评论