翻译界内/缩放位图? [英] Translate/scale bitmap within boundaries?

查看:127
本文介绍了翻译界内/缩放位图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我研究在图像上触摸的平移/缩放方法,我通常发现有效的,简单的code - 但没有,确实挺我想要什么。图像需要从不显示实际图像(位)的边缘,其视图之间的空白。如果位图是黑白200x100和View是50×50,用户应该只能够缩小以100x50,使他们能够水平滑动图像,而不是垂直方向。

我的code这是否以及何时移动(平移)图像 - 直到图像被放大。那么什么是揭去;我可以将位图足够远,看周围的空白。这也可能是一些简单而明显的相关保理像素测量由目前的规模因素,但我不能找到它。我怀疑它必须与MAXX和MAXY中的OnDraw计算()所示。任何想法?

 进口android.content.Context;
进口android.graphics.Bitmap;
进口android.graphics.Canvas;
进口android.graphics.drawable.BitmapDrawable;
进口android.graphics.drawable.Drawable;
进口android.util.AttributeSet;
进口android.util.Log;
进口android.view.MotionEvent;
进口android.view.ScaleGestureDetector;
进口android.widget.ImageView;

/ **
 从*大部分code
 * http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
 *
 * @author乍得舒尔茨
 *
 * /
公共类PanZoomImageView扩展ImageView的{

    公共静态最终字符串变量= PanZoomImageView.class.getName();

    私有静态最终诠释INVALID_POINTER_ID = -1;

    //有效指针是一个目前正在朝着我们的目标。
    私人诠释mActivePointerId = INVALID_POINTER_ID;

    私人位图位图;

    私人ScaleGestureDetector mScaleDetector;
    私人浮动mScaleFactor = 1.F;
    私人浮动minScaleFactor;

    私人浮动mPosX;
    私人浮动mPosY;

    私人浮动mLastTouchX,mLastTouchY;

    私人布尔firstDraw = TRUE;

    私人布尔panEnabled = TRUE;
    私人布尔zoomEnabled = TRUE;

    公共PanZoomImageView(上下文的背景下){
        超(上下文);
        建立();
    }

    公共PanZoomImageView(上下文的背景下,ATTRS的AttributeSet,诠释defStyle){
        超(背景下,ATTRS,defStyle);
        建立();
    }

    公共PanZoomImageView(上下文的背景下,ATTRS的AttributeSet){
        超(背景下,ATTRS);
        建立();
    }

    私人无效设置(){
        mScaleDetector =新ScaleGestureDetector(的getContext(),新ScaleListener());
    }

    @覆盖
    公共无效setImageBitmap(BMP位图){
        super.setImageBitmap(BMP);
        位= BMP;
        firstDraw = TRUE;
    }

    @覆盖
    公共无效setImageDrawable(绘制对象绘制){
        super.setImageDrawable(绘制);
        位图=((BitmapDrawable)可拉伸).getBitmap();
        firstDraw = TRUE;
    }

    公共无效的OnDraw(帆布油画){
        Log.v(TAG的OnDraw());
        如果(位图== NULL){
            Log.w(TAG,没有打水 - 位为空);
            super.onDraw(画布);
            返回;
        }

        如果(firstDraw
                &功放;&安培; (bitmap.getHeight()大于0)
                &功放;&安培; (bitmap.getWidth()大于0)
                &功放;&安培; (canvas.getHeight()大于0)
                &功放;&安培; (canvas.getWidth()大于0)){
            //不要让用户缩小了这么多,图像更小
            //比其含有框架
            浮动minXScaleFactor =(浮点)canvas.getWidth()/(浮点)bitmap.getWidth();
            浮动minYScaleFactor =(浮点)canvas.getHeight()/(浮点)bitmap.getHeight();
            minScaleFactor = Math.max(minXScaleFactor,minYScaleFactor);
            Log.d(TAG,minScaleFactor:+ minScaleFactor);
            firstDraw = FALSE;
        }
        mScaleFactor = Math.max(mScaleFactor,minScaleFactor);
        Log.d(TAG,mScaleFactor:+ mScaleFactor);

        //保存画布而不转换(平移)或缩放(缩放)
        //每次更改之后,恢复到这种状态,而不是加剧
        在修改//改变
        canvas.save();

        INT MAXX,其minX,MAXY,MINY;
        //多远才能水平移动的图像,而无需图像和框架之间的差距?
        MAXX =(int)的(mScaleFactor *(bitmap.getWidth()/ 2) - (canvas.getWidth()/ 2));
        其minX = -1 * MAXX;
        //多远,我们可以将图像垂直,而无需图像和框架之间的差距?
        MAXY =(int)的(mScaleFactor *(bitmap.getHeight()/ 2) - (canvas.getWidth()/ 2));
        MINY = -1 * MAXY;
        //不要超越的图像的边界
        如果(mPosX> MAXX){
            mPosX = MAXX;
        }
        如果(mPosX<其minX){
            mPosX =其minX;
        }
        如果(mPosY> MAXY){
            mPosY = MAXY;
        }
        如果(mPosY< MINY){
            mPosY = MINY;
        }

        Log.d(TAG,画布宽度:+ canvas.getWidth()+帆布高度:
                + canvas.getHeight());
        Log.d(TAG,位图宽度:+ bitmap.getWidth()+高度:+ bitmap.getHeight());
        Log.d(TAG,翻译mPosX:+ mPosX +mPosY:+ mPosY);

        如果(zoomEnabled){
            Log.d(TAG,缩放比例系数+ mScaleFactor);
            canvas.scale(mScaleFactor,mScaleFactor);
        } 其他 {
            Log.d(TAG,缩放禁用);
        }

        如果(panEnabled){
            Log.d(TAG,平移为+ mPosX +,+ mPosY);
            canvas.translate(mPosX,mPosY);
        } 其他 {
            Log.d(TAG,平移禁用);
        }

        super.onDraw(画布);
        canvas.restore(); //明确翻译/缩放
    }

    @覆盖
    公共布尔的onTouchEvent(MotionEvent EV){
        //让ScaleGestureDetector检查所有事件。
        mScaleDetector.onTouchEvent(EV);

        最终诠释行动= ev.getAction();
        开关(行动及放大器; MotionEvent.ACTION_MASK){
            案例MotionEvent.ACTION_DOWN:{
                最终浮动X = ev.getX();
                最终浮动Y = ev.getY();

                mLastTouchX = X;
                mLastTouchY = Y;
                mActivePointerId = ev.getPointerId(0);
                打破;
            }

            案例MotionEvent.ACTION_MOVE:{
                最终诠释pointerIndex = ev.findPointerIndex(mActivePointerId);
                最终浮动X = ev.getX(pointerIndex);
                最终浮动Y = ev.getY(pointerIndex);

                //只有当ScaleGestureDetector没有正在处理手势移动。
                如果(!mScaleDetector.isInProgress()){
                    浮DX = X  -  mLastTouchX;
                    浮DY = Y  -  mLastTouchY;

                    //调整的变焦倍数。否则,用户的手指移动10个像素
                    //在200%缩放导致滑动20个像素,而不是完美的形象
                    //以下用户的触摸
                    DX / = mScaleFactor;
                    DY / = mScaleFactor;

                    mPosX + = DX;
                    mPosY + = DY;

                    无效();
                }

                mLastTouchX = X;
                mLastTouchY = Y;

                打破;
            }

            案例MotionEvent.ACTION_UP:{
                mActivePointerId = INVALID_POINTER_ID;
                打破;
            }

            案例MotionEvent.ACTION_CANCEL:{
                mActivePointerId = INVALID_POINTER_ID;
                打破;
            }

            案例MotionEvent.ACTION_POINTER_UP:{
                最终诠释pointerIndex =(ev.getAction()及MotionEvent.ACTION_POINTER_INDEX_MASK)GT;> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                最终诠释pointerId = ev.getPointerId(pointerIndex);
                如果(pointerId == mActivePointerId){
                    //这是我们主动指针往上走。选择一个新的
                    //活跃的指针和相应的调整。
                    最终诠释newPointerIndex = pointerIndex == 0? 1:0;
                    mLastTouchX = ev.getX(newPointerIndex);
                    mLastTouchY = ev.getY(newPointerIndex);
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                }
                打破;
            }
        }

        返回true;
    }

    私有类ScaleListener扩展
            ScaleGestureDetector.SimpleOnScaleGestureListener {
        @覆盖
        公共布尔onScale(ScaleGestureDetector检测器){
            mScaleFactor * = detector.getScaleFactor();
            //不要让物体得到过小或过大。
            mScaleFactor = Math.max(0.1F,Math.min(mScaleFactor,5.0F));
            Log.d(TAG,检测比例因子:+ detector.getScaleFactor()+mscalefactor:+ mScaleFactor);

            无效();
            返回true;
        }
    }

    //目前zoomEnabled / panEnabled只能设置编程,而不是在XML

    公共布尔isPanEnabled(){
        返回panEnabled;
    }

    公共无效setPanEnabled(布尔panEnabled){
        this.panEnabled = panEnabled;
    }

    公共布尔isZoomEnabled(){
        返回zoomEnabled;
    }

    公共无效setZoomEnabled(布尔zoomEnabled){
        this.zoomEnabled = zoomEnabled;
    }

}
 

