84669 person learning
152542 person learning
20005 person learning
5487 person learning
7821 person learning
359900 person learning
3350 person learning
180660 person learning
48569 person learning
18603 person learning
40936 person learning
1549 person learning
1183 person learning
32909 person learning
我尝试实现一个在屏幕上拖动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.试着写了一下..发现我是讲不清楚这个问题了....