package com.aviary.android.feather.widget; import android.content.res.ColorStateList; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.MotionEvent; import android.view.View; import com.aviary.android.feather.R; import com.aviary.android.feather.library.graphics.Point2D; import com.aviary.android.feather.library.graphics.drawable.EditableDrawable; import com.aviary.android.feather.library.graphics.drawable.FeatherDrawable; // TODO: Auto-generated Javadoc /** * The Class DrawableHighlightView. */ public class DrawableHighlightView { static final String LOG_TAG = "drawable-view"; /** * The Enum Mode. */ enum Mode { /** The None. */ None, /** The Move. */ Move, /** The Grow. */ Grow, /** The Rotate. */ Rotate }; /** * The Enum AlignModeV. */ public enum AlignModeV { /** The Top. */ Top, /** The Bottom. */ Bottom, /** The Center. */ Center }; /** * The listener interface for receiving onDeleteClick events. The class that is interested in processing a onDeleteClick event * implements this interface, and the object created with that class is registered with a component using the component's * <code>addOnDeleteClickListener<code> method. When * the onDeleteClick event occurs, that object's appropriate * method is invoked. * * @see OnDeleteClickEvent */ public interface OnDeleteClickListener { /** * On delete click. */ void onDeleteClick(); } private int STATE_NONE = 1 << 0; private int STATE_SELECTED = 1 << 1; private int STATE_FOCUSED = 1 << 2; /** The m delete click listener. */ private OnDeleteClickListener mDeleteClickListener; /** The Constant GROW_NONE. */ static final int GROW_NONE = 1 << 0; // 1 /** The Constant GROW_LEFT_EDGE. */ static final int GROW_LEFT_EDGE = 1 << 1; // 2 /** The Constant GROW_RIGHT_EDGE. */ static final int GROW_RIGHT_EDGE = 1 << 2; // 4 /** The Constant GROW_TOP_EDGE. */ static final int GROW_TOP_EDGE = 1 << 3; // 8 /** The Constant GROW_BOTTOM_EDGE. */ static final int GROW_BOTTOM_EDGE = 1 << 4; // 16 /** The Constant ROTATE. */ static final int ROTATE = 1 << 5; // 32 /** The Constant MOVE. */ static final int MOVE = 1 << 6; // 64 // tolerance for buttons hits /** The Constant HIT_TOLERANCE. */ private static final float HIT_TOLERANCE = 40f; /** The m hidden. */ private boolean mHidden; /** The m context. */ private View mContext; /** The m mode. */ private Mode mMode; private int mState = STATE_NONE; /** The m draw rect. */ private RectF mDrawRect; /** The m crop rect. */ private RectF mCropRect; /** The m matrix. */ private Matrix mMatrix; /** The m content. */ private final FeatherDrawable mContent; private final EditableDrawable mEditableContent; // private Drawable mAnchorResize; /** The m anchor rotate. */ private Drawable mAnchorRotate; /** The m anchor delete. */ private Drawable mAnchorDelete; /** The m anchor width. */ private int mAnchorWidth; /** The m anchor height. */ private int mAnchorHeight; /** The m outline stroke color. */ private int mOutlineStrokeColorNormal; /** The m outline stroke color pressed. */ private int mOutlineStrokeColorPressed; private int mOutlineStrokeColorUnselected; /** The m rotate and scale. */ private boolean mRotateAndScale; /** The m show delete button. */ private boolean mShowDeleteButton = true; /** The m rotation. */ private float mRotation = 0; /** The m ratio. */ private float mRatio = 1f; /** The m rotate matrix. */ private Matrix mRotateMatrix = new Matrix(); /** The fpoints. */ private final float fpoints[] = new float[] { 0, 0 }; /** The m draw outline stroke. */ private boolean mDrawOutlineStroke = true; /** The m draw outline fill. */ private boolean mDrawOutlineFill = true; /** The m outline stroke paint. */ private Paint mOutlineStrokePaint; /** The m outline fill paint. */ private Paint mOutlineFillPaint; /** The m outline fill color normal. */ private int mOutlineFillColorNormal = 0x66000000; /** The m outline fill color pressed. */ private int mOutlineFillColorPressed = 0x66a5a5a5; private int mOutlineFillColorUnselected = 0; private int mOutlineFillColorFocused = 0x88FFFFFF; private int mOutlineStrokeColorFocused = 0x51000000; /** The m outline ellipse. */ private int mOutlineEllipse = 0; /** The m padding. */ private int mPadding = 0; /** The m show anchors. */ private boolean mShowAnchors = true; /** The m align vertical mode. */ private AlignModeV mAlignVerticalMode = AlignModeV.Center; /** * Instantiates a new drawable highlight view. * * @param ctx * the ctx * @param content * the content */ public DrawableHighlightView( final View ctx, final FeatherDrawable content ) { mContext = ctx; mContent = content; if( content instanceof EditableDrawable ){ mEditableContent = (EditableDrawable)content; } else { mEditableContent = null; } updateRatio(); setMinSize( 20f ); } /** * Sets the align mode v. * * @param mode * the new align mode v */ public void setAlignModeV( AlignModeV mode ) { mAlignVerticalMode = mode; } /** * Request a layout update. * * @return the rect f */ protected RectF computeLayout() { return getDisplayRect( mMatrix, mCropRect ); } /** * Dispose. */ public void dispose() { mContext = null; mDeleteClickListener = null; } /** The m outline path. */ private Path mOutlinePath = new Path(); // private FontMetrics metrics = new FontMetrics(); /** * Draw. * * @param canvas * the canvas */ protected void draw( final Canvas canvas ) { if ( mHidden ) return; RectF drawRectF = new RectF( mDrawRect ); drawRectF.inset( -mPadding, -mPadding ); final int saveCount = canvas.save(); canvas.concat( mRotateMatrix ); final Rect viewDrawingRect = new Rect(); mContext.getDrawingRect( viewDrawingRect ); mOutlinePath.reset(); mOutlinePath.addRoundRect( drawRectF, mOutlineEllipse, mOutlineEllipse, Path.Direction.CW ); if ( mDrawOutlineFill ) { canvas.drawPath( mOutlinePath, mOutlineFillPaint ); } if ( mDrawOutlineStroke ) { canvas.drawPath( mOutlinePath, mOutlineStrokePaint ); } boolean is_selected = getSelected(); boolean is_focused = getFocused(); if ( mEditableContent != null ) mEditableContent.setBounds( mDrawRect.left, mDrawRect.top, mDrawRect.right, mDrawRect.bottom ); else mContent.setBounds( (int) mDrawRect.left, (int) mDrawRect.top, (int) mDrawRect.right, (int) mDrawRect.bottom ); mContent.draw( canvas ); if ( is_selected && !is_focused ) { if ( mShowAnchors ) { final int left = (int) ( drawRectF.left ); final int right = (int) ( drawRectF.right ); final int top = (int) ( drawRectF.top ); final int bottom = (int) ( drawRectF.bottom ); if ( mAnchorRotate != null ) { mAnchorRotate.setBounds( right - mAnchorWidth, bottom - mAnchorHeight, right + mAnchorWidth, bottom + mAnchorHeight ); mAnchorRotate.draw( canvas ); } if ( ( mAnchorDelete != null ) && mShowDeleteButton ) { mAnchorDelete.setBounds( left - mAnchorWidth, top - mAnchorHeight, left + mAnchorWidth, top + mAnchorHeight ); mAnchorDelete.draw( canvas ); } } } canvas.restoreToCount( saveCount ); if ( mEditableContent != null && is_selected ) { if ( mEditableContent.isEditing() ) { mContext.postInvalidateDelayed( 300 ); } } } /** * Show anchors. * * @param value * the value */ public void showAnchors( boolean value ) { mShowAnchors = value; } /** * Draw. * * @param canvas * the canvas * @param source * the source */ public void draw( final Canvas canvas, final Matrix source ) { final Matrix matrix = new Matrix( source ); matrix.invert( matrix ); final int saveCount = canvas.save(); canvas.concat( matrix ); canvas.concat( mRotateMatrix ); mContent.setBounds( (int) mDrawRect.left, (int) mDrawRect.top, (int) mDrawRect.right, (int) mDrawRect.bottom ); mContent.draw( canvas ); canvas.restoreToCount( saveCount ); } /** * Returns the cropping rectangle in image space. * * @return the crop rect */ public Rect getCropRect() { return new Rect( (int) mCropRect.left, (int) mCropRect.top, (int) mCropRect.right, (int) mCropRect.bottom ); } /** * Gets the crop rect f. * * @return the crop rect f */ public RectF getCropRectF() { return mCropRect; } /** * Gets the crop rotation matrix. * * @return the crop rotation matrix */ public Matrix getCropRotationMatrix() { final Matrix m = new Matrix(); m.postTranslate( -mCropRect.centerX(), -mCropRect.centerY() ); m.postRotate( mRotation ); m.postTranslate( mCropRect.centerX(), mCropRect.centerY() ); return m; } /** * Gets the display rect. * * @param m * the m * @param supportRect * the support rect * @return the display rect */ protected RectF getDisplayRect( final Matrix m, final RectF supportRect ) { final RectF r = new RectF( supportRect ); m.mapRect( r ); return r; } /** * Gets the display rect f. * * @return the display rect f */ public RectF getDisplayRectF() { final RectF r = new RectF( mDrawRect ); mRotateMatrix.mapRect( r ); return r; } /** * Gets the draw rect. * * @return the draw rect */ public RectF getDrawRect() { return mDrawRect; } /** * Gets the hit. * * @param x * the x * @param y * the y * @return the hit */ public int getHit( float x, float y ) { final RectF rect = new RectF( mDrawRect ); rect.inset( -mPadding, -mPadding ); final float pts[] = new float[] { x, y }; final Matrix rotateMatrix = new Matrix(); rotateMatrix.postTranslate( -rect.centerX(), -rect.centerY() ); rotateMatrix.postRotate( -mRotation ); rotateMatrix.postTranslate( rect.centerX(), rect.centerY() ); rotateMatrix.mapPoints( pts ); x = pts[0]; y = pts[1]; mContext.invalidate(); int retval = DrawableHighlightView.GROW_NONE; final boolean verticalCheck = ( y >= ( rect.top - DrawableHighlightView.HIT_TOLERANCE ) ) && ( y < ( rect.bottom + DrawableHighlightView.HIT_TOLERANCE ) ); final boolean horizCheck = ( x >= ( rect.left - DrawableHighlightView.HIT_TOLERANCE ) ) && ( x < ( rect.right + DrawableHighlightView.HIT_TOLERANCE ) ); // if horizontal and vertical checks are good then // at least the move edge is selected if( verticalCheck && horizCheck ){ retval = DrawableHighlightView.MOVE; } if ( !mRotateAndScale ) { if ( ( Math.abs( rect.left - x ) < DrawableHighlightView.HIT_TOLERANCE ) && verticalCheck ) retval |= DrawableHighlightView.GROW_LEFT_EDGE; if ( ( Math.abs( rect.right - x ) < DrawableHighlightView.HIT_TOLERANCE ) && verticalCheck ) retval |= DrawableHighlightView.GROW_RIGHT_EDGE; if ( ( Math.abs( rect.top - y ) < DrawableHighlightView.HIT_TOLERANCE ) && horizCheck ) retval |= DrawableHighlightView.GROW_TOP_EDGE; if ( ( Math.abs( rect.bottom - y ) < DrawableHighlightView.HIT_TOLERANCE ) && horizCheck ) retval |= DrawableHighlightView.GROW_BOTTOM_EDGE; } if ( ( Math.abs( rect.right - x ) < DrawableHighlightView.HIT_TOLERANCE ) && ( Math.abs( rect.bottom - y ) < DrawableHighlightView.HIT_TOLERANCE ) && verticalCheck && horizCheck ) retval = DrawableHighlightView.ROTATE; if ( ( retval == DrawableHighlightView.GROW_NONE ) && rect.contains( (int) x, (int) y ) ) retval = DrawableHighlightView.MOVE; return retval; } /** * On single tap confirmed. * * @param x * the x * @param y * the y */ public void onSingleTapConfirmed( float x, float y ) { final RectF rect = new RectF( mDrawRect ); rect.inset( -mPadding, -mPadding ); final float pts[] = new float[] { x, y }; final Matrix rotateMatrix = new Matrix(); rotateMatrix.postTranslate( -rect.centerX(), -rect.centerY() ); rotateMatrix.postRotate( -mRotation ); rotateMatrix.postTranslate( rect.centerX(), rect.centerY() ); rotateMatrix.mapPoints( pts ); x = pts[0]; y = pts[1]; mContext.invalidate(); final boolean verticalCheck = ( y >= ( rect.top - DrawableHighlightView.HIT_TOLERANCE ) ) && ( y < ( rect.bottom + DrawableHighlightView.HIT_TOLERANCE ) ); final boolean horizCheck = ( x >= ( rect.left - DrawableHighlightView.HIT_TOLERANCE ) ) && ( x < ( rect.right + DrawableHighlightView.HIT_TOLERANCE ) ); if ( mShowDeleteButton ) if ( ( Math.abs( rect.left - x ) < DrawableHighlightView.HIT_TOLERANCE ) && ( Math.abs( rect.top - y ) < DrawableHighlightView.HIT_TOLERANCE ) && verticalCheck && horizCheck ) if ( mDeleteClickListener != null ) { mDeleteClickListener.onDeleteClick(); } } /** * Gets the invalidation rect. * * @return the invalidation rect */ protected Rect getInvalidationRect() { final RectF r = new RectF( mDrawRect ); r.inset( -mPadding, -mPadding ); mRotateMatrix.mapRect( r ); final Rect rect = new Rect( (int) r.left, (int) r.top, (int) r.right, (int) r.bottom ); rect.inset( -mAnchorWidth * 2, -mAnchorHeight * 2 ); return rect; } /** * Gets the matrix. * * @return the matrix */ public Matrix getMatrix() { return mMatrix; } /** * Gets the mode. * * @return the mode */ public Mode getMode() { return mMode; } /** * Gets the rotation. * * @return the rotation */ public float getRotation() { return mRotation; } public Matrix getRotationMatrix() { return mRotateMatrix; } /** * Increase the size of the View. * * @param dx * the dx */ protected void growBy( final float dx ) { growBy( dx, dx / mRatio, true ); } /** * Increase the size of the View. * * @param dx * the dx * @param dy * the dy * @param checkMinSize * the check min size */ protected void growBy( final float dx, final float dy, boolean checkMinSize ) { final RectF r = new RectF( mCropRect ); if ( mAlignVerticalMode == AlignModeV.Center ) { r.inset( -dx, -dy ); } else if ( mAlignVerticalMode == AlignModeV.Top ) { r.inset( -dx, 0 ); r.bottom += dy * 2; } else { r.inset( -dx, 0 ); r.top -= dy * 2; } RectF testRect = getDisplayRect( mMatrix, r ); Log.d( LOG_TAG, "growBy: " + testRect.width() + "x" + testRect.height() ); if ( !mContent.validateSize( testRect ) && checkMinSize ) { return; } mCropRect.set( r ); invalidate(); mContext.invalidate(); } /** * On mouse move. * * @param edge * the edge * @param event2 * the event2 * @param dx * the dx * @param dy * the dy */ void onMouseMove( int edge, MotionEvent event2, float dx, float dy ) { if ( edge == GROW_NONE ) { return; } fpoints[0] = dx; fpoints[1] = dy; float xDelta; @SuppressWarnings("unused") float yDelta; if ( edge == MOVE ) { moveBy( dx * ( mCropRect.width() / mDrawRect.width() ), dy * ( mCropRect.height() / mDrawRect.height() ) ); } else if ( edge == ROTATE ) { dx = fpoints[0]; dy = fpoints[1]; xDelta = dx * ( mCropRect.width() / mDrawRect.width() ); yDelta = dy * ( mCropRect.height() / mDrawRect.height() ); rotateBy( event2.getX(), event2.getY(), dx, dy ); invalidate(); mContext.invalidate( getInvalidationRect() ); } else { Matrix rotateMatrix = new Matrix(); rotateMatrix.postRotate( -mRotation ); rotateMatrix.mapPoints( fpoints ); dx = fpoints[0]; dy = fpoints[1]; if ( ( ( GROW_LEFT_EDGE | GROW_RIGHT_EDGE ) & edge ) == 0 ) dx = 0; if ( ( ( GROW_TOP_EDGE | GROW_BOTTOM_EDGE ) & edge ) == 0 ) dy = 0; xDelta = dx * ( mCropRect.width() / mDrawRect.width() ); yDelta = dy * ( mCropRect.height() / mDrawRect.height() ); growBy( ( ( ( edge & GROW_LEFT_EDGE ) != 0 ) ? -1 : 1 ) * xDelta ); invalidate(); mContext.invalidate( getInvalidationRect() ); } } void onMove( float dx, float dy ) { moveBy( dx * ( mCropRect.width() / mDrawRect.width() ), dy * ( mCropRect.height() / mDrawRect.height() ) ); } /** * Inits the. */ private void init() { final android.content.res.Resources resources = mContext.getResources(); // mAnchorResize = resources.getDrawable( R.drawable.camera_crop_width ); mAnchorRotate = resources.getDrawable( R.drawable.feather_resize_knob ); mAnchorDelete = resources.getDrawable( R.drawable.feather_highlight_delete_button ); mAnchorWidth = mAnchorRotate.getIntrinsicWidth() / 2; mAnchorHeight = mAnchorRotate.getIntrinsicHeight() / 2; mOutlineStrokeColorNormal = mContext.getResources().getColor( R.color.feather_drawable_highlight_focus ); mOutlineStrokeColorPressed = mContext.getResources().getColor( R.color.feather_drawable_highlight_down ); mOutlineStrokeColorUnselected = 0; mOutlineStrokePaint = new Paint( Paint.ANTI_ALIAS_FLAG ); mOutlineStrokePaint.setStrokeWidth( 2.0f ); mOutlineStrokePaint.setStyle( Paint.Style.STROKE ); mOutlineStrokePaint.setColor( mOutlineStrokeColorNormal ); mOutlineFillPaint = new Paint( Paint.ANTI_ALIAS_FLAG ); mOutlineFillPaint.setStyle( Paint.Style.FILL ); mOutlineFillPaint.setColor( mOutlineFillColorNormal ); setMode( Mode.None ); } /** * Invalidate. */ public void invalidate() { mDrawRect = computeLayout(); // true mRotateMatrix.reset(); mRotateMatrix.postTranslate( -mDrawRect.centerX(), -mDrawRect.centerY() ); mRotateMatrix.postRotate( mRotation ); mRotateMatrix.postTranslate( mDrawRect.centerX(), mDrawRect.centerY() ); } /** * Move by. * * @param dx * the dx * @param dy * the dy */ void moveBy( final float dx, final float dy ) { mCropRect.offset( dx, dy ); invalidate(); mContext.invalidate(); } /** * Rotate by. * * @param dx * the dx * @param dy * the dy * @param diffx * the diffx * @param diffy * the diffy */ void rotateBy( final float dx, final float dy, float diffx, float diffy ) { final float pt1[] = new float[] { mDrawRect.centerX(), mDrawRect.centerY() }; final float pt2[] = new float[] { mDrawRect.right, mDrawRect.bottom }; final float pt3[] = new float[] { dx, dy }; final double angle1 = Point2D.angleBetweenPoints( pt2, pt1 ); final double angle2 = Point2D.angleBetweenPoints( pt3, pt1 ); if ( !mRotateAndScale ) mRotation = -(float) ( angle2 - angle1 ); final Matrix rotateMatrix = new Matrix(); rotateMatrix.postRotate( -mRotation ); if ( mRotateAndScale ) { final float points[] = new float[] { diffx, diffy }; rotateMatrix.mapPoints( points ); diffx = points[0]; diffy = points[1]; final float xDelta = diffx * ( mCropRect.width() / mDrawRect.width() ); final float yDelta = diffy * ( mCropRect.height() / mDrawRect.height() ); final float pt4[] = new float[] { mDrawRect.right + xDelta, mDrawRect.bottom + yDelta }; final double distance1 = Point2D.distance( pt1, pt2 ); final double distance2 = Point2D.distance( pt1, pt4 ); final float distance = (float) ( distance2 - distance1 ); // float ratio = mDrawRect.width() / mDrawRect.height(); mRotation = -(float) ( angle2 - angle1 ); growBy( distance ); } } void onRotateAndGrow( double angle, float scaleFactor ) { if ( !mRotateAndScale ) mRotation -= (float) ( angle ); if ( mRotateAndScale ) { mRotation -= (float) ( angle ); growBy( scaleFactor * ( mCropRect.width() / mDrawRect.width() ) ); } invalidate(); mContext.invalidate( getInvalidationRect() ); } /** * Toggle visibility to the current View. * * @param hidden * the new hidden */ public void setHidden( final boolean hidden ) { mHidden = hidden; } /** * Sets the min size. * * @param size * the new min size */ public void setMinSize( final float size ) { if ( mRatio >= 1 ) { mContent.setMinSize( size, size / mRatio ); } else { mContent.setMinSize( size * mRatio, size ); } } /** * Sets the mode. * * @param mode * the new mode */ public void setMode( final Mode mode ) { if ( mode != mMode ) { mMode = mode; invalidateColors(); mContext.invalidate(); } } protected void invalidateColors() { boolean is_selected = getSelected(); boolean is_focused = getFocused(); if( is_selected ){ if( mMode == Mode.None ){ if( is_focused ){ mOutlineFillPaint.setColor( mOutlineFillColorFocused ); mOutlineStrokePaint.setColor( mOutlineStrokeColorFocused ); } else { mOutlineFillPaint.setColor( mOutlineFillColorNormal ); mOutlineStrokePaint.setColor( mOutlineStrokeColorNormal ); } } else { mOutlineFillPaint.setColor( mOutlineFillColorPressed ); mOutlineStrokePaint.setColor( mOutlineStrokeColorPressed ); } } else { mOutlineFillPaint.setColor( mOutlineFillColorUnselected ); mOutlineStrokePaint.setColor( mOutlineStrokeColorUnselected ); } } /** * Sets the on delete click listener. * * @param listener * the new on delete click listener */ public void setOnDeleteClickListener( final OnDeleteClickListener listener ) { mDeleteClickListener = listener; } /** * Sets the rotate and scale. * * @param value * the new rotate and scale */ public void setRotateAndScale( final boolean value ) { mRotateAndScale = value; } /** * Show delete. * * @param value * the value */ public void showDelete( boolean value ) { mShowDeleteButton = value; } /** * Sets the selected. * * @param selected * the new selected */ public void setSelected( final boolean selected ) { boolean is_selected = getSelected(); if ( is_selected != selected ) { mState ^= STATE_SELECTED; invalidateColors(); } mContext.invalidate(); } public boolean getSelected() { return ( mState & STATE_SELECTED ) == STATE_SELECTED; } public void setFocused( final boolean value ) { boolean is_focused = getFocused(); if ( is_focused != value ) { mState ^= STATE_FOCUSED; if( null != mEditableContent ){ if( value ){ mEditableContent.beginEdit(); } else { mEditableContent.endEdit(); } } invalidateColors(); } mContext.invalidate(); } public boolean getFocused() { return ( mState & STATE_FOCUSED ) == STATE_FOCUSED; } /** * Setup. * * @param m * the m * @param imageRect * the image rect * @param cropRect * the crop rect * @param maintainAspectRatio * the maintain aspect ratio */ public void setup( final Matrix m, final Rect imageRect, final RectF cropRect, final boolean maintainAspectRatio ) { init(); mMatrix = new Matrix( m ); mRotation = 0; mRotateMatrix = new Matrix(); mCropRect = cropRect; invalidate(); } /** * Update. * * @param imageMatrix * the image matrix * @param imageRect * the image rect */ public void update( final Matrix imageMatrix, final Rect imageRect ) { setMode( Mode.None ); mMatrix = new Matrix( imageMatrix ); mRotation = 0; mRotateMatrix = new Matrix(); invalidate(); } /** * Draw outline stroke. * * @param value * the value */ public void drawOutlineStroke( boolean value ) { mDrawOutlineStroke = value; } /** * Draw outline fill. * * @param value * the value */ public void drawOutlineFill( boolean value ) { mDrawOutlineFill = value; } /** * Gets the outline stroke paint. * * @return the outline stroke paint */ public Paint getOutlineStrokePaint() { return mOutlineStrokePaint; } /** * Gets the outline fill paint. * * @return the outline fill paint */ public Paint getOutlineFillPaint() { return mOutlineFillPaint; } public void setOutlineFillColor( ColorStateList colors ){ mOutlineFillColorNormal = colors.getColorForState( new int[]{ android.R.attr.state_selected }, 0 ); mOutlineFillColorFocused = colors.getColorForState( new int[]{ android.R.attr.state_focused }, 0 ); mOutlineFillColorPressed = colors.getColorForState( new int[]{ android.R.attr.state_pressed }, 0 ); mOutlineFillColorUnselected = colors.getColorForState( new int[]{ android.R.attr.state_active }, 0 ); invalidateColors(); invalidate(); mContext.invalidate(); } public void setOutlineStrokeColor( ColorStateList colors ){ mOutlineStrokeColorNormal = colors.getColorForState( new int[]{ android.R.attr.state_selected }, 0 ); mOutlineStrokeColorFocused = colors.getColorForState( new int[]{ android.R.attr.state_focused }, 0 ); mOutlineStrokeColorPressed = colors.getColorForState( new int[]{ android.R.attr.state_pressed }, 0 ); mOutlineStrokeColorUnselected = colors.getColorForState( new int[]{ android.R.attr.state_active }, 0 ); invalidateColors(); invalidate(); mContext.invalidate(); } /** * Sets the outline ellipse. * * @param value * the new outline ellipse */ public void setOutlineEllipse( int value ) { mOutlineEllipse = value; invalidate(); mContext.invalidate(); } /** * Gets the content. * * @return the content */ public FeatherDrawable getContent() { return mContent; } /** * Update ratio. */ private void updateRatio() { final int w = mContent.getIntrinsicWidth(); final int h = mContent.getIntrinsicHeight(); mRatio = (float) w / (float) h; } /** * Force update. */ public void forceUpdate() { Log.i( LOG_TAG, "forceUpdate" ); RectF cropRect = getCropRectF(); RectF drawRect = getDrawRect(); if ( mEditableContent != null ) { final int textWidth = mContent.getIntrinsicWidth(); final int textHeight = mContent.getIntrinsicHeight(); Log.d( LOG_TAG, "text.size: " + textWidth + "x" + textHeight ); updateRatio(); RectF textRect = new RectF( cropRect ); getMatrix().mapRect( textRect ); float dx = textWidth - textRect.width(); float dy = textHeight - textRect.height(); float[] fpoints = new float[] { dx, dy }; Matrix rotateMatrix = new Matrix(); rotateMatrix.postRotate( -mRotation ); // rotateMatrix.mapPoints( fpoints ); dx = fpoints[0]; dy = fpoints[1]; float xDelta = dx * ( cropRect.width() / drawRect.width() ); float yDelta = dy * ( cropRect.height() / drawRect.height() ); if ( xDelta != 0 || yDelta != 0 ) { growBy( xDelta / 2, yDelta / 2, false ); } invalidate(); mContext.invalidate( getInvalidationRect() ); } } /** * Sets the padding. * * @param value * the new padding */ public void setPadding( int value ) { mPadding = value; } }