解决方案

下面是一个很大的痛苦的实验后,我终于来到了我自己 - 学习沿途的一些有趣的事情有关的位图是如何在Android的处理。这code是远非完美,但它适合我的目的 - 希望这将帮助别人以及

 进口android.content.Context;
进口android.graphics.Bitmap;
进口android.graphics.Canvas;
进口android.graphics.drawable.BitmapDrawable;
进口android.graphics.drawable.Drawable;
进口android.util.AttributeSet;
进口android.util.Log;
进口android.view.MotionEvent;
进口android.view.ScaleGestureDetector;
进口android.view.View;

/ **
 * @author乍得舒尔茨
 * @version 1
 * /
公共类PanZoomView扩展视图{

    公共静态最终字符串变量= PanZoomView.class.getName();

    私有静态最终诠释INVALID_POINTER_ID = -1;

    //有效指针是一个目前正在朝着我们的目标。
    私人诠释mActivePointerId = INVALID_POINTER_ID;

    私人位图位图;
    私人浮动viewHeight;
    私人浮动viewWidth;
    浮canvasWidth,canvasHeight;

    私人ScaleGestureDetector mScaleDetector;
    私人浮动mScaleFactor = 1.F;
    私人浮动minScaleFactor;

    私人浮动mPosX;
    私人浮动mPosY;

    私人浮动mLastTouchX,mLastTouchY;

    私人布尔firstDraw = TRUE;

    私人布尔panEnabled = TRUE;
    私人布尔zoomEnabled = TRUE;

    公共PanZoomView(上下文的背景下){
        超(上下文);
        建立();
    }

    公共PanZoomView(上下文的背景下,ATTRS的AttributeSet,诠释defStyle){
        超(背景下,ATTRS,defStyle);
        建立();
    }

    公共PanZoomView(上下文的背景下,ATTRS的AttributeSet){
        超(背景下,ATTRS);
        建立();
    }

    私人无效设置(){
        mScaleDetector =新ScaleGestureDetector(的getContext(),新ScaleListener());
    }

    公共无效setBitmap(BMP位图){
        setImageBitmap(BMP);
    }

    公共无效setImageBitmap(BMP位图){
        位= BMP;
        resetZoom();
        resetPan();
        firstDraw = TRUE;
        无效();
    }

    公共位图getImageBitmap(){
        返回的位图;
    }

    公共位图getBitmap(){
        返回getImageBitmap();
    }

    公共无效resetZoom(){
        mScaleFactor = 1.0F;
    }

    公共无效resetPan(){
        mPosX = 0F;
        mPosY = 0F;
    }

    公共无效setImageDrawable(绘制对象绘制){
        setImageBitmap(((BitmapDrawable)可拉伸).getBitmap());
    }

    公共BitmapDrawable getImageDrawable(){
        BitmapDrawable BD =新BitmapDrawable(的getContext()getResources(),位图。);
        返回屋宇署;
    }

    公共BitmapDrawable getDrawable(){
        返回getImageDrawable();
    }

    公共无效的OnDraw(帆布油画){
// Log.v(TAG的OnDraw());

        如果(位图== NULL){
            Log.w(TAG,没有打水 - 位为空);
            super.onDraw(画布);
            返回;
        }

        如果(firstDraw
                &功放;&安培; (bitmap.getHeight()大于0)
                &功放;&安培; (bitmap.getWidth()大于0)){
            //不要让用户缩小了这么多,图像更小
            //比其含有框架
            浮动minXScaleFactor =(浮点)viewWidth /(浮点)bitmap.getWidth();
            浮动minYScaleFactor =(浮点)viewHeight /(浮点)bitmap.getHeight();
            minScaleFactor = Math.max(minXScaleFactor,minYScaleFactor);
            Log.d(TAG,minScaleFactor:+ minScaleFactor);
            mScaleFactor = minScaleFactor; //启动了缩小一路

            mPosX = mPosY = 0;
            firstDraw = FALSE;

        }
        mScaleFactor = Math.max(mScaleFactor,minScaleFactor);

        canvasHeight = canvas.getHeight();
        canvasWidth = canvas.getWidth();
// Log.d(TAG,画布密度:+ canvas.getDensity()+位图密度:+ bitmap.getDensity());

// Log.d(TAG,mScaleFactor:+ mScaleFactor);

        //保存画布而不转换(平移)或缩放(缩放)
        //每次更改之后,恢复到这种状态,而不是加剧
        在修改//改变
        canvas.save();
        INT MAXX,其minX,MAXY,MINY;
        //不管屏幕密度(HDPI,MDPI)或比例因子,
        //图像总是由位宽度2像素划分。如果一个图像
        //为200像素宽,你向右滚动100像素,您只需滚动的图像
        //关闭屏幕左侧。
        其minX =(int)的(((viewWidth / mScaleFactor) -  bitmap.getWidth())/ 2);
        MAXX = 0;
        //多远,我们可以将图像垂直,而无需图像和框架之间的差距?
        MINY =(int)的(((viewHeight / mScaleFactor) -  bitmap.getHeight())/ 2);
        MAXY = 0;
        Log.d(TAG,其minX:+其minX +MAXX:+ MAXX +MINY:+ MINY +MAXY:+ MAXY);
        //不要超越的图像的边界
        如果(mPosX> MAXX){
            mPosX = MAXX;
        }
        如果(mPosX<其minX){
            mPosX =其minX;
        }
        如果(mPosY> MAXY){
            mPosY = MAXY;
        }
        如果(mPosY< MINY){
            mPosY = MINY;
        }

// Log.d(TAG,鉴于宽:+ viewWidth +观的高度:
// + viewHeight);
// Log.d(TAG,位图宽度:+ bitmap.getWidth()+的高度:+ bitmap.getHeight());
// Log.d(TAG,翻译mPosX:+ mPosX +mPosY:+ mPosY);

// Log.d(TAG,缩放比例系数+ mScaleFactor);
        canvas.scale(mScaleFactor,mScaleFactor);

// Log.d(TAG,平移为+ mPosX +,+ mPosY);
        canvas.translate(mPosX,mPosY);

        super.onDraw(画布);
        canvas.drawBitmap(位图,mPosX,mPosY,NULL);
        canvas.restore(); //明确翻译/缩放
    }

    @覆盖
    公共布尔的onTouchEvent(MotionEvent EV){
        //让ScaleGestureDetector检查所有事件。
        如果(zoomEnabled){
            mScaleDetector.onTouchEvent(EV);
        }

        如果(panEnabled){
            最终诠释行动= ev.getAction();
            开关(行动及放大器; MotionEvent.ACTION_MASK){
                案例MotionEvent.ACTION_DOWN:{
                    最终浮动X = ev.getX();
                    最终浮动Y = ev.getY();

                    mLastTouchX = X;
                    mLastTouchY = Y;
                    mActivePointerId = ev.getPointerId(0);
                    打破;
                }

                案例MotionEvent.ACTION_MOVE:{
                    最终诠释pointerIndex = ev.findPointerIndex(mActivePointerId);
                    最终浮动X = ev.getX(pointerIndex);
                    最终浮动Y = ev.getY(pointerIndex);

                    //只有当ScaleGestureDetector没有正在处理手势移动。
                    如果(!mScaleDetector.isInProgress()){
                        浮DX = X  -  mLastTouchX;
                        浮DY = Y  -  mLastTouchY;

                        //调整的变焦倍数。否则,用户的手指移动10个像素
                        //在200%缩放导致滑动20个像素,而不是完美的形象
                        //以下用户的触摸
                        DX / =(mScaleFactor * 2);
                        DY / =(mScaleFactor * 2);

                        mPosX + = DX;
                        mPosY + = DY;

                        Log.v(TAG,+ DX +,+ DY +mScaleFactor:移动+ mScaleFactor);

                        无效();
                    }

                    mLastTouchX = X;
                    mLastTouchY = Y;

                    打破;
                }

                案例MotionEvent.ACTION_UP:{
                    mActivePointerId = INVALID_POINTER_ID;
                    打破;
                }

                案例MotionEvent.ACTION_CANCEL:{
                    mActivePointerId = INVALID_POINTER_ID;
                    打破;
                }

                案例MotionEvent.ACTION_POINTER_UP:{
                    最终诠释pointerIndex =(ev.getAction()及MotionEvent.ACTION_POINTER_INDEX_MASK)GT;> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                    最终诠释pointerId = ev.getPointerId(pointerIndex);
                    如果(pointerId == mActivePointerId){
                        //这是我们主动指针往上走。选择一个新的
                        //活跃的指针和相应的调整。
                        最终诠释newPointerIndex = pointerIndex == 0? 1:0;
                        mLastTouchX = ev.getX(newPointerIndex);
                        mLastTouchY = ev.getY(newPointerIndex);
                        mActivePointerId = ev.getPointerId(newPointerIndex);
                    }
                    打破;
                }
            }
        }
        返回true;
    }

    私有类ScaleListener扩展
            ScaleGestureDetector.SimpleOnScaleGestureListener {
        @覆盖
        公共布尔onScale(ScaleGestureDetector检测器){
            mScaleFactor * = detector.getScaleFactor();
            //不要让物体得到过小或过大。
            mScaleFactor = Math.max(0.1F,Math.min(mScaleFactor,5.0F));
// Log.d(TAG,检测比例因子:+ detector.getScaleFactor()+mscalefactor:+ mScaleFactor);

            无效();
            返回true;
        }
    }

    //目前zoomEnabled / panEnabled只能设置编程,而不是在XML

    公共布尔isPanEnabled(){
        返回panEnabled;
    }

    公共无效setPanEnabled(布尔panEnabled){
        this.panEnabled = panEnabled;
    }

    公共布尔isZoomEnabled(){
        返回zoomEnabled;
    }

    公共无效setZoomEnabled(布尔zoomEnabled){
        this.zoomEnabled = zoomEnabled;
    }

    / **
     *来电getCroppedBitmap(INT outputWidth,诠释outputHeight)无
     *缩放生成位图的任何特定的大小。
     * @返回
     * /
    公共位图getCroppedBitmap(){
        返回getCroppedBitmap(0,0);
    }

    / **
     *注意到在它看来对象的位图的可见部分
     *和出口的一个位图对象,同时考虑到
     *翻译(平移)和变焦(缩放)。
     *警告:在一个单独的线程中运行这个,不是UI线程!
     *如果您指定的200x200的图像应该有一个outputWidth
     * 400和50的outputHeight,图像将被压扁
     *和延伸到这些方面。
     输出位图的像素*所需@param outputWidth宽度
     * @参数outputHeight所需的像素输出位图的高度
     *在PanZoomImageView返回:图像的可见部分
     * /
    公共位图getCroppedBitmap(INT outputWidth,诠释outputHeight){
        INT origX = -1 *(int)的mPosX * 2;
        INT origY = -1 *(int)的mPosY * 2;
        INT宽度=(INT)(viewWidth / mScaleFactor);
        INT身高=(INT)(viewHeight / mScaleFactor);
        Log.e(TAG,origX:+ origX +origY:+ origY +宽:+宽+的高度:+高度+outputWidth:+ outputWidth +outputHeight:+ outputHeight +getLayoutParams ().WIDTH:+ getLayoutParams()宽+。getLayoutParams()高度:+ getLayoutParams()高度);
        位图B = Bitmap.createBitmap(位图,origX,origY,宽度,高度);

        如果(outputWidth大于0&安培;&安培; outputWidth大于0){
            //使用给出的确切尺寸 - 机会,这将不匹配的纵横比
            B = Bitmap.createScaledBitmap(B,outputWidth,outputHeight,真正的);
        }

        返回b;
    }

      @覆盖
        保护无效onSizeChanged(INT W,INT小时,INT oldw,诠释oldh){
            super.onSizeChanged(W,H,oldw,oldh);
            viewHeight = H;
            viewWidth = W;
        }
}
 

