【问题标题】:How do I move the ViewGroup while allowing the child view to handle clicks?如何在允许子视图处理点击的同时移动 ViewGroup?
【发布时间】:2017-10-16 12:15:49
【问题描述】:

我有 FrameLayout 封装了两个 ImageViews。这是我想要的:

在用户触摸和拖动时移动整个布局,但当点击时,应由个人ImageViews(通过他们各自的View.OnClickListener())处理

当前行为: 目前,当我尝试拖动视图组时,它会随机跳转一次(每个拖动事件),然后开始用手指移动。因此,就像您将手指移到视图组外,它也在移动。

其次,没有点击事件被路由到子ImageViews

我尝试过的:

我已尝试扩展封装ImageViews 的FrameLayout

public class ChatHeadFrameLayout extends FrameLayout {

    /**
     * Store the initial touch down x coordinate
     */
    private float initialTouchX;
    /**
     * Store the initial touch down y coordinate
     */
    private float initialTouchY;

    public ChatHeadFrameLayout(@NonNull Context context) {
        super(context);
    }

    public ChatHeadFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ChatHeadFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(21)
    public ChatHeadFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        // If the event is a move, the ViewGroup needs to handle it and return
        // the indication here
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:

                //remember the initial position
                initialTouchX = event.getRawX();
                initialTouchY = event.getRawY();
                return true;
        }
        return false;

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Will be called in case we decide to intercept the event
        // Handle the view move-with-touch behavior here

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:

                //remember the initial position
                initialTouchX = event.getRawX();
                initialTouchY = event.getRawY();
                return true;

            case MotionEvent.ACTION_UP:
                int Xdiff = (int) (event.getRawX() - initialTouchX);
                int Ydiff = (int) (event.getRawY() - initialTouchY);


                //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
                //So that is click event.
                        if (Xdiff < 10 && Ydiff < 10) {
                            return false;
                        }
                return true;

            case MotionEvent.ACTION_MOVE:
                //Calculate the X and Y coordinates of the view.
                setX(event.getRawX() - initialTouchX);
                setY(event.getRawY() - initialTouchY);


                //Update the layout with new X & Y coordinate
                invalidate();

                return true;
        }
        return false;
    }
}

那么,这段代码有什么问题?

【问题讨论】:

    标签: android


    【解决方案1】:

    首先getRawX()的结果被翻译到屏幕0,0,getX()是相对于自己的view的。

    另外,initialX 和 getCurrentX 的累积将只是拖动的值,您需要存储屏幕 x 并与拖动一起使用或计算 ACTION_MOVE 的每个差异并使用它

                //remember the initial position
                initialTouchX = event.getX();
                initialTouchY = event.getY();
                screenX = getX();
                screenY = getY();
    

    然后 ON_MOVE

                setX(screenX + event.getX() - initialTouchX);
                setY(screenY + event.getY() - initialTouchY);
    

    其次,您必须调用 super.onTouchEvent(MotionEvent.obtain(event)) 或 super.dispatchTouchEvent 作为 onTouchEvent 的第一行,因为您的视图正在消耗所有事件。

    【讨论】:

    • 我想通了。虽然您的 getX()(即相对距离策略)建议适用于正确计算偏移量,但我必须针对整体要求制定不同的策略。查看我的解决方案。
    • 这就是我提出的解决方案。如果这个答案有帮助请点赞,如果解决了请采纳为正确
    • 您提出的解决方案缺少以下内容:1) 调用super.onInterceptTouchEvent(event); 的建议,坦率地说这是解决方案的关键部分 2) 描述应该实施哪种方法,onTouchEvent()onInterceptTouchEvent() --我最终花了很多时间来调整流程。为计算投票。
    • 哦,对了,你确实问了两个问题,抱歉没有回答另一个问题
    【解决方案2】:

    结果比我预期的要简单得多(请参阅下面评论中的解释):

    @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            // The plan is to intercept the events here and if,
            // 1) The move is an ACTION_DOWN, return false indicating we want more events
            // to be able to ascertain if we want this ViewGroup to move or the children
            // views to handle the click event.
            // 2) On receiving the ACTION_MOVE, of course, move the ViewGroup but the sure short
            // indication of click would be to check it for in ACTION_DOWN with threshold of 5px.
            // In that case, we would return super.onInterceptTouchEvent(event); meaning, let the
            // event handling happen normally without interception
            // (Remember - the ACTION_DOWN had been recorded by the children previously when we
            // returned false and on receiving the ACTION_UP, it would constitute the complete event)
            switch (event.getAction()) {
    
                case MotionEvent.ACTION_DOWN:
    
                    // remember the initial position
                    initialTouchX = event.getX();
                    //Log.d(TAG, "onInterceptTouchEvent().ACTION_DOWN.initialTouchX: " + initialTouchX);
                    initialTouchY = event.getY();
                    //Log.d(TAG, "onInterceptTouchEvent().ACTION_DOWN.initialTouchY: " + initialTouchY);
                    return false;
    
                case MotionEvent.ACTION_UP:
                    int xDiff = (int) (event.getX() - initialTouchX);
                    int yDiff = (int) (event.getY() - initialTouchY);
    
    
                    //The check for Xdiff <5 && YDiff< 5 because sometime elements moves a little while clicking.
                    //So that is click event.
                    if (xDiff < 5 && yDiff < 5) {
                        return super.onInterceptTouchEvent(event);
                    }
                    return false;
    
                case MotionEvent.ACTION_MOVE:
    
                    int moveX = (int) (event.getX() - initialTouchX + getX());
                    int moveY = (int) (event.getY() - initialTouchY + getY());
    
                    setX(moveX);
                    setY(moveY);
    
                    //Update the layout with new X & Y coordinate
                    invalidate();
            }
            return false;
    
        }
    

    此解决方案有一个警告:点击事件仅基于移动的距离。通常,为了区分单击和长按,您还可以执行this 之类的操作:

    private long lastTouchDown;
    private static int CLICK_ACTION_THRESHHOLD = 200;
    
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastTouchDown = System.currentTimeMillis();
                break;
            case MotionEvent.ACTION_UP:
                if (System.currentTimeMillis() - lastTouchDown < CLICK_ACTION_THRESHHOLD) {
                    Log.w("App", "You clicked!");
                }
                break;
        }
        return true;
    }
    

    P.S:在提出的解决方案中不需要覆盖onTouchEvent() 方法。

    【讨论】:

      猜你喜欢
      • 2021-09-23
      • 1970-01-01
      • 2012-07-22
      • 1970-01-01
      • 2023-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多