草庐IT

Android SDK - 不在触摸中心缩放

coder 2023-11-22 原文

我的应用程序有点问题。我刚刚实现了双指缩放,但是当我双指缩放时,图像不会在 2 个点的中心缩放,如下图所示。图 1 是我的手指在捏合之前的位置,图 2 显示它没有像它应该的那样在我的手指中心扩展。

图 1 与图 2:

这是我的代码:

        package com.jpiionefourone.guitarimage;

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

    public class MultiTouchView extends View {
        private Drawable mIcon;
        private float mPosX;
        private float mPosY;

        private float mLastTouchX;
        private float mLastTouchY;

        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 ScaleGestureDetector mScaleDetector;
        private float mScaleFactor = 1.f;


        public MultiTouchView(Context context) {
            this(context, null, 0);
        }

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

        public MultiTouchView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            mIcon = context.getResources().getDrawable(R.drawable.guitar);
            mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());

            // Create our ScaleGestureDetector
            mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());

        }

        @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()) {
                    final float dx = x - mLastTouchX;
                    final float dy = y - mLastTouchY;

                    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;
        }

        @Override
        public void onDraw(Canvas canvas) {
            super.onDraw(canvas);

            canvas.save();
            canvas.translate(mPosX, mPosY);
            canvas.scale(mScaleFactor, mScaleFactor);
            mIcon.draw(canvas);
            canvas.restore();
        }

        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));

                invalidate();
                return true;
            }
        }
    }

谢谢,

贾里德

最佳答案

在您的 ScaleListener 类中,您只调整 mScaleFactor 因此,实际上,您的图像确实不会围绕多点触控的中心缩放手势!

此外,您需要更新 mPosXmPosY 以使用不同的缩放中心。

对于我自己的一个应用程序,我使用了这样的东西:

final float scale = detector.getScaleFactor();
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor * scale, 5.0f));

if (mScaleFactor < 5f) {
   // 1 Grabbing
   final float centerX = detector.getFocusX();
   final float centerY = detector.getFocusY();
   // 2 Calculating difference
   float diffX = centerX - mPosX;
   float diffY = centerY - mPosY;
   // 3 Scaling difference
   diffX = diffX * scale - diffX;
   diffY = diffY * scale - diffY;
   // 4 Updating image origin
   mPosX -= diffX;
   mPosY -= diffY;
}

基本上,它通过以下方式计算图像原点的相对变化

  1. 获取多点触控手势的来源 (getFocus())
  2. 计算图像来源(diffXdiffY)之间的差异
  3. 应用比例因子
  4. 并使用缩放后的差异更新图像的原点

这(仍然)适用于我的情况。我不知道我们是否都使用 (d) 相同的坐标系,但像这样的东西应该可以帮助您在多点触控手势的中心正确缩放图像。

关于Android SDK - 不在触摸中心缩放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13961817/

