Android Open Source - gti350-lab2-android-demo Drawing View






From Project

Back to project page gti350-lab2-android-demo.

License

The source code is released under:

MIT License

If you think the Android project gti350-lab2-android-demo listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package log350.example.example6;
//www .j a  v  a2s .  com
import java.util.ArrayList;
//import java.util.List;

import android.content.Context;
//import android.graphics.Matrix;
import android.graphics.Canvas;
//import android.graphics.Rect;
//import android.graphics.Path;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;



// This class stores the current position of a finger,
// as well as the history of previous positions of that finger
// during its drag.
//
// An instance of this class is created when a finger makes contact
// with the multitouch surface.  The instance stores all
// subsequent positions of the finger, and is destroyed
// when the finger is lifted off the multitouch surface.
class MyCursor {
  
  // Each finger in contact with the multitouch surface is given
  // a unique id by the framework (or computing platform).
  // There is no guarantee that these ids will be consecutive nor increasing.
  // For example, when two fingers are in contact with the multitouch surface,
  // their ids may be 0 and 1, respectively,
  // or their ids may be 14 and 9, respectively.
  public int id; // identifier

  // This stores the history of positions of the "cursor" (finger)
  // in pixel coordinates.
  // The first position is where the finger pressed down,
  // and the last position is the current position of the finger.
  private ArrayList< Point2D > positions = new ArrayList< Point2D >();



  // These are used to store what the cursor is being used for.
  public static final int TYPE_DRAGGING = 0; // the finger can be used for dragging objects, zooming, drawing a lasso, ...
  public static final int TYPE_BUTTON = 1; // the finger is pressing a virtual button
  public static final int TYPE_IGNORE = 2; // the finger should not be there and will be ignored
  public int type = TYPE_IGNORE;




  public MyCursor( int id, float x, float y ) {
    this.id = id;
    positions.add( new Point2D(x,y) );
  }

  public ArrayList< Point2D > getPositions() { return positions; }

  public void addPosition( Point2D p ) {
    positions.add( p );
  }

  public Point2D getFirstPosition() {
    if ( positions == null || positions.size() < 1 )
      return null;
    return positions.get( 0 );
  }
  public Point2D getCurrentPosition() {
    if ( positions == null || positions.size() < 1 )
      return null;
    return positions.get( positions.size()-1 );
  }
  public Point2D getPreviousPosition() {
    if ( positions == null || positions.size() == 0 )
      return null;
    if ( positions.size() == 1 )
      return positions.get( 0 );
    return positions.get( positions.size()-2 );
  }


  public int getType() { return type; }
  public void setType( int type ) { this.type = type; }
}


// This stores a set of instances of MyCursor.
// Each cursor can be identified by its id,
// which is assigned by the framework or computing platform.
// Each cursor can also be identified by its index in this class's container.
// For example, if an instance of this class is storing 3 cursors,
// their ids may be 2, 18, 7,
// but their indices should be 0, 1, 2.
class CursorContainer {
  private ArrayList< MyCursor > cursors = new ArrayList< MyCursor >();

  public int getNumCursors() { return cursors.size(); }
  public MyCursor getCursorByIndex( int index ) { return cursors.get( index ); }

  public int findIndexOfCursorById( int id ) {
    for ( int i = 0; i < cursors.size(); ++i ) {
      if ( cursors.get(i).id == id )
        return i;
    }
    return -1;
  }
  public MyCursor getCursorById( int id ) {
    int index = findIndexOfCursorById( id );
    return ( index == -1 ) ? null : cursors.get( index );
  }

  // Returns the number of cursors that are of the given type.
  public int getNumCursorsOfGivenType( int type ) {
    int num = 0;
    for ( int i = 0; i < cursors.size(); ++i ) {
      if ( cursors.get(i).getType() == type )
        num ++;
    }
    return num;
  }

