AndroidStudio DaveMorrissey's SubsamplingScaleImageView with gif image - android

I'm using this wonderful library to handle a Map in my Android application. I'm making a custom view named MapView that extends SubsamplingScaleImageView. I'm trying to use the base PinView extension of the library to set a Pin over the map but i don't want a static Pin image but a dinamic gif. I've read about the 3 scenarios of using "Movie", using GifDecoder and using WebView but none of them worked for me. I really can't figure out how to animate my Pin in the map. Help me please!
public class MapView extends SubsamplingScaleImageView{
private PointF sPin;
private Bitmap pin;
public MapView(Context context) {
this(context, null);
}
public MapView(Context context, AttributeSet attr) {
super(context, attr);
initialise();
}
private void initialise() {
float density = getResources().getDisplayMetrics().densityDpi;
pin = BitmapFactory.decodeResource(this.getResources(), R.drawable.pushpin_blue.gif); //<<<<<<<Here goes the GIF
float w = (density/420f) * pin.getWidth();
float h = (density/420f) * pin.getHeight();
pin = Bitmap.createScaledBitmap(pin, (int)w, (int)h, true);
}
public PointF getPin() {
return sPin;
}
public void setPin(PointF sPin) {
this.sPin = sPin;
initialise();
invalidate();
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!isReady()) {
return;
}
if (sPin != null && pin != null) {
Paint paint = new Paint();
paint.setAntiAlias(true);
PointF vPin = sourceToViewCoord(sPin);
float vX = vPin.x - (pin.getWidth()/2);
float vY = vPin.y - pin.getHeight();
canvas.drawBitmap(pin, vX, vY, paint);
}
}
}

Related

how is possible to set imageview or icon in the canvas circle?

