インスタントアンドロイドアプリを模したDragToFinishPhotoView実装

5387 ワード

効果のプレビュー


拡大ドラッグ
ドラッグして戻る
拡大してドラッグして戻る

実現構想.


このコントロールは主にPhotoViewに基づいて実現され、拡大、ドラッグ、およびViewPagerとの衝突処理はPhotoViewの処理方法を参考にし、その上でドラッグして戻る機能を追加します.
全体的な実装は主に2つの部分に分けられる:拡大ドラッグ、ドラッグ戻り.どちらの部分も変換行列によって実現され,ImageViewのsetImageMatrix(matrix)である.
もともとPhotoVIewは、拡大縮小および拡大後のドラッグ操作を記録し、適切な変換を行い、updataMatrix()の場合、画像の境界が正常範囲内に表示されないことを保証し、画像の高さがスクリーン高さより小さい場合に下にドラッグできないと判断した.ここでは、元のピクチャ表示範囲の制御ロジックに影響を及ぼさないように、ドラッグダウン距離を記録するマトリクスmDragDownMatrixを追加し、最終的にmScaleAndDragMatrixとともにピクチャのマトリクス変換を決定する.
スケーリングではアンドロイドのScaleGestureDetectorを使用して、現在スケーリング操作を行っているかどうかをリスニングし、スケーリングしている場合はドラッグ操作を禁止します.
タッチ操作全体のプロセスには、主に2つの状態がある:スケールmScaleGestureDetector.isInProgress()、ドラッグisDragging.ドラッグ再細分化は、画面を超えたドラッグおよびドロップダウンを拡大してisDraggingDownの状態に戻る.
ドラッグ状態に入る判断はACTION_MOVE中:
float dX = getActiveX(event) - mLastTouchX;
float dY = getActiveY(event) - mLastTouchY;
if (!isDragging) {
    isDragging = Math.sqrt(dX * dX + dY * dY) >= mTouchSlop;                            
    if (isDragging) {
        mLastTouchX = getActiveX(event);
        mLastTouchY = getActiveY(event);
    }
}

特定のドラッグ操作の判断に入るには、ドラッグ中に再スケールされる競合を回避します.
if(isDragging && !mScaleGestureDetector.isInProgress() && event.getPointerCount() == 1)

ドラッグ・アンド・ドロップ中に現在ドラッグ・アンド・ドロップしているかどうかを判断します.
RectF rectF = getDisplayRect(getDrawMatrix());
if (!isDraggingDown && rectF != null) {
    boolean isScrollVertical = Math.abs(getActiveX(event) - mActionDownX) + 
    mTouchSlop * 5 < Math.abs(getActiveY(event) - mActionDownY);                        
    boolean isScrollUp = getActiveY(event) < mActionDownY;                  
    boolean dragVerticalOutOfBounds = (Math.round(rectF.top) >= 0 && !isScrollUp) ||
 (Math.round(rectF.bottom) <= getViewHeight() && isScrollUp);
    if (isScrollVertical && dragVerticalOutOfBounds) {
        mActionDownX = getActiveX(event);                           
        mActionDownY = getActiveY(event);                           
        isDraggingDown = true;
    }
}

ドラッグ操作変換の具体的な実装:
if (isDraggingDown) {
    mDragDownMatrix.reset();
    float deltaY = getActiveY(event) - mActionDownY;
    mDragDownMatrix.postTranslate(0, deltaY / 3);
    updateMatrix();
    if (mOnPhotoViewDragListener != null) {
        mOnPhotoViewDragListener.onDragOffset(Math.abs(deltaY / 3), getViewHeight() / 6);
    }
    requestParentDisallowInterceptTouchEvent(true);
} else {
    mScaleAndDragMatrix.postTranslate(getActiveX(event) - mLastTouchX, getActiveY(event) - mLastTouchY);
    updateMatrix();

    mLastTouchX = getActiveX(event);
    mLastTouchY = getActiveY(event);

    //  ViewPager 
    boolean isScrollHorizontal = Math.abs(getActiveX(event) - mActionDownX) + mTouchSlop > Math.abs(getActiveY(event) - mActionDownY) * 5;
    boolean isScrollRight = getActiveX(event) > mActionDownX;

    if (rectF != null) {
        if (isScrollHorizontal && (isScrollRight && Math.round(rectF.left) >= 0)
                || (!isScrollRight && Math.round(rectF.right) <= getViewWidth())) {
            requestParentDisallowInterceptTouchEvent(false);
        } else {
            requestParentDisallowInterceptTouchEvent(true);
        }
    }
}

マトリックス変換を適用:
private void updateMatrix() {
    RectF rectF = getDisplayRect(getDrawMatrix());
    if (rectF != null) {
        float deltaX = 0;
        float deltaY = 0;

        if (rectF.width() < getViewWidth()) {
            deltaX = (getViewWidth() - rectF.width()) / 2 - rectF.left;
        } else if (rectF.left > 0) {
            deltaX = -rectF.left;
        } else if (rectF.right < getViewWidth()) {
            deltaX = getViewWidth() - rectF.right;
        }

        if (rectF.height() <= getViewHeight()) {
            deltaY = (getViewHeight() - rectF.height()) / 2 - rectF.top;
        } else if (rectF.top > 0) {
            deltaY = -rectF.top;
        } else if (rectF.bottom < getViewHeight()) {
            deltaY = getViewHeight() - rectF.bottom;
        }

        //  
        mScaleAndDragMatrix.postTranslate(deltaX, deltaY);

        Matrix drawMatrix = getDrawMatrix();
        drawMatrix.postConcat(mDragDownMatrix);
        setImageMatrix(drawMatrix);
    }
}

マルチタッチの場合、いずれかのタッチポイントをActiveとして選択し、指を持ち上げる場合、つまりズーム状態からドラッグ状態に入り、持ち上げた指が現在Activeとして扱われているかどうかを判断し、タッチポイントの位置データを更新しないとブレが発生します.
case MotionEvent.ACTION_POINTER_UP: {
    int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    int pointerId = event.getPointerId(pointerIndex);
    if (pointerId == mActivePointerId) {
        int newPointerIndex = pointerIndex == 0 ? 1 : 0;
        mActivePointerId = event.getPointerId(newPointerIndex);
        mActionDownX = mLastTouchX = event.getX(newPointerIndex);
        mActionDownY = mLastTouchY = event.getY(newPointerIndex);
    } else {
        mActionDownX = mLastTouchX = event.getX(mActivePointerIndex);
        mActionDownY = mLastTouchY = event.getY(mActivePointerIndex);
    }
    break;
}

ソース:https://github.com/okhochan/DragToFinishPhotoView