Android中实现Bitmap在自定义View中的放大与拖动

一:基本实现思路

基于View类实现自定义View �CMyImageView类。在使用View的Activity类中完成OnTouchListener接口,实现对自定义View的触摸事件监听

放大与拖动

基于单点触控实现Bitmap对象在View上的拖动、并且检测View的边缘,防止拖动过界。基于两个点触控实现Bitmap对象在View上的放大、并且检测放大倍数。基于Matrix对象实现对Bitmap在View上放大与平移变换

Bitmap对象在View中的更新与显示

通过重载onDraw方法,使用canvas实现绘制Bitmap对象、通过view.invalidate()方法实现View的刷新。

MyImageView类的重要方法说明:

initParameters()初始化所有需要用到的参数

setStartPoint()设置图像平移的开始点坐标

setMovePoint()设置图像平移的移动点坐标,然后集合开始点位置,计算它们之间的距离,从而得到Bitmap对象需要平移的两个参数值sx、sy。其中还包括保证图像不会越过View边界的检查代码。

savePreviousResult() 保存当前的平移数据,下次可以继续在次基础上平移Bitmap对象。

zoomIn()根据两个点之间的欧几里德距离,通过初始距离比较,得到放大比例,实现Bitmap在View对象上的放大


Matrix.postScale方法与Matrix.postTranslate方法可以不改变Bitmap对象本身实现平移与放大。


OnTouchListener支持以下的触摸事件处理

ACTION_DOWN事件,记录平移开始点

ACTION_UP事件,结束平移事件处理

ACTION_MOVE事件,记录平移点,计算与开始点距离,实现Bitmap平移,在多点触控时候,计算两点之间的距离,实现图像放大

ACTION_POINTER_DOWN事件,计算两点之间的距离,作为初始距离,实现图像手势放大时候使用。

ACTION_POINTER_UP事件,结束两点触控放大图像处理

二:代码实现

自定义View的在layout中的使用xml如下:

<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context=".MainActivity"><com.example.matrixdemo.MyImageViewandroid:id="@+id/myView"android:layout_width="fill_parent"android:layout_height="fill_parent"android:text="@string/hello_world"/></RelativeLayout>

自定义View类的实现代码如下:

