我想做一个简单的控件:一个里面有视图的容器。如果我触摸容器并移动手指,我想让视图跟着我的手指移动。
我应该使用什么样的容器(布局)?如何做到这一点?
我不需要使用一个表面,但一个简单的布局。
我想做一个简单的控件:一个里面有视图的容器。如果我触摸容器并移动手指,我想让视图跟着我的手指移动。
我应该使用什么样的容器(布局)?如何做到这一点?
我不需要使用一个表面,但一个简单的布局。
当前回答
就像这样:
public class MyActivity extends Activity implements View.OnTouchListener {
TextView _view;
ViewGroup _root;
private int _xDelta;
private int _yDelta;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_root = (ViewGroup)findViewById(R.id.root);
_view = new TextView(this);
_view.setText("TextView!!!!!!!!");
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 50);
layoutParams.leftMargin = 50;
layoutParams.topMargin = 50;
layoutParams.bottomMargin = -250;
layoutParams.rightMargin = -250;
_view.setLayoutParams(layoutParams);
_view.setOnTouchListener(this);
_root.addView(_view);
}
public boolean onTouch(View view, MotionEvent event) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
view.setLayoutParams(layoutParams);
break;
}
_root.invalidate();
return true;
}}
在main.xml中只需要RelativeLayout + @+id/root即可
其他回答
就像这样:
public class MyActivity extends Activity implements View.OnTouchListener {
TextView _view;
ViewGroup _root;
private int _xDelta;
private int _yDelta;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_root = (ViewGroup)findViewById(R.id.root);
_view = new TextView(this);
_view.setText("TextView!!!!!!!!");
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 50);
layoutParams.leftMargin = 50;
layoutParams.topMargin = 50;
layoutParams.bottomMargin = -250;
layoutParams.rightMargin = -250;
_view.setLayoutParams(layoutParams);
_view.setOnTouchListener(this);
_root.addView(_view);
}
public boolean onTouch(View view, MotionEvent event) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
view.setLayoutParams(layoutParams);
break;
}
_root.invalidate();
return true;
}}
在main.xml中只需要RelativeLayout + @+id/root即可
在Kotlin中实现相同
rightPanel.setOnTouchListener(View.OnTouchListener { view, event ->
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
rightDX = view!!.x - event.rawX
// rightDY = view!!.getY() - event.rawY;
}
MotionEvent.ACTION_MOVE -> {
var displacement = event.rawX + rightDX
view!!.animate()
.x(displacement)
// .y(event.getRawY() + rightDY)
.setDuration(0)
.start()
}
else -> { // Note the block
return@OnTouchListener false
}
}
true
})
我推荐使用view。translationX和view。翻译来移动你的观点。
Kotlin snippet:
yourView.translationX = xTouchCoordinate
yourView.translationY = yTouchCoordinate
我发现了一个简单的方法来做到这一点与ViewPropertyAnimator:
float dX, dY;
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
view.animate()
.x(event.getRawX() + dX)
.y(event.getRawY() + dY)
.setDuration(0)
.start();
break;
default:
return false;
}
return true;
}
在这个例子中,你可以在它的父边界内移动视图,无论它的大小、完美的动画和捕捉点击。
这种解决方案优于其他注释的原因是,这种方法使用了一个方向板,它可以计算自己,不会依赖于视图位置,这是许多错误的来源。
// we could use this gameobject as a wrapper that controls the touch event of the component(the table)
// and like so, we can have a click event and touch events
public abstract class GameObjectStackOverflow {
private static final int CLICK_DURATION = 175;
protected View view;
protected ViewGroup container;
protected Context mContext;
private boolean onMove = false;
private boolean firstAnimation = true;
private Animator.AnimatorListener listener;
protected float parentWidth;
protected float parentHeight;
protected float xmlHeight;
protected float xmlWidth;
// Those are the max bounds
// whiting the xmlContainer
protected float xBoundMax;
protected float yBoundMax;
// This variables hold the target
// ordinates for the next
// animation in case an animation
// is already in progress.
protected float targetX;
protected float targetY;
private float downRawX;
private float downRawY;
public GameObjectStackOverflow(@NonNull Context context, @NonNull ViewGroup container)
{
mContext = context;
this.container = container;
}
// This method is the reason the constructor
// does not get view to work with in the first
// place. This method helps us to work with
// android main thread in such way that we
// separate the UI stuff from the technical
// stuff
protected View initGraphicView(@NonNull LayoutInflater inflater, int resource, boolean add)
{
view = inflater.inflate(resource, container, add);
view.post(getOnViewAttach());
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return onTouchEvent(event);
}
});
return view;
}
// This method attach an existing
// view that is already inflated
protected void attachGraphicView(@NonNull final View view)
{
this.view = view;
view.post(getOnViewAttach());
}
// This method is anti-boiler code.
// attaching runnable to the view
// task queue to finish the
// initialization of the game object.
private Runnable getOnViewAttach()
{
return new Runnable() {
@Override
public void run() {
parentHeight = container.getHeight();
parentWidth = container.getWidth();
view.setX(currentX);
view.setY(currentY);
}
};
}
private void click() {
// recover the view to the previous location [not needed]
// not needed
//view.animate()
// .x(prevPosX)
// .y(prevPosY)
// .setDuration(0)
// .start();
}
// maybe restore the View view, Motion event
public boolean onTouchEvent(MotionEvent event)
{
view.getParent().requestDisallowInterceptTouchEvent(true);
//if(!selected) return false;
switch (event.getAction())
{
case MotionEvent.ACTION_UP:
if (event.getEventTime() - event.getDownTime() < CLICK_DURATION) click(); // are you missing break here?
onMove = false;
// if needed to update network entity do it here
break;
case MotionEvent.ACTION_DOWN:
firstAnimation = true;
xBoundMax = parentWidth - xmlWidth;
yBoundMax = parentHeight - xmlHeight;
downRawX = event.getRawX();
downRawY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
if (!onMove) {
if (event.getEventTime() - event.getDownTime() < CLICK_DURATION) break;
else onMove = true;
}
// Calculating the position the
// view should be posed at.
float offsetX = event.getRawX() - downRawX;
float offsetY = event.getRawY() - downRawY;
downRawX = event.getRawX();
downRawY = event.getRawY();
targetX = currentX + offsetX;
targetY = currentY + offsetY;
// Checking if view
// is within parent bounds
if (targetX > parentWidth - xmlWidth) targetX = xBoundMax;
else if (targetX < 0) targetX = 0;
if (targetY > parentHeight - xmlHeight) targetY = yBoundMax;
else if (targetY < 0) targetY = 0;
// This check is becuase the user may just click on the view
// So if it's a not a click, animate slowly but fastly
// to the desired position
if (firstAnimation) {
firstAnimation = false;
animate(70, getNewAnimationListener());
break;
}
if (listener != null) break;
animate(0, null);
break;
case MotionEvent.ACTION_BUTTON_PRESS:
default:
return false;
}
return true;
}
// this method gets used only in
// one place. it's wrapped in a method
// block because i love my code like
// i love women - slim, sexy and smart.
public Animator.AnimatorListener getNewAnimationListener() {
listener = new Animator.AnimatorListener() {
@Override public void onAnimationStart(Animator animation) { }
@Override public void onAnimationCancel(Animator animation) { }
@Override public void onAnimationRepeat(Animator animation) { }
@Override public void onAnimationEnd(Animator animation) {
animation.removeListener(listener);
listener = null;
view.setAnimation(null);
animate(0, null);
}
};
return listener;
}
float currentX = 0, currentY = 0;
private void animate(int duration, @Nullable Animator.AnimatorListener listener) {
view.animate()
.x(targetX)
.y(targetY)
.setDuration(duration)
.setListener(listener)
.start();
currentX = targetX;
currentY = targetY;
}
protected void setSize(float width, float height)
{
xmlWidth = width;
xmlHeight = height;
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.width = (int) width;
layoutParams.height = (int) height;
view.setLayoutParams(layoutParams);
}
public View getView() {
return view;
}
//This interface catches the onclick even
// that happened and need to decide what to do.
public interface GameObjectOnClickListener {
void onGameObjectClick(GameObjectStackOverflow object);
}
public float getXmlWidth() {
return xmlWidth;
}
public float getXmlHeight() {
return xmlHeight;
}
}
这个版本被剥夺了大的东西,过去有网络实体,得到更新的live和这样,它应该工作。
你应该这样使用它
public class Tree extends GameObject
{
public Tree(Context context, ViewGroup container, View view, int width, int height) {
super(context, manager, container);
attachGraphicView(view);
super.setSize(_width, _height);
}
}
和比
mTree= new Tree(mContext, mContainer, xmlTreeView);
mTree.getView().setOnTouchListener(getOnTouchListener(mTree));
你也应该有这个,但这个很容易去掉
//Construct new OnTouchListener that reffers to the gameobject ontouchevent
private View.OnTouchListener getOnTouchListener(final GameObject object) {
return new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
return object.onTouchEvent(event);
}
};
}
如果你有容器在一个ScrollView或双层ScrollView,你应该添加这行到onTouch
view.getParent().requestDisallowInterceptTouchEvent(true);