Android Multi-Touch задержка просмотра изображений - PullRequest
1 голос
/ 22 декабря 2011

Я пытаюсь реализовать сенсорный просмотр изображений - как в приложении «Галерея».Мне удалось это сделать, и он работает хорошо, за исключением того, что он ОЧЕНЬ запаздывает по сравнению с обычной галереей.

Я ищу способы сделать его более плавным.Вот что у меня сейчас:

@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();
    }
}
}

Что я могу сделать, чтобы сделать это менее медленным?Любая помощь высоко ценится.

Ответы [ 3 ]

0 голосов
/ 26 января 2012

Вот кое-что, что может помочь:

Ознакомьтесь с разделом «Пакетирование» на странице ресурсов Android Dev:

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

Я заметил, что как устройствоПакеты вместе исторических точек в непрерывном движении могут значительно различаться.Это также может значительно отличаться между устройствами.В вашем коде вы используете только getX и getY, которые принимают самое последнее событие, игнорируя все исторические точки.Если ваше устройство обрабатывает большое количество исторических событий вместе, использование только самых последних (через getX и getY) может привести к задержке.

Ссылка указывает, как можно использовать все исторические события.points.

Вы можете реализовать решение, которое проверяет, было ли слишком много исторических точек объединено (event.getHistorySize() > x).Если количество исторических точек превышает пороговое значение по вашему выбору, сделайте промежуточные обновления кадров, например, на historySize / 2.Нечто подобное.

0 голосов
/ 26 января 2012

Пожалуйста, просмотрите этот вопрос здесь Как получить функцию масштабирования для изображений?

theres ответ Mike Ortiz down, реализующий Mutli-Touch ImageView

0 голосов
/ 22 декабря 2011

Вы пробовали это на реальном устройстве? Эмулятор в общеизвестно медленном.

...