今天做了个图片缩放的特效,类似于在图片查看器中用两根手指向外滑动和向内滑动实现的对图片的放大与缩小的效果,由于没有GIF制作软件,故只给出静态截图, 效果如下:原图:
放大后的效果:
万花镜式效果
效果2
上文的图片都只是静态截图,实际场景会随用户的手指动作的改变而改变。下面介绍实现过程
本实例的XML文件中只给出了一个imageview的组件:
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/tm3" />
mainactivity中的java代码如下:
public class MainActivity extends Activity {
ImageView img;
LinearLayout layout;
int imgWidth, imgHeight;//图片的宽与高
Point imgpoint;//图片左上角坐标
Bitmap bitmap, newbitmap;
//操作接口
SurfaceHolder surfaceHolder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取图片资源
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.tm3);
imgWidth = bitmap.getWidth();
imgHeight = bitmap.getHeight();
//实例化左上角坐标
imgpoint = new Point();
// 新建区域
layout = new LinearLayout(this);
// 铺满屏幕
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
//获取区域的宽度与高度
layout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
// 默认调用两次,这里只让他执行一次回调
boolean flag = true;
@Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
if (flag) {
flag = false;
imgpoint.x = layout.getMeasuredWidth() / 2 - imgWidth / 2;
imgpoint.y = layout.getMeasuredHeight() / 2 - imgHeight / 2;
}
}
});
// 加入自定义surfaceview
layout.addView(new MySurfaceview(getApplicationContext()));
setContentView(layout);
}
/**
* 自定义surfaceView
*
* @author fangwei
*
*/
class MySurfaceview extends SurfaceView implements Callback {
public MySurfaceview(Context context) {
super(context);
// TODO Auto-generated constructor stub
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
//可聚焦
setFocusable(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
// 缩放比1
setImg(1f);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}
/**
* 绘制图片
*/
public void setImg(float scale) {
Canvas canvas = surfaceHolder.lockCanvas();
//******************************
//注销这一行就会出现万花筒特效
canvas.drawRGB(0, 0, 0);
//******************************
//新画笔
Paint paint = new Paint();
Matrix matrix = new Matrix();
// 缩放
matrix.preScale(scale, scale);
newbitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
// 更新
imgWidth = newbitmap.getWidth();
imgHeight = newbitmap.getHeight();
//计算坐标
imgpoint.x=layout.getMeasuredWidth()/2-imgWidth/2;
imgpoint.y=layout.getMeasuredHeight()/2-imgHeight/2;
canvas.translate(imgpoint.x,imgpoint.y);
canvas.drawBitmap(bitmap, matrix, paint);
// 解锁
surfaceHolder.unlockCanvasAndPost(canvas);
}
/**
* 触摸事件
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getPointerCount()) {
// 两根手指触摸
case 2:
float p1 = event.getY(0);
float p2 = event.getY(1);
if (event.getAction() != MotionEvent.ACTION_UP) {
if (p1<p2) {
float temp = p1 ;
p1 = p2 ;
p2 = temp ;
}
// 计算缩放比例
float scale = Math.abs((p1/p2))/2;
setImg(scale);
}
break;
}
return true;
}
}
简要的解释一下:在oncreat方法里用java代码实现一个线性区域,这是为了把自定义的surfaceview塞到里面,然后自定义一个类去继承surfaceview,并实现回调接口,初次显示surfaceview的时候,会调用setImg方法,这是按缩放比为1显示,也就是按原图显示,当监听到触摸事件后,也会调用setImg方法,通过简单的计算可以得出缩放比例,回传给setImg,对图片做相应调整。
关于setImg方法,主要是通过矩阵的变换来控制bitmap的大小,bitmap有是绘画在canvas之上,通过canvas实行的移动机制来实现,(这里的缩放不是固定一个点的缩放,而是类似于放大的特效,所以,它其实就是canvas里的平移,translate)。
有一点我很不明白的就是,在setImg里的那以行我标注的代码,只要不给画布设置背景色,它就会呈现出后两幅图的特效,我叫他万花筒(火影迷),只要给画布设置一个颜色,无论是什么颜色,这种现象就会消失,我想如果不设置画布背景色,它用的就是surfaceview的背景色,那么是不是与这个surfaceview有关系呢,希望睿智的读者朋友们能给我一个答案,解开我心中的迷惑。~~~