您的位置:首页 > 移动开发 > Android开发

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

2016-07-29 00:00 651 查看
一基本实现思路:

基于View类实现自定义View –MyImageView类。在使用View的Activity类中完成OnTouchListener接口,实现对MotionEvent事件的监听与处理,常见的MotionEvent事件如下:

ACTION_DOWN事件,记录平移开始点
ACTION_UP事件,结束平移事件处理
ACTION_MOVE事件,记录平移点,计算与开始点距离,实现Bitmap平移,在多点触控时候,计算两点之间的距离,实现图像放大
ACTION_POINTER_DOWN事件,计算两点之间的距离,作为初始距离,实现图像手势放大时候使用。
ACTION_POINTER_UP事件,结束两点触控放大图像处理

放大与拖动
基于单点触控实现Bitmap对象在View上的拖动、并且检测View的边缘,防止拖动过界。基于两个点触控实现Bitmap对象在View上的放大、并且检测放大倍数。基于Matrix对象实现对Bitmap在View上放大与平移变换,Matrix对象是android中实现图像几何变换的矩阵,支持平移、放大、缩小、错切、旋转等常见操作。

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

MyImageView类的重要方法说明:
initParameters()初始化所有需要用到的参数
setStartPoint()设置图像平移的开始点坐标

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

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

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

Matrix中关于放大与平移的API

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

二:代码实现

自定义View类使用xml布局如下:

<RelativeLayout xmlns: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.MyImageView
android:id="@+id/myView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="@string/hello_world" />

</RelativeLayout>
自定义View实现代码如下:

package com.example.matrixdemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;

public class MyImageView extends View {
private Paint mPaint;
private Bitmap bitmap;
private Matrix matrix;

// 平移开始点与移动点
private Point startPoint;
private Point movePoint;
private float initDistance;

// 记录当前平移距离
private int sx;
private int sy;

// 保存平移状态
private int oldsx;
private int oldsy;

// scale rate
private float widthRate;
private float heightRate;

public MyImageView(Context context) {
super(context);
}

public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}

private void initParameters() {
// 初始化画笔
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
matrix = new Matrix();
if(bitmap != null)
{
float iw = bitmap.getWidth();
float ih = bitmap.getHeight();
float width = this.getWidth();
float height = this.getHeight();
// 初始放缩比率
widthRate = width / iw;
heightRate = height / ih;
}

sx = 0;
sy = 0;

oldsx = 0;
oldsy = 0;

}

public void setStartPoint(Point startPoint) {
this.startPoint = startPoint;
}

public void setInitDistance(float initDistance) {
this.initDistance = initDistance;
}

public void zoomIn(float distance)
{
float rate = distance / this.initDistance;
float iw = bitmap.getWidth();
float ih = bitmap.getHeight();
float width = this.getWidth();
float height = this.getHeight();
// get scale rate
widthRate = (width / iw ) * rate;
heightRate = (height / ih) * rate;

// make it same as view size
float iwr = (width / iw );
float ihr = (height / ih);
if(iwr >= widthRate)
{
widthRate = (width / iw );
}
if(ihr >= heightRate)
{
heightRate = (height / ih);
}

// go to center
oldsx = (int)((width - widthRate * iw) / 2);
oldsy = (int)((height - heightRate * ih) / 2);
}

public void setMovePoint(Point movePoint) {
this.movePoint = movePoint;
sx = this.movePoint.x - this.startPoint.x;
sy = this.movePoint.y - this.startPoint.y;

float iw = bitmap.getWidth();
float ih = bitmap.getHeight();

// 检测边缘
int deltax = (int)((widthRate * iw) - this.getWidth());
int deltay = (int)((heightRate * ih) - this.getHeight());
if((sx + this.oldsx) >= 0)
{
this.oldsx = 0;
sx = 0;
}
else if((sx + this.oldsx) <= -deltax)
{
this.oldsx = -deltax;
sx = 0;
}

if((sy + this.oldsy) >= 0)
{
this.oldsy = 0;
this.sy = 0;
}
else if((sy + this.oldsy) <= -deltay)
{
this.oldsy = -deltay;
this.sy = 0;
}

float width = this.getWidth();

// 初始放缩比率
float iwr = width / iw;
if(iwr == widthRate)
{
sx = 0;
sy = 0;
oldsx = 0;
oldsy = 0;
}
}

public void savePreviousResult()
{
this.oldsx = this.sx + this.oldsx;
this.oldsy = this.sy + this.oldsy;

// zero
sx = 0;
sy = 0;
}

@Override
protected void onDraw(Canvas canvas) {
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
{
// fill rect
Rect rect = new Rect(0, 0, getWidth(), getHeight());
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Style.FILL_AND_STROKE);
canvas.drawRect(rect, mPaint);
}
}
}
Activity类中实现对View的OnTouchListener监听与MotionEvent事件处理的代码如下:

package com.example.matrixdemo;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class MainActivity extends Activity implements OnTouchListener {

public static final int SCALE_MODE = 4;
public static final int TRANSLATION_MODE = 2;
public static final int NULL_MODE = 1;
private MyImageView myView;
private int mode;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startMyImageView();
}

private void startMyImageView() {
myView = (MyImageView) this.findViewById(R.id.myView);
Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(),
R.drawable.flower_001);
myView.setBitmap(bitmap);
myView.setOnTouchListener(this);
myView.invalidate();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onTouch(View view, MotionEvent event) {
Log.i("touch event","touch x = " + event.getX());
switch (MotionEvent.ACTION_MASK & event.getAction())
{
case MotionEvent.ACTION_DOWN:
mode = TRANSLATION_MODE;
myView.setStartPoint(new Point((int)event.getX(), (int)event.getY()));
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_UP:
mode = NULL_MODE;
myView.savePreviousResult();
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = SCALE_MODE;
myView.setInitDistance(calculateDistance(event));
break;
case MotionEvent.ACTION_MOVE:
if(mode == SCALE_MODE)
{
float dis = calculateDistance(event);
myView.zoomIn(dis);
}
else if(mode == TRANSLATION_MODE)
{
myView.setMovePoint(new Point((int)event.getX(), (int)event.getY()));
}
else
{
Log.i("unknow mode tag","do nothing......");
}
break;
}
myView.invalidate();
return true;
}

private float calculateDistance(MotionEvent event) {
float dx = event.getX(0) - event.getX(1);
float dy = event.getY(0)  - event.getY(1);
float distance = (float)Math.sqrt(dx*dx + dy*dy);
return distance;
}

}
三:运行效果如下

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