【问题标题】:Android Multi-Touch image view lagAndroid Multi-Touch 图像查看延迟
【发布时间】:2011-12-22 17:10:19
【问题描述】:

我正在尝试实现触摸响应式图像视图 - 就像画廊应用程序中的那样。我已经做到了,而且效果很好,只是与库存图库相比它非常滞后。

我正在寻找使它更流畅的方法。 这是我现在拥有的:

@SuppressWarnings("unused")
public class ImageDisplayView extends ImageView {
    private static final String TAG = "ImageDisplayView";

private static final int MODE_NONE = 0;
private static final int MODE_DRAG = 1;
private static final int MODE_ZOOM = 2;

// Zoom Bounds
private static final float SCALE_MIN = 0.8f;
private static final float SCALE_BOTTOM = 1.0f;
private static final float SCALE_TOP = 10.0f;
private static final float SCALE_MAX = 12.0f;

// Transformation
private Matrix mMatrix = new Matrix();
private Matrix mPreMatrix = new Matrix();
private PointF mPivot = new PointF();
private PointF mNewPivot = new PointF();
private float mDist;

// State
private boolean mInitialScaleDone = false;
private boolean mEnabled = true;
private RectF mBounds = new RectF();
private Float mScale = null;
private float mMode = MODE_NONE;

public ImageDisplayView(Context context) {
    super(context);
    initialScale();
}

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

public ImageDisplayView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initialScale();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    initialScale();
}

private void initialScale() {
    if (!mInitialScaleDone && getDrawable() != null) {
        mInitialScaleDone = true;
        final Runnable r = new Runnable() {

            @Override
            public void run() {
                // Fit to screen
                float scale = calculateFitScreenScale();
                mMatrix.reset();
                mMatrix.postScale(scale, scale);
                setImageMatrix(mMatrix);
                // Center
                float dx = (getWidth() - mBounds.width()) / 2, dy = (getHeight() - mBounds
                        .height()) / 2;
                mMatrix.postTranslate(dx, dy);
                setImageMatrix(mMatrix);
                mScale = 1.0f;
            }
        };
        if (getWidth() == 0) {
            getViewTreeObserver().addOnGlobalLayoutListener(
                    new OnGlobalLayoutListener() {
                        @Override
                        public void onGlobalLayout() {
                            r.run();
                            getViewTreeObserver()
                                    .removeGlobalOnLayoutListener(this);
                        }
                    });
        } else {
            r.run();
        }
    }
}

@Override
public void setImageMatrix(Matrix matrix) {
    super.setImageMatrix(matrix);
    mBounds.set(getDrawable().getBounds());
    matrix.mapRect(mBounds);
}

@Override
public void setEnabled(boolean enabled) {
    mEnabled = enabled;
    if (!enabled && mMode != MODE_NONE) {
        if (mMode == MODE_DRAG) {
            checkLimits(null);
        } else {
            mPivot.set(getWidth() / 2, getHeight() / 2);
            updateScale();
            mPreMatrix.set(mMatrix);
            checkLimits(null);
        }
        mMode = MODE_NONE;
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    float scale, dx, dy;
    if (mEnabled) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_POINTER_DOWN:
            if (event.getPointerCount() == 1) { // Start mode: drag only.
                mMode = MODE_DRAG;
                mPivot.set(event.getX(), event.getY());
            } else { // Start mode: zoom and drag.
                mMode = MODE_ZOOM;
                mDist = distance(event);
                midPoint(mPivot, event);
            }
            mPreMatrix.set(mMatrix);
            return true;
        case MotionEvent.ACTION_UP:
            mMode = MODE_NONE;
            checkLimits(event);
            break;
        case MotionEvent.ACTION_POINTER_UP:
            if (event.getPointerCount() == 2) {
                mMode = MODE_DRAG;
                mPivot.set(event.getX(), event.getY());
                updateScale();
                mPreMatrix.set(mMatrix);
            }
            return true;
        case MotionEvent.ACTION_MOVE:
            mMatrix.set(mPreMatrix);
            if (mMode == MODE_DRAG) {
                dx = event.getX() - mPivot.x;
                dy = event.getY() - mPivot.y;
                mMatrix.postTranslate(dx, dy);
            } else if (mMode == MODE_ZOOM) {
                scale = distance(event) / mDist;
                midPoint(mNewPivot, event);
                dx = mNewPivot.x - mPivot.x;
                dy = mNewPivot.y - mPivot.y;
                mMatrix.postTranslate(dx, dy);
                float postScale = mScale * scale;
                if (postScale < SCALE_MIN) {
                    scale = SCALE_MIN / mScale;
                } else if (postScale > SCALE_MAX) {
                    scale = SCALE_MAX / mScale;
                }
                mMatrix.postScale(scale, scale, mNewPivot.x, mNewPivot.y);
            }
            setImageMatrix(mMatrix);
            return true;
        }
    }
    return super.onTouchEvent(event);
}

private void updateScale() {
    mScale = (mBounds.width() / getDrawable().getIntrinsicWidth())
            / calculateFitScreenScale();
}