When I research approaches for touch pan/zoom on an image, I generally find effective, simple code--but nothing that does quite what I want. The image needs to never show a blank space between the edge of the actual image (bitmap) and its View. If the bitmap is 200x100 and the View is 50x50, the user should only be able to zoom out to 100x50, allowing them to slide the image horizontally, but not vertically.

My code does this well when moving (translating) the image--until the image is zoomed. Then something is thrown off; I can move the bitmap far enough to see gaps around it. It's probably something simple and obvious related to factoring pixel measurements by the current scale factor, but I can't find it. I suspect it has to do with the calculations of maxX and maxY in onDraw() below. Any ideas?

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;

/**
 * Most code from
 * http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
 * 
 * @author Chad Schultz
 * 
 */
public class PanZoomImageView extends ImageView {

    public static final String TAG = PanZoomImageView.class.getName();

    private static final int INVALID_POINTER_ID = -1;

    // The ‘active pointer’ is the one currently moving our object.
    private int mActivePointerId = INVALID_POINTER_ID;

    private Bitmap bitmap;

    private ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.f;
    private float minScaleFactor;

    private float mPosX;
    private float mPosY;

    private float mLastTouchX, mLastTouchY;

    private boolean firstDraw = true;

    private boolean panEnabled = true;
    private boolean zoomEnabled = true;