  // Returns the (i)th cursor of the given type,
  // or null if no such cursor exists.
  // Can be used for retrieving both cursors of type TYPE_DRAGGING, for example,
  // by calling getCursorByType( MyCursor.TYPE_DRAGGING, 0 )
  // and getCursorByType( MyCursor.TYPE_DRAGGING, 1 ),
  // when there may be cursors of other type present at the same time.
  public MyCursor getCursorByType( int type, int i ) {
    for ( int ii = 0; ii < cursors.size(); ++ii ) {
      if ( cursors.get(ii).getType() == type ) {
        if ( i == 0 )
          return cursors.get(ii);
        else
          i --;
      }
    }
    return null;
  }

  // Returns index of updated cursor.
  // If a cursor with the given id does not already exist, a new cursor for it is created.
  public int updateCursorById(
    int id,
    float x, float y
  ) {
    Point2D updatedPosition = new Point2D( x, y );
    int index = findIndexOfCursorById( id );
    if ( index == -1 ) {
      cursors.add( new MyCursor( id, x, y ) );
      index = cursors.size() - 1;
    }
    MyCursor c = cursors.get( index );
    if ( ! c.getCurrentPosition().equals( updatedPosition ) ) {
      c.addPosition( updatedPosition );
    }
    return index;
  }
  public void removeCursorByIndex( int index ) {
    cursors.remove( index );
  }
}



public class DrawingView extends View {

  Paint paint = new Paint();

  GraphicsWrapper gw = new GraphicsWrapper();

  ShapeContainer shapeContainer = new ShapeContainer();
  ArrayList< Shape > selectedShapes = new ArrayList< Shape >();
  CursorContainer cursorContainer = new CursorContainer();

  static final int MODE_NEUTRAL = 0; // the default mode
  static final int MODE_CAMERA_MANIPULATION = 1; // the user is panning/zooming the camera
  static final int MODE_SHAPE_MANIPULATION = 2; // the user is translating/rotating/scaling a shape
  static final int MODE_LASSO = 3; // the user is drawing a lasso to select shapes
  static final int MODE_DELETE = 4; // the user is deleting shapes
  int currentMode = MODE_NEUTRAL;

  // This is only used when currentMode==MODE_SHAPE_MANIPULATION, otherwise it is equal to -1
  int indexOfShapeBeingManipulated = -1;

  MyButton lassoButton = new MyButton( "Lasso", 10, 70, 140, 140 );
  MyButton deleteButton = new MyButton( "Effacer", 10, 220, 140, 140 );
  
  OnTouchListener touchListener;
  