@IntendedCaller("onTouchEvent(MotionEvent)")
private void checkLimits(MotionEvent event) {
    float scale, dx, dy;
    if (mScale < SCALE_BOTTOM) {
        scale = SCALE_BOTTOM;
    } else if (mScale > SCALE_TOP) {
        scale = SCALE_TOP;
    } else {
        scale = mScale;
    }
    RectF scaleBounds = new RectF(mBounds); // Use the scale of the image
                                            // to determine drag
                                            // threshold.
    scaleBounds.offset(getWidth() / 2 - scaleBounds.centerX(), getHeight()
            / 2 - scaleBounds.centerY());
    dx = Math.min(0.0f, Math.max(0.0f, scaleBounds.left) - mBounds.left)
            + Math.max(0.0f, Math.min(getWidth(), scaleBounds.right)
                    - mBounds.right);
    dy = Math.min(0.0f, Math.max(0.0f, scaleBounds.top) - mBounds.top)
            + Math.max(0.0f, Math.min(getHeight(), scaleBounds.bottom)
                    - mBounds.bottom);
    animateTransformation(event, 500, null, scale, dx, dy);
}

public void animateTransformation(MotionEvent event, long duration,
        final Runnable callback, final float targetScale, final float dx,
        final float dy) {
    mPreMatrix.set(mMatrix);
    final float scale = targetScale / mScale;
    final float px, py;
    if (event != null) {
        PointF pivot = new PointF();
        midPoint(pivot, event);
        px = scale > 1.0f ? mBounds.centerX() : dx > 0 ? mBounds.right
                : dx < 0 ? mBounds.left : pivot.x;
        py = scale > 1.0f ? mBounds.centerY() : dy > 0 ? mBounds.bottom
                : dy < 0 ? mBounds.top : pivot.y;
    } else {
        px = mBounds.centerX();
        py = mBounds.centerY();
    }
    Utils.animate(this, new Animator() {

        @Override
        public void onAnimationEnd() {
            updateScale();
            if (callback != null) {
                callback.run();
            }
        }

        @Override
        public void makeStep(float percent) {
            mMatrix.set(mPreMatrix);
            float s = 1.0f + percent * (scale - 1.0f);
            float tempx = dx * percent, tempy = dy * percent;
            mMatrix.postTranslate(tempx, tempy);
            mMatrix.postScale(s, s, px + tempx, py + tempy);
            setImageMatrix(mMatrix);
        }
    }, duration);
}

public RectF getBounds() {
    return mBounds;
}

public float getScale() {
    return mScale;
}

public void scale(float scale, float px, float py) {
    mMatrix.set(getImageMatrix());
    mMatrix.postScale(scale, scale, px, py);
    setImageMatrix(mMatrix);
    mScale = scale;
}

public void translate(float dx, float dy) {
    mMatrix.set(getImageMatrix());
    mMatrix.postTranslate(dx, dy);
    setImageMatrix(mMatrix);
}

public void resetAndCenter() {
    mMatrix.set(getImageMatrix());
    mMatrix.postScale(1 / mScale, 1 / mScale, mBounds.centerX(),
            mBounds.centerY());
    setImageMatrix(mMatrix);
    // Center
    float dx = (getWidth() - mBounds.width()) / 2, dy = (getHeight() - mBounds
            .height()) / 2;
    mMatrix.postTranslate(dx, dy);
    setImageMatrix(mMatrix);
    updateScale();
}

@Override
public void setImageBitmap(Bitmap bm) {
    super.setImageBitmap(bm);
    mInitialScaleDone = false;
    initialScale();
}

private float distance(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 p, MotionEvent event) {
    if (event.getPointerCount() == 1) {
        p.set(event.getX(), event.getY());
    } else {
        p.set((event.getX(1) + event.getX(0)) / 2,
                (event.getY(1) + event.getY(0)) / 2);
    }
}

private float calculateFitScreenScale() {
    RectF r = new RectF(getDrawable().getBounds());
    float w = getWidth(), h = getHeight();
    if (r.width() > r.height()) {
        return w / r.width();
    } else if (r.width() == r.height()) {
        if (w < h) {
            return w / r.width();
        } else {
            return h / r.height();
        }
    } else {
        return h / r.height();
    }
}
}

我可以做些什么来减少延迟?非常感谢任何帮助。

【问题讨论】:

    标签: java android imageview gallery multi-touch


    【解决方案1】:

    您是否在实际设备上尝试过?模拟器速度非常慢。

    【讨论】:

    • 在三星 Galaxy Vibrant 上测试,Gallery 版本运行非常流畅,而我的版本比较滞后。
    【解决方案2】:

    这可能会有所帮助:

    查看 Android 开发资源页面上的批处理部分:

    http://developer.android.com/reference/android/view/MotionEvent.html

    我注意到,设备在连续运动中将历史点批处理在一起的方式可能会有很大差异。这也可能在设备之间有很大差异。 在您的代码中,您只使用getXgetY,它们采用最近的事件,忽略所有历史点。 如果您的设备碰巧将大量历史事件批处理在一起,则仅使用最近的事件(通过getXgetY)可能会导致延迟。

    该链接建议如何消耗所有历史点。

    您可以实施一个解决方案来检查是否有太多的历史点被批处理在一起 (event.getHistorySize() &gt; x)。如果历史点的数量高于您选择的阈值,则根据 historySize / 2 进行中间帧更新。类似的东西。

    【讨论】:

      【解决方案3】:

      请在此处查看此问题 How can I get zoom functionality for images?

      Mike Ortiz 有答案,执行Mutli-Touch ImageView

      【讨论】:

        猜你喜欢
        • 2011-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多