how is possible to set an image or icon in the canvas Circle? i have a custom canvas view that by move the camera on map, resize method will be activate and change the shape of the view and by stop the moving of map it will be like factory setting...happy english :-))))
public class CancvasCircle extends androidx.appcompat.widget.AppCompatImageView {
///main circle resize fields
public static final int MAIN_CIRCLE_PRE_RADIUS = 40;
public static final int MAIN_CIRCLE_POST_RADIUS = 43;
public static final String MAIN_CIRCLE_PRE_STROKE_COLOR = "#69DAE2";
//4CEAE3
public static final String MAIN_CIRCLE_POST_STROKE_COLOR = "#FBCC38";
///// line resize fields
public static final int LINE_HIEGHT_STOP = 45;
public static final int LINE_HIEGHT_START_PRE = 0;
public static final int LINE_HIEGHT_START_POST = 23;
public static final int SHADOW_CIRCLE_PRE_RADIUS = 15;
//circle paint fields
Paint strokeCircle;
Paint fillCircle;
Paint shadowCircle;
Paint line;
Paint dot;
//center
float centerX;
float centerY;
//main circle fields
private int radiusMain = MAIN_CIRCLE_PRE_RADIUS;
private String colorMain = MAIN_CIRCLE_PRE_STROKE_COLOR;
//line fields
private float lineStartY;
private float lineStopY;
//shadow circle fields
private int shadowRadius = SHADOW_CIRCLE_PRE_RADIUS;
public CancvasCircle(Context context) {
super(context);
init();
}
public CancvasCircle(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CancvasCircle(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init() {
strokeCircle = new Paint();
strokeCircle.setAntiAlias(true);
strokeCircle.setAntiAlias(true);
strokeCircle.setStrokeWidth(10);
strokeCircle.setStyle(Paint.Style.STROKE);
fillCircle = new Paint();
fillCircle.setAntiAlias(true);
fillCircle.setColor(Color.parseColor("#BEC6CC"));
fillCircle.setStyle(Paint.Style.FILL);
shadowCircle = new Paint();
shadowCircle.setAntiAlias(true);
shadowCircle.setColor(Color.parseColor("#1f000000"));
shadowCircle.setStyle(Paint.Style.FILL);
line = new Paint();
line.setAntiAlias(true);
line.setColor(Color.parseColor("#000000"));
line.setStrokeWidth(4);
line.setStyle(Paint.Style.FILL);
dot = new Paint();
dot.setAntiAlias(true);
dot.setColor(Color.parseColor("#000000"));
dot.setStrokeWidth(4);
dot.setStyle(Paint.Style.FILL);
}
public void resizeStrokeCircleParams(Boolean resize) {
if (resize) {
this.radiusMain = MAIN_CIRCLE_POST_RADIUS;
this.colorMain = MAIN_CIRCLE_POST_STROKE_COLOR;
this.lineStartY = centerY - LINE_HIEGHT_START_POST;
this.lineStopY = LINE_HIEGHT_STOP + LINE_HIEGHT_START_POST - 10;
this.shadowRadius = LINE_HIEGHT_START_POST;
} else {
this.radiusMain = MAIN_CIRCLE_PRE_RADIUS;
this.colorMain = MAIN_CIRCLE_PRE_STROKE_COLOR;
this.lineStartY = centerY - LINE_HIEGHT_START_PRE;
this.lineStopY = LINE_HIEGHT_STOP;
this.shadowRadius = SHADOW_CIRCLE_PRE_RADIUS;
}
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
centerX = getWidth() / 2f;
centerY = getHeight() / 2f;
int radius = radiusMain;
float lineStopY = this.lineStopY;
float lineStartY = this.lineStartY;
int shadowRadius = this.shadowRadius;
strokeCircle.setColor(Color.parseColor(colorMain));
canvas.drawCircle(centerX, centerY - lineStopY - radius, radius, strokeCircle);
canvas.drawCircle(centerX, centerY - lineStopY - radius, radius, fillCircle);
canvas.drawCircle(centerX, centerY, shadowRadius, shadowCircle);
canvas.drawLine(centerX, lineStartY, centerX, centerY - lineStopY, line);
canvas.drawPoint(centerX,centerY,dot);
}
}
is it possible to flip two images like coin in it????
See example as custom View.
public class ViewCircle extends View{
final Bitmap bms; //source
final Bitmap bmm; //mask
final Paint paint;
public ViewCircle( Context context ){
super( context );
bms = BitmapFactory.decodeResource( getResources(), R.drawable.pr_0000 );
bmm = Bitmap.createBitmap( bms.getWidth(), bms.getHeight(), Bitmap.Config.ARGB_8888 );
Canvas canvas = new Canvas( bmm );
paint = new Paint( Paint.ANTI_ALIAS_FLAG );
canvas.drawCircle( bmm.getWidth()/2, bmm.getHeight()/2, Math.min(bmm.getWidth()/2,bmm.getHeight()/2), paint );
paint.setXfermode( new PorterDuffXfermode( PorterDuff.Mode.SRC_IN ) );
canvas.drawBitmap( bms, 0, 0, paint );
}
#Override
protected void onDraw( Canvas canvas ){
super.onDraw( canvas );
canvas.drawBitmap( bms, 0,0, null );
canvas.drawBitmap( bmm, bms.getWidth(),0, null );
}
}
i used this method to create a bitmap from XML layout and set it in my codes...
public Bitmap getMarkerBitmapFromView(#DrawableRes int resIdMain, #DrawableRes int resId) {
View customMarkerView = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.custom_marker, null);
customMarkerView.findViewById(R.id.custom_marker_firstlayout);
if(resIdMain != 0){
customMarkerView.setBackgroundResource(resIdMain);
}
ImageView markerImageView = (ImageView) customMarkerView.findViewById(R.id.custom_marker_secondLayout);
if(resId != 0){
markerImageView.setImageResource(resId);
}
customMarkerView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
customMarkerView.layout(0, 0, customMarkerView.getMeasuredWidth(), customMarkerView.getMeasuredHeight());
customMarkerView.buildDrawingCache();
Bitmap returnedBitmap = Bitmap.createBitmap(customMarkerView.getMeasuredWidth(), customMarkerView.getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_IN);
Drawable drawable = customMarkerView.getBackground();
if (drawable != null)
drawable.draw(canvas);
customMarkerView.draw(canvas);
return returnedBitmap;
}
and set it in my canvas view in onDraw() and set it in the middle of my circle:
canvas.drawBitmap(getMarkerBitmapFromView(0,guildMarkerICON),convertDpToPixel(76,context),imageHieght,null);
because of drawing wrong in different devices i used this methods to change pixel and dp:
public static float convertPixelsToDp(float px,Context context){
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float dp = px / (metrics.densityDpi / 160f);
return dp;
}
public static float convertDpToPixel(float dp,Context context){
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float px = dp * (metrics.densityDpi/160f);
return px;
}
and XML layout that provide my bit map (that you can use image or any things you want in it) is (custom_marker.xml) :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/custom_marker_firstlayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/center_marker_background">
<ImageView
android:id="#+id/custom_marker_secondLayout"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center"
android:contentDescription="#null"
/>
</FrameLayout>
and use this method when you want to change the marker(like moving the map):
map.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {
#Override
public void onCameraMoveStarted(int i) {
canvasView.resizeStrokeCircleParams(true,guildMarkerICON);
}
});
map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
#Override
public void onCameraIdle() {
canvasView.resizeStrokeCircleParams(false,guildMarkerICON);
}
});
and finally here i s my custom canvas class that i used and i passed my custom icon to it by resize method and set it as i want...enjoy it:
public class CancvasCircle extends androidx.appcompat.widget.AppCompatImageView {
///main circle resize fields
public static final int MAIN_CIRCLE_PRE_RADIUS = 24;
public static final int MAIN_CIRCLE_POST_RADIUS = 26;
public static final String MAIN_CIRCLE_PRE_STROKE_COLOR = "#F44336";
public static final String MAIN_CIRCLE_POST_STROKE_COLOR = "#FFCC59";
//FFCC59
//FBCC38
///// line resize fields
public static final int LINE_HIEGHT_STOP = 24;
public static final int LINE_HIEGHT_START_PRE = 0;
public static final int LINE_HIEGHT_START_POST = 11;
public static final int SHADOW_CIRCLE_PRE_RADIUS = 14;
private final Context context;
/// image view fields
//circle paint fields
Paint strokeCircle;
Paint fillCircle;
Paint shadowCircle;
Paint line;
Paint dot;
Paint imagePaint;
//center
float centerX;
float centerY;
//main circle fields
private int radiusMain = MAIN_CIRCLE_PRE_RADIUS;
private String colorMain = MAIN_CIRCLE_PRE_STROKE_COLOR;
//line fieldsاذغ
private float lineStartY;
private float lineStopY;
//shadow circle fields
private int shadowRadius = SHADOW_CIRCLE_PRE_RADIUS;
private Bitmap canvasBitmap;
private int imageTopPadding;
private int guildMarkerICON
;
public CancvasCircle(Context context) {
super(context);
this.context = context;
init();
}
public CancvasCircle(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public CancvasCircle(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init();
}
public void init() {
strokeCircle = new Paint();
strokeCircle.setAntiAlias(true);
strokeCircle.setAntiAlias(true);
strokeCircle.setStrokeWidth(12);
strokeCircle.setStyle(Paint.Style.STROKE);
fillCircle = new Paint();
fillCircle.setAntiAlias(true);
fillCircle.setColor(Color.parseColor("#af283232"));
fillCircle.setStyle(Paint.Style.FILL);
shadowCircle = new Paint();
shadowCircle.setAntiAlias(true);
shadowCircle.setColor(Color.parseColor("#1f000000"));
shadowCircle.setStyle(Paint.Style.FILL);
line = new Paint();
line.setAntiAlias(true);
line.setColor(Color.parseColor("#000000"));
line.setStrokeWidth(4);
line.setStyle(Paint.Style.FILL);
dot = new Paint();
dot.setAntiAlias(true);
dot.setColor(Color.parseColor("#000000"));
dot.setStrokeWidth(4);
dot.setStyle(Paint.Style.FILL);
}
public void resizeStrokeCircleParams(Boolean resize , int guildMarkerICON) {
this.guildMarkerICON = guildMarkerICON;
if (resize) {
this.radiusMain = (int) convertDpToPixel(MAIN_CIRCLE_POST_RADIUS,context);
this.colorMain = MAIN_CIRCLE_POST_STROKE_COLOR;
this.lineStartY = centerY -convertDpToPixel( LINE_HIEGHT_START_POST,context);
this.lineStopY =convertDpToPixel( LINE_HIEGHT_STOP,context) + convertDpToPixel(LINE_HIEGHT_START_POST,context) -8;
this.shadowRadius = (int) convertDpToPixel(LINE_HIEGHT_START_POST,context);
this.imageTopPadding = (int) convertDpToPixel(19,context);
} else {
this.radiusMain = (int) convertDpToPixel( MAIN_CIRCLE_PRE_RADIUS,context);
this.colorMain = MAIN_CIRCLE_PRE_STROKE_COLOR;
this.lineStartY = centerY - convertDpToPixel(LINE_HIEGHT_START_PRE,context);
this.lineStopY = convertDpToPixel(LINE_HIEGHT_STOP,context);
this.shadowRadius = (int) convertDpToPixel(SHADOW_CIRCLE_PRE_RADIUS,context);
this.imageTopPadding = (int) convertDpToPixel( 28,context);
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
centerX = getWidth() / 2f;
centerY = getHeight() / 2f;
int radius = radiusMain;
float lineStopY = this.lineStopY;
float lineStartY = this.lineStartY;
int shadowRadius = this.shadowRadius;
int imageHieght = this.imageTopPadding;
strokeCircle.setColor(Color.parseColor(colorMain));
// if map is moving (imageHieght == 41)
// if map is not moving (imageHieght == 56)
canvas.drawLine(centerX, lineStartY, centerX, centerY - lineStopY, line);
canvas.drawCircle(centerX, centerY - lineStopY - radius, radius, strokeCircle);
canvas.drawCircle(centerX, centerY - lineStopY - radius, radius, fillCircle);
canvas.drawCircle(centerX, centerY, shadowRadius, shadowCircle);
canvas.drawPoint(centerX,centerY,dot);
canvas.drawBitmap(getMarkerBitmapFromView(0,guildMarkerICON),convertDpToPixel(76,context),imageHieght,null);
invalidate();
}
public static float convertPixelsToDp(float px,Context context){
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float dp = px / (metrics.densityDpi / 160f);
return dp;
}
public static float convertDpToPixel(float dp,Context context){
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float px = dp * (metrics.densityDpi/160f);
return px;
}
public Bitmap getMarkerBitmapFromView(#DrawableRes int resIdMain, #DrawableRes int resId) {
View customMarkerView = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.custom_marker, null);
customMarkerView.findViewById(R.id.custom_marker_firstlayout);
if(resIdMain != 0){
customMarkerView.setBackgroundResource(resIdMain);
}
ImageView markerImageView = (ImageView) customMarkerView.findViewById(R.id.custom_marker_secondLayout);
if(resId != 0){
markerImageView.setImageResource(resId);
}
customMarkerView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
customMarkerView.layout(0, 0, customMarkerView.getMeasuredWidth(), customMarkerView.getMeasuredHeight());
customMarkerView.buildDrawingCache();
Bitmap returnedBitmap = Bitmap.createBitmap(customMarkerView.getMeasuredWidth(), customMarkerView.getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_IN);
Drawable drawable = customMarkerView.getBackground();
if (drawable != null)
drawable.draw(canvas);
customMarkerView.draw(canvas);
return returnedBitmap;
}
}
result:

android.graphics draw a line from one View pointing to another View

I know android.graphics is old, but i am having trouble doing a simple stuff.
I want to draw a line animation where one View points an arrow/line into another View
First Button-------------------------------->Second Button
I have tried creating a custom View class and overriding the onDraw(Canvas c) method and then using the drawLine(startX, startY, stopX, stopY, paint) method from the Canvas Object. But i don't know which coordinates to get in order to point one View to the other View
I don't want to create a static View in the XML layout with a slim height because the View can be added dynamically by the user, which i think drawing the line dynamically is the best way.
Please help me out. Thank you!
For drawing lines between views better if all of it lays on same parent layout. For the conditions of the question (Second Button is exactly to the right of First Button) you can use custom layout like that:
public class ArrowLayout extends RelativeLayout {
public static final String PROPERTY_X = "PROPERTY_X";
public static final String PROPERTY_Y = "PROPERTY_Y";
private final static double ARROW_ANGLE = Math.PI / 6;
private final static double ARROW_SIZE = 50;
private Paint mPaint;
private boolean mDrawArrow = false;
private Point mPointFrom = new Point(); // current (during animation) arrow start point
private Point mPointTo = new Point(); // current (during animation) arrow end point
public ArrowLayout(Context context) {
super(context);
init();
}
public ArrowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ArrowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public ArrowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
setWillNotDraw(false);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(5);
}
#Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.save();
if (mDrawArrow) {
drawArrowLines(mPointFrom, mPointTo, canvas);
}
canvas.restore();
}
private Point calcPointFrom(Rect fromViewBounds, Rect toViewBounds) {
Point pointFrom = new Point();
pointFrom.x = fromViewBounds.right;
pointFrom.y = fromViewBounds.top + (fromViewBounds.bottom - fromViewBounds.top) / 2;
return pointFrom;
}
private Point calcPointTo(Rect fromViewBounds, Rect toViewBounds) {
Point pointTo = new Point();
pointTo.x = toViewBounds.left;
pointTo.y = toViewBounds.top + (toViewBounds.bottom - toViewBounds.top) / 2;
return pointTo;
}
private void drawArrowLines(Point pointFrom, Point pointTo, Canvas canvas) {
canvas.drawLine(pointFrom.x, pointFrom.y, pointTo.x, pointTo.y, mPaint);
double angle = Math.atan2(pointTo.y - pointFrom.y, pointTo.x - pointFrom.x);
int arrowX, arrowY;
arrowX = (int) (pointTo.x - ARROW_SIZE * Math.cos(angle + ARROW_ANGLE));
arrowY = (int) (pointTo.y - ARROW_SIZE * Math.sin(angle + ARROW_ANGLE));
canvas.drawLine(pointTo.x, pointTo.y, arrowX, arrowY, mPaint);
arrowX = (int) (pointTo.x - ARROW_SIZE * Math.cos(angle - ARROW_ANGLE));
arrowY = (int) (pointTo.y - ARROW_SIZE * Math.sin(angle - ARROW_ANGLE));
canvas.drawLine(pointTo.x, pointTo.y, arrowX, arrowY, mPaint);
}
public void animateArrows(int duration) {
mDrawArrow = true;
View fromView = getChildAt(0);
View toView = getChildAt(1);
// find from and to views bounds
Rect fromViewBounds = new Rect();
fromView.getDrawingRect(fromViewBounds);
offsetDescendantRectToMyCoords(fromView, fromViewBounds);
Rect toViewBounds = new Rect();
toView.getDrawingRect(toViewBounds);
offsetDescendantRectToMyCoords(toView, toViewBounds);
// calculate arrow sbegin and end points
Point pointFrom = calcPointFrom(fromViewBounds, toViewBounds);
Point pointTo = calcPointTo(fromViewBounds, toViewBounds);
ValueAnimator arrowAnimator = createArrowAnimator(pointFrom, pointTo, duration);
arrowAnimator.start();
}
private ValueAnimator createArrowAnimator(Point pointFrom, Point pointTo, int duration) {
final double angle = Math.atan2(pointTo.y - pointFrom.y, pointTo.x - pointFrom.x);
mPointFrom.x = pointFrom.x;
mPointFrom.y = pointFrom.y;
int firstX = (int) (pointFrom.x + ARROW_SIZE * Math.cos(angle));
int firstY = (int) (pointFrom.y + ARROW_SIZE * Math.sin(angle));
PropertyValuesHolder propertyX = PropertyValuesHolder.ofInt(PROPERTY_X, firstX, pointTo.x);
PropertyValuesHolder propertyY = PropertyValuesHolder.ofInt(PROPERTY_Y, firstY, pointTo.y);
ValueAnimator animator = new ValueAnimator();
animator.setValues(propertyX, propertyY);
animator.setDuration(duration);
// set other interpolator (if needed) here:
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mPointTo.x = (int) valueAnimator.getAnimatedValue(PROPERTY_X);
mPointTo.y = (int) valueAnimator.getAnimatedValue(PROPERTY_Y);
invalidate();
}
});
return animator;
}
}
with .xml layout like:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_main"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<{YOUR_PACKAGE_NAME}.ArrowLayout
android:id="#+id/arrow_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/first_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="First Button"/>
<Button
android:id="#+id/second_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="Second Button"/>
</{YOUR_PACKAGE_NAME}.ArrowLayout>
</RelativeLayout>
and MainActivity.java like:
public class MainActivity extends AppCompatActivity {
private ArrowLayout mArrowLayout;
private Button mFirstButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mArrowLayout = (ArrowLayout) findViewById(R.id.arrow_layout);
mFirstButton = (Button) findViewById(R.id.first_button);
mFirstButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mArrowLayout.animateArrows(1000);
}
});
}
}
you got something like that (on First Button click):
For other cases ( Second Button is exactly to the left (or above, or below) or more complex above-right/below-left etc. of First Button) you should modify part for calculating arrow begin and end points:
private Point calcPointFrom(Rect fromViewBounds, Rect toViewBounds) {
Point pointFrom = new Point();
// Second Button above
// ----------+----------
// | |
// Second Button tho the left + First Button + Second Button tho the right
// | |
// ----------+----------
// Second Button below
//
// + - is arrow start point position
if (toViewBounds to the right of fromViewBounds){
pointFrom.x = fromViewBounds.right;
pointFrom.y = fromViewBounds.top + (fromViewBounds.bottom - fromViewBounds.top) / 2;
} else if (toViewBounds to the left of fromViewBounds) {
pointFrom.x = fromViewBounds.left;
pointFrom.y = fromViewBounds.top + (fromViewBounds.bottom - fromViewBounds.top) / 2;
} else if () {
...
}
return pointFrom;
}
Use Path and Pathmeasure for Drawing Animated Line. I have Made and test it.
Make Custom View and pass view coordinates points array to it,
public class AnimatedLine extends View {
private final Paint mPaint;
public Canvas mCanvas;
AnimationListener animationListener;
Path path;
private static long animSpeedInMs = 2000;
private static final long animMsBetweenStrokes = 100;
private long animLastUpdate;
private boolean animRunning = true;
private int animCurrentCountour;
private float animCurrentPos;
private Path animPath;
private PathMeasure animPathMeasure;
float pathLength;
float distance = 0;
float[] pos;
float[] tan;
Matrix matrix;
Bitmap bm;
public AnimatedLine(Context context) {
this(context, null);
mCanvas = new Canvas();
}
public AnimatedLine(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(15);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setColor(context.getResources().getColor(R.color.materialcolorpicker__red));
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
setLayerType(LAYER_TYPE_SOFTWARE, mPaint);
}
bm = BitmapFactory.decodeResource(getResources(), R.drawable.hand1);
bm = Bitmap.createScaledBitmap(bm, 20,20, false);
distance = 0;
pos = new float[2];
tan = new float[2];
matrix = new Matrix();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mCanvas = canvas;
if (path != null) {
if (animRunning) {
drawAnimation(mCanvas);
} else {
drawStatic(mCanvas);
}
}
}
/**
* draw Path With Animation
*
* #param time in milliseconds
*/
public void drawWithAnimation(ArrayList<PointF> points, long time,AnimationListener animationListener) {
animRunning = true;
animPathMeasure = null;
animSpeedInMs = time;
setPath(points);
setAnimationListener(animationListener);
invalidate();
}
public void setPath(ArrayList<PointF> points) {
if (points.size() < 2) {
throw new IllegalStateException("Pass atleast two points.");
}
path = new Path();
path.moveTo(points.get(0).x, points.get(0).y);
path.lineTo(points.get(1).x, points.get(1).y);
}
private void drawAnimation(Canvas canvas) {
if (animPathMeasure == null) {
// Start of animation. Set it up.
animationListener.onAnimationStarted();
animPathMeasure = new PathMeasure(path, false);
animPathMeasure.nextContour();
animPath = new Path();
animLastUpdate = System.currentTimeMillis();
animCurrentCountour = 0;
animCurrentPos = 0.0f;
pathLength = animPathMeasure.getLength();
} else {
// Get time since last frame
long now = System.currentTimeMillis();
long timeSinceLast = now - animLastUpdate;
if (animCurrentPos == 0.0f) {
timeSinceLast -= animMsBetweenStrokes;
}
if (timeSinceLast > 0) {
// Get next segment of path
float newPos = (float) (timeSinceLast) / (animSpeedInMs / pathLength) + animCurrentPos;
boolean moveTo = (animCurrentPos == 0.0f);
animPathMeasure.getSegment(animCurrentPos, newPos, animPath, moveTo);
animCurrentPos = newPos;
animLastUpdate = now;
//start draw bitmap along path
animPathMeasure.getPosTan(newPos, pos, tan);
matrix.reset();
matrix.postTranslate(pos[0], pos[1]);
canvas.drawBitmap(bm, matrix, null);
//end drawing bitmap
//take current position
animationListener.onAnimationUpdate(pos);
// If this stroke is done, move on to next
if (newPos > pathLength) {
animCurrentPos = 0.0f;
animCurrentCountour++;
boolean more = animPathMeasure.nextContour();
// Check if finished
if (!more) {
animationListener.onAnimationEnd();
animRunning = false;
}
}
}
// Draw path
canvas.drawPath(animPath, mPaint);
}
invalidate();
}
private void drawStatic(Canvas canvas) {
canvas.drawPath(path, mPaint);
canvas.drawBitmap(bm, matrix, null);
}
public void setAnimationListener(AnimationListener animationListener) {
this.animationListener = animationListener;
}
public interface AnimationListener {
void onAnimationStarted();
void onAnimationEnd();
void onAnimationUpdate(float[] pos);
}
}