packagecom.example.matrixdemo;importandroid.content.Context;importandroid.graphics.Bitmap;importandroid.graphics.Canvas;importandroid.graphics.Color;importandroid.graphics.Matrix;importandroid.graphics.Paint;importandroid.graphics.Paint.Style;importandroid.graphics.Point;importandroid.graphics.Rect;importandroid.util.AttributeSet;importandroid.view.View;publicclassMyImageViewextendsView{privatePaintmPaint;privateBitmapbitmap;privateMatrixmatrix;//平移开始点与移动点privatePointstartPoint;privatePointmovePoint;privatefloatinitDistance;//记录当前平移距离privateintsx;privateintsy;//保存平移状态privateintoldsx;privateintoldsy;//scalerateprivatefloatwidthRate;privatefloatheightRate;publicMyImageView(Contextcontext){super(context);}publicMyImageView(Contextcontext,AttributeSetattrs){super(context,attrs);}publicvoidsetBitmap(Bitmapbitmap){this.bitmap=bitmap;}privatevoidinitParameters(){//初始化画笔mPaint=newPaint();mPaint.setColor(Color.BLACK);matrix=newMatrix();if(bitmap!=null){floatiw=bitmap.getWidth();floatih=bitmap.getHeight();floatwidth=this.getWidth();floatheight=this.getHeight();//初始放缩比率widthRate=width/iw;heightRate=height/ih;}sx=0;sy=0;oldsx=0;oldsy=0;}publicvoidsetStartPoint(PointstartPoint){this.startPoint=startPoint;}publicvoidsetInitDistance(floatinitDistance){this.initDistance=initDistance;}publicvoidzoomIn(floatdistance){floatrate=distance/this.initDistance;floatiw=bitmap.getWidth();floatih=bitmap.getHeight();floatwidth=this.getWidth();floatheight=this.getHeight();//getscaleratewidthRate=(width/iw)*rate;heightRate=(height/ih)*rate;//makeitsameasviewsizefloatiwr=(width/iw);floatihr=(height/ih);if(iwr>=widthRate){widthRate=(width/iw);}if(ihr>=heightRate){heightRate=(height/ih);}//gotocenteroldsx=(int)((width-widthRate*iw)/2);oldsy=(int)((height-heightRate*ih)/2);}publicvoidsetMovePoint(PointmovePoint){this.movePoint=movePoint;sx=this.movePoint.x-this.startPoint.x;sy=this.movePoint.y-this.startPoint.y;floatiw=bitmap.getWidth();floatih=bitmap.getHeight();//检测边缘intdeltax=(int)((widthRate*iw)-this.getWidth());intdeltay=(int)((heightRate*ih)-this.getHeight());if((sx+this.oldsx)>=0){this.oldsx=0;sx=0;}elseif((sx+this.oldsx)<=-deltax){this.oldsx=-deltax;sx=0;}if((sy+this.oldsy)>=0){this.oldsy=0;this.sy=0;}elseif((sy+this.oldsy)<=-deltay){this.oldsy=-deltay;this.sy=0;}floatwidth=this.getWidth();//初始放缩比率floatiwr=width/iw;if(iwr==widthRate){sx=0;sy=0;oldsx=0;oldsy=0;}}publicvoidsavePreviousResult(){this.oldsx=this.sx+this.oldsx;this.oldsy=this.sy+this.oldsy;//zerosx=0;sy=0;}@OverrideprotectedvoidonDraw(Canvascanvas){if(matrix==null){initParameters();}if(bitmap!=null){matrix.reset();matrix.postScale(widthRate,heightRate);matrix.postTranslate(oldsx+sx,oldsy+sy);canvas.drawBitmap(bitmap,matrix,mPaint);}else{//fillrectRectrect=newRect(0,0,getWidth(),getHeight());mPaint.setAntiAlias(true);mPaint.setColor(Color.BLACK);mPaint.setStyle(Style.FILL_AND_STROKE);canvas.drawRect(rect,mPaint);}}}

MainActivity的代码如下:

packagecom.example.matrixdemo;importandroid.app.Activity;importandroid.graphics.Bitmap;importandroid.graphics.BitmapFactory;importandroid.graphics.Point;importandroid.os.Bundle;importandroid.util.Log;importandroid.view.Menu;importandroid.view.MotionEvent;importandroid.view.View;importandroid.view.View.OnTouchListener;publicclassMainActivityextendsActivityimplementsOnTouchListener{publicstaticfinalintSCALE_MODE=4;publicstaticfinalintTRANSLATION_MODE=2;publicstaticfinalintNULL_MODE=1;privateMyImageViewmyView;privateintmode;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);startMyImageView();}privatevoidstartMyImageView(){myView=(MyImageView)this.findViewById(R.id.myView);Bitmapbitmap=BitmapFactory.decodeResource(this.getResources(),R.drawable.flower_001);myView.setBitmap(bitmap);myView.setOnTouchListener(this);myView.invalidate();}@OverridepublicbooleanonCreateOptionsMenu(Menumenu){getMenuInflater().inflate(R.menu.main,menu);returntrue;}@OverridepublicbooleanonTouch(Viewview,MotionEventevent){Log.i("touchevent","touchx="+event.getX());switch(MotionEvent.ACTION_MASK&event.getAction()){caseMotionEvent.ACTION_DOWN:mode=TRANSLATION_MODE;myView.setStartPoint(newPoint((int)event.getX(),(int)event.getY()));break;caseMotionEvent.ACTION_POINTER_UP:caseMotionEvent.ACTION_OUTSIDE:caseMotionEvent.ACTION_UP:mode=NULL_MODE;myView.savePreviousResult();break;caseMotionEvent.ACTION_POINTER_DOWN:mode=SCALE_MODE;myView.setInitDistance(calculateDistance(event));break;caseMotionEvent.ACTION_MOVE:if(mode==SCALE_MODE){floatdis=calculateDistance(event);myView.zoomIn(dis);}elseif(mode==TRANSLATION_MODE){myView.setMovePoint(newPoint((int)event.getX(),(int)event.getY()));}else{Log.i("unknowmodetag","donothing......");}break;}myView.invalidate();returntrue;}privatefloatcalculateDistance(MotionEventevent){floatdx=event.getX(0)-event.getX(1);floatdy=event.getY(0)-event.getY(1);floatdistance=(float)Math.sqrt(dx*dx+dy*dy);returndistance;}}

运行截图如下:


更多相关文章

  1. 源码分析为什么requestDisallowInterceptTouchEvent(true)能阻止
  2. Android中对象的序列化
  3. EditText组件drawableLeft属性设置的图片和hint设置的文字之间的
  4. Android应用程序窗口(Activity)窗口对象(Window)创建指南
  5. Android设计模式学习之Builder模式
  6. Android(安卓)spinner取值
  7. android解析xml文件的方式(其二)
  8. Android中的内存管理机制
  9. Android(安卓)View体系(一)视图坐标系

随机推荐

  1. android studio2.0解决办法 Plugin is to
  2. android 之JSON
  3. 在android上监听网络状态的变更
  4. Android 判断网络是否可用以及网络类型(WI
  5. android (22)
  6. Android:Galaxy Nexus升级到4.1.2,并root(设
  7. This Android SDK requires Android Deve
  8. Android(1)进程通信基础知识
  9. Android学习之线性布局管理器
  10. Unity与Android通信