有关Android SDK - 不在触摸中心缩放的更多相关文章

  1. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  2. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

  3. ruby-on-rails - 语言环境不在 Rails 4 中切换 - 2

    我的Rails应用程序在rails4.0.2上,我在使用locale变量和params[:locale]切换翻译时遇到问题官方railsguide.我在mysite有一个单页网站.我的国际化路线:scope"(:locale)",locale:/en|de/do#myrouteshereend我的应用程序Controllerbefore_filter:set_localedefset_localeI18n.locale=params[:locale]||I18n.default_locale#Rails.application.routes.default_url_options[:l

  4. ruby-on-rails - Phusion Passenger 不在 Apache 上工作 - 2

    更新:当输入“passenger-memory-stats”时,我显示:---Passengerprocesses---Processes:0我该如何解决这个问题?为什么即使我在httpd.conf中添加它并重新启动apache,passenger也不会启动?我无法让PhusionPassenger在服务器上运行RubyonRails。我已经按照Phusion网站上的所有说明安装了passenger并修改并创建了ApacheVirtualHost以指向新目录并验证所有.conf文件都已成功加载。还加载了httpd-Mpassenger_module。我还在本地主机上成功运行了Passe

  5. ruby-on-rails - `allow_any_instance_of` 模拟不在范围内工作 - 2

    我的mock只有在如下所示的beforeblock中时才有效。这只是我对我的问题的快速而肮脏的表述。从字面上看,当我将行从beforeblock移动到doesnotquack断言时,它停止模拟:(describe'Ducks',type::featuredobeforedo...allow_any_instance_of(Duck).toreceive(:quack).and_return('bark!')visitanimal_farm_pathendcontext'isanoddduck'it'doesnotquack'doexpect(Duck.new.quack).toeq('

  6. ruby - 如何不在 ruby​​ 中编写完整的模块路径? - 2

    假设我在一个非常长的模块路径中有一个类:sux=Really::Long::Module::Path::Sucks.new我能否以某种方式“导入”这个模块,这样我就可以直接使用这个类,而不必担心每次使用它时都要写这个路径?编辑:我知道在同一个模块中会让事情变得更容易。但在这种情况下,我不能在同一个模块中。 最佳答案 模块是ruby​​中的对象,因此您可以只引用较短的模块。Sux=Really::Long::Module::Path::SucksSux.new 关于ruby-如何不在rub

  7. ruby - Gem 在 irb 中加载但不在控制台中加载 - 2

    这个让我抓狂。我可以通过irb加载gem:steve@server:/var/www/listings$irbirb(main):001:0>Gem.path=>["/home/steve/.gem/ruby/1.9.1","/usr/local/ruby/lib/ruby/gems/1.9.1"]irb(main):002:0>require'nokogiri'=>true但我无法通过Rails控制台加载它:irb(main):001:0>Gem.path=>["/home/steve/.gem/ruby/1.9.1","/usr/local/ruby/lib/ruby/gems/1

  8. ruby-on-rails - 使用 geokit 或其他 ruby​​ gem 计算一系列地理坐标的中心 - 2

    我使用geokit和geokit-railsgemforrails有一段时间了,但我还没有找到答案的一个问题是如何找到一组点的计算聚合中心。我知道如何计算两点之间的距离,但不会超过2。我的理由是,我在同一个城市中有一系列的点……一切都完美的城市会有一个我可以使用的中心,但有些城市,比如柏林没有一个完美的中心。他们有多个中心,我只想使用我数据库中的所有地点列表来计算特定分布的中心。还有其他人遇到过这个问题吗?有什么建议吗?谢谢 最佳答案 之前从未使用过Geokit,这个操作背后的数学原理相对容易自己实现。假设这些点由纬度和经度组成,您

  9. ruby - 创建一个临时文件而不在 Ruby 中打开它 - 2

    有没有办法在不打开临时文件的情况下创建它?我必须运行一个可执行文件,将它的输出重定向到一个文件,然后读取并解析它。tempfile创建的所有内容都已打开,这会触发错误,因为文件已锁定。 最佳答案 你也可以使用Dir::TmpnameDir::Tmpname.create('your_application_prefix'){|path|putspath}路径将包含唯一路径参见https://github.com/ruby/ruby/blob/ruby_1_9_3/lib/tmpdir.rb#L116

  10. 有仰拍相机和俯拍相机时,俯拍相机中心和吸嘴中心的标定 - 2

    俯拍相机中心和吸嘴中心的标定文章目录俯拍相机中心和吸嘴中心的标定前言适用模型如下:一、使用一个标定片进行标定1.关键注意:2.标定步骤:二、使用一个L型的工件1.关键注意:2.标定步骤:总结前言在自动化设备领域,使用相机进行定位是很普遍存在的,而使用相机定位就必定会用到标定,本文介绍两种关于吸嘴上方的俯拍相机和吸嘴中心的标定方法(前提是带有仰拍相机和俯拍相机)。【还有很多相机的使用场景的标定方法将在以后的文章中进行阐述】适用模型如下:一、使用一个标定片进行标定1.关键注意:关键是使用两个相机的中心和识别偏差,得到两个相机的中心固定偏差。注:后续俯拍相机拍物料识别得到的偏差以吸嘴中心在俯拍相机中

随机推荐