    public PanZoomImageView(Context context) {
        super(context);
        setup();
    }

    public PanZoomImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup();
    }

    public PanZoomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup();
    }

    private void setup() {
        mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
    }

    @Override
    public void setImageBitmap(Bitmap bmp) {
        super.setImageBitmap(bmp);
        bitmap = bmp;
        firstDraw = true;
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        bitmap = ((BitmapDrawable) drawable).getBitmap();
        firstDraw = true;
    }

    public void onDraw(Canvas canvas) {
        Log.v(TAG, "onDraw()");
        if (bitmap == null) {
            Log.w(TAG, "nothing to draw - bitmap is null");
            super.onDraw(canvas);
            return;
        }

        if (firstDraw 
                && (bitmap.getHeight() > 0) 
                && (bitmap.getWidth() > 0) 
                && (canvas.getHeight() > 0) 
                && (canvas.getWidth() > 0)) {
            //Don't let the user zoom out so much that the image is smaller
            //than its containing frame
            float minXScaleFactor = (float) canvas.getWidth() / (float) bitmap.getWidth();
            float minYScaleFactor = (float) canvas.getHeight() / (float) bitmap.getHeight();
            minScaleFactor = Math.max(minXScaleFactor, minYScaleFactor);
            Log.d(TAG, "minScaleFactor: " + minScaleFactor);
            firstDraw = false;
        }
        mScaleFactor = Math.max(mScaleFactor, minScaleFactor);
        Log.d(TAG, "mScaleFactor: " + mScaleFactor);

        //Save the canvas without translating (panning) or scaling (zooming)
        //After each change, restore to this state, instead of compounding
        //changes upon changes
        canvas.save();

        int maxX, minX, maxY, minY;
        //How far can we move the image horizontally without having a gap between image and frame?
        maxX = (int) (mScaleFactor * (bitmap.getWidth() / 2) - (canvas.getWidth() / 2));
        minX = -1 * maxX;
        //How far can we move the image vertically without having a gap between image and frame?
        maxY = (int) (mScaleFactor * (bitmap.getHeight() / 2) - (canvas.getWidth() / 2));
        minY = -1 * maxY;
        //Do not go beyond the boundaries of the image
        if (mPosX > maxX) {
            mPosX = maxX;
        }
        if (mPosX < minX) {
            mPosX = minX;
        }
        if (mPosY > maxY) {
            mPosY = maxY;
        }
        if (mPosY < minY) {
            mPosY = minY;
        }

        Log.d(TAG, "canvas width: " + canvas.getWidth() + " canvas height: "
                + canvas.getHeight());
        Log.d(TAG, "bitmap width: " + bitmap.getWidth() + " height: " + bitmap.getHeight());
        Log.d(TAG, "translating mPosX: " + mPosX + " mPosY: " + mPosY);

        if (zoomEnabled) {
            Log.d(TAG, "zooming to scale factor of " + mScaleFactor);
            canvas.scale(mScaleFactor, mScaleFactor);
        } else {
            Log.d(TAG, "zooming disabled");
        }

        if (panEnabled) {
            Log.d(TAG, "panning to " + mPosX + "," + mPosY); 
            canvas.translate(mPosX, mPosY);
        } else {
            Log.d(TAG, "panning disabled");
        }

        super.onDraw(canvas);
        canvas.restore(); //clear translation/scaling
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // Let the ScaleGestureDetector inspect all events.
        mScaleDetector.onTouchEvent(ev);

        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                final float x = ev.getX();
                final float y = ev.getY();

                mLastTouchX = x;
                mLastTouchY = y;
                mActivePointerId = ev.getPointerId(0);
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                final float x = ev.getX(pointerIndex);
                final float y = ev.getY(pointerIndex);

                // Only move if the ScaleGestureDetector isn't processing a gesture.
                if (!mScaleDetector.isInProgress()) {
                    float dx = x - mLastTouchX;
                    float dy = y - mLastTouchY;

                    //Adjust for zoom factor. Otherwise, the user's finger moving 10 pixels
                    //at 200% zoom causes the image to slide 20 pixels instead of perfectly
                    //following the user's touch
                    dx /= mScaleFactor;
                    dy /= mScaleFactor;

                    mPosX += dx;
                    mPosY += dy;

                    invalidate();
                }

                mLastTouchX = x;
                mLastTouchY = y;

                break;
            }

            case MotionEvent.ACTION_UP: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }

            case MotionEvent.ACTION_CANCEL: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }

            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int pointerId = ev.getPointerId(pointerIndex);
                if (pointerId == mActivePointerId) {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly.
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mLastTouchX = ev.getX(newPointerIndex);
                    mLastTouchY = ev.getY(newPointerIndex);
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                }
                break;
            }
        }

        return true;
    }

    private class ScaleListener extends
            ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            // Don't let the object get too small or too large.
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
            Log.d(TAG, "detector scale factor: " + detector.getScaleFactor() + " mscalefactor: " + mScaleFactor);

            invalidate();
            return true;
        }
    }

    //Currently zoomEnabled/panEnabled can only be set programmatically, not in XML

    public boolean isPanEnabled() {
        return panEnabled;
    }

    public void setPanEnabled(boolean panEnabled) {
        this.panEnabled = panEnabled;
    }

    public boolean isZoomEnabled() {
        return zoomEnabled;
    }

    public void setZoomEnabled(boolean zoomEnabled) {
        this.zoomEnabled = zoomEnabled;
    }

}

