Buscar..


Creación de vistas personalizadas

Si necesita una vista completamente personalizada, tendrá que hacer una subclase de View (la superclase de todas las vistas de Android) y proporcionar los onMeasure(...) tamaño personalizado ( onMeasure(...) ) y drawing ( onDraw(...) ):

  1. Crea tu esqueleto de vista personalizada: esto es básicamente el mismo para cada vista personalizada. Aquí creamos el esqueleto para una vista personalizada que puede dibujar un emoticono, llamado SmileyView :

    public class SmileyView extends View {
        private Paint mCirclePaint;
        private Paint mEyeAndMouthPaint;
    
        private float mCenterX;
        private float mCenterY;
        private float mRadius;
        private RectF mArcBounds = new RectF();
    
        public SmileyView(Context context) {
            this(context, null, 0);
        }
    
        public SmileyView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public SmileyView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initPaints();
        }
    
        private void initPaints() {/* ... */}
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {/* ... */}
    
        @Override
        protected void onDraw(Canvas canvas) {/* ... */}
    }
    
  2. Inicialice sus pinturas: los objetos de Paint son los pinceles de su lienzo virtual que definen cómo se representan los objetos geométricos (por ejemplo, color, estilo de relleno y trazo, etc.). Aquí creamos dos Paint s, una pintura llenado amarillo para el círculo y una pintura de trazo negro para los ojos y la boca:

    private void initPaints() {
        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCirclePaint.setStyle(Paint.Style.FILL);
        mCirclePaint.setColor(Color.YELLOW);
        mEyeAndMouthPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mEyeAndMouthPaint.setStyle(Paint.Style.STROKE);
        mEyeAndMouthPaint.setStrokeWidth(16 * getResources().getDisplayMetrics().density);
        mEyeAndMouthPaint.setStrokeCap(Paint.Cap.ROUND);
        mEyeAndMouthPaint.setColor(Color.BLACK);
    }
    
  3. Implemente su propio onMeasure(...) : esto es necesario para que los diseños principales (por ejemplo, FrameLayout ) puedan alinear correctamente su vista personalizada. Proporciona un conjunto de measureSpecs de measureSpecs que puede usar para determinar la altura y el ancho de su vista. Aquí creamos un cuadrado asegurándonos de que la altura y el ancho son iguales:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int w = MeasureSpec.getSize(widthMeasureSpec);
        int h = MeasureSpec.getSize(heightMeasureSpec);
    
        int size = Math.min(w, h);
        setMeasuredDimension(size, size);
    }
    

    Tenga en cuenta que onMeasure(...) debe contener al menos una llamada a setMeasuredDimension(..) o, de lo contrario, su vista personalizada se bloqueará con una IllegalStateException .

  4. Implemente su propio onSizeChanged(...) : esto le permite capturar la altura y el ancho actuales de su vista personalizada para ajustar adecuadamente su código de representación. Aquí solo calculamos nuestro centro y nuestro radio:

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mCenterX = w / 2f;
        mCenterY = h / 2f;
        mRadius = Math.min(w, h) / 2f;
    }
    
  5. Implemente su propio onDraw(...) : aquí es donde implementa la representación real de su vista. Proporciona un objeto Canvas que puede dibujar (consulte la documentación oficial de Canvas para conocer todos los métodos de dibujo disponibles).

    @Override
    protected void onDraw(Canvas canvas) {
        // draw face
        canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint);
        // draw eyes
        float eyeRadius = mRadius / 5f;
        float eyeOffsetX = mRadius / 3f;
        float eyeOffsetY = mRadius / 3f;
        canvas.drawCircle(mCenterX - eyeOffsetX, mCenterY - eyeOffsetY, eyeRadius, mEyeAndMouthPaint);
        canvas.drawCircle(mCenterX + eyeOffsetX, mCenterY - eyeOffsetY, eyeRadius, mEyeAndMouthPaint);
        // draw mouth
        float mouthInset = mRadius /3f;
        mArcBounds.set(mouthInset, mouthInset, mRadius * 2 - mouthInset, mRadius * 2 - mouthInset);
        canvas.drawArc(mArcBounds, 45f, 90f, false, mEyeAndMouthPaint);
    }
    
  6. Agregue su vista personalizada a un diseño: la vista personalizada ahora se puede incluir en cualquier archivo de diseño que tenga. Aquí simplemente lo FrameLayout dentro de un FrameLayout :

    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.example.app.SmileyView
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
    

