【实训】可以移动放大缩小的图片控件

手机上的app有很多图片控件是可以支持手势放大缩小,并且可以移动的。

那如何可以自定义这样的控件呢?

有两种选择,使用ImageView或者SurfaceView。

ImageView的话,查了很多资料,发现并没有想象中的灵活,他比较适合静态的图片。

而SurfaceView的话就灵活多了,都知道可以使用SurfaceView来用作播放视频的控件。

使用View.OnTouchListener来监听手势事件,


代码如下:

package com.chengmeng.advertise.customview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewConfiguration;

/**
 * Created by linorz on 2016/4/11.
 */
public class CustomSurfaceView extends SurfaceView implements
        SurfaceHolder.Callback, View.OnTouchListener {

    private static final int NONE = 0;// 原始
    private static final int DRAG = 1;// 拖动
    private static final int ZOOM = 2;// 放大
    private int mStatus = NONE;

    private static final float MAX_ZOOM_SCALE = 4.0f;
    private static final float MIN_ZOOM_SCALE = 1.0f;
    private static final float FLOAT_TYPE = 1.0f;
    private float mCurrentMaxScale = MAX_ZOOM_SCALE;
    private float mCurrentScale = 1.0f;

    private Rect mRectSrc = new Rect();
    private Rect mRectDes = new Rect();

    private int mCenterX, mCenterY;
    int mSurfaceHeight, mSurfaceWidth, mImageHeight, mImageWidth;

    private PointF mStartPoint = new PointF();
    private float mStartDistance = 0f;

    private SurfaceHolder mSurHolder = null;
    private Bitmap mBitmap;

    private LongTouchEvent mlongTouchEvent;

    private int mLastMotionX, mLastMotionY;
    private Runnable mLongPressRunnable;
    //计数器,防止多次点击导致最后一次形成longpress的时间变短
    private int mCounter;
    //是否移动了
    private boolean isMoved;
    //是否释放了
    private boolean isReleased;

    public CustomSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mSurHolder = getHolder();
        mSurHolder.addCallback(this);
        this.setOnTouchListener(this);
        mLongPressRunnable = new Runnable() {
            @Override
            public void run() {
                mCounter--;
                //计数器大于0,说明当前执行的Runnable不是最后一次down产生的。
                if (mCounter > 0 || isReleased || isMoved) return;
                if (mlongTouchEvent != null)
                    mlongTouchEvent.longTouch();
            }
        };
    }

    private void init() {
        mCurrentMaxScale = Math.max(
                MIN_ZOOM_SCALE,
                4 * Math.min(FLOAT_TYPE * mImageHeight / mSurfaceHeight, 1.0f
                        * mImageWidth / mSurfaceWidth));
        mCurrentScale = MIN_ZOOM_SCALE;
        mCenterX = mImageWidth / 2;
        mCenterY = mImageHeight / 2;
        calcRect();

    }

    private void adjustCenter() {
        int w = mRectSrc.right - mRectSrc.left;
        int h = mRectSrc.bottom - mRectSrc.top;

        if (mCenterX - w / 2 < 0) {
            mCenterX = w / 2;
            mRectSrc.left = 0;
            mRectSrc.right = w;
        } else if (mCenterX + w / 2 >= mImageWidth) {
            mCenterX = mImageWidth - w / 2;
            mRectSrc.right = mImageWidth;
            mRectSrc.left = mRectSrc.right - w;

        } else {
            mRectSrc.left = mCenterX - w / 2;
            mRectSrc.right = mRectSrc.left + w;
        }

        if (mCenterY - h / 2 < 0) {
            mCenterY = h / 2;
            mRectSrc.top = 0;
            mRectSrc.bottom = h;
        } else if (mCenterY + h / 2 >= mImageHeight) {
            mCenterY = mImageHeight - h / 2;
            mRectSrc.bottom = mImageHeight;
            mRectSrc.top = mRectSrc.bottom - h;
        } else {
            mRectSrc.top = mCenterY - h / 2;
            mRectSrc.bottom = mRectSrc.top + h;
        }

    }

    private void calcRect() {
        int w, h;
        float imageRatio, surfaceRatio;
        imageRatio = FLOAT_TYPE * mImageWidth / mImageHeight;
        surfaceRatio = FLOAT_TYPE * mSurfaceWidth / mSurfaceHeight;

        if (imageRatio < surfaceRatio) {
            h = mSurfaceHeight;
            w = (int) (h * imageRatio);
        } else {
            w = mSurfaceWidth;
            h = (int) (w / imageRatio);
        }

        if (mCurrentScale > MIN_ZOOM_SCALE) {
            w = Math.min(mSurfaceWidth, (int) (w * mCurrentScale));
            h = Math.min(mSurfaceHeight, (int) (h * mCurrentScale));
        } else {
            mCurrentScale = MIN_ZOOM_SCALE;
        }

        mRectDes.left = (mSurfaceWidth - w) / 2;
        mRectDes.top = (mSurfaceHeight - h) / 2;
        mRectDes.right = mRectDes.left + w;
        mRectDes.bottom = mRectDes.top + h;

        float curImageRatio = FLOAT_TYPE * w / h;
        int h2, w2;
        if (curImageRatio > imageRatio) {
            h2 = (int) (mImageHeight / mCurrentScale);
            w2 = (int) (h2 * curImageRatio);
        } else {

            w2 = (int) (mImageWidth / mCurrentScale);
            h2 = (int) (w2 / curImageRatio);
        }
        mRectSrc.left = mCenterX - w2 / 2;
        mRectSrc.top = mCenterY - h2 / 2;
        mRectSrc.right = mRectSrc.left + w2;
        mRectSrc.bottom = mRectSrc.top + h2;
    }

    public void setMaxZoom(float value) {
        mCurrentMaxScale = value;
    }

    public void setBitmap(Bitmap b) {
        if (b == null)
            return;
        synchronized (CustomSurfaceView.class) {
            mBitmap = b;
            if (mImageHeight != mBitmap.getHeight()
                    || mImageWidth != mBitmap.getWidth()) {
                mImageHeight = mBitmap.getHeight();
                mImageWidth = mBitmap.getWidth();
                init();
            }
            showBitmap();
        }

    }

    private void showBitmap() {
        synchronized (CustomSurfaceView.class) {
            Canvas c = getHolder().lockCanvas();
            if (c != null && mBitmap != null) {
                c.drawColor(Color.BLACK);
                c.drawBitmap(mBitmap, mRectSrc, mRectDes, null);
                getHolder().unlockCanvasAndPost(c);
            }
        }
    }

    private void dragAction(MotionEvent event) {

        synchronized (CustomSurfaceView.class) {
            PointF currentPoint = new PointF();
            currentPoint.set(event.getX(), event.getY());
            int offsetX = (int) currentPoint.x - (int) mStartPoint.x;
            int offsetY = (int) currentPoint.y - (int) mStartPoint.y;
            mStartPoint = currentPoint;

            mCenterX -= offsetX;
            mCenterY -= offsetY;

            adjustCenter();
            showBitmap();
        }
    }

    private void zoomAcition(MotionEvent event) {

        synchronized (CustomSurfaceView.class) {

            float newDist = spacing(event);
            float scale = newDist / mStartDistance;
            mStartDistance = newDist;

            mCurrentScale *= scale;
            mCurrentScale = Math.max(FLOAT_TYPE,
                    Math.min(mCurrentScale, mCurrentMaxScale));

            calcRect();
            adjustCenter();
            showBitmap();
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mStartPoint.set(event.getX(), event.getY());
                mLastMotionX = (int) event.getX();
                mLastMotionY = (int) event.getY();
                mStatus = DRAG;
                //长按事件监听
                mCounter++;
                isReleased = false;
                isMoved = false;
                postDelayed(mLongPressRunnable, ViewConfiguration.getLongPressTimeout());
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                float distance = spacing(event);
                if (distance > 10f) {
                    mStatus = ZOOM;
                    mStartDistance = distance;
                    isMoved = true;//移动
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mStatus == DRAG) {
                    dragAction(event);
                } else {
                    if (event.getPointerCount() == 1)
                        return true;
                    zoomAcition(event);
                }
                if (Math.abs(mLastMotionX - event.getX()) > 20
                        || Math.abs(mLastMotionY - event.getY()) > 20) {
                    //移动超过阈值,则表示移动了
                    isMoved = true;
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mStatus = NONE;
                isReleased = true;//释放了
                break;
            default:
                break;
        }

        return true;
    }

    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // TODO Auto-generated method stub
    }

    // 初始化
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {

        synchronized (CustomSurfaceView.class) {
            mRectDes.set(0, 0, width, height);
            mSurfaceHeight = height;
            mSurfaceWidth = width;
            init();
            if (mBitmap != null) {
                showBitmap();
            }
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    public void setLongTouch(LongTouchEvent longTouchEvent) {
        mlongTouchEvent = longTouchEvent;
    }

    public interface LongTouchEvent {
        public void longTouch();
    }
}


### 回答1: MFC图片控件是Windows操作系统中的一种可视化控件,用于显示位图等图片放大缩小是该控件的一个常用功能,可以通过MFC图像控件的某些方法来实现。 其中,CBitmap类提供了LoadBitmap()方法用于加载位图,而CDC类提供了BitBlt()方法用于操作图像。为实现图像控件放大缩小功能,应该通过CBitmap和CDC结合使用,先加载原图像,然后在CDC中创建一个缩放后的位图。变换的时候,利用BitBlt()函数进行图像的复制,并在复制时对其缩放。这样就实现了缩小的功能。对于放大,可以使用StretchBlt()函数实现可伸缩复制。 在具体实现上,可以按照以下步骤进行: 1. 加载原图像,获取其宽高信息,并创建一个与原图像大小相等的CDC,用于复制原图像。 2. 缩放原图像,创建一个缩放后的CDC,用于复制缩放后的图像。 3. 对图像控件进行缩放,使用BitBlt()将缩放后图像的内容复制到图像控件的DC上。 4. 对图像控件进行放大,使用StretchBlt()函数将缩放后图像的内容复制到图像控件的DC上。 通过以上步骤可以实现MFC图像控件放大缩小功能。同时,需要注意,在进行缩放时,应将宽高比例计算好,保证图像不会出现扭曲等失真情况。 ### 回答2: MFC(Microsoft Foundation Classes)是用于开发Windows应用程序的一个类库,它包括了许多常用的工具和控件。MFC图片控件是用于显示静态和动态位图的控件。通过使用MFC图片控件,用户可以在应用程序中方便地显示和处理图形信息。 当我们需要通过MFC图片控件实现放大缩小时,可以采用以下的步骤: 1. 获取位图信息 在MFC中,使用CBitmap类表示位图,可以使用CBitmap类的GetBitmap方法获取位图的信息,包括位图的宽度、高度、深度等信息。 2. 创建缩略图 首先,我们需要根据缩放比例计算出缩略图的宽度和高度。然后,我们可以使用CBitmap类的CreateCompatibleBitmap方法创建一个新的缩略图,使用CImageDC类的SelectObject方法将位图DC对象选入缩略图中,然后使用CImageDC类的StretchBlt方法将原始位图中的图像缩放到缩略图中。 3. 显示缩略图 最后,将缩略图显示在MFC图片控件中即可。可以使用CImageDC类的BitBlt方法将缩略图绘制到设备上下文DC对象中,或者可以使用CImage类的Attach方法将缩略图附加到MFC图片控件中。 需要注意的是,放大缩小操作是会改变图像质量的,如果需要保证图像质量,可以采用更高级别的算法进行图像处理,例如基于向量的放大缩小算法,双三次插值算法等。 总之,通过以上步骤,我们可以轻松地实现MFC图片控件放大缩小功能,为用户提供更加友好的图像查看体验。 ### 回答3: MFC图片控件放大缩小功能是指在图片控件显示图片可以通过程序控制进行放大缩小操作。这个功能非常实用,在实际开发中经常会用到。 在MFC框架下,我们可以通过图片控件的CDC对象进行控制。CDC是Device Context的缩写,意味着设备上下文,它是一个与设备有关的结构,可以访问设备的属性和方法,包括绘图、字体、颜色等。我们可以通过获取CDC对象来操作图片控件中的图片,并且进行缩放操作。 对于放大缩小功能的实现,我们可以通过修改图片控件显示的大小来实现,这个过程中,需要获取图片的原始大小,通过计算比例,获取放大缩小后的大小,然后使用SetWindowPos函数进行控制。具体操作代码如下所示: ```cpp //获取显示图片控件大小 CRect rect; GetClientRect(&rect); //获取图片的原始大小 CSize sizeImage = m_image.GetImageSize(); //缩放计算 float fScale = max(float(sizeImage.cx) / rect.Width(), float(sizeImage.cy) / rect.Height()); int nWidth = sizeImage.cx / fScale; int nHeight = sizeImage.cy / fScale; //缩放图片控件 SetWindowPos(NULL, 0, 0, nWidth, nHeight, SWP_NOZORDER | SWP_NOMOVE); ``` 上述代码中,首先获取图片控件显示大小,然后获取图片的原始大小。通过计算比例,得到需要放大缩小后的大小,最后使用SetWindowPos函数进行控制。 需要注意的是,对于图片放大缩小。不仅仅是控件的大小发生改变,而是图片也需要发生改变。因此,在修改完控件大小后,还需要重新绘制图片,在绘图完成之后,就可以实现图片放大缩小操作了。 总之,MFC图片控件放大缩小功能可以通过获取CDC对象进行控制,实现方法比较简单。在实际开发中,根据具体情况进行相应的代码修改即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值