the studio recognition pro evento event descargar android touch

studio - android: mueve una vista en movimiento táctil(ACTION_MOVE)



swipe event android (5)

Algo como esto:

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; }}

En main.xml solo RelativeLayout con @+id/root

Me gustaría hacer un control simple: un contenedor con una vista dentro. Si toco el contenedor y muevo el dedo, quiero mover la vista para seguir mi dedo.

¿Qué tipo de contenedor (diseño) debo usar? ¿Como hacer esto?

No necesito usar una superficie, sino un diseño simple.


Cambié un poco una solución provista por @Vyacheslav Shylkin para eliminar las dependencias de los números ingresados ​​manualmente.

import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.RelativeLayout; public class MainActivity extends Activity implements View.OnTouchListener { private int _xDelta; private int _yDelta; private int _rightMargin; private int _bottomMargin; private ImageView _floatingView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this._floatingView = (ImageView) findViewById(R.id.textView); this._floatingView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (_floatingView.getViewTreeObserver().isAlive()) _floatingView.getViewTreeObserver().removeOnPreDrawListener(this); updateLayoutParams(_floatingView); return false; } }); this._floatingView.setOnTouchListener(this); } private void updateLayoutParams(View view) { this._rightMargin = -view.getMeasuredWidth(); this._bottomMargin = -view.getMeasuredHeight(); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(view.getMeasuredWidth(), view.getMeasuredHeight()); layoutParams.bottomMargin = this._bottomMargin; layoutParams.rightMargin = this._rightMargin; view.setLayoutParams(layoutParams); } @Override public boolean onTouch(View view, MotionEvent event) { if (view == this._floatingView) { 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(); this._xDelta = X - lParams.leftMargin; this._yDelta = Y - lParams.topMargin; break; case MotionEvent.ACTION_MOVE: RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams(); layoutParams.leftMargin = X - this._xDelta; layoutParams.topMargin = Y - this._yDelta; layoutParams.rightMargin = this._rightMargin; layoutParams.bottomMargin = this._bottomMargin; view.setLayoutParams(layoutParams); break; } return true; } else { return false; } } }


En el siguiente código, he creado algo llamado RegionView ( git ), que es un contenedor reutilizable responsable de administrar las operaciones de arrastrar y acercar para cada uno de sus elementos secundarios anidados.

Aquí, manipulamos los coeficientes top e left de un niño View ''s LayoutParams para simular el movimiento sobre el diagrama. Al desacoplar la interpretación del manejo de lo que se entiende como una operación de arrastre, y lo que se determina que es una operación de escala, podemos proporcionar una manipulación confiable de una View niño.

package com.zonal.regionview; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.os.Vibrator; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; import android.widget.RelativeLayout; import java.util.HashMap; import java.util.Map; /** * Created by Alexander Thomas (@Cawfree) on 20/07/2017. */ /** Enables users to customize Regions Of Interest on a Canvas. */ public class RegionView extends RelativeLayout implements View.OnTouchListener, GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener { /* Member Variables. */ private final GestureDetector mGestureDetector; private final ScaleGestureDetector mScaleGestureDetector; private final Map<Integer, View> mViewMap; private boolean mScaling; private float mScale; private boolean mWrapContent; private boolean mDropOnScale; public RegionView(Context context) { // Implement the Parent. super(context); // Initialize Member Variables. this.mGestureDetector = new GestureDetector(context, this); this.mViewMap = new HashMap<>(); this.mScaleGestureDetector = new ScaleGestureDetector(context, this); this.mScaling = false; this.mScale = Float.NaN; this.mWrapContent = false; this.mDropOnScale = false; // Register ourself as the OnTouchListener. this.setOnTouchListener(this); } public RegionView(Context context, @Nullable AttributeSet attrs) { // Implement the Parent. super(context, attrs); // Initialize Member Variables. this.mGestureDetector = new GestureDetector(context, this); this.mViewMap = new HashMap<>(); this.mScaleGestureDetector = new ScaleGestureDetector(context, this); this.mScaling = false; this.mWrapContent = false; this.mDropOnScale = false; // Register ourself as the OnTouchListener. this.setOnTouchListener(this); } public RegionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { // Implement the Parent. super(context, attrs, defStyleAttr); // Initialize Member Variables. this.mGestureDetector = new GestureDetector(context, this); this.mViewMap = new HashMap<>(); this.mScaleGestureDetector = new ScaleGestureDetector(context, this); this.mScaling = false; this.mWrapContent = false; this.mDropOnScale = false; // Register ourself as the OnTouchListener. this.setOnTouchListener(this); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public RegionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { // Implement the Parent. super(context, attrs, defStyleAttr, defStyleRes); // Initialize Member Variables. this.mGestureDetector = new GestureDetector(context, this); this.mViewMap = new HashMap<>(); this.mScaleGestureDetector = new ScaleGestureDetector(context, this); this.mScaling = false; this.mWrapContent = false; this.mDropOnScale = false; // Register ourself as the OnTouchListener. this.setOnTouchListener(this); } @Override public boolean onTouch(final View v, final MotionEvent event) { // Calculate the PointerId. final int lPointerId = event.getPointerId(event.getActionIndex()); // Handle the TouchEvent. this.getGestureDetector().onTouchEvent(event); this.getScaleGestureDetector().onTouchEvent(event); // Did the user release a pointer? if(event.getAction() == MotionEvent.ACTION_UP) { // Was there a View associated with this Action? final View lView = this.getViewMap().get(lPointerId); // Does the View exist? if(lView != null) { // Remove the View from the Map. this.getViewMap().remove(lPointerId); /** TODO: Provide a Callback? */ } } // Consume all events for now. return true; } @Override public boolean onDown(MotionEvent e) { // Calculate the PointerId. final Integer lPointerId = Integer.valueOf(e.getPointerId(e.getActionIndex())); // Fetch the View. final View lView = this.getViewFor(Math.round(e.getRawX()), Math.round(e.getRawY())); // Is it valid? if(lView != null) { // Watch the View. this.getViewMap().put(lPointerId, lView); // Configure the Anchor. lView.setPivotX(0); lView.setPivotY(0); // Assert that we handled the event. return true; } // Assert that we ignored the event. return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // Are we not scaling? if(!this.isScaling()) { // Calculate the PointerId. final Integer lPointerId = Integer.valueOf(e1.getPointerId(e1.getActionIndex())); // Fetch the View. final View lView = this.getViewMap().get(lPointerId); // Is the scroll valid for a given View? if(lView != null) { // Calculate the Scaled Width and Height of the View. final float lWidth = lView.getWidth() * lView.getScaleX(); final float lHeight = lView.getHeight() * lView.getScaleY(); // Declare the initial position. final int[] lPosition = new int[] { (int)(e2.getX() - ((lWidth) / 2)), (int)(e2.getY() - ((lHeight) / 2)) }; // Are we wrapping content? if(this.isWrapContent()) { // Wrap the Position. this.onWrapContent(lPosition, lWidth, lHeight); } // Update the Drag. this.onUpdateDrag(lView, lPosition); } // Assert we handled the scroll. return true; } // Otherwise, don''t permit scrolling. Don''t consume the MotionEvent. return false; } /** Forces X/Y values to be coerced within the confines of the RegionView. */ private final void onWrapContent(final int[] pPosition, final float pWidth, final float pHeight) { // Limit the parameters. (Top-Left) pPosition[0] = Math.max(pPosition[0], 0); pPosition[1] = Math.max(pPosition[1], 0); // Limit the parameters. (Bottom-Right) pPosition[0] = Math.min(pPosition[0], (int)(this.getWidth() - pWidth)); pPosition[1] = Math.min(pPosition[1], (int)(this.getHeight() - pHeight)); } /** Updates the Drag Position of a child View within the Layout. Implicitly, we update the LayoutParams of the View. */ private final void onUpdateDrag(final View pView, final int pLeft, final int pTop) { // Allocate some new MarginLayoutParams. final MarginLayoutParams lMarginLayoutParams = new MarginLayoutParams(pView.getLayoutParams()); // Update the Margin. lMarginLayoutParams.setMargins(pLeft, pTop, 0, 0); // Refactor the MarginLayoutParams into equivalent LayoutParams for the RelativeLayout. pView.setLayoutParams(new RelativeLayout.LayoutParams(lMarginLayoutParams)); } @Override public boolean onScale(ScaleGestureDetector detector) { // Calculate the ScaleFactor. float lScaleFactor = detector.getScaleFactor() - 1; // Fetch the Scaled View. final View lView = this.getViewMap().entrySet().iterator().next().getValue(); // Update the ScaleFactor. final float lScale = this.getScale() + lScaleFactor; // Calculate the Proposed Width and Height. final int lWidth = Math.round(lView.getWidth() * lScale); final int lHeight = Math.round(lView.getHeight() * lScale); // Is the View already too large for wrap content? if(lWidth >= this.getWidth() || lHeight >= this.getHeight()) { // Don''t update the scale. return false; } // Persist this Scale for the View. lView.setScaleX(lScale); lView.setScaleY(lScale); // Assign the Scale. this.setScale(lScale); // Compute the Position. final int[] lPosition = new int[] { Math.round(detector.getFocusX()) - (lWidth / 2), Math.round(detector.getFocusY()) - (lHeight / 2) }; // Are we wrapping the Position? if(this.isWrapContent()) { // Wrap the Position. this.onWrapContent(lPosition, lWidth, lHeight); } // Update the Drag. this.onUpdateDrag(lView, lPosition); // Assert that we handled the scale. return true; } /** Update the Drag. */ private final void onUpdateDrag(final View pView, final int[] pPosition) { // Call the sub-implementation. this.onUpdateDrag(pView, pPosition[0], pPosition[1]); } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { // Is the user not dragging at all? if(this.getViewMap().size() == 1) { // Fetch the View. final View lView = this.getViewMap().entrySet().iterator().next().getValue(); // Initialize the Scale. this.setScale(lView.getScaleX()); // Assert that we''ve started scaling. this.setScaling(true); // Inform the callback. return true; } // Otherwise, don''t allow scaling. return false; } @Override public void onScaleEnd(ScaleGestureDetector detector) { // Were we scaling? if(this.isScaling()) { // Assert that we''ve stopped scaling. this.setScaling(false); // Reset the Scale. this.setScale(Float.NaN); // Should we stop dragging now that we''ve finished scaling? if(this.isDropOnScale()) { // Clear the ViewMap. this.getViewMap().clear(); } } } /** Returns the View colliding with the given co-ordinates. */ private final View getViewFor(final int pX, final int pY) { // Declare the LocationBuffer. final int[] lLocationBuffer = new int[2]; // Iterate the Views. for(int i = 0; i < this.getChildCount(); i++) { // Fetch the child View. final View lView = this.getChildAt(i); // Fetch its absolute position. lView.getLocationOnScreen(lLocationBuffer); // Determine if the MotionEvent collides with the View. if(pX > lLocationBuffer[0] && pY > lLocationBuffer[1] && (pX < lLocationBuffer[0] + (lView.getWidth() * lView.getScaleX())) && (pY < lLocationBuffer[1] + (lView.getHeight() * lView.getScaleY()))) { // Return the View. return lView; } } // We couldn''t find a View. return null; } /* Unused Overrides. */ @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; } /* Getters and Setters. */ private final GestureDetector getGestureDetector() { return this.mGestureDetector; } private final ScaleGestureDetector getScaleGestureDetector() { return this.mScaleGestureDetector; } private final Map<Integer, View> getViewMap() { return this.mViewMap; } private final void setScaling(final boolean pIsScaling) { this.mScaling = pIsScaling; } private final boolean isScaling() { return this.mScaling; } private final void setScale(final float pScale) { this.mScale = pScale; } private final float getScale() { return this.mScale; } /** Defines whether we coerce the drag and zoom of child Views within the confines of the Layout. */ public final void setWrapContent(final boolean pIsWrapContent) { this.mWrapContent = pIsWrapContent; } public final boolean isWrapContent() { return this.mWrapContent; } /** Defines whether a drag operation is considered ''finished'' once the user finishes scaling a view. */ public final void setDropOnScale(final boolean pIsDropOnScale) { this.mDropOnScale = pIsDropOnScale; } public final boolean isDropOnScale() { return this.mDropOnScale; } }

Aquí muestro un ejemplo de caso de uso:

package com.zonal.regionview; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.AnalogClock; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Allocate a RegionView. final RegionView lRegionView = new RegionView(this); // Add some example items to drag. lRegionView.addView(new AnalogClock(this)); lRegionView.addView(new AnalogClock(this)); lRegionView.addView(new AnalogClock(this)); // Assert that we only want to drag Views within the confines of the RegionView. lRegionView.setWrapContent(true); // Assert that after we''ve finished scaling a View, we want to stop being able to drag it until a new drag is started. lRegionView.setDropOnScale(true); // Look at the RegionView. this.setContentView(lRegionView); } }


He encontrado un enfoque sencillo para hacer eso con 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; }


Siguiendo el enfoque de @Andrey, si quieres mover la vista desde su centro, solo tienes que restar la mitad de la altura y el ancho de la vista al movimiento.

float dX, dY; @Override public boolean onTouchEvent(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 - (view.getWidth() / 2)) .y(event.getRawY() + dY - (view.getHeight() / 2)) .setDuration(0) .start(); break; default: return false; } return true; }