2011-12-22 9 views
1

私は、ギャラリーアプリケーションのようなタッチ反応の画像表示を実装しようとしています。私はそれをすることができたし、それはストックギャラリーに比べて非常に遅いことを除いて、うまくいきます。Androidマルチタッチ画像の表示遅れ

私はそれをスムーズにする方法を探しています。 ここで私が今持っているものです。

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

は、私は、これはあまりラグにするために何ができますか?どんな助けでも大歓迎です。

答えて

0

実際のデバイスで試しましたか?エミュレータは悪名高く遅いです。

チェックアウトのAndroid Devのリソースページでバッチ処理セクション:ここで

+0

を実施し、Mike Ortizダウンで答えます。 – saarraz1

0

は役立つかもしれない何か私が気づいた

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

どのように継続的にデバイス一緒にバッチ歴史的なポイント動きはかなり異なることがあります。これは、デバイス間でもかなり異なる場合があります。 コードでは、getXgetYを使用します。これは最も最近のイベントをとり、すべての履歴ポイントを無視します。 デバイスが大量の履歴イベントをまとめてバッチ処理している場合は、最新のものだけを使用して(getXgetY)、遅延が発生する可能性があります。

このリンクは、すべての歴史的ポイントをどのように消費するかを示しています。

あまりに多くの履歴ポイントがまとめてバッチ処理されているかどうかを確認するソリューションを実装できます(event.getHistorySize() > x)。履歴ポイントの数が選択したしきい値を超えている場合は、たとえばhistorySize/2に基づいて中間フレームを更新します。そんな感じ。

0

How can I get zoom functionality for images?

ここでこの質問を確認してくださいtheresのは、私のバージョンはラグである一方、ギャラリーのバージョンは、完全に滑らかな実行されているサムスンギャラクシー活気に満ちた、でテスト Mutli-Touch ImageView