Animation Scale Arc by Angle

My task is Drawing a Circle Diagram.
I have 4 arcs.
On each arc, I have 2 values to make View Animate.
this image
will show you more detail.
Can Anyone Suggest me the Animation should I use for each value?
And how can I make it Animate by Specific Angle?
Edit: I upload .gif file for more detail.
You can make a custom View and a custom animation. Override the applyTransformation() Method in the custom Animation to apply the transformation.
public class ArcView extends View {
private final Paint mPaint;
private final RectF mRect;
private float arcAngle;
public ArcView(Context context, AttributeSet attrs) {
super(context, attrs);
// Set Angle to 0 initially or whatever you want
arcAngle = 0;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(4);
mPaint.setColor(Color.WHITE);
mRect = new RectF(2, 2, 78, 78);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(mRect, 90, arcAngle, false, mPaint);
}
public float getArcAngle() {
return arcAngle;
}
public void setArcAngle(float arcAngle) {
this.arcAngle = arcAngle;
}
}
And here is how your animation Class looks like
public class ArcAngleAnimation extends Animation {
private ArcView arcView;
private float oldAngle;
private float newAngle;
public ArcAngleAnimation(ArcView arcView, int newAngle) {
this.oldAngle = arcView.getArcAngle();
this.newAngle = newAngle;
this.arcView = arcView;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation transformation) {
float angle = oldAngle + ((newAngle - oldAngle) * interpolatedTime);
arcView.setArcAngle(angle);
arcView.requestLayout();
}
public void setNewAngle(int newAngle){
this.newAngle = newAngle;
}
public void setOldAngle(float oldAngle) {
this.oldAngle = oldAngle;
}
}
Now all you need to do is apply animation and run it in your activity or fragment
ArcView view = (ArcView) findViewById(R.id.arc_view);
ArcAngleAnimation arcAnimation;
arcAnimation = new ArcAngleAnimation(view, 30);
arcAnimation.setFillAfter(true);
arcAnimation.setDuration(200);
view.startAnimation(arcAnimation);