Tenga en cuenta que se recomienda compilar su proyecto una vez finalizado el código de vista. Sin construirlo, no podrá ver la vista en una pantalla de vista previa en Android Studio.

Después de poner todo junto, debe recibir la siguiente pantalla después de iniciar la actividad que contiene el diseño anterior:

SmileyView utiliza actividad interior

Agregando atributos a las vistas

Las vistas personalizadas también pueden tomar atributos personalizados que pueden usarse en archivos de recursos de diseño de Android. Para agregar atributos a su vista personalizada, debe hacer lo siguiente:

  1. Defina el nombre y el tipo de sus atributos: esto se hace dentro de res/values/attrs.xml ( res/values/attrs.xml si es necesario). El siguiente archivo define un atributo de color para el color de la cara de nuestro smiley y un atributo de enumeración para la expresión del smiley:

    <resources>
        <declare-styleable name="SmileyView">
            <attr name="smileyColor" format="color" />
            <attr name="smileyExpression" format="enum">
                <enum name="happy" value="0"/>
                <enum name="sad" value="1"/>
            </attr>
        </declare-styleable>
        <!-- attributes for other views -->
    </resources>
    
  2. Use sus atributos dentro de su diseño: esto se puede hacer dentro de cualquier archivo de diseño que use su vista personalizada. El siguiente archivo de diseño crea una pantalla con un smiley amarillo feliz:

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_height="match_parent"
        android:layout_width="match_parent">
        
        <com.example.app.SmileyView
            android:layout_height="56dp"
            android:layout_width="56dp"
            app:smileyColor="#ffff00"
            app:smileyExpression="happy" />
    </FrameLayout>
    

    Consejo: los atributos personalizados no funcionan con las tools: prefijo en Android Studio 2.1 y versiones anteriores (y posiblemente en versiones futuras). En este ejemplo, reemplazar la app:smileyColor con tools:smileyColor resultaría en que smileyColor no se establezca durante el tiempo de ejecución ni en el momento del diseño.

  3. Lea sus atributos: esto se hace dentro del código fuente de su vista personalizada. El siguiente fragmento de SmileyView demuestra cómo se pueden extraer los atributos:

    public class SmileyView extends View {
        // ...
    
        public SmileyView(Context context) {
            this(context, null);
        }
    
        public SmileyView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public SmileyView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SmileyView, defStyleAttr, 0);
            mFaceColor = a.getColor(R.styleable.SmileyView_smileyColor, Color.TRANSPARENT);
            mFaceExpression = a.getInteger(R.styleable.SmileyView_smileyExpression, Expression.HAPPY);
            // Important: always recycle the TypedArray
            a.recycle();
    
            // initPaints(); ...
        }
    }
    
  4. (Opcional) Agregar estilo predeterminado: esto se hace agregando un estilo con los valores predeterminados y cargándolo dentro de su vista personalizada. El siguiente estilo de emoticono predeterminado representa un color amarillo feliz:

    <!-- styles.xml -->
    <style name="DefaultSmileyStyle">
        <item name="smileyColor">#ffff00</item>
        <item name="smileyExpression">happy</item>
    </style>
    

    Que se aplica en nuestro SmileyView al agregarlo como el último parámetro de la llamada para obtener obtainStyledAttributes de obtainStyledAttributes (vea el código en el paso 3):

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SmileyView, defStyleAttr, R.style.DefaultSmileyViewStyle);
    

    Tenga en cuenta que cualquier valor de atributo establecido en el archivo de diseño inflado (ver código en el paso 2) anulará los valores correspondientes del estilo predeterminado.

  5. (Opcional) Proporcione estilos dentro de los temas: esto se hace agregando un nuevo atributo de referencia de estilo que puede usarse dentro de sus temas y proporcionando un estilo para ese atributo. Aquí simplemente smileyStyle nuestro atributo de referencia smileyStyle :

    <!-- attrs.xml -->
    <attr name="smileyStyle" format="reference" />
    

    A continuación, proporcionamos un estilo en el tema de nuestra aplicación (aquí solo reutilizamos el estilo predeterminado del paso 4):

    <!-- themes.xml -->
    <style name="AppTheme" parent="AppBaseTheme">
        <item name="smileyStyle">@style/DefaultSmileyStyle</item>
    </style>
    

