Android拖拽和缩放

拖拽和缩放

多点触控的理论学完了之后,这里开始实践。本节主要介绍使用onTouchEvent()方法处理触控事件。

拖动一个对象

如果你使用的是Android 3.0或者之后的系统,那么你可以使用内置的拖拽实践监听器`View.OnDragListener`。

用touch手势把一个对象从屏幕的一边拽到另一边是很常见的一种用法。以下代码展示了如何拖拽一个屏幕上的图片。但是主要一下内容:

  • 在一个拖动(或者叫做scroll)操作里,app需要知道开始触控点,不管有几根指头放在屏幕上只需要记住第一根指头的点。但是,在拖动图片的过程中,用户有放上了好几个手指,然后把第一个触控的手指拿开了屏幕。如果你的app只记录单个手指的触控操作,那么就需要把第二个手指的触控点作为默认触控点并把图片移动到该位置。

  • 为了防止上面的情况发生,你的app需要区分初始触控点和其他的触控点。要记录初始触控点以外的触控点,就需要用到前文所说的ACTION_POINTER_DOWNACTION_POINTER_UP事件。ACTION_POINTER_DOWNACTION_POINTER_UP可以在onTouchEvent()回调中取得。

  • ACTION_POINTER_UP发生的时候,下面的示例代码可以获取到index,并确保触控ID指向的触控事件是有效的。如果触控点已经无效,则获取一个有效的,并获取其X和Y坐标。这个坐标是在ACTION_MOVE事件中获取,图片将被移动到该位置。

以下代码会记录起始触控点的位置,追踪手指移动的新位置,并把图片移动到该位置。并按照上面描述的方法处理可能的异常情况。

注意以下代码用了getActionMasked()方法。为了保证兼容可以使MotionEventCompat.getActionMasked()来获取触控的MotionEvent

补充说明,为了不让读者态度困惑。所以没有使用Google的示例代码。原来的示例代码并没有突出在Activity中拖动一个View的功能。而是主要在一个View内部如何相应拖动和缩放功能。这和前文所述的主题有一定的不符合,所以笔者做了下面的修改。如有不妥请指出。

override fun onTouchEvent(event: MotionEvent?): Boolean {
    var action = MotionEventCompat.getActionMasked(event)

    when (action) {
        MotionEvent.ACTION_DOWN -> {
            val pointerIndex = MotionEventCompat.getActionIndex(event!!)
            val x = MotionEventCompat.getX(event!!, pointerIndex)
            val y = MotionEventCompat.getY(event!!, pointerIndex)

            mLastTouchX = x
            mLastTouchY = y

            mActivePointerId = MotionEventCompat.getPointerId(event, 0)
        }
        MotionEvent.ACTION_MOVE -> {
            val pointerIndex = MotionEventCompat.findPointerIndex(event!!, mActivePointerId)
            val x = MotionEventCompat.getX(event!!, pointerIndex)
            val y = MotionEventCompat.getY(event!!, pointerIndex)

            val dx = x - mLastTouchX!!
            val dy = y - mLastTouchY!!

            mPosX = mPosX ?: 0.0f + dx
            mPosY = mPosY ?: 0.0f + dy

            //                mImageView?.x = mPosX!!
            //                mImageView?.y = mPosY!!
            mImageView?.x = x
            mImageView?.y = y

            Log.d("##DRAG", "Pointer x- $x | y- $y")
            Log.d("##DRAG", "View x- ${mImageView?.x} y- ${mImageView?.y}")

            mLastTouchX = x
            mLastTouchY = y


        }
        MotionEvent.ACTION_UP -> {
            mActivePointerId = INVALID_POINTER_ID
        }
        MotionEvent.ACTION_CANCEL -> {
            mActivePointerId = INVALID_POINTER_ID
        }
        MotionEvent.ACTION_POINTER_UP -> {
            val pointerIndex = MotionEventCompat.getActionIndex(event!!)
            val pointerId = MotionEventCompat.getPointerId(event!!, pointerIndex)

            if (pointerId == mActivePointerId) {
                val newPointerIndex = if (pointerIndex == 0) 1 else 0
                mLastTouchX = MotionEventCompat.getX(event!!, newPointerIndex)
                mLastTouchY = MotionEventCompat.getY(event!!, newPointerIndex)
                mActivePointerId = MotionEventCompat.getPointerId(event!!, newPointerIndex)
            }
        }
    }

    return true
}

总结以下上述内容:

  1. 触控ID(pointer Id)可以唯一指定一个触控事件
  2. index对应的触控事件可能发生改变。但是,只有通过触控的index可以获得触控的坐标。

所以,使用的时候先在ACTION_DOWN里得到触控index,再用触控index获得触控Id存起来。之后每次在ACTION_MOVE事件中,用触控Id得到触控index,再用这个index得到触控的坐标。

在ACTION_UP、ACTION_CANCEL事件里把触控Id设置为空(或者无效)。在ACTION_POINTER_UP里把作废的触控Id置换为有效的触控Id。

这个是关于触控的坐标的。在默认的Activity里实现这个拖拽的功能,你会发现这个图片在拖动的一瞬间图片会下移一定的距离。由于图片的大小设定为50dp,下移的距离和这个距离非常接近。在设定为全屏的时候,下移不会再发生。说明触控点的坐标是基于全屏的,但是ImageView定位的坐标是基于当前的ViewGroup的。这一点也非常重要,如果你需要保留ActionBar的话,坐标数据需要从全屏转化为当前ImageView所在的ViewGroup的。

GestureDetector实现拖拽

前面的示例说明了如何用在onTouchEvent()里实现一个拖动。Android提供了一个更加简单的方法来实现拖拽。下面的代码演示了如何使用GestureDetector.SimpleOnGestureListeneronScroll()方法里实现拖动。

当用户在屏幕上拖动ImageView的时候,onScroll()就会被调用。用户的手指放到屏幕上的时候就会调用onScroll()。手指离开屏幕的时候,要么手势结束,要不就会触发一个fling手势(如果手指离开屏幕前以一定的速度滑动的话)。

下面是onScroll()的代码:

class DragDropScrollActivity : Activity() {
    private val TAG: String = DragDropScrollActivity::class.java.simpleName

    val mPanGesture: GestureDetector by lazy {
        GestureDetector(this@DragDropScrollActivity, MySimpleOnGestureListener())
    }

    var mImageView: ImageView? = null
    var mPosX: Float? = null
    var mPosY: Float? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_drag_drop_scroll)

        mImageView = findViewById(R.id.drag_drop_image_view) as ImageView
        mPosX = mImageView?.x
        mPosY = mImageView?.y
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        mPanGesture.onTouchEvent(event)
        return super.onTouchEvent(event)
    }

    inner class MySimpleOnGestureListener() : GestureDetector.SimpleOnGestureListener() {
        override fun onDown(e: MotionEvent?): Boolean {
            return true
        }

        override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean {
            mImageView?.x = mPosX!! - distanceX
            mImageView?.y = mPosY!! - distanceY

            mPosX = mImageView?.x
            mPosY = mImageView?.y

            Log.d(TAG, "distance X: $distanceX, Y: $distanceY")
            Log.d(TAG, "position X: ${mImageView!!.x} Y: ${mImageView!!.y}")

            return true
        }
    }
}

缩放

to be continued...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容