我尝试实现一个在屏幕上拖动view, 让view移动的功能.
我是这样做得:
但是出现了下面很奇怪的问题: (见录制的屏幕)
似乎按钮只会在某个小窗口内部分是visible的, 其他的话就被盖住.
可是自己设置了framelayout的大小都是match_parent, 可能framelayout给每个fragment分配的空间太小了? 可是我尝试改变了view的maxwidth也不行.....
自己不太知道该如何解决, 希望大家帮忙! 多谢!
认证0级讲师
监听TouchEvent和你想的差不多..我一般这样操作.
1.Down事件发生以后,获得需要移动的View的cache bitmap
Bitmap bitmap = targetView.getDrawingCache() 或者调用targetView的draw方法绘制在自己创建的canvas上来得到当前快照
2.新建一个ImageView,把快照扔进去 3.使用WindowMananger在屏幕上添加一个图层,把ImageView扔到你的按钮原来的位置上,把你原来那个按钮隐藏 4.然后剩下的和你的思路一致,你只要移动WindowManager里的ImageView就好了...这个图层是全屏...不受限制 5.ACTION_UP以后...把WindowMananger的内容删掉,把你的Button移到Action_up发生的位置.
不推荐的你 做法是因为 直接修改View的位置...会引起整个View tree的重布局...非常影响性能.. 而且你要改的不是setx sety....是LayoutParamters里的xy View的setx sety是绘图的起始位置...相当于setTranslationX/Y view根本就没动...
补充: 1.关于WindowMananger方案请参考这段代码:
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其实有个ClipChildren的私有字段控制当childview超出子布局的时候是否要绘制. 一个应用的例子: https://github.com/dkmeteor/CircleList
核心代码:
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); } } }
注意这个属性是私有字段,该Demo中使用反射修改了mGroupFlags字段...不建议使用.
不建议使用
3.如何在setLayoutParams里面像setX, setY一样设置位置 那取决于LayoutParams的种类,LayoutParams又取决于该View的父布局 LayoutParams有很多很多种 比如 ViewGroup.LayoutParams RelativeLayout.LayoutParams WindowManager.LayoutParams 等很多种,每一种支持的属性不一样.
比如AbsoluteLayout.LayoutParams 就有 x y属性可以直接设置位置 但是 LayoutParams.LayoutParams 就只能设置margin
4.你对View绘制流程的理解有偏差. 这部分有点复杂......View的绘制是由自身完成的...View不会绘制超出自己bounds/rect区域以外的部分....我讲不大清楚... 你可以参考Android 5.0 View源码 14959行~14977行(在View.draw 中间那一段) 关于clipRect部分的逻辑
我在上面回答2里 CircleList 里使用方法 实际上最终就是通过反射修改了 ViewGroup.mGroupFlags的值,该值在绘图流程中会影响子View绘制时Clip这部分的逻辑,
PS.试着写了一下..发现我是讲不清楚这个问题了....
监听TouchEvent和你想的差不多..我一般这样操作.
1.Down事件发生以后,获得需要移动的View的cache bitmap
2.新建一个ImageView,把快照扔进去
3.使用WindowMananger在屏幕上添加一个图层,把ImageView扔到你的按钮原来的位置上,把你原来那个按钮隐藏
4.然后剩下的和你的思路一致,你只要移动WindowManager里的ImageView就好了...这个图层是全屏...不受限制
5.ACTION_UP以后...把WindowMananger的内容删掉,把你的Button移到Action_up发生的位置.
不推荐的你 做法是因为 直接修改View的位置...会引起整个View tree的重布局...非常影响性能..
而且你要改的不是setx sety....是LayoutParamters里的xy
View的setx sety是绘图的起始位置...相当于setTranslationX/Y view根本就没动...
补充:
1.关于WindowMananger方案请参考这段代码:
--
2.ViewGroup其实有个ClipChildren的私有字段控制当childview超出子布局的时候是否要绘制.
一个应用的例子: https://github.com/dkmeteor/CircleList
核心代码:
注意这个属性是私有字段,该Demo中使用反射修改了mGroupFlags字段...
不建议使用
.3.如何在setLayoutParams里面像setX, setY一样设置位置
那取决于LayoutParams的种类,LayoutParams又取决于该View的父布局
LayoutParams有很多很多种
比如 ViewGroup.LayoutParams RelativeLayout.LayoutParams WindowManager.LayoutParams 等很多种,每一种支持的属性不一样.
比如AbsoluteLayout.LayoutParams 就有 x y属性可以直接设置位置
但是 LayoutParams.LayoutParams 就只能设置margin
4.你对View绘制流程的理解有偏差.
这部分有点复杂......View的绘制是由自身完成的...View不会绘制超出自己bounds/rect区域以外的部分....我讲不大清楚...
你可以参考Android 5.0 View源码 14959行~14977行(在View.draw 中间那一段) 关于clipRect部分的逻辑
我在上面回答2里 CircleList 里使用方法 实际上最终就是通过反射修改了 ViewGroup.mGroupFlags的值,该值在绘图流程中会影响子View绘制时Clip这部分的逻辑,
PS.试着写了一下..发现我是讲不清楚这个问题了....