image/svg+xml $ $ ing$ ing$ ces$ ces$ Res Res ea ea Res->ea ou ou Res->ou r r ea->r ch ch ea->ch r->ces$ r->ch ch->$ ch->ing$ T T T->ea ou->r

View

Arbre de vues

Arbre de vues

Dessin d'une vue

Principe

Canvas

Une sélection de méthodes de Canvas :

Exemple

Code de SquareView :

// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.squareview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/** A variable-sized view displaying a black-red square */
public class SquareView extends View {

    // Implementation of all the ancestor constructors
    public SquareView(Context context) {
        super(context);
    }

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

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

    public SquareView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    private float redness = 0.0f;
    private float size = 0.0f;

    public void setSize(float size) {
        if (size != this.size) {
            this.size = size;
            requestLayout(); // to resize
        }
    }

    public void setRedness(float redness) {
        if (redness != this.redness) {
            this.redness = redness;
            invalidate();
        }
    }

    protected int computeDimension(int spec) {
        if (MeasureSpec.getMode(spec) == MeasureSpec.EXACTLY)
            return MeasureSpec.getSize(spec); // we have no choice
        else
            return (int)(MeasureSpec.getSize(spec) * size);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(computeDimension(widthMeasureSpec), computeDimension(heightMeasureSpec));
    }

    private final Paint paint = new Paint();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setARGB(255, (int)(255 * redness), 0, 0); // set the color
        canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
    }
}

Réalisation d'une activité utilisant SquareView :

<?xml version="1.0" encoding="utf-8"?>
<!-- Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License -->

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".squareview.SquareActivity">

    <TextView
        android:id="@+id/textView5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Size"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteX="16dp" />

    <TextView
        android:id="@+id/textView6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Redness"
        app:layout_constraintTop_toBottomOf="@+id/textView5"
        tools:layout_editor_absoluteX="16dp" />

    <SeekBar
        android:id="@+id/sizeBar"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        app:layout_constraintTop_toTopOf="@id/textView5"
        app:layout_constraintStart_toEndOf="@+id/textView5"
        app:layout_constraintEnd_toEndOf="parent"/>

    <SeekBar
        android:id="@+id/rednessBar"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        app:layout_constraintStart_toEndOf="@+id/textView6"
        app:layout_constraintTop_toTopOf="@id/textView6"
        app:layout_constraintEnd_toEndOf="parent"
        tools:layout_editor_absoluteY="99dp" />

    <fr.upem.coursand.squareview.SquareView
        android:id="@+id/squareView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/rednessBar" />
</androidx.constraintlayout.widget.ConstraintLayout>

// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.squareview;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.SeekBar;

import fr.upem.coursand.R;

public class SquareActivity extends AppCompatActivity {

    private SquareView squareView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_square);
        squareView = findViewById(R.id.squareView);
        for (int id: new int[]{R.id.sizeBar, R.id.rednessBar})
            ((SeekBar)findViewById(id)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    float value = getValue(seekBar.getId());
                    switch (seekBar.getId()) {
                        case R.id.sizeBar: squareView.setSize(value); break;
                        case R.id.rednessBar: squareView.setRedness(value); break;
                    }
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) { }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) { }
            });
    }

    public float getValue(int viewId) {
        SeekBar bar = (SeekBar)findViewById(viewId);
        return (float)bar.getProgress() / bar.getMax();
    }
}

Exercices d'application

Optimisation : utilisation d'un buffer

Paint paint = new Paint();

@Override
protected void onDraw(Canvas c) {
	canvas.drawBitmap(bufferBitmap, csanvas.getWidth(), canvas.getHeight(), paint);
}	

Implantations élémentaires de View

Éléments de formulaire

Listeners d'événements

Objectif : associer une action à réaliser lors de la survenue d'un événement

Réaction à des événements avec un listener

Attribut XML onClick

View dispose d'une propriété onClick dont la valeur est définissable dans le layout XML :

Redéfinition de la méthode onEvent()

On peut créer un composant dérivé du composant souhaité et redéfinir certaines méthodes... dont les méthodes de type onEvent() qui sont appelées à la survenue d'un événement. À déconseiller en règle générale.

Interception globale d'événements au niveau de l'activité

boolean Activity.dispatchXEvent(XEvent) est une méthode qui dispatche un événément reçu par l'activité vers la vue concernée

Types d'événéments traités :

On peut redéfinir la méthode dans l'activité pour capturer l'événement avant sa transmission à la vue concernée. Si on souhaite tout de même assurer la transmission normale à la vue, il ne faut pas oublier d'appeler super.dispatchX(...).

Interception d'événément par une vue parent

Des méthodes permettent d'intercepter un événement par une vue parent (avant sa transmission à une vue enfant) :

Une vue enfant peut temporairement désactiver l'interception d'un parent en appelant sa méthode requestDisallowInterceptTouchEvent(boolean disallowIntercept) (valable pour la séquence de touché courante).

Evénéments courants

Valeur de retour boolean : permet d'indiquer si l'événement a été consommé, i.e. s'il ne doit plus être communiqué à d'autres listeners (de la même vue ou de vues enfant) ou si la fin d'un événement composé doit être ignorée.

Événements de touché

Un exemple de OnTouchListener

view.setOnTouchListener(new OnTouchListener() {
	@Override public boolean onTouch(View v, MotionEvent e)
	{
		switch (e.getActionMasked())
		{
			case MotionEvent.ACTION_DOWN :
				// Starting a new touch action (first finger pressed)
				// Coordinates of the starting point can be fetched with e.getX() and e.getY()
				// The first finger has the index 0
			case MotionEvent.ACTION_POINTER_DOWN :
				// A new finger is pressed
				// Its identifier can be obtained with e.getPointerId(e.getActionIndex())
			case MotionEvent.ACTION_MOVE :
				// One or several fingers are moved
				// Their coordinates can be obtained with e.getX(int index) and e.getY(int index)
				// e.getPointerCount() specifies the number of implied fingers
				// e.getPointerId(int) converts a pointer index to a universal id 
				// that can be tracked across events
				// e.findPointerIndex(int) does the reverse operation
			case MotionEvent.ACTION_POINTER_UP :
				// A finger has been raised
				// Its identifier is e.getPointerId(e.getActionIndex())
			case MotionEvent.ACTION_UP :
				// The last finger has been raised
		}
		return true; // returning true means that the event is consumed
	} 
});

Le OnTouchListener est toujours appelé avant les listeners de gestion de clic et de clic long : si la méthode de gestion d'événement de touché retourne true l'événement est consommé et les listeners de clic ne seront pas appelés.

Exercice d'application : le voyageur de commerce

  1. Afficher plusieurs points à l'écran
  2. Proposer à l'utilisateur de tracer avec le doigt un chemin entre tous ces points
  3. Calculer la longueur du chemin tracé en pixels et le comparer par rapport à un chemin théoriquement calculé en utilisant une heuristique de voyageur de commerce

Reconnaissance de gestures

Drawable

Gestion du focus