自定义ImageView实现图片手势滑动、放大缩小效果

简介: 上一节说了通过自定义来加载超大图片,同时实现手指的拖动效果。不过,另一种情况就是,我们希望应用能够加载整张图片,如果图片太大就先压缩,如果小于屏幕就直接显示,同时用户可以拖拽移动和缩放图片大小,梳理下流程:1.自定义View继承ImageView,重新onDraw方法2.在onDraw先画图片,图片大于屏幕就把图片缩小后显示,图片小于屏幕就直接显示,显示之前要计算显示图片的Rect,Rect是其实就是四个坐标,用来控制显示图片的范围,这个Rect是根据图片的长宽比例计算而来,显示在屏幕中间。
上一节说了通过自定义来加载超大图片,同时实现手指的拖动效果。不过,另一种情况就是,我们希望应用能够加载整张图片,如果图片太大就先压缩,如果小于屏幕就直接显示,同时用户可以拖拽移动和缩放图片大小,梳理下流程:
1.自定义View继承ImageView,重新onDraw方法

2.在onDraw先画图片,图片大于屏幕就把图片缩小后显示,图片小于屏幕就直接显示,显示之前要计算显示图片的Rect,Rect是其实就是四个坐标,用来控制显示图片的范围,这个Rect是根据图片的长宽比例计算而来,显示在屏幕中间。

3.重写onTouchEvent方法,处理View随着手指移动的相关操作:在touchEvent事件中处理好按下和松开事件之后,剩下的就是移动事件,通过不断的改变Rect的坐标,然后不断的调用invalidate(),该方法用来重新绘制我们的View


看下DragImageView.java


public class DragImageView extends ImageView {
    private Drawable mDrawable;
    private Rect mDrawableRect;
    private Context mContext;
    private float mRation_WH = 0;
    private float mOldX = 0;
    private float mOldY = 0;
    private double mD1;
    private boolean isFirst = true;

    private int NONE = 0;// 无操作
    private int SINGAL_MOVE = 1;// 单点移动
    private int MUTIL_MOVE = 2;// 双点拖拽
    private int mStatus = 0;

    public DragImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

    public DragImageView(Context context) {
        super(context);
        this.mContext = context;
        mDrawableRect = new Rect();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // getIntrinsicHeight为返回对象的实际高度
        if (mDrawable == null || mDrawable.getIntrinsicHeight() == 0
                || mDrawable.getIntrinsicWidth() == 0) {
            return;
        }
        setBounds(); // 设置图片
        mDrawable.draw(canvas);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mOldX = event.getX();
            mOldY = event.getY();
            break;
        case MotionEvent.ACTION_UP:
            mStatus = NONE;
            checkBounds();
            break;
        case MotionEvent.ACTION_MOVE:
            if (event.getPointerCount() == 1)
                mStatus = SINGAL_MOVE;
            else
                mStatus = MUTIL_MOVE;
            onTouchMove(event);
            break;
        default:
            break;
        }
        return true;
    }

    private void onTouchMove(MotionEvent event) {
        // 判断有几个触摸点
        if (mStatus == SINGAL_MOVE) {
            int offsetWidth = (int) (event.getX() - mOldX);
            int offsetHeight = (int) (event.getY() - mOldY);
            mOldX = event.getX();
            mOldY = event.getY();
            mDrawableRect.offset(offsetWidth, offsetHeight);
            invalidate();
        } else if (mStatus == MUTIL_MOVE) {
            float X0 = event.getX(0);
            float Y0 = event.getY(0);
            float X1 = event.getX(1);
            float Y1 = event.getY(1);
            double mD2 = Math.sqrt(Math.pow(X0 - X1, 2) + Math.pow(Y0 - Y1, 2));

            if (mD1 < mD2) {
                // 放大操作
                if (mDrawableRect.width() < mContext.getResources()
                        .getDisplayMetrics().widthPixels * 2) {
                    int offsetwidth = 10;
                    int offsettop = (int) (offsetwidth / mRation_WH);
                    mDrawableRect.set(mDrawableRect.left - offsetwidth,
                            mDrawableRect.top - offsettop, mDrawableRect.right
                                    + offsetwidth, mDrawableRect.bottom
                                    + offsettop);
                    invalidate();
                }

            } else {
                // 设置为只能缩小为屏幕的1/3,可以根据需要自己调整
                if (mDrawableRect.width() > mContext.getResources()
                        .getDisplayMetrics().widthPixels / 3) {
                    int offsetwidth = 10;
                    int offsettop = (int) (offsetwidth / mRation_WH);
                    mDrawableRect.set(mDrawableRect.left + offsetwidth,
                            mDrawableRect.top + offsettop, mDrawableRect.right
                                    - offsetwidth, mDrawableRect.bottom
                                    - offsettop);
                    invalidate();
                }
            }
            mD1 = mD2;
        }

    }

    /**
     * 设置mDrawable
     */
    public void setBounds() {
        if (isFirst) {
            // 图片宽高比
            mRation_WH = (float) mDrawable.getIntrinsicWidth()
                    / (float) mDrawable.getIntrinsicHeight();
            // 取屏幕宽和图片宽较小的一个
            int px_w = Math.min(getWidth(),
                    dp2px(mContext, mDrawable.getIntrinsicWidth()));
            int px_h = (int) (px_w / mRation_WH);// 获得图片高
            int left = (getWidth() - px_w) / 2;
            int top = (getHeight() - px_h) / 2;
            int right = px_w + left;
            int bottom = px_h + top;
            mDrawableRect.set(left, top, right, bottom);
            isFirst = false;
        }
        mDrawable.setBounds(mDrawableRect);

    }

    /**
     * 检测边框范围
     */
    public void checkBounds() {
        int newLeft = mDrawableRect.left;
        int newTop = mDrawableRect.top;
        boolean isChange = false;
        // 向左移动范围<=屏幕宽度
        if (newLeft < -mDrawableRect.width()) {
            newLeft = -mDrawableRect.width();
            isChange = true;
        }
        // 向上移动范围<=屏幕高度
        if (newTop < -mDrawableRect.height()) {
            newTop = -mDrawableRect.height();
            isChange = true;
        }
        // 向右移动范围<=屏幕宽度
        if (newLeft > getWidth()) {
            newLeft = getWidth();
            isChange = true;
        }
        // 向下移动范围<=屏幕高度
        if (newTop > getHeight()) {
            newTop = getHeight();
            isChange = true;
        }
        if (isChange) {
            mDrawableRect.offsetTo(newLeft, newTop);
            invalidate();
        }
    }

    public Drawable getmDrawable() {
        return mDrawable;
    }

    public void setmDrawable(Drawable mDrawable) {
        this.mDrawable = mDrawable;
    }

    /**
     * 将dp单位换算成px
     * @param context
     * @param value
     * @return
     */
    public int dp2px(Context context, int value) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (value * scale + 0.5f);
    }

}