Get Touch Coordinates Relative To A Custom ImageView

I implemented custom ImageView. Because my image is large I'm using ScrollView and HorizontalScrollView to scrolling image. I want to draw circle in onTouch event, but its not work in all cases. When I scroll image and fire touch event the x,y coordinates apply to current visible sector of image ( in top right corner its e.g. 0x0, but when I scroll somwhere and in visible top corner it will be 0x0 too), but circle is drawing according to image size.
Another problem is that app could be used on various screen size.
I know that it xamarin c# but in native Android it should be the same. Anyone knows how to draw/get coordinates properly?
public class DrawViewInside : ImageView
{
public static Bitmap bitmapInside;
private Paint paint = new Paint();
private Point point = new Point();
private Canvas mCanvas;
private static Bitmap mutableBitmap;
private Context mContext;
public static Bitmap b;
public void SetCarId(int id)
{
carId = id;
}
public DrawViewInside(Context context, IAttributeSet attrs) : base(context, attrs)
{
mContext = context;
setDefault(drawable);
}
public void SetBitmap(int drawableId)
{
setDefault(drawableId);
Invalidate();
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.GetSize(widthMeasureSpec);
int height = width;
var metrics = Resources.DisplayMetrics;
if (b != null)
{
SetMeasuredDimension(b.Width, b.Height);
}
}
public async void setDefault(int drawableId)
{
BitmapFactory.Options options = await GetBitmapOptionsOfImage(drawableId);
paint.Color = Color.Red;
paint.StrokeWidth = 15;
paint.SetStyle(Paint.Style.Stroke);
var metrics = Resources.DisplayMetrics;
var widthInDp = ConvertPixelsToDp(metrics.WidthPixels);
var heightInDp = ConvertPixelsToDp(metrics.HeightPixels);
b = BitmapFactory.DecodeResource(Resources, drawableId);esources, drawableId);
mutableBitmap = b.Copy(Bitmap.Config.Argb8888, true);
mCanvas = new Canvas(mutableBitmap);
mCanvas.Save();
}
protected override void OnDraw(Canvas canvas)
{
DrawCircle(canvas);
}
private void DrawCircle(Canvas canvas)
{
if (mCanvas == null)
{
setDefault(drawable);
}
else
{
mCanvas.Restore();
mCanvas.DrawCircle(point.x, point.y, 10, paint);
mCanvas.Save();
canvas.DrawBitmap(mutableBitmap, 0, 0, paint);
bitmapInside = mutableBitmap;
}
}
public override bool OnTouchEvent(MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
break;
case MotionEventActions.Up:
Activity act = (Activity)mContext;
float viewX = e.RawX - this.Left;
float viewY = e.RawY - this.Top;
point.x = viewX;// e.RawX;
point.y = viewY;// e.RawY;
break;
}
Invalidate();
return true;
}
}
public class Point
{
public float x, y;
}
I found solution. Just replace
float viewX = e.RawX - this.Left;
float viewY = e.RawY - this.Top;
point.x = viewX;// e.RawX;
point.y = viewY;// e.RawY;
with
float viewX = e.RawX;// - this.Left;
float viewY = e.RawY;// - this.Top;
int[] viewCoords = new int[2];
this.GetLocationOnScreen(viewCoords);
point.x = viewX - viewCoords[0];// e.RawX;
point.y = viewY - viewCoords[1];// e.RawY;

Android : draw arc without resetting the previous drawn circle

I am trying to draw a circle which will represent time and after each complete circle, I want to change the color to indicate to the user that the next time unit has started and draw over the previous circle color rather than reset as shown in the example below.
I am trying to draw a circle using Draw Arc method in the following way
canvas.drawArc(mRect, 270, sweepAngle, false, fgPaint);
The sweep angle is controlled by an Object Animator :
outerCircleAnimator = ObjectAnimator.ofFloat(timeView, TimeView.SET_SWEEPWANGLE, 0, 360);
With the following code, I am able to achieve the following
The following is my View class :
public class TimeView extends View {
final protected Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
final protected Paint fgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
final protected Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private RectF mRect = new RectF();
private float sweepAngle;
private float radiusInDPI = 100;
private float radiusInPixels;
private float strokeWidthInDPI = 4;
private float stokeWidthInPixels;
private float dpi;
private int heightByTwo;
private int widthByTwo;
public TimeView(Context context) {
super(context);
init();
}
public TimeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TimeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
heightByTwo = h / 2;
widthByTwo = w / 2;
mRect = new RectF(w / 2 - radiusInPixels, h / 2 - radiusInPixels, w / 2 + radiusInPixels, h / 2 + radiusInPixels);
}
private void init() {
DisplayMetrics metrics = getResources().getDisplayMetrics();
dpi = metrics.density;
radiusInPixels = dpi * radiusInDPI;
stokeWidthInPixels = dpi * strokeWidthInDPI;
bgPaint.setStrokeWidth(stokeWidthInPixels);
bgPaint.setStyle(Paint.Style.STROKE);
bgPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
fgPaint.setStrokeWidth(stokeWidthInPixels);
fgPaint.setStyle(Paint.Style.STROKE);
fgPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
textPaint.setTextSize(24 * 3);
textPaint.setStyle(Paint.Style.STROKE);
textPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.drawCircle(widthByTwo, heightByTwo, radiusInPixels, bgPaint);
canvas.drawArc(mRect, 270, sweepAngle, false, fgPaint);
}
public static final Property<TimeView, Float> SET_SWEEPWANGLE =
new Property<TimeView, Float>(Float.class, "outerCircleRadiusProgress") {
#Override
public Float get(TimeView object) {
return object.getSweepAngle();
}
#Override
public void set(TimeView object, Float value) {
object.setSweepAngle(value);
}
};
public float getSweepAngle() {
return sweepAngle;
}
public void setSweepAngle(float sweepAngle) {
Log.v("Testing", "Sweep angle is " + sweepAngle + " " + (sweepAngle + 270));
this.sweepAngle = sweepAngle;
postInvalidate();
}
public void setColor(boolean change) {
if (change) {
fgPaint.setColor(ContextCompat.getColor(getContext(), R.color.mint_green));
} else {
fgPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
}
}
}
onDraw is used to draw on the empty canvas. It is always starting from the beginning. You'll need to save the last fgPaint and:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (lastFgPaint != null) {
canvas.drawArc(mRect, sweepAngle, 360, false, lastFgPaint);
}
canvas.drawArc(mRect, 270, sweepAngle, false, fgPaint);
}

Categories

Resources