Hi.
I'm currently teaching myself some Android SDK stuff and completed a tutorial to make a pan and zoom ImageView.
The code is straightforward so i thought i'd try to make a B4A library based on it.
This is the Android SDK code:
It works as desired (but is far from perfect).
The ImageView has it's scaleType property set to matrix in the layout xml file:
So my library code is pretty much a copy/paste:
And my B4A code:
layoutMain contains just ImageView1 with all properties left at default, position at (0, 0) with both width and height set to -1 to fill the parent container.
The image doesn't pan or zoom
Here's a typical log output for an image drag using the emulator:
It looks like the code is functioning correctly and the correct parts of the switch statement are being executed but the image just doesn't pan.
I added:
It reports MATRIX so it looks like the ImageView scaleType is being correctly set.
Now the only thing i can think is that the pan and zoom stuff will only work if the ImageView image is set as it's src property and NOT it's background image.
The B4A ImageViewWrapper.class looks to set ONLY the background image of a B4A ImageView:
Could this be why the library doesn't work?
I looked at setting the ImageView src in the library but found no way to reference my ship.jpg image.
It's not in the Objects\res\drawable folder and not a part of the B4A generated R.java class but is added correctly to the B4A project using the Add files button.
Any ideas?
My B4A code and Eclipse project are attached.
Thanks.
Martin.
I'm currently teaching myself some Android SDK stuff and completed a tutorial to make a pan and zoom ImageView.
The code is straightforward so i thought i'd try to make a B4A library based on it.
This is the Android SDK code:
B4X:
package uk.co.martinpearman.panandzoomimageview;
import android.app.Activity;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
public class PanAndZoomImageViewActivity extends Activity implements OnTouchListener{
private static final String TAG="PanAndZoomImageViewActivity";
Matrix matrix=new Matrix();
Matrix savedMatrix=new Matrix();
static final int NONE=0;
static final int DRAG=1;
static final int ZOOM=2;
int mode=NONE;
PointF start=new PointF();
PointF mid=new PointF();
float oldDist=1f;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_view);
ImageView imageView=(ImageView) findViewById(R.id.imageView);
imageView.setOnTouchListener(this);
}
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);
}
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));
}
@Override
public boolean onTouch(View v, MotionEvent event) {
ImageView imageView=(ImageView) v;
switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode=DRAG;
Log.d(TAG, "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;
}
imageView.setImageMatrix(matrix);
return true;
}
}
It works as desired (but is far from perfect).
The ImageView has it's scaleType property set to matrix in the layout xml file:
B4X:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:id="@+id/imageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="matrix"
android:src="@drawable/ship">
</ImageView>
</FrameLayout>
So my library code is pretty much a copy/paste:
B4X:
package uk.co.martinpearman.b4a.imageviewextras;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import anywheresoftware.b4a.BA.ActivityObject;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;
@ActivityObject
@Author("Martin Pearman")
@ShortName("ImageViewExtras")
@Version((float) 1.0)
public class ImageViewExtras {
private static final String TAG = "B4A";
public static void enablePanAndZoom(ImageView imageView1) {
Log.d(TAG, "enablePanAndZoom");
imageView1.setScaleType(ImageView.ScaleType.MATRIX);
imageView1.setOnTouchListener(new OnTouchListener() {
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);
}
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));
}
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
final int NONE = 0;
final int DRAG = 1;
final int ZOOM = 2;
int mode = NONE;
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
@Override
public boolean onTouch(View v, MotionEvent event) {
ImageView imageView = (ImageView) v;
// imageView.setVisibility(View.GONE);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
Log.d(TAG, "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);
Log.d(TAG, "postTranslate");
} 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;
}
Log.d(TAG, imageView.getImageMatrix().toString());
imageView.setImageMatrix(matrix);
Log.d(TAG, imageView.getImageMatrix().toString());
return true;
}
});
}
}
And my B4A code:
B4X:
'Activity module
Sub Process_Globals
End Sub
Sub Globals
Dim ImageView1 As ImageView
Dim MyImageView As ImageViewExtras
End Sub
Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("layoutMain")
End Sub
Sub Activity_Resume
MyImageView.enablePanAndZoom(ImageView1)
ImageView1.Bitmap=LoadBitmap(File.DirAssets, "ship.jpg")
End Sub
Sub Activity_Pause (UserClosed As Boolean)
End Sub
layoutMain contains just ImageView1 with all properties left at default, position at (0, 0) with both width and height set to -1 to fill the parent container.
The image doesn't pan or zoom
Here's a typical log output for an image drag using the emulator:
B4X:
LogCat connected to: emulator-5554
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
enablePanAndZoom
mode=DRAG
Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}
Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}
postTranslate
Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}
Matrix{[1.0, 0.0, -3.0][0.0, 1.0, 2.0][0.0, 0.0, 1.0]}
postTranslate
Matrix{[1.0, 0.0, -3.0][0.0, 1.0, 2.0][0.0, 0.0, 1.0]}
Matrix{[1.0, 0.0, -35.0][0.0, 1.0, 69.0][0.0, 0.0, 1.0]}
postTranslate
Matrix{[1.0, 0.0, -35.0][0.0, 1.0, 69.0][0.0, 0.0, 1.0]}
Matrix{[1.0, 0.0, -54.0][0.0, 1.0, 87.0][0.0, 0.0, 1.0]}
postTranslate
Matrix{[1.0, 0.0, -54.0][0.0, 1.0, 87.0][0.0, 0.0, 1.0]}
Matrix{[1.0, 0.0, -67.0][0.0, 1.0, 94.0][0.0, 0.0, 1.0]}
mode=NONE
Matrix{[1.0, 0.0, -67.0][0.0, 1.0, 94.0][0.0, 0.0, 1.0]}
Matrix{[1.0, 0.0, -67.0][0.0, 1.0, 94.0][0.0, 0.0, 1.0]}
It looks like the code is functioning correctly and the correct parts of the switch statement are being executed but the image just doesn't pan.
I added:
B4X:
Log.d(TAG, imageView.getScaleType().toString());
It reports MATRIX so it looks like the ImageView scaleType is being correctly set.
Now the only thing i can think is that the pan and zoom stuff will only work if the ImageView image is set as it's src property and NOT it's background image.
The B4A ImageViewWrapper.class looks to set ONLY the background image of a B4A ImageView:
B4X:
public void setBitmap(Bitmap value) {
int gravity = getGravity();
anywheresoftware.b4a.objects.drawable.BitmapDrawable bd = new anywheresoftware.b4a.objects.drawable.BitmapDrawable();
bd.Initialize(value);
bd.setGravity(gravity);
setBackground((Drawable)bd.getObject());
}
public void SetBackgroundImage(Bitmap Bitmap) {
setBitmap(Bitmap);
}
Could this be why the library doesn't work?
I looked at setting the ImageView src in the library but found no way to reference my ship.jpg image.
It's not in the Objects\res\drawable folder and not a part of the B4A generated R.java class but is added correctly to the B4A project using the Add files button.
Any ideas?
My B4A code and Eclipse project are attached.
Thanks.
Martin.
Last edited: