Android控件全解手册 - 任意View缩放平移工具-源码

news2024/11/23 21:08:26
Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总
游戏脚本-辅助自动化Android控件全解手册再战Android系列
Scratch编程案例软考全系列Unity3D学习专栏
蓝桥系列ChatGPT和AIGC

👉关于作者

专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)
有什么需要欢迎底部卡片私我,交流让学习不再孤单

在这里插入图片描述

👉实践过程

GestureViewBinder

public class GestureViewBinder {

    private ScaleGestureBinder scaleGestureBinder;
    private ScrollGestureBinder scrollGestureBinder;
    private ScaleGestureListener scaleGestureListener;
    private ScrollGestureListener scrollGestureListener;
    private View targetView;
    private ViewGroup viewGroup;
    private boolean isScaleEnd = true;

    private OnScaleListener onScaleListener;

    private boolean isFullGroup = false;

    public static GestureViewBinder bind(Context context, ViewGroup viewGroup, View targetView) {
        return new GestureViewBinder(context, viewGroup, targetView);
    }

    private GestureViewBinder(Context context, ViewGroup viewGroup, View targetView) {
        this.targetView = targetView;
        this.viewGroup = viewGroup;
        scaleGestureListener = new ScaleGestureListener(targetView, viewGroup);
        scrollGestureListener = new ScrollGestureListener(targetView, viewGroup);
        scaleGestureBinder = new ScaleGestureBinder(context, scaleGestureListener);
        scrollGestureBinder = new ScrollGestureBinder(context, scrollGestureListener);
        targetView.setClickable(false);
        viewGroup.setOnTouchListener(new View.OnTouchListener() {
            @SuppressLint("ClickableViewAccessibility")
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getPointerCount() == 1 && isScaleEnd) {
                    return scrollGestureBinder.onTouchEvent(event);
                } else if (event.getPointerCount() == 2 || !isScaleEnd) {
                    isScaleEnd = event.getAction() == MotionEvent.ACTION_UP;
                    if (isScaleEnd) {
                        scaleGestureListener.onActionUp();
                    }
                    scrollGestureListener.setScale(scaleGestureListener.getScale());
                    if (onScaleListener != null) {
                        onScaleListener.onScale(scaleGestureListener.getScale());
                    }
                    return scaleGestureBinder.onTouchEvent(event);
                }
                return false;
            }
        });
    }

    private void fullGroup() {
        targetView.getViewTreeObserver().addOnPreDrawListener(
                new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        targetView.getViewTreeObserver().removeOnPreDrawListener(this);
                        float viewWidth = targetView.getWidth();
                        float viewHeight = targetView.getHeight();
                        float groupWidth = viewGroup.getWidth();
                        float groupHeight = viewGroup.getHeight();
                        ViewGroup.LayoutParams layoutParams = targetView.getLayoutParams();
                        float widthFactor = groupWidth / viewWidth;
                        float heightFactor = groupHeight / viewHeight;
                        if (viewWidth < groupWidth && widthFactor * viewHeight <= groupHeight) {
                            layoutParams.width = (int) groupWidth;
                            layoutParams.height = (int) (widthFactor * viewHeight);
                        } else if (viewHeight < groupHeight && heightFactor * viewWidth <= groupWidth) {
                            layoutParams.height = (int) groupHeight;
                            layoutParams.width = (int) (heightFactor * viewWidth);
                        }
                        targetView.setLayoutParams(layoutParams);
                        return true;
                    }
                });
    }

    public boolean isFullGroup() {
        return isFullGroup;
    }

    public void setFullGroup(boolean fullGroup) {
        isFullGroup = fullGroup;
        scaleGestureListener.setFullGroup(fullGroup);
        scrollGestureListener.setFullGroup(fullGroup);
        fullGroup();
    }

    public void setOnScaleListener(OnScaleListener onScaleListener) {
        this.onScaleListener = onScaleListener;
    }

    public interface OnScaleListener {
        void onScale(float scale);
    }
}

ScaleGestureBinder

public class ScaleGestureBinder extends ScaleGestureDetector {

    ScaleGestureBinder(Context context, ScaleGestureListener scaleGestureListener) {
        super(context, scaleGestureListener);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }

}

ScaleGestureListener

public class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener/*, GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener */ {

    private View targetView;
    private float scale = 1;
    private float scaleTemp = 1;

    private boolean isFullGroup = false;

    ScaleGestureListener(View targetView, ViewGroup viewGroup) {
        this.targetView = targetView;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        scale = scaleTemp * detector.getScaleFactor();
        targetView.setScaleX(scale);
        targetView.setScaleY(scale);
        return false;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        scaleTemp = scale;
    }



    float getScale() {
        return scale;
    }

    public boolean isFullGroup() {
        return isFullGroup;
    }

    void setFullGroup(boolean fullGroup) {
        isFullGroup = fullGroup;
    }

    void onActionUp() {
        if (isFullGroup && scaleTemp < 1) {
            scale = 1;
            targetView.setScaleX(scale);
            targetView.setScaleY(scale);
            scaleTemp = scale;
        }
    }
}

ScrollGestureBinder

class ScrollGestureBinder extends GestureDetector {

    ScrollGestureBinder(Context context, ScrollGestureListener scrollGestureListener) {
        super(context, scrollGestureListener);
    }
}

ScrollGestureListener

public class ScrollGestureListener extends GestureDetector.SimpleOnGestureListener {

    private float scale = 1;
    private View targetView;
    private ViewGroup viewGroup;
    private float distanceXTemp = 0;
    private float distanceYTemp = 0;

    private float viewWidthReal = 0;
    private float viewHeightReal = 0;

    private float viewWidthRealTemp = 0;
    private float viewHeightRealTemp = 0;

    private boolean isCalculate = false;
    private int viewWidthNormal = 0;
    private int viewHeightNormal = 0;
    private int groupWidth = 0;
    private int groupHeight = 0;
    private float maxTranslationLeft = 0;
    private float maxTranslationTop = 0;
    private float maxTranslationRight = 0;
    private float maxTranslationBottom = 0;

    private boolean isFullGroup = false;

    ScrollGestureListener(View targetView, ViewGroup viewGroup) {
        this.targetView = targetView;
        this.viewGroup = viewGroup;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

        distanceX = -distanceX;
        distanceY = -distanceY;

        if (isFullGroup || scale > 1) {
            if (viewWidthReal > groupWidth) {
                translationXOnScrollEvent(distanceX);
            }
            if (viewHeightReal > groupHeight) {
                translationYOnScrollEvent(distanceY);
            }
        } else {
            translationXOnScrollEvent(distanceX);
            translationYOnScrollEvent(distanceY);
        }

        return super.onScroll(e1, e2, distanceX, distanceY);
    }

    private void translationXOnScrollEvent(float distanceX) {
        //最大移动距离全部为正数,所以需要通过判断distanceX的正负,来判断是向左移动还是向右移动,
        // 然后通过取distanceX的绝对值来和相应移动方向的最大移动距离比较
        if ((distanceX < 0 && Math.abs(distanceXTemp + distanceX) < maxTranslationLeft)
                || (distanceX > 0 && distanceXTemp + distanceX < maxTranslationRight)) {
            distanceXTemp += distanceX;
            targetView.setTranslationX(distanceXTemp);
            //如果超出边界,就移动到最大距离,防止边界有剩余量
        } else if ((distanceX < 0 && Math.abs(distanceXTemp + distanceX) > maxTranslationLeft)) {
            distanceXTemp = -maxTranslationLeft;
            targetView.setTranslationX(-maxTranslationLeft);
        } else if ((distanceX > 0 && distanceXTemp + distanceX > maxTranslationRight)) {
            distanceXTemp = maxTranslationRight;
            targetView.setTranslationX(maxTranslationRight);
        }
    }

    private void translationYOnScrollEvent(float distanceY) {
        if ((distanceY < 0 && Math.abs(distanceYTemp + distanceY) < maxTranslationTop)
                || (distanceY > 0 && distanceYTemp + distanceY < maxTranslationBottom)) {
            distanceYTemp += distanceY;
            targetView.setTranslationY(distanceYTemp);
            //如果超出边界,就移动到最大距离,防止边界有剩余量
        } else if ((distanceY < 0 && Math.abs(distanceYTemp + distanceY) > maxTranslationTop)) {
            distanceYTemp = -maxTranslationTop;
            targetView.setTranslationY(-maxTranslationTop);
        } else if ((distanceY > 0 && distanceYTemp + distanceY > maxTranslationBottom)) {
            distanceYTemp = maxTranslationBottom;
            targetView.setTranslationY(maxTranslationBottom);
        }
    }

    @Override
    public boolean onDown(MotionEvent e) {
        //计算能移动的最大距离
        if (!isCalculate) {
            isCalculate = true;
            maxTranslationLeft = targetView.getLeft();
            maxTranslationTop = targetView.getTop();
            maxTranslationRight = viewGroup.getWidth() - targetView.getRight();
            maxTranslationBottom = viewGroup.getHeight() - targetView.getBottom();
            viewWidthNormal = targetView.getWidth();
            viewHeightNormal = targetView.getHeight();
            viewWidthRealTemp = viewWidthNormal;
            viewHeightRealTemp = viewHeightNormal;
            viewWidthReal = viewWidthNormal;
            viewHeightReal = viewHeightNormal;
            groupWidth = viewGroup.getWidth();
            groupHeight = viewGroup.getHeight();
        }
        return true;
    }

    void setScale(float scale) {

        viewWidthReal = viewWidthNormal * scale;
        viewHeightReal = viewHeightNormal * scale;
        //如果view比group小
        if (viewWidthReal < groupWidth) {
            if (isFullGroup) {
                distanceXTemp = 0;
                targetView.setTranslationX(0);
            }
            maxTranslationLeft = targetView.getLeft() - (viewWidthReal - viewWidthNormal) / 2;
            maxTranslationRight = (viewGroup.getWidth() - targetView.getRight()) - (viewWidthReal - viewWidthNormal) / 2;
            //如果移动距离超过最大可移动距离
            if (scale > this.scale && distanceXTemp < 0 && -distanceXTemp > maxTranslationLeft) {
                float translate = (viewWidthReal - viewWidthRealTemp) / 2;
                targetView.setTranslationX(targetView.getTranslationX() + translate);
                distanceXTemp = distanceXTemp + translate;
            } else if (scale > this.scale && distanceXTemp > 0 && distanceXTemp > maxTranslationRight) {
                float translate = (viewWidthReal - viewWidthRealTemp) / 2;
                targetView.setTranslationX(targetView.getTranslationX() - translate);
                distanceXTemp = distanceXTemp - translate;
            }

        } else {
            maxTranslationLeft = (viewWidthReal - viewWidthNormal) / 2 - (viewGroup.getWidth() - targetView.getRight());
            maxTranslationRight = (viewWidthReal - viewWidthNormal) / 2 - targetView.getLeft();
            if (scale < this.scale && distanceXTemp < 0 && -distanceXTemp > maxTranslationLeft) {
                float translate = (viewWidthRealTemp - viewWidthReal) / 2;
                targetView.setTranslationX(targetView.getTranslationX() + translate);
                distanceXTemp = distanceXTemp + translate;
            } else if (scale < this.scale && distanceXTemp > 0 && distanceXTemp > maxTranslationRight) {
                float translate = (viewWidthRealTemp - viewWidthReal) / 2;
                targetView.setTranslationX(targetView.getTranslationX() - translate);
                distanceXTemp = distanceXTemp - translate;
            }
        }

        if (viewHeightReal < groupHeight) {
            maxTranslationTop = targetView.getTop() - (viewHeightReal - viewHeightNormal) / 2;
            maxTranslationBottom = (viewGroup.getHeight() - targetView.getBottom()) - (viewHeightReal - viewHeightNormal) / 2;
            if (isFullGroup) {
                distanceYTemp = 0;
                targetView.setTranslationY(0);
            }
            //如果移动距离超过最大可移动距离
            if (scale > this.scale && distanceYTemp < 0 && -distanceYTemp > maxTranslationTop) {
                float translate = (viewHeightReal - viewHeightRealTemp) / 2;
                targetView.setTranslationY(targetView.getTranslationY() + translate);
                distanceYTemp = distanceYTemp + translate;
            } else if (scale > this.scale && distanceYTemp > 0 && distanceYTemp > maxTranslationBottom) {
                float translate = (viewHeightReal - viewHeightRealTemp) / 2;
                targetView.setTranslationY(targetView.getTranslationY() - translate);
                distanceYTemp = distanceYTemp - translate;
            }
        } else {
            maxTranslationTop = (viewHeightReal - viewHeightNormal) / 2 - (viewGroup.getHeight() - targetView.getBottom());
            maxTranslationBottom = (viewHeightReal - viewHeightNormal) / 2 - targetView.getTop();
            if (scale < this.scale && distanceYTemp < 0 && -distanceYTemp > maxTranslationTop) {
                float translate = (viewHeightRealTemp - viewHeightReal) / 2;
                targetView.setTranslationY(targetView.getTranslationY() + translate);
                distanceYTemp = distanceYTemp + translate;
            } else if (scale < this.scale && distanceYTemp > 0 && distanceYTemp > maxTranslationBottom) {
                float translate = (viewHeightRealTemp - viewHeightReal) / 2;
                targetView.setTranslationY(targetView.getTranslationY() - translate);
                distanceYTemp = distanceYTemp - translate;
            }
        }
        viewWidthRealTemp = viewWidthReal;
        viewHeightRealTemp = viewHeightReal;
        this.scale = scale;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        float left = viewWidthReal > groupWidth ? 0 : (targetView.getLeft() - ((viewWidthReal - viewWidthNormal) / 2));
        float top = viewHeightReal > groupHeight ? 0 : (targetView.getTop() - ((viewHeightReal - viewHeightNormal) / 2));
        float right = viewWidthReal > groupWidth ? groupWidth : viewGroup.getWidth() - ((viewGroup.getWidth() - targetView.getRight()) - (viewWidthReal - viewWidthNormal) / 2);
        float bottom = viewHeightReal > groupHeight ? groupHeight : viewGroup.getHeight() - ((viewGroup.getHeight() - targetView.getBottom()) - (viewHeightReal - viewHeightNormal) / 2);
        RectF rectF = new RectF(left, top, right, bottom);
        if (rectF.contains(e.getX(), e.getY())) {
            targetView.performClick();
        }
        return super.onSingleTapUp(e);
    }

    public boolean isFullGroup() {
        return isFullGroup;
    }

    void setFullGroup(boolean fullGroup) {
        isFullGroup = fullGroup;
    }
}

👉其他

📢作者:小空和小芝中的小空
📢转载说明-务必注明来源:https://zhima.blog.csdn.net/
📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。

温馨提示点击下方卡片获取更多意想不到的资源。
空名先生

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1265842.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数学建模-基于BL回归模型和决策树模型对早产危险因素的探究和预测

整体求解过程概述(摘要) 近年来&#xff0c;全球早产率总体呈上升趋势&#xff0c;在我国&#xff0c;早产儿以每年 20 万的数目逐年递增&#xff0c;目前早产已经成为重大的公共卫生问题之一。据研究,早产是威胁胎儿及新生儿健康的重要因素&#xff0c;可能会造成死亡或智力体…

每日一题:LeetCode-202.面试题 08.06. 汉诺塔问题

每日一题系列&#xff08;day 07&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…

20世纪30年代的大危机

背景 1929年9月&#xff0c;美国财政部部长安德鲁梅隆向公众保证“现在没有担心的理由&#xff0c;这一繁荣的高潮将会继续下去”。 当时流行的一首儿歌&#xff1a;“梅隆拉响汽笛&#xff0c;胡佛敲起钟&#xff0c;华尔街发出信号&#xff0c;美国往地狱里冲&#xff01;”…

水库大坝安全在线监测系统守护水利工程的坚实屏障

随着科技的发展&#xff0c;水库大坝的安全监测已经进入了一个全新的时代。过去&#xff0c;我们无法实时监测大坝的安全状况&#xff0c;只能在灾难发生后进行补救&#xff0c;现在&#xff0c;通过WX-DB1水库大坝安全在线监测系统&#xff0c;我们能够在第一时间掌握大坝的运…

【创建和排查隐藏进程和隐藏计划任务】

Window 创建隐藏进程和隐藏计划任务&#xff1a; 隐藏进程&#xff1a; 在Windows中&#xff0c;隐藏进程主要通过修改进程属性或使用第三方工具实现。以下是一个使用PowerShell脚本创建隐藏进程的示例&#xff1a; $Script {Start-Process -FilePath "notepad.exe"…

Kubernetes Pod 介绍

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容Pod 介绍与原理讲解Pod 生命周期管理Pod 的健康检查 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51…

Peter算法小课堂—高精度减法

给大家看个小视频高精度减法_哔哩哔哩_bilibili 基本思想 计算机模拟人类做竖式计算&#xff0c;从而得到正确答案 大家还记得小学时学的“减法竖式”吗&#xff1f;是不是这样 x-y问题 函数总览&#xff1a; 1.converts() 字符串转为高精度大数 2.le() 判断大小 3.sub() …

这个蓄电池监控神技,谁用谁知道!

随着电力需求的不断增长&#xff0c;蓄电池作为能量存储的关键组件在各个领域得到了广泛应用&#xff0c;为了确保蓄电池的可靠性和性能&#xff0c;监控系统变得至关重要。 蓄电池监控系统可以实时监测电池的状态、健康状况以及充放电过程&#xff0c;从而提高电池的寿命、降低…

比尔盖茨:GPT-5不会比GPT-4好多少,生成式AI已达到极限

比尔盖茨一句爆料&#xff0c;成为机器学习社区热议焦点&#xff1a; “GPT-5不会比GPT-4好多少。” 虽然他已不再正式参与微软的日常运营&#xff0c;但仍在担任顾问&#xff0c;并且熟悉OpenAI领导团队的想法。 消息来自德国《商报》&#xff08;Handelsblatt&#xff09;对…

麒麟操作系统光盘救援模式

麒麟操作系统光盘救援模式 Kylin V4 桌面版&#xff1a; 启动主机后&#xff0c;插入系统光盘&#xff0c;在 BIOS 启动项里设置成从光盘启动后保存退出重启主机。 稍等片刻就会到启动菜单选项&#xff0c;到启动菜单界面后选择第一项试用银河麒麟操作系统而不安 装&#xff…

酷开系统 | 追求娱乐不止一种方式,酷开科技带你开启新体验!

在当今社会&#xff0c;娱乐方式多种多样&#xff0c;人们对于娱乐的需求和追求也在日益增长。然而&#xff0c;传统的娱乐方式已经无法满足大家对于多元化、个性化的体验需求。此时&#xff0c;酷开科技以其独特的视角和领先的技术&#xff0c;为消费者们带来了全新的娱乐体验…

DockerCompose修改某个服务的配置(添加或编辑端口号映射)后如何重启单个服务使其生效

场景 docker-compose入门以及部署SpringBootVueRedisMysql(前后端分离项目)以若依前后端分离版为例&#xff1a; docker-compose入门以及部署SpringBootVueRedisMysql(前后端分离项目)以若依前后端分离版为例_docker-compose部署java mysql redis-CSDN博客 上面讲了docker c…

数据安全建设的六大关键步骤

随着数字化时代的到来&#xff0c;数据安全已经成为企业和社会组织必须面对的重要问题。数据泄露、网络攻击等安全事件频发&#xff0c;给个人隐私、企业利益和国家安全带来了严重威胁。因此&#xff0c;加强数据安全建设已成为刻不容缓的任务。以下是数据安全建设的六大关键步…

解决Maven项目jar包下载失败的问题

文章目录 配置国内的Maven源引入正确的settings.xml文件重新下载jar包对后面要创建的新项目也统一配置仍然失败的解决办法 配置国内的Maven源 引入正确的settings.xml文件 如果该目录下的 settings.xml文件不存在或者错误&#xff0c;要创建一个 settings.xml文件并写入正确的…

SVD recommendation systems

SVD recommendation systems 为什么在推荐系统中使用SVD 一个好的推荐系统一定有小的RMSE R M S E 1 m ∑ i 1 m ( Y i − f ( x i ) 2 RMSE \sqrt{\frac{1}{m} \sum_{i1}^m(Y_i-f(x_i)^2} RMSEm1​i1∑m​(Yi​−f(xi​)2 ​ 希望模型能够在已知的ratings上有好的结果的…

物理层之三种数据交换方式(电路交换、报文交换、分组交换(数据报方式、虚电路方式))

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

idea打开.class文件没有反编译

1 问题描述 新安装的idea开发工具&#xff0c;打开.class文件查看内容时发现没有将文件进行反编译&#xff0c;所以具体的代码实现看不到。如图所示&#xff1a; 尝试了各种办法解决&#xff0c;最终都没有解决我的问题&#xff0c;其他同事的idea开发工具都可以打开.class文件…

mac电脑下载Netflix Mac(奈飞客户端)安装教程

Netflix Mac&#xff0c;奈飞官方客户端&#xff0c;带给您无限的电影和剧集体验&#xff01;与朋友分享最新热门剧集、电影&#xff0c;与家人一起享受高品质的流媒体内容。 通过Netflix Mac&#xff0c;您可以轻松地搜索、浏览和观看各种类型的影片&#xff0c;包括剧情片、…

【RESTful API】RESTful接口设计练习

参考: BV1Ps4y1J7Ve ---------------------------------------------------------------------------------------------------------- 一、RESTful框架 常见的有SpringMVC,jersey,play 二、API测试工具 Postman,Insomnia 三、RESTful接口设计练习 3.1 项目准备 构…

java设计模式 开闭原则

开闭原则&#xff08;Open-Closed Principle&#xff0c;OCP&#xff09;是面向对象设计中的一个重要原则&#xff0c;它指导着我们如何设计和组织代码&#xff0c;以便使系统在扩展性和可维护性方面更加优秀。 开闭原则的定义是&#xff1a;软件实体&#xff08;类、模块、函数…