我正在尝试创建一个支持平移和缩放其内容的ViewGroup。我在网上找到的只是在ImageView中实现的想法和实现,但绝不是容器。我想显示一个地图,在它上面我想显示多个标记,这些标记是ImageButtons,因此用户可以点击它们以获取更多信息。这是通过UIScrollView在iOS上实现的,但我在Android上找不到替代方案。
我决定使用FrameView,所以我可以将图像设置为背景图像,并在其上添加一个RelativeLayout,我可以在其上添加ImageButtons并使用边距定位它们。
我借用了TouchImageView here的部分实现,但我遇到了并发症。我已经开始进行平移,而且我部分成功了,它将容器放在一边,但是平移工作很可怕,它非常抖动。这是我的代码:
public class ScrollingViewGroup extends FrameLayout {
private int x = 0;
private int y = 0;
// 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 last = new PointF();
PointF start = new PointF();
public ScrollingViewGroup(Context context) {
super(context);
sharedConstructing(context);
}
public ScrollingViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(event.getX(), event.getY());
start.set(last);
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
Log.d("ScrollingViewGroup", Float.toString(deltaX));
Log.d("ScrollingViewGroup", Float.toString(deltaY));
float scaleWidth = Math.round(origWidth * saveScale);
float scaleHeight = Math.round(origHeight * saveScale);
x += deltaX;
y += deltaY;
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
// setImageMatrix(matrix);
setTranslationX(x);
setTranslationY(y);
invalidate();
return true; // indicate event was handled
}
});
}
非常感谢任何想法。
编辑:抖动似乎是原因,因为在移动时,deltaX和deltaY在正数和负数之间交替,检查LogCat ......仍然不确定原因。 这是由curr变量引起的,每次都给出不同的值,但是它们看起来好像是手指来回移动而不是向前移动而不是一致。例如,而不是curr.x是0,1,2,3,4等,它是0,1,0.5,2,1.5等。不知道为什么。
答案 0 :(得分:0)
我有一个符合某些先决条件的解决方案:
容器中的项目可以缩放和平移(但不能同时进行)。该代码还能够确定用户是否已点击而不是移动或缩放。 ImageView存储在FrameLayout中的ArrayList中,并进行缩放和移动。这段代码的某些部分取自Ed Burnette(Link)在ZDNet上的一篇非常好的文章,该文章取自真正优秀的Android书籍“Hello, Android”。
查看此代码。您可以在任何活动中将此类用作布局。您甚至应该能够在XML中使用它。现在有一个方法initializeViews()在构造函数中调用,您可以在其中硬编码在创建布局时应加载的图像视图。您应该在“ArrayList sampleBitmaps = new ArrayList();”行之后在此方法中添加一些位图。 对于实际使用,最好实现一个方法addImageView(ImageView项),您可以在其中动态添加视图。
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
public class TouchContainer extends FrameLayout implements OnTouchListener {
// constants
private static final Config DEFAULT_COLOR_DEPTH = Bitmap.Config.ARGB_4444;
private static final String TAG = "TouchContainer";
// fields
private ArrayList<ImageView> items;
public TouchContainer(Context ctx) {
this(ctx, null);
}
public TouchContainer(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
initializeViews(); // initialize some sample Bitmaps
}
/**
* This method is just to make an example
*/
protected void initializeViews() {
ScaleType scaleType = ScaleType.MATRIX;
// array needs to be created here if used in XML
items = new ArrayList<ImageView>();
ArrayList<Bitmap> sampleBitmaps = new ArrayList<Bitmap>();
// here you should add some bitmaps to the Array that will then be displayed in the container
// e.g. sampleBitmaps.add(blabla I'm a bitmap) :-)
ImageView iv = null;
boolean firstLoop = true;
for (Bitmap bitmap : sampleBitmaps) {
// Load the bitmaps into imageviews
iv = new ImageView(getContext());
iv.setImageBitmap(bitmap);
iv.setScaleType(scaleType);
if (firstLoop) {
// add the touch listener to the first image view that is stored in the ArrayList
iv.setOnTouchListener(this);
firstLoop = false;
}
// add view to the FrameLayout
this.addView(iv);
// add the imageview to the array
items.add(iv);
}
}
protected void transformImages(Matrix matrix) {
for (ImageView image : items) {
image.setImageMatrix(matrix);
}
}
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
static final int CLICK = 3;
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
public boolean onTouch(View v, MotionEvent event) {
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:
// figure out if user clicked
mode = NONE;
int xDiff = (int) Math.abs(event.getX() - start.x);
int yDiff = (int) Math.abs(event.getY() - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;
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;
}
transformImages(matrix);
return true;
}
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);
}
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);
}
}