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