Creando una vista compuesta

Una vista compuesto es una costumbre ViewGroup que se trata como una única vista por el código del programa circundante. Tal ViewGroup puede ser realmente útil en diseño similar a DDD , ya que puede corresponder a un agregado, en este ejemplo, un Contacto. Se puede reutilizar en cualquier lugar donde se muestre el contacto.

Esto significa que el código del controlador circundante, una Actividad, un Fragmento o un Adaptador, simplemente puede pasar el objeto de datos a la vista sin separarlo en una serie de widgets de IU diferentes.

Esto facilita la reutilización del código y permite un mejor diseño de acuerdo con los principios de SOLID .

El diseño XML

Esto suele ser donde empiezas. Tiene un bit de XML existente que reutiliza, tal vez como <include/> . Extráigalo en un archivo XML separado y envuelva la etiqueta raíz en un elemento <merge> :

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

        <ImageView
            android:id="@+id/photo"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_alignParentRight="true" />

        <TextView
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_toLeftOf="@id/photo" />

        <TextView
            android:id="@+id/phone_number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/name"
            android:layout_toLeftOf="@id/photo" />
</merge>

Este archivo XML sigue funcionando perfectamente en el editor de diseño en Android Studio. Puedes tratarlo como cualquier otro diseño.

El compuesto ViewGroup

Una vez que tenga el archivo XML, cree el grupo de vista personalizado.

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.ImageView;
import android.widget.TextView;

import myapp.R;

/**
 * A compound view to show contacts.
 *
 * This class can be put into an XML layout or instantiated programmatically, it
 * will work correctly either way.
 */
public class ContactView extends RelativeLayout {

    // This class extends RelativeLayout because that comes with an automatic
    // (MATCH_PARENT, MATCH_PARENT) layout for its child item. You can extend
    // the raw android.view.ViewGroup class if you want more control. See the
    // note in the layout XML why you wouldn't want to extend a complex view
    // such as RelativeLayout.

    // 1. Implement superclass constructors.
    public ContactView(Context context) {
        super(context);
        init(context, null);
    }

    // two extra constructors left out to keep the example shorter

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ContactView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    // 2. Initialize the view by inflating an XML using `this` as parent
    private TextView mName;
    private TextView mPhoneNumber;
    private ImageView mPhoto;

    private void init(Context context, AttributeSet attrs) {
        LayoutInflater.from(context).inflate(R.layout.contact_view, this, true);
        mName = (TextView) findViewById(R.id.name);
        mPhoneNumber = (TextView) findViewById(R.id.phone_number);
        mPhoto = (ImageView) findViewById(R.id.photo);
    }

    // 3. Define a setter that's expressed in your domain model. This is what the example is
    //    all about. All controller code can just invoke this setter instead of fiddling with
    //    lots of strings, visibility options, colors, animations, etc. If you don't use a
    //    custom view, this code will usually end up in a static helper method (bad) or copies 
    //    of this code will be copy-pasted all over the place (worse).
    public void setContact(Contact contact) {
        mName.setText(contact.getName());
        mPhoneNumber.setText(contact.getPhoneNumber());
        if (contact.hasPhoto()) {
            mPhoto.setVisibility(View.VISIBLE);
            mPhoto.setImageBitmap(contact.getPhoto());
        } else {
            mPhoto.setVisibility(View.GONE);
        }
    }
}

El método init(Context, AttributeSet) es donde leería cualquier atributo XML personalizado tal como se explica en Agregar atributos a las vistas .

Con estas piezas en su lugar, puedes usarlo en tu aplicación.

Uso en XML

Aquí hay un ejemplo de fragment_contact_info.xml que ilustra cómo pondría un único ContactView encima de una lista de mensajes:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- The compound view becomes like any other view XML element -->
    <myapp.ContactView
        android:id="@+id/contact"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/message_list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

</LinearLayout>

Uso en Código

Aquí hay un ejemplo de RecyclerView.Adapter que muestra una lista de contactos. Este ejemplo ilustra cuánto más limpio está el código del controlador cuando está completamente libre de manipulación de vistas.