在java类中直接调用即可

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        DragImageView mView = new DragImageView(this);
        mView.setmDrawable(getResources().getDrawable(R.drawable.bg));
        setContentView(mView);
    }

效果如下:


相关文章
|
编解码 Android开发
Android native层实现MediaCodec编码H264/HEVC
Android平台在上层实现mediacodec的编码,资料泛滥,已经不再是难事,今天给大家介绍下,如何在Android native层实现MediaCodec编码H264/HEVC,网上千篇一律的接口说明,这里不再赘述,本文主要介绍下,一些需要注意的点,权当抛砖引玉,相关设计界面如下:
628 0
|
Android开发
Android通过手势(多点)缩放和拖拽图片
Android通过手势(多点)缩放和拖拽图片
297 4
|
10月前
|
机器学习/深度学习 人工智能 安全
制造业工业4.0落地案例:AMR如何提升车间效率?
第四次工业革命为制造业带来了巨大的机遇与挑战。本文综合分析了工业4.0在制造业应用中的优势与局限,为企业更清晰地理解潜在障碍与收益提供参考。以自主移动机器人替代传统叉车为例,通过在主入口服务器、制造执行系统与仓储管理系统之间采用先进的API通信协议,不仅实现了物料搬运的智能化,还显著节省了工艺时间。通过对约15个工位的时间研究,记录到每班次节省约1.14小时。同时,时间与成本分析被用于支持企业的决策流程。成本分析基于5年周期构建了OPEX和CAPEX模型。在三种模型中,自主移动机器人的CAPEX模型被证明节省了30%的成本。
566 3
|
Linux
CentOS 7 安装Boost 1.67及boost_python
CentOS 7 安装Boost 1.67及boost_python
1861 0
|
数据采集 分布式计算 监控
智能数据建设与治理 Dataphin:阿里云的一站式数据治理利器
阿里云Dataphin是一款企业级数据治理与智能建设平台,专注于解决数据孤岛、质量低下和开发效率低等问题。它提供从数据集成、规范建模、智能开发到质量监控及资产管理的全生命周期解决方案,特别适用于中大型企业构建数据中台或推进数字化转型。Dataphin通过自动化生成代码、内置质量规则模板和全局血缘追踪等功能,显著提升数据开发效率与跨团队协作能力。尽管学习曲线较陡峭且资源消耗较高,但其深度集成阿里云生态的优势,使其成为追求规范化数据治理企业的理想选择。推荐已采用阿里云技术栈并具备一定数据团队规模的企业使用。
767 1
|
XML Java Android开发
34. 【Android教程】菜单:Menu
34. 【Android教程】菜单:Menu
793 2
|
JavaScript 前端开发
js中的bind,call,apply方法的区别以及用法
JavaScript中,`bind`、`call`和`apply`均可改变函数的`this`指向并传递参数。其中,`bind`返回一个新函数,不立即执行;`call`和`apply`则立即执行,且`apply`的参数以数组形式传递。三者在改变`this`指向及传参上功能相似,但在执行时机和参数传递方式上有所区别。
626 1
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
677 5
|
存储 缓存 网络协议
深入理解Linux网络——TCP连接的开销
在应用程序里,我们使用多少内存都是自己能掌握和控制的,但是纵观Linux整台服务器,除了应用程序以外,内核也会申请和管理大量的内存。 一、相关实际问题 内核是如何管理内存的
|
存储 芯片
51单片机--DS1302时钟
51单片机--DS1302时钟
480 0