  public DrawingView(Context context) {
    super(context);
    
    setFocusable(true);
    setFocusableInTouchMode(true);
    
    this.setOnTouchListener(getTouchListener());
    
    this.setBackgroundColor(Color.WHITE);
    paint.setColor(Color.BLACK);
    paint.setAntiAlias(true);

    ArrayList< Point2D > arrayList = new ArrayList< Point2D >();
    arrayList.add( new Point2D(100,50) );
    arrayList.add( new Point2D(100,650) );
    arrayList.add( new Point2D(400,350) );
    shapeContainer.addShape( arrayList );
    arrayList.clear();
    arrayList.add( new Point2D(500,100) );
    arrayList.add( new Point2D(800,100) );
    arrayList.add( new Point2D(800,300) );
    arrayList.add( new Point2D(500,300) );
    shapeContainer.addShape( arrayList );
    arrayList.clear();
    arrayList.add( new Point2D(450,400) );
    arrayList.add( new Point2D(750,400) );
    arrayList.add( new Point2D(950,600) );
    arrayList.add( new Point2D(850,700) );
    arrayList.add( new Point2D(650,700) );
    shapeContainer.addShape( arrayList );
    arrayList.clear();
  }
  
  
  @Override
  protected void onDraw(Canvas canvas) {
    // The view is constantly redrawn by this method

    gw.set( paint, canvas );
    gw.clear( 0.0f, 0.0f, 0.0f );

    gw.setCoordinateSystemToWorldSpaceUnits();

    gw.setLineWidth( 1 );

    // draw a polygon around the currently selected shapes
    if ( selectedShapes.size() > 0 ) {
      ArrayList< Point2D > points = new ArrayList< Point2D >();
      AlignedRectangle2D rect = new AlignedRectangle2D();
      for ( Shape s : selectedShapes ) {
        for ( Point2D p : s.getPoints() ) {
          points.add( p );
          rect.bound( p );
        }
      }
      points = Point2DUtil.computeConvexHull( points );
      points = Point2DUtil.computeExpandedPolygon( points, rect.getDiagonal().length()/30 );

      gw.setColor( 1.0f, 0.0f, 0.0f, 0.8f );
      gw.fillPolygon( points );
    }

    // draw all the shapes
    shapeContainer.draw( gw, indexOfShapeBeingManipulated );

    gw.setCoordinateSystemToPixels();

    lassoButton.draw(gw, currentMode == MODE_LASSO);
    deleteButton.draw(gw, currentMode == MODE_DELETE);

    if ( currentMode == MODE_LASSO ) {
      MyCursor lassoCursor = cursorContainer.getCursorByType( MyCursor.TYPE_DRAGGING, 0 );
      if ( lassoCursor != null ) {
        gw.setColor(1.0f,0.0f,0.0f,0.5f);
        gw.fillPolygon( lassoCursor.getPositions() );
      }
    }

    if ( cursorContainer.getNumCursors() > 0 ) {
      gw.setFontHeight( 30 );
      gw.setLineWidth( 2 );
      gw.setColor( 1.0f, 1.0f, 1.0f );
      gw.drawString( 50, 50, "[" + cursorContainer.getNumCursors() + " contacts]");
    }

  }
  
  /**
   * Returns a listener
   * @return a listener
   */
  private OnTouchListener getTouchListener(){
    if ( touchListener == null ) {
      touchListener = new OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {

          int type = MotionEvent.ACTION_MOVE;
          switch ( event.getActionMasked() ) {
          case MotionEvent.ACTION_DOWN :
            type = MotionEvent.ACTION_DOWN;
            break;
          case MotionEvent.ACTION_MOVE :
            type = MotionEvent.ACTION_MOVE;
            break;
          case MotionEvent.ACTION_UP :
          case MotionEvent.ACTION_POINTER_UP :
          case MotionEvent.ACTION_CANCEL :
            type = MotionEvent.ACTION_UP;
            break;
          }

          int id = event.getPointerId(event.getActionIndex());
          float x = event.getX(event.getActionIndex());
          float y = event.getY(event.getActionIndex());
          // Find the cursor that corresponds to the event id, if such a cursor already exists.
          // If no such cursor exists, the below index will be -1, and the reference to cursor will be null.
          int cursorIndex = cursorContainer.findIndexOfCursorById( id );
          MyCursor cursor = (cursorIndex==-1) ? null : cursorContainer.getCursorByIndex( cursorIndex );

          Point2D p_pixels = new Point2D(x, y);
          Point2D p_world = gw.convertPixelsToWorldSpaceUnits(p_pixels);
          indexOfShapeBeingManipulated = shapeContainer.indexOfShapeContainingGivenPoint(p_world);

          if ( cursor == null ) {
            // The event does not correspond to any existing cursor.
            // In other words, this is a new finger touching the screen.
            // The event is probably of type DOWN.
            // A new cursor will need to be created for the event.
            if ( type == MotionEvent.ACTION_UP ) {
              // This should never happen, but if it does, just ignore the event.
              return true;
            }
            type = MotionEvent.ACTION_DOWN;
            // Cause a new cursor to be created to keep track of this event id in the future
            cursorIndex = cursorContainer.updateCursorById( id, x, y );
            cursor = cursorContainer.getCursorByIndex( cursorIndex );

            // we will set the type of the cursor later, by calling cursor.setType( MyCursor.TYPE_... );
          }
          else {
            // The event corresponds to an already existing cursor
            // (and the cursor was probably created during an earlier event of type TOUCH_EVENT_DOWN).
            // The current event is probably of type MOVE or UP.

            cursorContainer.updateCursorById( id, x, y );
            
            if ( type == MotionEvent.ACTION_MOVE ) {
              // Other fingers may have also moved, and there new positions are available in the event passed to us.
              // For safety, we update all cursors now with their latest positions.
              for ( int i = 0; i < event.getPointerCount(); ++i ) {
                int tmp_id = event.getPointerId(i);
                cursorContainer.updateCursorById( tmp_id, event.getX(i), event.getY(i) );
              }
            }
          }
          
          switch ( currentMode ) {

          case MODE_NEUTRAL :

            if ( cursorContainer.getNumCursors() == 1 ) {
              if (type == MotionEvent.ACTION_DOWN) {
                if ( lassoButton.contains(p_pixels) ) {
                  currentMode = MODE_LASSO;
                  cursor.setType( MyCursor.TYPE_BUTTON );
                } else if ( indexOfShapeBeingManipulated >= 0 ) {
                  currentMode = MODE_SHAPE_MANIPULATION;
                  cursor.setType( MyCursor.TYPE_DRAGGING );
                } else if (deleteButton.contains(p_pixels)) {
                  cursor.setType( MyCursor.TYPE_BUTTON );
                } else if (indexOfShapeBeingManipulated >= 0) {
                  currentMode = MODE_SHAPE_MANIPULATION;
                  cursor.setType( MyCursor.TYPE_DRAGGING );
                } else {
                  currentMode = MODE_CAMERA_MANIPULATION;
                  cursor.setType( MyCursor.TYPE_DRAGGING );
                }
              } else if (type == MotionEvent.ACTION_UP) {
                if (deleteButton.contains(p_pixels)) {
                  currentMode = MODE_DELETE;
                  cursor.setType(MyCursor.TYPE_BUTTON);
                }
              }
            }
            
            break;
          case MODE_CAMERA_MANIPULATION :
            if ( cursorContainer.getNumCursors() == 1 && type == MotionEvent.ACTION_MOVE ) {
              MyCursor cursor0 = cursorContainer.getCursorByIndex( 0 );

              gw.panAndZoomBasedOnDisplacementOfTwoPoints(
                cursor0.getPreviousPosition(),
                cursor0.getPreviousPosition(),
                cursor0.getCurrentPosition(),
                cursor0.getCurrentPosition()
              );
            } else if ( cursorContainer.getNumCursors() == 2 && type == MotionEvent.ACTION_MOVE ) {
              MyCursor cursor0 = cursorContainer.getCursorByIndex( 0 );
              MyCursor cursor1 = cursorContainer.getCursorByIndex( 1 );

              gw.panAndZoomBasedOnDisplacementOfTwoPoints(
                cursor0.getPreviousPosition(),
                cursor1.getPreviousPosition(),
                cursor0.getCurrentPosition(),
                cursor1.getCurrentPosition()
              );
            }
            else if ( type == MotionEvent.ACTION_UP ) {
              cursorContainer.removeCursorByIndex( cursorIndex );
              if ( cursorContainer.getNumCursors() == 0 )
                currentMode = MODE_NEUTRAL;
            }
            break;
            
          case MODE_SHAPE_MANIPULATION :
            if ( cursorContainer.getNumCursors() == 1 && type == MotionEvent.ACTION_MOVE && indexOfShapeBeingManipulated>=0 ) {
              MyCursor cursor0 = cursorContainer.getCursorByIndex( 0 );
              Shape shape = shapeContainer.getShape( indexOfShapeBeingManipulated );
              Point2DUtil.transformPointsBasedOnDisplacementOfOnePoint(
                  shape.getPoints()
                  , gw.convertPixelsToWorldSpaceUnits(cursor0.getPreviousPosition())
                  , gw.convertPixelsToWorldSpaceUnits(cursor0.getCurrentPosition())
                  );
              
            } else if ( cursorContainer.getNumCursors() == 2 && type == MotionEvent.ACTION_MOVE && indexOfShapeBeingManipulated>=0 ) {
              MyCursor cursor0 = cursorContainer.getCursorByIndex( 0 );
              MyCursor cursor1 = cursorContainer.getCursorByIndex( 1 );
              Shape shape = shapeContainer.getShape( indexOfShapeBeingManipulated );

              Point2DUtil.transformPointsBasedOnDisplacementOfTwoPoints(
                shape.getPoints(),
                gw.convertPixelsToWorldSpaceUnits( cursor0.getPreviousPosition() ),
                gw.convertPixelsToWorldSpaceUnits( cursor1.getPreviousPosition() ),
                gw.convertPixelsToWorldSpaceUnits( cursor0.getCurrentPosition() ),
                gw.convertPixelsToWorldSpaceUnits( cursor1.getCurrentPosition() )
              );
            }
            else if ( type == MotionEvent.ACTION_UP ) {
              cursorContainer.removeCursorByIndex( cursorIndex );
              if ( cursorContainer.getNumCursors() == 0 ) {
                currentMode = MODE_NEUTRAL;
                indexOfShapeBeingManipulated = -1;
              }
            }
            break;
            
          case MODE_LASSO :
            if ( type == MotionEvent.ACTION_DOWN ) {
              if ( cursorContainer.getNumCursorsOfGivenType(MyCursor.TYPE_DRAGGING) == 1 )
                // there's already a finger dragging out the lasso
                cursor.setType(MyCursor.TYPE_IGNORE);
              else
                cursor.setType(MyCursor.TYPE_DRAGGING);
            }
            else if ( type == MotionEvent.ACTION_MOVE ) {
              // no further updating necessary here
            }
            else if ( type == MotionEvent.ACTION_UP ) {
              if ( cursor.getType() == MyCursor.TYPE_DRAGGING ) {
                // complete a lasso selection
                selectedShapes.clear();

                // Need to transform the positions of the cursor from pixels to world space coordinates.
                // We will store the world space coordinates in the following data structure.
                ArrayList< Point2D > lassoPolygonPoints = new ArrayList< Point2D >();
                for ( Point2D p : cursor.getPositions() )
                  lassoPolygonPoints.add( gw.convertPixelsToWorldSpaceUnits( p ) );

                for ( Shape s : shapeContainer.shapes ) {
                  if ( s.isContainedInLassoPolygon( lassoPolygonPoints ) ) {
                    selectedShapes.add( s );
                  }
                }
              }
              cursorContainer.removeCursorByIndex( cursorIndex );
              if ( cursorContainer.getNumCursors() == 0 ) {
                currentMode = MODE_NEUTRAL;
              }
            }
            break;
            
          case MODE_DELETE :
            if (type == MotionEvent.ACTION_UP) {
              cursorContainer.removeCursorByIndex(cursorIndex);
              
              if (deleteButton.contains(p_pixels)) {
                currentMode = MODE_NEUTRAL;
              } else {
                if (indexOfShapeBeingManipulated >= 0) {
                  selectedShapes.clear();
                  shapeContainer.shapes.remove(indexOfShapeBeingManipulated);
                  indexOfShapeBeingManipulated = -1;
                }
              }
            }
            break;
          }
          
          v.invalidate();
          return true;
        }
      };
    }
    
    return touchListener;
  }
}




Java Source Code List

log350.example.example6.AlignedRectangle2D.java
log350.example.example6.DrawingView.java
log350.example.example6.GraphicsWrapper.java
log350.example.example6.Log350Example6Activity.java
log350.example.example6.MyButton.java
log350.example.example6.Point2DUtil.java
log350.example.example6.Point2D.java
log350.example.example6.ShapeContainer.java
log350.example.example6.Shape.java
log350.example.example6.Vector2D.java