package myapp;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

public class ContactsAdapter extends RecyclerView.Adapter<ContactsViewHolder> {

    private final Context context;

    public ContactsAdapter(final Context context) {
        this.context = context;
    }

    @Override
    public ContactsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ContactView v = new ContactView(context); // <--- this
        return new ContactsViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ContactsViewHolder holder, int position) {
        Contact contact = this.getItem(position);
        holder.setContact(contact);  // <--- this
    }

    static class ContactsViewHolder extends RecyclerView.ViewHolder {

        public ContactsViewHolder(ContactView itemView) {
            super(itemView);
        }

        public void setContact(Contact contact) {
            ((ContactView) itemView).setContact(contact); // <--- this
        }
    }
}

Consejos de rendimiento de CustomView

No asignar nuevos objetos en onDraw

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint paint = new Paint(); //Do not allocate here
}

En lugar de dibujar dibujables en lienzo ...

drawable.setBounds(boundsRect);

drawable.draw(canvas);

Use un mapa de bits para un dibujo más rápido:

canvas.drawBitmap(bitmap, srcRect, boundsRect, paint);

No vuelva a dibujar la vista completa para actualizar solo una pequeña parte de ella. En su lugar, vuelva a dibujar la parte específica de la vista.

invalidate(boundToBeRefreshed);

Si su vista está haciendo una animación continua, por ejemplo, una onStop() muestra cada segundo, al menos detenga la animación en onStop() de la actividad y comience de nuevo en onStart() de la actividad.

No realice ningún cálculo dentro del método onDraw de una vista, en lugar de eso, debe terminar de dibujar antes de llamar a invalidate() . Al utilizar esta técnica, puede evitar que el cuadro se caiga en su vista.

Rotaciones

Las operaciones básicas de una vista son traducir, rotar, etc. Casi todos los desarrolladores se han enfrentado a este problema cuando usan mapas de bits o degradados en su vista personalizada. Si la vista va a mostrar una vista girada y el mapa de bits debe girarse en esa vista personalizada, muchos de nosotros pensamos que será caro. Muchos piensan que rotar un mapa de bits es muy costoso porque para hacer eso, es necesario traducir la matriz de píxeles del mapa de bits. Pero la verdad es que no es tan difícil! En lugar de rotar el mapa de bits, ¡simplemente gire el lienzo!

// Save the canvas state
int save = canvas.save();
// Rotate the canvas by providing the  center point as pivot and angle
canvas.rotate(pivotX, pivotY, angle);
// Draw whatever you want
// Basically whatever you draw here will be drawn as per the angle you rotated the canvas
canvas.drawBitmap(...);
// Now restore your your canvas to its original state
canvas.restore(save);
// Unless canvas is restored to its original state, further draw will also be rotated.

Vista compuesta para SVG / VectorDrawable as drawableRight

El motivo principal para desarrollar esta vista compuesta es que, por debajo de 5.0, los dispositivos no son compatibles con svg en drawable dentro de TextView / EditText. Uno más es pros, podemos establecer height y width de drawableRight dentro EditText . Lo he separado de mi proyecto y lo he creado en un módulo separado.

Nombre del módulo: custom_edit_drawable (nombre corto para prefijo-c_d_e)

Se utiliza el prefijo "c_d_e_" para que los recursos del módulo de la aplicación no los anulen por error. Ejemplo: Google utiliza el prefijo "abc" en la biblioteca de soporte.

construir.gradle

dependencies {
   compile 'com.android.support:appcompat-v7:25.3.1'
}

utilizar AppCompat> = 23

Archivo de diseño: c_e_d_compound_view.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <EditText
        android:id="@+id/edt_search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:maxLines="1"
        android:paddingEnd="40dp"
        android:paddingLeft="5dp"
        android:paddingRight="40dp"
        android:paddingStart="5dp" />

    <!--make sure you are not using ImageView instead of this-->
    <android.support.v7.widget.AppCompatImageView
        android:id="@+id/drawbleRight_search"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="right|center_vertical"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp" />
</FrameLayout>

