search

Home  >  Q&A  >  body text

关于android如何实现view组件的drag功能的疑问

我尝试实现一个在屏幕上拖动view, 让view移动的功能.

我是这样做得:

但是出现了下面很奇怪的问题: (见录制的屏幕)

似乎按钮只会在某个小窗口内部分是visible的, 其他的话就被盖住.

可是自己设置了framelayout的大小都是match_parent, 可能framelayout给每个fragment分配的空间太小了?
可是我尝试改变了view的maxwidth也不行.....

自己不太知道该如何解决, 希望大家帮忙! 多谢!

PHP中文网PHP中文网2772 days ago833

reply all(1)I'll reply

  • 怪我咯

    怪我咯2017-04-17 13:08:26

    Listening to TouchEvent is pretty much what you think. I usually do it this way.

    1. After the Down event occurs, obtain the cache bitmap of the View that needs to be moved

    Bitmap bitmap = targetView.getDrawingCache()
    或者调用targetView的draw方法绘制在自己创建的canvas上来得到当前快照
    

    2. Create a new ImageView and throw the snapshot into it
    3. Use WindowMananger to add a layer to the screen, throw the ImageView to the original position of your button, and hide your original button
    4. Then the rest is the same as your idea, you just need to move the ImageView in WindowManager... This layer is full screen... unrestricted
    5.After ACTION_UP...delete the content of WindowMananger and move your Button to the position where Action_up occurs.

    What you do is not recommended because directly modifying the position of the View... will cause the entire View tree to be re-layout... which will greatly affect performance..
    And what you want to change is not setx sety....but xy
    in LayoutParamters View's setx sety is the starting position of drawing...equivalent to setTranslationX/Y. The view does not move at all...


    Supplement:
    1. Please refer to this code for the WindowMananger solution:

     wm = (WindowManager) getApplicationContext().getSystemService("window");  
    
        wmParams = new WindowManager.LayoutParams();  
        wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;  
        wmParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;  
        wmParams.gravity = Gravity.LEFT | Gravity.TOP;  
    
        wmParams.x = 0;  
        wmParams.y = 0;  
    
        wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
        wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
        wmParams.format = PixelFormat.RGBA_8888;  
    
        wm.addView(view, wmParams);  
    
        view.setOnTouchListener(new OnTouchListener() {  
    
            public boolean onTouch(View v, MotionEvent event) {  
    
                x = event.getRawX();  
                y = event.getRawY();  
    
                switch (event.getAction()) {  
    
                case MotionEvent.ACTION_DOWN:  
                    mTouchStartX = event.getX();  
                    mTouchStartY = event.getY() + view.getHeight() / 2;  
                    break;  
    
                case MotionEvent.ACTION_MOVE:  
                    updateViewPosition();  
                    break;  
    
                case MotionEvent.ACTION_UP:  
                    updateViewPosition();  
                    mTouchStartX = mTouchStartY = 0;  
                    break;  
                }  
    
                return true;  
            }  
        });  
    

    --

         private void updateViewPosition() {  
    
        wmParams.x = (int) (x - mTouchStartX);  
        wmParams.y = (int) (y - mTouchStartY);  
        wm.updateViewLayout(view, wmParams);  
    }  
    

    2.ViewGroup actually has a private field of ClipChildren to control whether the childview should be drawn when it exceeds the sublayout.
    An application example: https://github.com/dkmeteor/CircleList

    Core code:

    public void changeGroupFlag(Object obj) throws Exception
    {
        Field[] f = obj.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredFields(); // 获得成员映射数组
        for (Field tem : f)
        {
        if (tem.getName().equals("mGroupFlags")) {
        tem.setAccessible(true);
        Integer mGroupFlags = (Integer)tem.get(obj);
        int newGroupFlags = mGroupFlags & 0xfffff8;
        tem.set(obj, newGroupFlags);
        }
        }
    }
    

    Note that this attribute is a private field. In this demo, reflection is used to modify the mGroupFlags field...不建议使用.

    3. How to set the position in setLayoutParams like setX, setY
    That depends on the type of LayoutParams, which in turn depends on the parent layout of the View
    There are many, many types of LayoutParams
    For example, there are many types such as ViewGroup.LayoutParams RelativeLayout.LayoutParams WindowManager.LayoutParams, etc., each of which supports different attributes.

    For example, AbsoluteLayout.LayoutParams has x y attributes that can directly set the position
    But LayoutParams.LayoutParams can only set margin

    4. Your understanding of the View drawing process is biased.
    This part is a bit complicated... View's drawing is done by itself... View will not draw parts beyond its own bounds/rect area... I can't explain clearly...
    You can refer to Android 5.0 View source code line 14959~14977 (the section in the middle of View.draw) about the logic of the clipRect part

    The method I used in CircleList in answer 2 above actually ended up modifying the value of ViewGroup.mGroupFlags through reflection. This value will affect the logic of the Clip part when drawing sub-Views in the drawing process,

    PS. I tried writing it and found that I couldn’t explain the problem clearly....

    reply
    0
  • Cancelreply