Android 图像视图捏缩放
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10630373/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android Image View Pinch Zooming
提问by MobDev
I am using code sample from Making Sense of Multitouchfor zooming image view. On ScaleListener I added ScaleGestureDetector.getFocusX() and getFocusY()
for content to zoom about the focal point of the gesture. It is working fine.
我正在使用来自Making Sense of Multitouch 的代码示例
来缩放图像视图。在 ScaleListener 上,我添加ScaleGestureDetector.getFocusX() and getFocusY()
了内容以缩放手势的焦点。它工作正常。
The problem is, on first multitouch the entire Image drawing position is changing to the current touch point and zooming it from there. Could you help me to resolve this issue?
问题是,在第一次多点触控时,整个图像绘制位置正在更改为当前触摸点并从那里缩放。你能帮我解决这个问题吗?
Here is My Code Sample For TouchImageView.
这是我的 TouchImageView 代码示例。
public class TouchImageViewSample extends ImageView {
private Paint borderPaint = null;
private Paint backgroundPaint = null;
private float mPosX = 0f;
private float mPosY = 0f;
private float mLastTouchX;
private float mLastTouchY;
private static final int INVALID_POINTER_ID = -1;
private static final String LOG_TAG = "TouchImageView";
// The ‘active pointer' is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;
public TouchImageViewSample(Context context) {
this(context, null, 0);
}
public TouchImageViewSample(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
// Existing code ...
public TouchImageViewSample(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Create our ScaleGestureDetector
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
borderPaint = new Paint();
borderPaint.setARGB(255, 255, 128, 0);
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(4);
backgroundPaint = new Paint();
backgroundPaint.setARGB(32, 255, 255, 255);
backgroundPaint.setStyle(Paint.Style.FILL);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
/*
* (non-Javadoc)
*
* @see android.view.View#draw(android.graphics.Canvas)
*/
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawRect(0, 0, getWidth() - 1, getHeight() - 1, borderPaint);
}
@Override
public void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, getWidth() - 1, getHeight() - 1, backgroundPaint);
if (this.getDrawable() != null) {
canvas.save();
canvas.translate(mPosX, mPosY);
Matrix matrix = new Matrix();
matrix.postScale(mScaleFactor, mScaleFactor, pivotPointX,
pivotPointY);
// canvas.setMatrix(matrix);
canvas.drawBitmap(
((BitmapDrawable) this.getDrawable()).getBitmap(), matrix,
null);
// this.getDrawable().draw(canvas);
canvas.restore();
}
}
/*
* (non-Javadoc)
*
* @see
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable
* )
*/
@Override
public void setImageDrawable(Drawable drawable) {
// Constrain to given size but keep aspect ratio
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
mLastTouchX = mPosX = 0;
mLastTouchY = mPosY = 0;
int borderWidth = (int) borderPaint.getStrokeWidth();
mScaleFactor = Math.min(((float) getLayoutParams().width - borderWidth)
/ width, ((float) getLayoutParams().height - borderWidth)
/ height);
pivotPointX = (((float) getLayoutParams().width - borderWidth) - (int) (width * mScaleFactor)) / 2;
pivotPointY = (((float) getLayoutParams().height - borderWidth) - (int) (height * mScaleFactor)) / 2;
super.setImageDrawable(drawable);
}
float pivotPointX = 0f;
float pivotPointY = 0f;
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
pivotPointX = detector.getFocusX();
pivotPointY = detector.getFocusY();
Log.d(LOG_TAG, "mScaleFactor " + mScaleFactor);
Log.d(LOG_TAG, "pivotPointY " + pivotPointY + ", pivotPointX= "
+ pivotPointX);
mScaleFactor = Math.max(0.05f, mScaleFactor);
invalidate();
return true;
}
}
And here how I used it within my activity.
在这里我如何在我的活动中使用它。
ImageView imageView = (ImageView) findViewById(R.id.imgView);
int hMargin = (int) (displayMetrics.widthPixels * .10);
int vMargin = (int) (displayMetrics.heightPixels * .10);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(displayMetrics.widthPixels - (hMargin * 2), (int)(displayMetrics.heightPixels - btnCamera.getHeight()) - (vMargin * 2));
params.leftMargin = hMargin;
params.topMargin = vMargin;
imageView.setLayoutParams(params);
imageView.setImageDrawable(drawable);
回答by user
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
ImageView view = (ImageView) v;
dumpEvent(event);
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
// ...
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, event.getY()
- start.y);
} else if (mode == ZOOM) {
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
view.setImageMatrix(matrix);
return true;
}
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
Log.d(TAG, sb.toString());
}
/** Determine the space between the first two fingers */
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
/** Calculate the mid point of the first two fingers */
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
and dont forget to set scaleType
property to matrix of ImageView
tag like:
并且不要忘记将scaleType
属性设置为ImageView
标签矩阵,例如:
<ImageView
android:id="@+id/imageEnhance"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="15dp"
android:background="@drawable/enhanceimageframe"
android:scaleType="matrix" >
</ImageView>
and the variables used are:
使用的变量是:
// These matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
String savedItemClicked;
回答by Anjula
You can use this class : TouchImageView
你可以使用这个类:TouchImageView
回答by user1888162
I made my own custom imageview with pinch to zoom. There is no limits/borders on Chirag Ravals code, so user can drag the image off the screen. This will fix it.
我用捏缩放制作了我自己的自定义图像视图。Chirag Raval的代码没有限制/边界,因此用户可以将图像拖出屏幕。这将修复它。
Here is the CustomImageView class:
这是 CustomImageView 类:
public class CustomImageVIew extends ImageView implements OnTouchListener {
private Matrix matrix = new Matrix();
private Matrix savedMatrix = new Matrix();
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
private int mode = NONE;
private PointF mStartPoint = new PointF();
private PointF mMiddlePoint = new PointF();
private Point mBitmapMiddlePoint = new Point();
private float oldDist = 1f;
private float matrixValues[] = {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f};
private float scale;
private float oldEventX = 0;
private float oldEventY = 0;
private float oldStartPointX = 0;
private float oldStartPointY = 0;
private int mViewWidth = -1;
private int mViewHeight = -1;
private int mBitmapWidth = -1;
private int mBitmapHeight = -1;
private boolean mDraggable = false;
public CustomImageVIew(Context context) {
this(context, null, 0);
}
public CustomImageVIew(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomImageVIew(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setOnTouchListener(this);
}
@Override
public void onSizeChanged (int w, int h, int oldw, int oldh){
super.onSizeChanged(w, h, oldw, oldh);
mViewWidth = w;
mViewHeight = h;
}
public void setBitmap(Bitmap bitmap){
if(bitmap != null){
setImageBitmap(bitmap);
mBitmapWidth = bitmap.getWidth();
mBitmapHeight = bitmap.getHeight();
mBitmapMiddlePoint.x = (mViewWidth / 2) - (mBitmapWidth / 2);
mBitmapMiddlePoint.y = (mViewHeight / 2) - (mBitmapHeight / 2);
matrix.postTranslate(mBitmapMiddlePoint.x, mBitmapMiddlePoint.y);
this.setImageMatrix(matrix);
}
}
@Override
public boolean onTouch(View v, MotionEvent event){
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
mStartPoint.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if(oldDist > 10f){
savedMatrix.set(matrix);
midPoint(mMiddlePoint, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if(mode == DRAG){
drag(event);
} else if(mode == ZOOM){
zoom(event);
}
break;
}
return true;
}
public void drag(MotionEvent event){
matrix.getValues(matrixValues);
float left = matrixValues[2];
float top = matrixValues[5];
float bottom = (top + (matrixValues[0] * mBitmapHeight)) - mViewHeight;
float right = (left + (matrixValues[0] * mBitmapWidth)) -mViewWidth;
float eventX = event.getX();
float eventY = event.getY();
float spacingX = eventX - mStartPoint.x;
float spacingY = eventY - mStartPoint.y;
float newPositionLeft = (left < 0 ? spacingX : spacingX * -1) + left;
float newPositionRight = (spacingX) + right;
float newPositionTop = (top < 0 ? spacingY : spacingY * -1) + top;
float newPositionBottom = (spacingY) + bottom;
boolean x = true;
boolean y = true;
if(newPositionRight < 0.0f || newPositionLeft > 0.0f){
if(newPositionRight < 0.0f && newPositionLeft > 0.0f){
x = false;
} else{
eventX = oldEventX;
mStartPoint.x = oldStartPointX;
}
}
if(newPositionBottom < 0.0f || newPositionTop > 0.0f){
if(newPositionBottom < 0.0f && newPositionTop > 0.0f){
y = false;
} else{
eventY = oldEventY;
mStartPoint.y = oldStartPointY;
}
}
if(mDraggable){
matrix.set(savedMatrix);
matrix.postTranslate(x? eventX - mStartPoint.x : 0, y? eventY - mStartPoint.y : 0);
this.setImageMatrix(matrix);
if(x)oldEventX = eventX;
if(y)oldEventY = eventY;
if(x)oldStartPointX = mStartPoint.x;
if(y)oldStartPointY = mStartPoint.y;
}
}
public void zoom(MotionEvent event){
matrix.getValues(matrixValues);
float newDist = spacing(event);
float bitmapWidth = matrixValues[0] * mBitmapWidth;
float bimtapHeight = matrixValues[0] * mBitmapHeight;
boolean in = newDist > oldDist;
if(!in && matrixValues[0] < 1){
return;
}
if(bitmapWidth > mViewWidth || bimtapHeight > mViewHeight){
mDraggable = true;
} else{
mDraggable = false;
}
float midX = (mViewWidth / 2);
float midY = (mViewHeight / 2);
matrix.set(savedMatrix);
scale = newDist / oldDist;
matrix.postScale(scale, scale, bitmapWidth > mViewWidth ? mMiddlePoint.x : midX, bimtapHeight > mViewHeight ? mMiddlePoint.y : midY);
this.setImageMatrix(matrix);
}
/** Determine the space between the first two fingers */
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float)Math.sqrt(x * x + y * y);
}
/** Calculate the mid point of the first two fingers */
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
}
This is how you can use it in your activity:
这是您在活动中使用它的方式:
CustomImageVIew mImageView = (CustomImageVIew)findViewById(R.id.customImageVIew1);
mImage.setBitmap(your bitmap);
And layout:
和布局:
<your.package.name.CustomImageVIew
android:id="@+id/customImageVIew1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="15dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:scaleType="matrix"/> // important
回答by Aleem Momin
Add bellow line in build.gradle:
在 build.gradle 中添加以下行:
compile 'com.commit451:PhotoView:1.2.4'
or
或者
compile 'com.github.chrisbanes:PhotoView:1.3.0'
In Java file:
在 Java 文件中:
PhotoViewAttacher photoAttacher;
photoAttacher= new PhotoViewAttacher(Your_Image_View);
photoAttacher.update();
回答by Suragch
Using a ScaleGestureDetector
使用ScaleGestureDetector
When learning a new concept I don't like using libraries or code dumps. I found a good description hereand in the documentationof how to resize an image by pinching. This answer is a slightly modified summary. You will probably want to add more functionality later, but it will help you get started.
在学习新概念时,我不喜欢使用库或代码转储。我在此处和有关如何通过捏合调整图像大小的文档中找到了很好的描述。这个答案是一个稍微修改的总结。您可能希望稍后添加更多功能,但它会帮助您入门。
Layout
布局
The ImageView
just uses the app logo since it is already available. You can replace it with any image you like, though.
在ImageView
刚刚使用app的标志,因为它已经可用。不过,您可以将其替换为您喜欢的任何图像。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@mipmap/ic_launcher"
android:layout_centerInParent="true"/>
</RelativeLayout>
Activity
活动
We use a ScaleGestureDetector
on the activity to listen to touch events. When a scale (ie, pinch) gesture is detected, then the scale factor is used to resize the ImageView
.
我们ScaleGestureDetector
在 Activity 上使用 a来监听触摸事件。当检测到缩放(即捏合)手势时,缩放因子用于调整ImageView
.
public class MainActivity extends AppCompatActivity {
private ScaleGestureDetector mScaleGestureDetector;
private float mScaleFactor = 1.0f;
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// initialize the view and the gesture detector
mImageView = findViewById(R.id.imageView);
mScaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
}
// this redirects all touch events in the activity to the gesture detector
@Override
public boolean onTouchEvent(MotionEvent event) {
return mScaleGestureDetector.onTouchEvent(event);
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
// when a scale gesture is detected, use it to resize the image
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector){
mScaleFactor *= scaleGestureDetector.getScaleFactor();
mImageView.setScaleX(mScaleFactor);
mImageView.setScaleY(mScaleFactor);
return true;
}
}
}
Notes
笔记
- Although the activity had the gesture detector in the example above, it could have also been set on the image view itself.
You can limit the size of the scaling with something like
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
Thanks again to Pinch-to-zoom with multi-touch gestures In Android
- Documentation
- Use Ctrl+ mouse drag to simulate a pinch gesture in the emulator.
- 尽管 Activity 在上面的示例中具有手势检测器,但它也可以设置在图像视图本身上。
您可以使用类似的方法限制缩放的大小
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
- 文档
- 使用Ctrl+ 鼠标拖动在模拟器中模拟捏合手势。
Going on
继续
You will probably want to do other things like panning and scaling to some focus point. You can develop these things yourself, but if you would like to use a pre-made custom view, copy TouchImageView.java
into your project and use it like a normal ImageView
. It worked well for me and I only ran into one bug. I plan to further edit the code to remove the warning and the parts that I don't need. You can do the same.
您可能想要做其他事情,例如平移和缩放到某个焦点。您可以自己开发这些东西,但是如果您想使用预制的自定义视图,TouchImageView.java
请将其复制到您的项目中并像普通ImageView
. 它对我来说效果很好,而且我只遇到了一个错误。我打算进一步编辑代码以删除警告和我不需要的部分。你也可以做到的。
回答by Pramesh Bhalala
I made code for imageview with pinch to zoom using zoomageview. so user can drag the image off the screen and zoom-In , zoom-out the image.
我为图像视图制作了代码,使用缩放视图进行缩放。因此用户可以将图像拖离屏幕并放大、缩小图像。
You can follow this link
to get the Step By Step
Code and also given Output Screenshot.
您可以按照此link
获取Step By Step
代码并提供输出屏幕截图。