自定义ZoomRecyclerView可缩放可点击

2023-11-13

可直接使用喔~ 

public class PinchRecyclerView extends RecyclerView implements View.OnTouchListener {
    private static final int INVALID_POINTER_ID = -1;
    private int mActivePointerId = INVALID_POINTER_ID;
    private ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.f;
    private float maxWidth = 0.0f;
    private float maxHeight = 0.0f;
    private float mLastTouchX;
    private float mLastTouchY;
    private float mPosX;
    private float mPosY;
    private float width;
    private float height;

    public PinchRecyclerView(Context context) {
        this(context, null);
    }

    public PinchRecyclerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PinchRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnTouchListener(this);
        if (!isInEditMode())
            mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        width = View.MeasureSpec.getSize(widthMeasureSpec);
        height = View.MeasureSpec.getSize(heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        try {
            boolean resp = super.onInterceptTouchEvent(ev);
            Log.d(TAG, "onInterceptTouchEvent: " + resp);
            return resp;
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent ev) {
        super.onTouchEvent(ev);
        final int action = ev.getAction();
        mScaleDetector.onTouchEvent(ev);
        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 = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final float x = ev.getX(pointerIndex);
                final float y = ev.getY(pointerIndex);
                final float dx = x - mLastTouchX;
                final float dy = y - mLastTouchY;

                mPosX += dx;
                mPosY += dy;

                if (mPosX > 0.0f)
                    mPosX = 0.0f;
                else if (mPosX < maxWidth)
                    mPosX = maxWidth;

                if (mPosY > 0.0f)
                    mPosY = 0.0f;
                else if (mPosY < maxHeight)
                    mPosY = maxHeight;

                mLastTouchX = x;
                mLastTouchY = y;
                invalidate();
                break;
            }

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

            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int pointerId = ev.getPointerId(pointerIndex);
                if (pointerId == mActivePointerId) {
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mLastTouchX = ev.getX(newPointerIndex);
                    mLastTouchY = ev.getY(newPointerIndex);
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                }
                break;
            }
        }

        return true;
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        canvas.translate(mPosX, mPosY);
        canvas.scale(mScaleFactor, mScaleFactor);
        canvas.restore();
    }

    @Override
    protected void dispatchDraw(@NonNull Canvas canvas) {
        canvas.save();
        if (mScaleFactor == 1.0f) {
            mPosX = 0.0f;
            mPosY = 0.0f;
        }
        canvas.translate(mPosX, mPosY);
        canvas.scale(mScaleFactor, mScaleFactor);
        super.dispatchDraw(canvas);
        canvas.restore();
        invalidate();
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 3.0f));
            maxWidth = width - (width * mScaleFactor);
            maxHeight = height - (height * mScaleFactor);
            invalidate();
            return true;
        }
    }

    private float getScaleFactor() {
        return mScaleFactor;
    }

    private float getPosX() {
        return mPosX;
    }

    private float getPosY() {
        return mPosY;
    }

    // Map what appeared to be clicked in the potentially zoomed view back to the scale = 1 view.
    private View getMappedView(float x, float y) {
        final int NO_POSITION = -1;
        View expectedView = null;
        View scanView;
        final int mappedX = (int) (getWidth() * (x - getPosX()) / (getWidth() * getScaleFactor()));
        final int mappedY = (int) (getHeight() * (y - getPosY()) / (getHeight() * getScaleFactor()));
        int foundColumn = NO_POSITION;
        int lastLeft = NO_POSITION;

        // Look for the column of the expected view.
        for (int i = 0; i < getChildCount() && foundColumn == NO_POSITION; i++) {
            scanView = getChildAt(i);
            int thisLeft = scanView.getLeft();
            if ((mappedX <= scanView.getRight()) && (mappedX >= thisLeft)) {
                foundColumn = i;
            }
            if (thisLeft < lastLeft) { // Wrapped around. Touch outside of our area.
                break;
            }
            lastLeft = scanView.getLeft();
        }
        if (foundColumn == NO_POSITION) {
            return null;
        }

        // Find out how many columns we have.
        int colCount = foundColumn;
        while (++colCount < getChildCount()) {
            if (getChildAt(colCount).getLeft() <= lastLeft) {
                break;
            }
        }

        // Look for the row.
        for (int i = foundColumn; i < getChildCount(); i += colCount) {
            scanView = getChildAt(i);
            if ((mappedY >= scanView.getTop()) && (mappedY <= scanView.getBottom())) {
                expectedView = scanView;
                break;
            }
        }
        return expectedView;
    }

    private boolean mClickCandidate = false;
    private float mLastX;
    private float mLastY;

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mClickCandidate = true;
                break;
            case MotionEvent.ACTION_MOVE:
                float x = Math.abs(event.getX() - mLastX);
                float y = Math.abs(event.getY() - mLastY);
                if (x > 0 || y > 0) {
                    mClickCandidate = false;
                } else {
                    mClickCandidate = true;
                }
                mLastX = event.getX();
                mLastY = event.getY();

                break;
            case MotionEvent.ACTION_UP:
                if (mClickCandidate) {
                    View v = getMappedView(event.getX() + view.getLeft(), event.getY() + view.getTop());
                    if (v != null && v.hasOnClickListeners()) {
                        v.performClick();
                    }
                    mClickCandidate = false;
                }

                return true;
        }
        return false;
    }

    private static final String TAG = PinchRecyclerView.class.getClass().getSimpleName();
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义ZoomRecyclerView可缩放可点击 的相关文章

  • 像 Java 这样的静态类型语言中动态方法解析背后的原因是什么

    我对 Java 中引用变量的动态 静态类型和动态方法解析的概念有点困惑 考虑 public class Types Override public boolean equals Object obj System out println i
  • Android 手机作为 GSM 调制解调器在 PC 上发送/接收短信?

    是否可以将 Android 移动设备用作 PC 上的 GSM 调制解调器 我正在 net下开发应用程序来发送 接收短信等 现在我想通过 USB 将我的 Android 设备连接到我的 PC 并将其用作 GSM 调制解调器来与其通信 这里是参
  • volatile、final 和synchronized 安全发布的区别

    给定一个带有变量 x 的 A 类 变量 x 在类构造函数中设置 A x 77 我们想将 x 发布到其他线程 考虑以下 3 种变量 x 线程安全 发布的情况 1 x is final 2 x is volatile 3 x 设定为同步块 sy
  • Mipmap 与可绘制文件夹[重复]

    这个问题在这里已经有答案了 我正在使用 Android Studio 1 1 Preview 1 我注意到 当我创建一个新项目时 我得到以下层次结构 不同 DPI 的 Mipmap 文件夹 不再有不同 DPI 的可绘制文件夹 我应该将所有资
  • java for windows 中的文件图标叠加

    我正在尝试像 Tortoise SVN 或 Dropbox 一样在文件和文件夹上实现图标叠加 我在网上查了很多资料 但没有找到Java的解决方案 Can anyone help me with this 很抱歉确认您的担忧 但这无法在 Ja
  • 关键字“table”附近的语法不正确,无法提取结果集

    我使用 SQL Server 创建了一个项目 其中包含以下文件 UserDAO java public class UserDAO private static SessionFactory sessionFactory static se
  • OnLongClickListener 不工作

    我有一个ImageView 我需要使用onLongClickListener对于图像视图 当我使用这段代码时 什么也没有发生 Code gallery Gallery findViewById R id gall1 gallery setA
  • 最新的 Hibernate 和 Derby:无法建立 JDBC 连接

    我正在尝试创建一个使用 Hibernate 连接到 Derby 数据库的准系统项目 我正在使用 Hibernate 和 Derby 的最新版本 但我得到的是通用的Unable to make JDBC Connection error 这是
  • 如何在 Android 中从 WorkManager 取消工作?

    我已经保存了 WorkManagerUUID转换成String在领域数据库中 这是代码 Constraints constraints new Constraints Builder setRequiredNetworkType Netwo
  • 我如何在java中读取二进制数据文件

    因此 我正在为学校做一个项目 我需要读取二进制数据文件并使用它来生成角色的统计数据 例如力量和智慧 它的设置是让前 8 位组成一个统计数据 我想知道执行此操作的实际语法是什么 是不是就像读文本文件一样 这样 File file new Fi
  • Opencv Java 灰度

    我编写了以下程序 尝试从彩色转换为灰度 Mat newImage Imgcodecs imread q1 jpg Mat image new Mat new Size newImage cols newImage rows CvType C
  • 在命令行上卸载 Android SDK 的选定部分

    这与 卸载旧的 Android SDK 版本 https stackoverflow com questions 15182377 uninstall old android sdk versions 除非我想在无头 Linux CI 服务
  • 找不到符号 NOTIFICATION_SERVICE?

    package com test app import android app Notification import android app NotificationManager import android app PendingIn
  • 通过系统应用程序以编程方式静默安装 apk(无需 root)

    我有带有 android sharedUserId android uid system UID 1000 的系统级应用程序 设备未root INSTALL PACKAGES 权限包含在清单中 我可以静默安装下载的 apk 吗 我已经发现这
  • 通过电子邮件发送文本文件附件

    我正在尝试附加一个文本文件以便通过电子邮件发送 但每当我打开电子邮件应用程序时 它都会说该文件不存在 请帮助 Intent i new Intent Intent ACTION SEND i setType text plain i put
  • 没有支持 FEATURE_CAMERA_EXTERNAL 的 Android 设备

    根据this doc https source android com devices camera external usb cameras一些 Android 设备允许使用 Camera2 API 访问外部 USB 摄像头 我检查了大约
  • 在 Android 应用程序资源中使用 JSON 文件

    假设我的应用程序的原始资源文件夹中有一个包含 JSON 内容的文件 我如何将其读入应用程序 以便我可以解析 JSON See 开放原始资源 http developer android com reference android conte
  • 如何将双精度/浮点四舍五入为二进制精度?

    我正在编写对浮点数执行计算的代码的测试 不出所料 结果很少是准确的 我想在计算结果和预期结果之间设置一个容差 我已经证实 在实践中 使用双精度 在对最后两位有效小数进行四舍五入后 结果始终是正确的 但是usually四舍五入最后一位小数后
  • 如何使用 AccessibilityService 在 Android 中模拟按键

    我正在编写一个辅助服务 我一直在尝试在应用程序上进行一些自动搜索 我使用accessibilityservice action paste来填充EditText 然后我需要模拟软键盘上的按键 但我不知道如何做 你们能帮我一下吗 你可以尝试A
  • Git 实验分支还是单独的实验存储库?

    我正在开发一个 Android 应用程序 并且在整个开发周期中一直使用 Git 现在 我想构建并发布实验性功能 供人们尝试和安装 同时仍将原始的 稳定的应用程序安装在他们的设备上 现在 这意味着我需要使用不同的包名称 这会更改开发项目中的一