解决方案

Here's what I eventually came up with on my own after a great deal of painful experimentation--learning some interesting things along the way about how Bitmaps are handled in Android. This code is far from perfect, but it suits my purposes--hopefully it will help others as well.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

/**
 * @author Chad Schultz
 * @version 1
 */
public class PanZoomView extends View {

    public static final String TAG = PanZoomView.class.getName();

    private static final int INVALID_POINTER_ID = -1;

    // The ‘active pointer’ is the one currently moving our object.
    private int mActivePointerId = INVALID_POINTER_ID;

    private Bitmap bitmap;
    private float viewHeight;
    private float viewWidth;
    float canvasWidth, canvasHeight;

    private ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.f;
    private float minScaleFactor;

    private float mPosX;
    private float mPosY;

    private float mLastTouchX, mLastTouchY;

    private boolean firstDraw = true;

    private boolean panEnabled = true;
    private boolean zoomEnabled = true;

    public PanZoomView(Context context) {
        super(context);
        setup();
    }

    public PanZoomView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup();
    }

    public PanZoomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup();
    }

    private void setup() {
        mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
    }

    public void setBitmap(Bitmap bmp) {
        setImageBitmap(bmp);
    }

    public void setImageBitmap(Bitmap bmp) {
        bitmap = bmp;
        resetZoom();
        resetPan();
        firstDraw = true;
        invalidate();
    }

    public Bitmap getImageBitmap() {
        return bitmap;
    }

    public Bitmap getBitmap() {
        return getImageBitmap();
    }

    public void resetZoom() {
        mScaleFactor = 1.0f;
    }

    public void resetPan() {
        mPosX = 0f;
        mPosY = 0f;
    }

    public void setImageDrawable(Drawable drawable) {
        setImageBitmap(((BitmapDrawable) drawable).getBitmap());
    }

    public BitmapDrawable getImageDrawable() {
        BitmapDrawable bd = new BitmapDrawable(getContext().getResources(), bitmap);
        return bd;
    }

    public BitmapDrawable getDrawable() {
        return getImageDrawable();
    }

    public void onDraw(Canvas canvas) {
//      Log.v(TAG, "onDraw()");

        if (bitmap == null) {
            Log.w(TAG, "nothing to draw - bitmap is null");
            super.onDraw(canvas);
            return;
        }

        if (firstDraw 
                && (bitmap.getHeight() > 0) 
                && (bitmap.getWidth() > 0)) {
            //Don't let the user zoom out so much that the image is smaller
            //than its containing frame
            float minXScaleFactor = (float) viewWidth / (float) bitmap.getWidth();
            float minYScaleFactor = (float) viewHeight / (float) bitmap.getHeight();
            minScaleFactor = Math.max(minXScaleFactor, minYScaleFactor);
            Log.d(TAG, "minScaleFactor: " + minScaleFactor);
            mScaleFactor = minScaleFactor; //start out "zoomed out" all the way

            mPosX = mPosY = 0;
            firstDraw = false;

        }
        mScaleFactor = Math.max(mScaleFactor, minScaleFactor);

        canvasHeight = canvas.getHeight();
        canvasWidth = canvas.getWidth();
//      Log.d(TAG, "canvas density: " + canvas.getDensity() + " bitmap density: " + bitmap.getDensity());

//      Log.d(TAG, "mScaleFactor: " + mScaleFactor);

        //Save the canvas without translating (panning) or scaling (zooming)
        //After each change, restore to this state, instead of compounding
        //changes upon changes
        canvas.save();
        int maxX, minX, maxY, minY;
        //Regardless of the screen density (HDPI, MDPI) or the scale factor, 
        //The image always consists of bitmap width divided by 2 pixels. If an image
        //is 200 pixels wide and you scroll right 100 pixels, you just scrolled the image
        //off the screen to the left.
        minX = (int) (((viewWidth / mScaleFactor) - bitmap.getWidth()) / 2);
        maxX = 0;
        //How far can we move the image vertically without having a gap between image and frame?
        minY = (int) (((viewHeight / mScaleFactor) - bitmap.getHeight()) / 2);
        maxY = 0;
        Log.d(TAG, "minX: " + minX + " maxX: " + maxX + " minY: " + minY + " maxY: " + maxY);
        //Do not go beyond the boundaries of the image
        if (mPosX > maxX) {
            mPosX = maxX;
        }
        if (mPosX < minX) {
            mPosX = minX;
        }
        if (mPosY > maxY) {
            mPosY = maxY;
        }
        if (mPosY < minY) {
            mPosY = minY;
        }

//      Log.d(TAG, "view width: " + viewWidth + " view height: "
//              + viewHeight);
//      Log.d(TAG, "bitmap width: " + bitmap.getWidth() + " height: " + bitmap.getHeight());
//      Log.d(TAG, "translating mPosX: " + mPosX + " mPosY: " + mPosY);

//      Log.d(TAG, "zooming to scale factor of " + mScaleFactor);
        canvas.scale(mScaleFactor, mScaleFactor);

//      Log.d(TAG, "panning to " + mPosX + "," + mPosY); 
        canvas.translate(mPosX, mPosY);

        super.onDraw(canvas);
        canvas.drawBitmap(bitmap, mPosX, mPosY, null);
        canvas.restore(); //clear translation/scaling
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // Let the ScaleGestureDetector inspect all events.
        if (zoomEnabled) {
            mScaleDetector.onTouchEvent(ev);
        }

        if (panEnabled) {
            final int action = ev.getAction();
            switch (action & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN: {
                    final float x = ev.getX();
                    final float y = ev.getY();

                    mLastTouchX = x;
                    mLastTouchY = y;
                    mActivePointerId = ev.getPointerId(0);
                    break;
                }

                case MotionEvent.ACTION_MOVE: {
                    final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                    final float x = ev.getX(pointerIndex);
                    final float y = ev.getY(pointerIndex);

                    // Only move if the ScaleGestureDetector isn't processing a gesture.
                    if (!mScaleDetector.isInProgress()) {
                        float dx = x - mLastTouchX;
                        float dy = y - mLastTouchY;

                        //Adjust for zoom factor. Otherwise, the user's finger moving 10 pixels
                        //at 200% zoom causes the image to slide 20 pixels instead of perfectly
                        //following the user's touch
                        dx /= (mScaleFactor * 2);
                        dy /= (mScaleFactor * 2);

                        mPosX += dx;
                        mPosY += dy;

                        Log.v(TAG, "moving by " + dx + "," + dy + " mScaleFactor: " + mScaleFactor);

                        invalidate();
                    }

                    mLastTouchX = x;
                    mLastTouchY = y;

                    break;
                }

                case MotionEvent.ACTION_UP: {
                    mActivePointerId = INVALID_POINTER_ID;
                    break;
                }

                case MotionEvent.ACTION_CANCEL: {
                    mActivePointerId = INVALID_POINTER_ID;
                    break;
                }

                case MotionEvent.ACTION_POINTER_UP: {
                    final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                    final int pointerId = ev.getPointerId(pointerIndex);
                    if (pointerId == mActivePointerId) {
                        // This was our active pointer going up. Choose a new
                        // active pointer and adjust accordingly.
                        final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                        mLastTouchX = ev.getX(newPointerIndex);
                        mLastTouchY = ev.getY(newPointerIndex);
                        mActivePointerId = ev.getPointerId(newPointerIndex);
                    }
                    break;
                }
            }
        }
        return true;
    }

    private class ScaleListener extends
            ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            // Don't let the object get too small or too large.
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
//          Log.d(TAG, "detector scale factor: " + detector.getScaleFactor() + " mscalefactor: " + mScaleFactor);

            invalidate();
            return true;
        }
    }

    //Currently zoomEnabled/panEnabled can only be set programmatically, not in XML

    public boolean isPanEnabled() {
        return panEnabled;
    }

    public void setPanEnabled(boolean panEnabled) {
        this.panEnabled = panEnabled;
    }

    public boolean isZoomEnabled() {
        return zoomEnabled;
    }

    public void setZoomEnabled(boolean zoomEnabled) {
        this.zoomEnabled = zoomEnabled;
    }

    /**
     * Calls getCroppedBitmap(int outputWidth, int outputHeight) without
     * scaling the resulting bitmap to any specific size.
     * @return
     */
    public Bitmap getCroppedBitmap() {
        return getCroppedBitmap(0, 0);
    }

    /**
     * Takes the section of the bitmap visible in its View object
     * and exports that to a Bitmap object, taking into account both
     * the translation (panning) and zoom (scaling).
     * WARNING: run this in a separate thread, not on the UI thread!
     * If you specify that a 200x200 image should have an outputWidth
     * of 400 and an outputHeight of 50, the image will be squished
     * and stretched to those dimensions.
     * @param outputWidth desired width of output Bitmap in pixels
     * @param outputHeight desired height of output Bitmap in pixels
     * @return the visible portion of the image in the PanZoomImageView
     */
    public Bitmap getCroppedBitmap(int outputWidth, int outputHeight) {
        int origX = -1 * (int) mPosX * 2;
        int origY = -1 * (int) mPosY * 2;
        int width = (int) (viewWidth / mScaleFactor);
        int height = (int) (viewHeight / mScaleFactor);
        Log.e(TAG, "origX: " + origX + " origY: " + origY + " width: " + width + " height: " + height + " outputWidth: " + outputWidth + " outputHeight: " + outputHeight + "getLayoutParams().width: " + getLayoutParams().width + " getLayoutParams().height: " + getLayoutParams().height);
        Bitmap b = Bitmap.createBitmap(bitmap, origX, origY, width, height);

        if (outputWidth > 0 && outputWidth > 0) {
            //Use the exact dimensions given--chance this won't match the aspect ratio
            b = Bitmap.createScaledBitmap(b, outputWidth, outputHeight, true);
        }

        return b;
    }

      @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            viewHeight = h;
            viewWidth = w;
        }
}

这篇关于翻译界内/缩放位图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