android view.sety没效果,关于android如何实现view组件的drag功能的疑问

博客探讨了在Android中处理触摸事件以实现移动视图的技术。通过获取View的缓存快照,创建新的ImageView并在WindowManager上添加图层来模拟移动。避免直接修改View位置以提高性能,并通过反射修改ViewGroup的ClipChildren字段控制子视图绘制。还讨论了LayoutParams的不同类型及其在设置位置时的角色,以及对View绘制流程的理解。
摘要由CSDN通过智能技术生成

监听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.试着写了一下..发现我是讲不清楚这个问题了....

package com.blog.demo11; import android.annotation.SuppressLint; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import androidx.annotation.Nullable; import com.blog.BaseActivity; import com.blog.R; import static android.view.MotionEvent.INVALID_POINTER_ID; /** * 可拖拽效果类。 */ public class ViewDragActivity extends BaseActivity { private ImageView backgroundImage; private ImageView image1; private ImageView image2; @SuppressLint("MissingInflatedId") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.drag_layout); backgroundImage = findViewById(R.id.background_image); image1 = findViewById(R.id.image1); image2 = findViewById(R.id.image2); // Set touch listeners for each image image1.setOnTouchListener(new MultiTouchListener()); image2.setOnTouchListener(new MultiTouchListener()); } private class MultiTouchListener implements View.OnTouchListener { private float lastTouchX, lastTouchY; private int activePointerId = INVALID_POINTER_ID; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: { final int pointerIndex = event.getActionIndex(); final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); lastTouchX = x; lastTouchY = y; activePointerId = event.getPointerId(pointerIndex); break; } case MotionEvent.ACTION_MOVE: { final int pointerIndex = event.findPointerIndex(activePointerId); final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); final float dx = x - lastTouchX; final float dy = y - lastTouchY; v.setX(v.getX() + dx); v.setY(v.getY() + dy); lastTouchX = x; lastTouchY = y; break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { activePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_POINTER_UP: { final int pointerIndex = event.getActionIndex(); final int pointerId = event.getPointerId(pointerIndex); if (pointerId == activePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; lastTouchX = event.getX(newPointerIndex); lastTouchY = event.getY(newPointerIndex); activePointerId = event.getPointerId(newPointerIndex); } break; } } return true; } } }
06-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值