随机推荐

  • [算法]最大子串和

    题目描述 首先有一串数字 共有n个 从n个数中找到连续子序列和的最大值 解法一 暴力破解法 看到这个问题时第一时间想到的就是暴力破解法 遍历所有子序列 最终得到最大值 e g 1 2 3 4 5 6 共有六个数 所有组合为 1 2 3 4
  • Linux Shell编程之数组及参数传递

    1 Shell数组 Bash Shell 只支持一维数组 不支持多维数组 初始化时不需要定义数组大小 数组元素的下标由 0 开始 下标可以是整数或算术表达式 其值应大于或等于 0 1 1 定义数组 语法 数组名 值1 值2 值n 或者 数组
  • vue cli npm run build打生产环境包报错Cannot read property ‘pop‘ of undefined

    问题出在webpack配置的代码拆分splitChunks 解决办法 每个cacheGroups中配置enforce true
  • SpringGateway转发过程

    为什么写 就想看看springgateway的限流咋做的 但是看着看着就想知道转发过程 然后就写了 总之 转发是通过重组请求头header uri等信息建立netty客户端连接的访问过程 Lettuce相较于Jedis有哪些优缺点 Lett
  • LaTeX 常用符号命令大全

    函数 符号及特殊字符 声调 语法 效果 语法 效果 语法 效果 bar x acute eta check alpha grave eta breve a ddot y dot x hat alpha tilde iota 函数 语法 效果
  • 开个坑, 写个阿里云开放储存服务(OSS)的C++版SDK以及客户端

    这应该是继我研究手册QQ协议后的第2个稍微正式一点的网络程序 不只是Scoket套接字编程 还涉及到更多的HTTP协议知识 阿里云开放储存服务OSS官方已经提供了不少SDK 包括PHP Python Java C 但唯独没有C C 的 很可
  • Python 编程进阶经典算法逻辑编程 剑指Offer

    目录 1 找到数组中重复数字 字符 返回出现频次最多 2 给定一个二维数组 其每一行从左到右递增排序 从上到下也是递增排序 给定一个数 判断这个数是否在该二维数组中 3 从尾到头打印链表 4 用两个栈实现队列 5 第n项斐波那契数列 矩形覆
  • java详解动态代理中的代理对象

    相信大家都使用过动态代理 就算没有写过 应该也用过Spring来做过Bean的组织管理 如果使用过Spring 那大多数情况应该已经不知不觉地用到动态代理了 动态代理中所说的 动态 是针对使用Java代码实际编写了代理类的 静态 代理而言的
  • sql和MySQL的语句执行顺序

    sql和mysql执行顺序 发现内部机制是一样的 最大区别是在别名的引用上 一 sql执行顺序 1 from 3 join 2 on 4 where 5 group by 开始使用select中的别名 后面的语句中都可以使用 6 avg s
  • Linux软件安装-rpm详解

    Linux软件安装 rpm详解 在Linux系统中 RPM Red Hat Package Manager 是一种常见的软件包管理器 提供了方便的软件安装 升级和卸载功能 本文将详细介绍rpm的语法 实操和各种方法之间的区别及重点内容 RP
  • Mysql8.0.16在win10的安装以及navicat连接

    Mysql8 0 16在win10的安装以及navicat连接 一 安装过程 1 去mysql官网下载适合自己电脑的版本https www mysql com downloads 进入官网 官网下载极慢 建议下载个迅雷 复制链接到迅雷 体验
  • 拥抱数字经济 商用终端成为企业“必需品”

    随着各行业数字化转型进程的不断推进 英特尔作为商用终端领域的 领路人 将继续联手生态伙伴推动商用领域生产工具的变革 赋能广大企业 机构用户最终实现业务创新和产业升级 助力中国经济高质量发展和数字中国建设 作者 贾贵鹏 来源 天极网 近年来
  • Uboot启动参数说明

    29 Uboot 启动参数说明 bootcmd cp b 0xc4200000 0x7fc0 0x200000 bootm 倒计时到 0 以后 自动执行的指令 bootdelay 2 baudrate 38400 串口波特率 一般使用 38
  • Springboot实现MQTT通信

    目录 一 MQTT简介 1 MQTT协议 2 MQTT协议特点 二 MQTT服务器搭建 三 使用Springboot整合MQTT协议 1 在父工程下创建一个Springboot项目作为消息的提供者 1 1 导入依赖包 1 2 修改配置文件
  • vue3 的 ref、 toRef 、 toRefs

    1 ref 对原始数据进行拷贝 当修改 ref 响应式数据的时候 模版中引用 ref 响应式数据的视图处会发生改变 但原始数据不会发生改变
  • 同行评审

    在IBM 微软等很多公司都有一个很好的实践 那就是代码复审 这种代码审查的过程 不是将代码发给某一个人或某几个人去看 而是强调程序员自己定期走上台 向其他人讲解自己源程序的活动 因为要向大家讲解自己的程序 程序员会极其重视自己的工作进度 代
  • SeleniumLibrary4.5.0 关键字详解(九)

    SeleniumLibrary4 5 0 关键字详解 九 库版本 4 5 0 库范围 全局 命名参数 受支持 简介 SeleniumLibrary是Robot Framework的Web测试库 本文档说明了如何使用SeleniumLibra
  • linux安装rz、sz上传下载文件工具

    在centos版本linux系统中执行如下命令 yum install lrzsz 如下图所以即可安装成功
  • windows 7编辑启动选项

    问题 开机之后 提示编辑启动选项 路径 windows system32 winload exe 分区 1 硬盘 f3c3f39 NOEXECUTE OPTIN 如图 解决步骤 1 按回车键 进入操作系统之后 查看启动项配置 msconfi
  • 自定义ZoomRecyclerView可缩放可点击

    可直接使用喔 public class PinchRecyclerView extends RecyclerView implements View OnTouchListener private static final int INVA