Atributos personalizados: attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="EditTextWithDrawable">
        <attr name="c_e_d_drawableRightSVG" format="reference" />
        <attr name="c_e_d_hint" format="string" />
        <attr name="c_e_d_textSize" format="dimension" />
        <attr name="c_e_d_textColor" format="color" />
    </declare-styleable>
</resources>

Código: EditTextWithDrawable.java

public class EditTextWithDrawable extends FrameLayout {
    public AppCompatImageView mDrawableRight;
    public EditText mEditText;

    public EditTextWithDrawable(Context context) {
        super(context);
        init(null);
    }

    public EditTextWithDrawable(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EditTextWithDrawable(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public EditTextWithDrawable(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        if (attrs != null && !isInEditMode()) {
            LayoutInflater inflater = (LayoutInflater) getContext()
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            inflater.inflate(R.layout.c_e_d_compound_view, this, true);
            mDrawableRight = (AppCompatImageView) ((FrameLayout) getChildAt(0)).getChildAt(1);
            mEditText = (EditText) ((FrameLayout) getChildAt(0)).getChildAt(0);

            TypedArray attributeArray = getContext().obtainStyledAttributes(
                    attrs,
                    R.styleable.EditTextWithDrawable);

            int drawableRes =
                    attributeArray.getResourceId(
                            R.styleable.EditTextWithDrawable_c_e_d_drawableRightSVG, -1);
            if (drawableRes != -1) {
                mDrawableRight.setImageResource(drawableRes);
            }

            mEditText.setHint(attributeArray.getString(
                    R.styleable.EditTextWithDrawable_c_e_d_hint));
            mEditText.setTextColor(attributeArray.getColor(
                    R.styleable.EditTextWithDrawable_c_e_d_textColor, Color.BLACK));
            int textSize = attributeArray.getDimensionPixelSize(R.styleable.EditTextWithDrawable_c_e_d_textSize, 15);
            mEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
            android.view.ViewGroup.LayoutParams layoutParams = mDrawableRight.getLayoutParams();
            layoutParams.width = (textSize * 3) / 2;
            layoutParams.height = (textSize * 3) / 2;
            mDrawableRight.setLayoutParams(layoutParams);

            attributeArray.recycle();
        }
    }
}

Ejemplo: Cómo usar la vista superior

Diseño: activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.customeditdrawable.AppEditTextWithDrawable
        android:id="@+id/edt_search_emp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:c_e_d_drawableRightSVG="@drawable/ic_svg_search"
        app:c_e_d_hint="@string/hint_search_here"
        app:c_e_d_textColor="@color/text_color_dark_on_light_bg"
        app:c_e_d_textSize="@dimen/text_size_small" />
</LinearLayout> 

Actividad: MainActivity.java

public class MainActivity extends AppCompatActivity {
    EditTextWithDrawable mEditTextWithDrawable;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mEditTextWithDrawable= (EditTextWithDrawable) findViewById(R.id.edt_search_emp);
    }
}

Respondiendo a los eventos táctiles

Muchas vistas personalizadas deben aceptar la interacción del usuario en forma de eventos táctiles. Puede obtener acceso a eventos táctiles anulando onTouchEvent . Hay una serie de acciones que puedes filtrar. Los principales son

  • ACTION_DOWN : Esto se activa una vez cuando su dedo toca la vista por primera vez.
  • ACTION_MOVE : se llama cada vez que su dedo se mueve un poco en la vista. Se llama muchas veces.
  • ACTION_UP : esta es la última acción a la que se llama cuando levanta el dedo de la pantalla.

Puede agregar el siguiente método a su vista y luego observar la salida del registro cuando toca y mueve su dedo alrededor de su vista.

@Override
public boolean onTouchEvent(MotionEvent event) {

    int x = (int) event.getX();
    int y = (int) event.getY();
    int action = event.getAction();

    switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.i("CustomView", "onTouchEvent: ACTION_DOWN: x = " + x + ", y = " + y);
            break;

        case MotionEvent.ACTION_MOVE:
            Log.i("CustomView", "onTouchEvent: ACTION_MOVE: x = " + x + ", y = " + y);
            break;

        case MotionEvent.ACTION_UP:
            Log.i("CustomView", "onTouchEvent: ACTION_UP: x = " + x + ", y = " + y);
            break;
    }
    return true;
}

Otras lecturas:



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow