I know how to move the image in circle in android while using this source codeMove image in circle
This code is perfect in moving imageview in circle but i want to move aeroplane picture in circle and i want that it should be look like real motion of airplane. Can any one help me
public class AnimationView extends View {
Paint paint;
Bitmap bm;
int bm_offsetX, bm_offsetY;
Path animPath;
PathMeasure pathMeasure;
float pathLength;
float step; //distance each step
float distance; //distance moved
float[] pos;
float[] tan;
Matrix matrix;
public AnimationView(Context context) {
super(context);
initMyView();
}
public AnimationView(Context context, AttributeSet attrs) {
super(context, attrs);
initMyView();
}
public AnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initMyView();
}
public void initMyView(){
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(4);
paint.setStyle(Paint.Style.STROKE);
DashPathEffect dashPath = new DashPathEffect(new float[]{6,6}, (float)2.0);
paint.setPathEffect(dashPath);
bm = BitmapFactory.decodeResource(getResources(), R.drawable.airplane);
bm_offsetX = bm.getWidth()/2;
bm_offsetY = bm.getHeight()/2;
animPath = new Path();
animPath.addCircle(600, 600, 500, Path.Direction.CW);
animPath.close();
pathMeasure = new PathMeasure(animPath, false);
pathLength = pathMeasure.getLength();
step = 1;
distance = 0;
pos = new float[2];
tan = new float[2];
matrix = new Matrix();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(animPath, paint);
if(distance < pathLength){
pathMeasure.getPosTan(distance, pos, tan);
matrix.reset();
float degrees = (float)(Math.atan2(tan[0], tan[1])*0.0/Math.PI);
matrix.postRotate(degrees, bm_offsetX, bm_offsetY);
matrix.postTranslate(pos[0]-bm_offsetX, pos[1]-bm_offsetY);
canvas.drawBitmap(bm, matrix, null);
distance += step;
}else{
distance = 0;
}
invalidate();
}
Related
I want to draw a polyline on a view. I don't want to draw it on a MapView because I only want the polyline without any background.
I have the polyline coordinates and I also have its bounds (northeast and southwest coordinates).
The idea was to create a custom view and draw the path there. The problem I believe I'm having is with the scale. I can convert the LatLng coordinates to x and y coordinates but the polyline that is shown on the view is too small. The view is set with a 64dp width and height.
Here is what I'm doing:
public PolylineView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
polylineSize = (int) context.getResources().getDimension(R.dimen.polyline_size);
setupAttributes(attrs);
}
private void setupAttributes(AttributeSet attrs) {
// Obtain a typed array of attributes
TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.PolylineView, 0, 0);
// Extract custom attributes into member variables
try {
polylineColor = a.getColor(R.styleable.PolylineView_polylineColor, Color.BLACK);
} finally {
// TypedArray objects are shared and must be recycled.
a.recycle();
}
polyline = new Paint(Paint.ANTI_ALIAS_FLAG);
polyline.setAntiAlias(true);
polyline.setStyle(Paint.Style.STROKE);
polyline.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()));
polyline.setStrokeCap(Paint.Cap.ROUND);
polyline.setColor(polylineColor);
path = new Path();
smp = new SphericalMercatorProjection(polylineSize);
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
point = smp.toPoint(polylineCoordinates.get(0));
path.moveTo((float) (point.x * scale), (float) (point.y * scale));
for (int i = 1; i < polylineCoordinates.size(); i++) {
point = smp.toPoint(polylineCoordinates.get(i));
path.lineTo((float) (point.x * scale), (float) (point.y * scale));
}
canvas.drawPath(path, polyline);
}
public void setPolyline(List<LatLng> polylineCoordinates, RealmLatLngBounds polylineBounds) {
this.polylineBounds = polylineBounds;
this.polylineCoordinates = polylineCoordinates;
//Find the scale
Point northeast = smp.toPoint(polylineBounds.getNortheast());
Point southwest = smp.toPoint(polylineBounds.getSouthwest());
scale = polylineSize / Math.max(Math.max(northeast.x, southwest.x), Math.max(northeast.y, southwest.y));
invalidate();
requestLayout();
}
After can I transform the polyline coordinates so that the full size of the view is used?
Edit:
One of the problems that I can see is when converting to x and y, values are two close (precision)
With the help of #pskink I've managed to achieve this
private int polylineSize;
private Paint polyline;
private SphericalMercatorProjection smp;
PointF northeast;
PointF[] points;
PointF southwest;
Path originalPath;
Path transformedPath;
public PolylineView(Context context) {
super(context);
}
public PolylineView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
polylineSize = (int) context.getResources().getDimension(R.dimen.polyline_size);
setupAttributes(attrs);
}
public PolylineView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
polylineSize = (int) context.getResources().getDimension(R.dimen.polyline_size);
setupAttributes(attrs);
}
private void setupAttributes(AttributeSet attrs) {
// Obtain a typed array of attributes
TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.PolylineView, 0, 0);
// Extract custom attributes into member variables
int polylineColor;
try {
polylineColor = a.getColor(R.styleable.PolylineView_polylineColor, Color.BLACK);
} finally {
// TypedArray objects are shared and must be recycled.
a.recycle();
}
polyline = new Paint(Paint.ANTI_ALIAS_FLAG);
polyline.setAntiAlias(true);
polyline.setStyle(Paint.Style.STROKE);
polyline.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()));
polyline.setStrokeCap(Paint.Cap.ROUND);
polyline.setColor(polylineColor);
smp = new SphericalMercatorProjection(polylineSize);
}
public void setPolylineData(List<LatLng> polylineCoordinates, RealmLatLngBounds polylineBounds) {
originalPath = new Path();
transformedPath = new Path();
Point point = smp.toPoint(polylineBounds.getNortheast());
northeast = new PointF((float) point.x, (float) point.y);
point = smp.toPoint(polylineBounds.getSouthwest());
southwest = new PointF((float) point.x, (float) point.y);
points = new PointF[polylineCoordinates.size()];
for (int i = 0; i < polylineCoordinates.size(); i++) {
point = smp.toPoint(polylineCoordinates.get(i));
points[i] = new PointF((float) point.x, (float) point.y);
}
originalPath.moveTo(points[0].x, points[0].y);
for (PointF p : points) {
originalPath.lineTo(p.x, p.y);
}
RectF src = new RectF(southwest.x, northeast.y, northeast.x, southwest.y);
RectF dst = new RectF(0, 0, polylineSize, polylineSize);
Matrix matrix = new Matrix();
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
originalPath.transform(matrix, transformedPath);
invalidate();
requestLayout();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(transformedPath, polyline);
}
This view is used on a RecyclerView and the data is set on onBindViewHolder
I come again for an android brain idea!
I would like to put a circle hole hole in a imageView which bitmap is scaled as centerCrop. I know where I need to put the circle hole( in dp from left and bottom) and the hole radius. But don't know how to build it!
I know that I can use Porterduff to do the hole but what you suggest to do?
Custom bitmap
Custom drawable/view
Custom code
Thanks
Following Answers there is my CustomImage with hole:
public class MyImageView extends ImageView {
private AttributeSet attrs;
private float y;
private float x;
private float r;
private Paint paint;
private Rect mSrcRect;
private Rect mDestRect;
private Bitmap mBitmap;
private int alreadycalled = 0;
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
this.attrs = attrs;
initView();
}
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.attrs = attrs;
initView();
}
public MyImageView(Context context, float x, float y, float radius) {
super(context);
this.x = x;
this.y = y;
this.r = radius;
Log.d("parameters", String.format("left:%s , right:%s, radius:%s", String.valueOf(x), String.valueOf(y), String.valueOf(r)));
initView();
}
private void initView() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeWidth(10);
paint.setColor(0xff000000);
}
#Override
protected void onDraw(Canvas canvas) {
alreadycalled++;
Log.d("alreadycalled", "called " + alreadycalled);
Drawable mDrawable = getDrawable();
if (mDrawable == null) {
return; // couldn't resolve the URI
}
int dWidth = mDrawable.getIntrinsicWidth();
int dHeight = mDrawable.getIntrinsicHeight();
float scale = 1.0f;
scale = Math.max(getWidth() * 1.0f / dWidth, getHeight()
* 1.0f / dHeight);
int nWidth = (int) (dWidth * scale);
int nHeight = (int) (dHeight * scale);
int offsetLeft = (nWidth - getWidth()) / 2;
int offsetTop = (nHeight - getHeight()) / 2;
mBitmap = ((BitmapDrawable) mDrawable).getBitmap();
//custom mSrcRect mDestRect to achieve centerCrop
mSrcRect = new Rect(0, 0, dWidth, dWidth);
mDestRect = new Rect(-offsetLeft, -offsetTop, getWidth() + offsetLeft, getHeight() + offsetTop);
Log.d("src", mSrcRect.toString());
Log.d("dest", mDestRect.toString());
int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG
| Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
| Canvas.FULL_COLOR_LAYER_SAVE_FLAG
| Canvas.CLIP_TO_LAYER_SAVE_FLAG);
paint.setColor(0xffffffff);
canvas.drawBitmap(mBitmap, mSrcRect, mDestRect, paint);
paint.setColor(0xffff0000);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
Log.d("position", String.format("%s , %s", String.valueOf(x), String.valueOf(y)));
canvas.drawCircle(x, y, r, paint);
paint.setXfermode(null);
canvas.restoreToCount(sc);
}
I called it programmatically with theses lines :
BitmapDrawable bd=(BitmapDrawable) getResources().getDrawable(R.drawable.triangle_bas_accueil2);
MyImageView customImView = new MyImageView(getApplicationContext(), mX, mY, mRadius);
customImView.setImageDrawable(bd);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.BOTTOM;
customImView.setLayoutParams(params);
down_relative.addView(customImView);
But the onDraw() method is called twice (maybe it needs) but that makes me two holes, one that I can change parameters but the other still at the same place ! The container of the MyImageView is a RelativeLayout.
If someOne has an idea?
#tiny-sunlight ?
This one is just for CenterCrop and can't deal with scaleType.And this code may have some problems because I'm not good at canvas.
public class MyImageView extends ImageView {
private final AttributeSet attrs;
private Paint paint;
private Rect mSrcRect;
private Rect mDestRect;
private Bitmap mBitmap;
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
this.attrs = attrs;
initView();
}
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.attrs = attrs;
initView();
}
private void initView() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeWidth(10);
paint.setColor(0xff000000);
}
#Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
//create the drawable.Maybe you can cache it.
Drawable mDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.triangle_bas_accueil2);
if (mDrawable == null) {
return; // couldn't resolve the URI
}
int dWidth = mDrawable.getIntrinsicWidth();
int dHeight = mDrawable.getIntrinsicHeight();
float scale = 1.0f;
scale = Math.max(getWidth() * 1.0f / dWidth, getHeight()
* 1.0f / dHeight);
int nWidth = (int) (dWidth*scale);
int nHeight = (int) (dHeight*scale);
int offsetLeft = (nWidth - getWidth())/2;
int offsetTop = (nHeight - getHeight())/2;
//cache mBitmap
mBitmap = mBitmap == null ? ((BitmapDrawable) mDrawable).getBitmap(): mBitmap;
//custom mSrcRect mDestRect to achieve centerCrop
mSrcRect = new Rect(0, 0, dWidth, dWidth);
mDestRect = new Rect(-offsetLeft, -offsetTop,getWidth()+offsetLeft, getHeight()+offsetTop);
int x = 250;int r = 100;
int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG
| Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
| Canvas.FULL_COLOR_LAYER_SAVE_FLAG
| Canvas.CLIP_TO_LAYER_SAVE_FLAG);
paint.setColor(0xffffffff);
canvas.drawBitmap(mBitmap,mSrcRect,mDestRect,paint);
paint.setColor(0xff000000);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
canvas.drawCircle(x,x,r,paint);
paint.setXfermode(null);
canvas.restoreToCount(sc);
}
}
Custom code. Loop through each pixel and change the alpha. Here is some pseudocode:
for (int y = 0; y < imageHeight; y++) {
for (int x = 0; x < imageWidth; x++) {
// Check if the current pixel is within the circle
if (dist(x, y, centerX, centerY) <= radius) {
// Change the alpha
image.setPixelAlpha(x, y, 0);
}
}
}
I would like to draw a rectangle on an ImageView as a picture below (black border and transparent as background).
Basically I download an Image, put in in a ImageView and after I receive 4 points to draw a rectangle.
Thanks in advance
Your problem for android.view.InflateException can be solved by deleting the constructors from DrawView class and auto generate them again. Now for the rectangle you have to have the onDraw similar like this:
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.TRANSPARENT);
paint.setStyle(Paint.Style.FILL);
float leftx = 50;
float topy = 50;
float rightx = 150;
float bottomy = 150;
// FILL
canvas.drawRect(leftx, topy, rightx, bottomy, paint);
paint.setStrokeWidth(10);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
// BORDER
canvas.drawRect(leftx, topy, rightx, bottomy, paint);
}
If you want to get the coordinates on click an then draw the rectangle override the onTouchEvent method and do something like this
class DrawView extends ImageView {
float x, y;
float width = 60.0f;
float height = 50.0f;
boolean touched = false;
public DrawView(Context context) {
super(context);
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DrawView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (touched) {
Paint paint = new Paint();
paint.setColor(Color.TRANSPARENT);
paint.setStyle(Paint.Style.FILL);
// FILL
canvas.drawRect(x, y, (x + width) / 0.5f, (y + height) / 0.5f, paint);
paint.setStrokeWidth(10);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
// BORDER
canvas.drawRect(x, y, (x + width) / 0.5f, (y + height) / 0.5f, paint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
touched = true;
//getting the touched x and y position
x = event.getX();
y = event.getY();
invalidate();
return true;
}
}
I solved my problem in thanks the help of #vasilis.
I created:
class DrawView extends ImageView {
private int numberOfRectangles=0;
private ArrayList<Rectangles> listRect;
public DrawView(Context context) {
super(context);
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DrawView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (numberOfRectangles> 0 && listRect!= null)
{
for (int i=0; i< numberOfRectangles; i++)
{
Paint paint = new Paint();
paint.setColor(Color.TRANSPARENT);
paint.setStyle(Paint.Style.FILL);
float leftx = listRect.get(i).getLeftx();
float topy = listRect.get(i).getTopy();
float rightx = listRect.get(i).getRightx();
float bottomy = listRect.get(i).getBottomy();
// FILL
canvas.drawRect(leftx, topy, rightx, bottomy, paint);
paint.setStrokeWidth(10);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
// BORDER
canvas.drawRect(leftx, topy, rightx, bottomy, paint);
}
}
}
public void creatRectangles(ArrayList<Rectangles> arrayRettangoliTest) {
numberOfRectangles=arrayRettangoliTest.size();
this.listRect= arrayRettangoliTest;
this.invalidate();
}
}
in my xml file:
<it.path.DrawView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="#drawable/placeholder" />
I create a Class Rectangles:
public class Rectangles {
private float leftx;
private float topy ;
private float rightx;
private float bottomy;
public Rectangles(float leftx, float topy, float rightx, float bottomy) {
this.leftx = leftx;
this.topy = topy;
this.rightx = rightx;
this.bottomy = bottomy;
}
#Override
public String toString() {
return "Rectangles{" +
"leftx=" + leftx +
", topy=" + topy +
", rightx=" + rightx +
", bottomy=" + bottomy +
'}';
}
public void setLeftx(float leftx) {
this.leftx = leftx;
}
public void setTopy(float topy) {
this.topy = topy;
}
public void setRightx(float rightx) {
this.rightx = rightx;
}
public void setBottomy(float bottomy) {
this.bottomy = bottomy;
}
public float getLeftx() {
return leftx;
}
public float getTopy() {
return topy;
}
public float getRightx() {
return rightx;
}
public float getBottomy() {
return bottomy;
}
}
And in my MainActivity (ex after an onClick()):
...
ArrayList<Rectangles> arrayRectanglesTest= new ArrayList<>(4);
arrayRectanglesTest.add(new Rectangles(50, 50, 100, 100));
arrayRectanglesTest.add(new Rectangles(150, 150, 200, 200));
arrayRectanglesTest.add(new Rectangles(250, 250, 300, 300));
arrayRectanglesTest.add(new Rectangles(350, 350, 400, 400));
arrayRectanglesTest.add(new Rectangles(450, 450, 500, 500));
imageView.creatRectangles(arrayRectanglesTest);
...
So I made dynamic the number of rectangles
here the Result
1、Here is the chart:
2、Here is the code:
private void drawCircle( Canvas canvas, String content ){
mPath.addCircle(getWidth() / 2, getHeight() / 2, mRadius, Direction.CCW);
canvas.rotate(180, getWidth() / 2, getHeight() / 2);
canvas.drawTextOnPath(content, mPath, 0, 0, mPaint);
}
3、My problem is how to let the circle one point one point display,Not all one-time display。I'm try to use Handler/Thread/Timer to draw a dynamic chart by drawTextOnPath and drawPath method, but there show nothing.
You need to use postDelayed() and add a new character each time.
public class MyView extends View {
private Path mPath = new Path();
private float mRadius = 100;
private Paint mPaint = new Paint();
private String theWholeText = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private String theCurrentText = "";
private int i = 0;
private long frequency = 150;
private Runnable addCharacter = new Runnable() {
#Override
public void run() {
theCurrentText = theWholeText.substring(0, ++i);
invalidate();
}
};
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint.setColor(Color.RED);
mPaint.setTextSize(20);
}
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context) {
this(context, null, 0);
}
private void drawCircle(Canvas canvas, String content) {
mPath.addCircle(getWidth() / 2, getHeight() / 2, mRadius, Path.Direction.CCW);
canvas.rotate(180, getWidth() / 2, getHeight() / 2);
canvas.drawTextOnPath(content, mPath, 0, 0, mPaint);
}
#Override
protected void onDraw(Canvas canvas) {
drawCircle(canvas, theCurrentText);
if (theCurrentText.length() < theWholeText.length())
postDelayed(addCharacter, frequency);
}
}
Please mark as correct answer if that works out for you!
I'd like to have an animated image in my activity:
just a white circle moving on a trajectory (black line).
What is the best way to do it?
Translate animation
FrameAnimation
Canvas
The implementation could be:
The white circle is a small ImageView with transparent background. It is placed on top of another ImageView (black curve).
FrameAnimation: For each position of the circle there is a separate png-Image of the whole screen, which is a frame of the animation.
Use drawCircle(), and restoreBackgroundImage() for each movement of the white dot.
So far I tried a FrameAnimation, but I get outOfMemoryError for just 10 Frames.
The following code implements the Canvas way. Efficient and no OOM. You just need to change your trajectory into a Path object.
public class TestView extends View {
private Path path;
private Paint pathPaint;
private Paint dotPaint;
private long beginTime;
private long duration = 3000;
private float dotRadius = 3;
private PathMeasure pm;
public TestView(Context context) {
super(context);
init();
}
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
path = new Path();
path.moveTo(0, 100);
path.lineTo(100, 200);
path.lineTo(200, 50);
//TODO: Put your path here
pm = new PathMeasure(path, false);
pathPaint = new Paint();
pathPaint.setARGB(255, 0, 0, 0);
pathPaint.setStrokeWidth(2);
pathPaint.setStyle(Paint.Style.STROKE);
dotPaint = new Paint();
dotPaint.setARGB(255, 255, 255, 255);
dotPaint.setStyle(Paint.Style.FILL);
beginTime = 0;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(0, 0, 0, 0);
canvas.drawPath(path, pathPaint);
long currentTime = System.currentTimeMillis();
float currentDistance;
if (beginTime == 0) {
beginTime = currentTime;
currentDistance = 0;
} else if (beginTime > 0 && currentTime - beginTime < duration) {
currentDistance = (float) (currentTime - beginTime) / (float) duration * pm.getLength();
} else {
beginTime = -1;
return;
}
float pos[] = new float[2];
pm.getPosTan(currentDistance, pos, null);
canvas.drawCircle(pos[0], pos[1], dotRadius, dotPaint);
invalidate();
}
}