Edit :
my first part of question is unique, but the second part is similar to some other questions.
Main Problem:
this is my class code,I use this class to draw lines with custom stroke shape from drawable . I want to draw a line, but it just draw some dots as shown in the picture below. Is there any idea to fix this problem?
public class MainDrawingView extends View {
private Bitmap mBitmapBrush;
private Bitmap rBitmapBrush;
private Vector2 mBitmapBrushDimensions;
private List<Vector2> mPositions = new ArrayList<Vector2>(100);
private static final class Vector2 {
public Vector2(float x, float y) {
this.x = x;
this.y = y;
}
public final float x;
public final float y;
}
public MainDrawingView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
mBitmapBrush = BitmapFactory.decodeResource(context.getResources(), R.drawable.brush01);
rBitmapBrush = Bitmap.createScaledBitmap(mBitmapBrush, 10, 10, false);
mBitmapBrushDimensions = new Vector2(rBitmapBrush.getWidth(), rBitmapBrush.getHeight());
}
#Override
protected void onDraw(Canvas canvas) {
for (Vector2 pos : mPositions) {
canvas.drawBitmap(rBitmapBrush, pos.x, pos.y, null);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
final float posX = event.getX();
final float posY = event.getY();
mPositions.add(new Vector2(posX - mBitmapBrushDimensions.x / 2, posY - mBitmapBrushDimensions.y / 2));
invalidate();
}
return true;
}
}
Update:
I try another class to draw a line with my drawable, but I faced 2 new problem! the first; when I use setShader , there is no any line when touch and move my finger on the screen. and the second;when comment the setShader line in the code, I could draw a simple black line, but if I draw a curved line rapidly, the result is a broken line ! and not a true curve line.so what is your idea to solve these problems?
public class MainDrawingView extends View {
private Bitmap mBitmapBrush;
private Bitmap rBitmapBrush;
private BitmapShader mBitmapShader;
private Paint paint = new Paint();
private Path path = new Path();
public MainDrawingView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
mBitmapBrush = BitmapFactory.decodeResource(context.getResources(), R.drawable.brush01);
rBitmapBrush = Bitmap.createScaledBitmap(mBitmapBrush, 10, 10, false);
mBitmapShader = new BitmapShader(rBitmapBrush, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setAntiAlias(true);
paint.setStrokeWidth(5f);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
//paint.setShader(mBitmapShader);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(eventX, eventY);
break;
default:
return false;
}
invalidate();
return true;
}
}
Related
I made a custom ImageView which has a canvas and gets two bitmaps so that I can draw on top of an existing image.
While the orientation changes the images is visible but the lines I've drawn disappear.
public class DrawableImageView extends ImageView implements View.OnTouchListener, DrawableImageViewControlListener {
float downx = 0;
float downy = 0;
float upx = 0;
float upy = 0;
Canvas canvas;
Paint paint;
Matrix matrix;
boolean isChanged;
public DrawableImageView(Context context) {
super(context);
setOnTouchListener(this);
}
public DrawableImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
setOnTouchListener(this);
}
public DrawableImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOnTouchListener(this);
}
public void setNewImage(Bitmap alteredBitmap, Bitmap bmp)
{
canvas = new Canvas(alteredBitmap);
paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
paint.setStrokeCap(Paint.Cap.ROUND);
matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
isChanged = false;
setImageBitmap(alteredBitmap);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
isChanged = true;
switch (action)
{
case MotionEvent.ACTION_DOWN:
downx = getPointerCoords(event)[0];//event.getX();
downy = getPointerCoords(event)[1];//event.getY();
break;
case MotionEvent.ACTION_MOVE:
upx = getPointerCoords(event)[0];//event.getX();
upy = getPointerCoords(event)[1];//event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
invalidate();
downx = upx;
downy = upy;
break;
case MotionEvent.ACTION_UP:
upx = getPointerCoords(event)[0];//event.getX();
upy = getPointerCoords(event)[1];//event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}
final float[] getPointerCoords(MotionEvent e)
{
final int index = e.getActionIndex();
final float[] coords = new float[] { e.getX(index), e.getY(index) };
Matrix matrix = new Matrix();
getImageMatrix().invert(matrix);
matrix.postTranslate(getScrollX(), getScrollY());
matrix.mapPoints(coords);
return coords;
}
#Override
public void colorChanged(int color) {
paint.setColor(color);
}
#Override
public void brushChanged(int size) {
paint.setStrokeWidth(size);
}
public boolean isChanged() {
return isChanged;
}
And here is the code where I initialize the ImageView in my OnCreate:
imageView = (DrawableImageView) findViewById(R.id.editImageView);
IStorage storage = BootLoader.resolve(this).getStorage();
imageFile = storage.getImageFile(fileName);
Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath());
if (alteredBitmap == null) {
alteredBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
}
imageView.setNewImage(alteredBitmap, bitmap);
Note: I am using two layout files for the two different orientations, and would like to keep it this way. So not manually handling configuration changes.
Android tears down the whole Activity when the screen orientation is changed and restarts it again, so you need to save the altered bitmap when this happens and restore it afterwards.
Different storage options are described here
When to save is described here,
I am hand drawing images on a canvas and then converting them to a bitmap. Then, I am dragging this on screen. That's all fine, What I want is onTouchEvent to draw a rectangle round this image/s but NOT the whole canvas. So I'm wondering if I can measure the drawing width and height and subtract this from canvas width and height but I'm struggling and not sure How can I achieve this? Unless there's a better idea someone can suggest.
private static class DrawingView extends View {
float x = 0f,y = 0f;
float dX,dY;
Paint paint;
public DrawingView(Context context){
super(context);
bitmap = BitmapFactory.decodeResource(context.getResources(), R.id.main);
paint = new Paint();
}
public boolean onTouchEvent(MotionEvent event){
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
dX = this.getX() - event.getRawX();
dY = this.getY() - event.getRawY();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
this.setX(event.getRawX() + dX);
this.setY(event.getRawY() + dY);
invalidate();
break;
case MotionEvent.ACTION_UP:
invalidate();
break;
}
return true;
}
#Override
public void onDraw(Canvas canvas) {
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.BLACK);
canvas.drawBitmap(bitmap, x, y, paint);
float left = (x + (bitmap.getWidth()/2));
float top = (y + (bitmap.getHeight()/2));
float right = bitmap.getWidth() - left;
float bottom = bitmap.getHeight() - top;
canvas.drawRect(left, top, right, bottom, paint);
}
}//End of inner class
My drawing class
public class DrawView extends View {
private Paint drawPaint, canvasPaint;
private Canvas drawCanvas;
private Bitmap canvasBitmap;
private SparseArray<Path> paths;
public DrawView(Context context) {
super(context);
setupDrawing();
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
public DrawView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setupDrawing();
}
private void setupDrawing() {
paths = new SparseArray<>();
drawPaint = new Paint();
drawPaint.setColor(Color.BLACK);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(9);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
for (int i = 0; i < paths.size(); i++) {
canvas.drawPath(paths.valueAt(i), drawPaint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int id = event.getPointerId(index);
final Point p = new Point();
Path path;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
path = new Path();
path.moveTo(event.getX(index), event.getY(index));
paths.put(id, path);
break;
case MotionEvent.ACTION_MOVE:
for (int i=0; i<event.getPointerCount(); i++) {
id = event.getPointerId(i);
path = paths.get(id);
if (path != null) path.lineTo(event.getX(i), event.getY(i));
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
path = paths.get(id);
if (path != null) {
drawCanvas.drawPath(path, drawPaint);
paths.remove(id);
}
setPenEnabled(true);
break;
default:
return false;
}
invalidate();
return true;
}
/**
* change path color here
*/
public void setPathColor(int color) {
drawPaint.setColor(color);
}
//Clear screen
public void clear() {
canvasBitmap.eraseColor(Color.TRANSPARENT);
paths.clear();
invalidate();
System.gc();
}
}//End of Class
I have a problem with canvas I have two options either to draw with a paint or to add an image but when I choose to add an image after drawing the drawing is gone but when I choose drawing again and start to draw the previous drawing show up and remove the image.
public class DrawCanvas extends View {
Bitmap bitmap;
private Paint paint = new Paint();
private Path path;
ArrayList<Path> paths = new ArrayList<>();
ArrayList<Path> undo = new ArrayList<>();
final Paint mBackgroundPaint;
ArrayList<Emotion> emotions;
private boolean mDrawingEnabled = true;
float currentX = 0, currentY = 0;
public DrawCanvas(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.launcher);
paint.setAntiAlias(false);
paint.setColor(Color.BLACK);
paint.setStrokeJoin(Paint.Join.MITER);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(30f);
emotions = new ArrayList<>();
path = new Path();
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(Color.WHITE);
}
#Override
protected void onDraw(Canvas canvas){
if (mDrawingEnabled ) {
canvas.drawPath(path, paint);
}
else{
canvas.drawBitmap(bitmap, currentX, currentY, null);
}
}
#Override
public boolean onTouchEvent(MotionEvent motionEvent){
int xPos =(int) motionEvent.getX(), yPos =(int) motionEvent.getY();
final int action = motionEvent.getAction();
if (mDrawingEnabled){
switch (action) {
case MotionEvent.ACTION_DOWN:
path.moveTo(xPos, yPos);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(xPos, yPos);
break;
case MotionEvent.ACTION_UP:
paths.add(path);
break;
default:
return false;
}
}
else {
final float me_x = motionEvent.getX();
final float me_y = motionEvent.getY();
switch ( action ) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
currentX = me_x;
currentY = me_y;
break;
}
}
invalidate();
return true;
}
public void undo(){
if (paths.size()>0){
undo.add(paths.remove(paths.size()-1));
}
}
public void redo(){
if (undo.size()>0){
paths.add(undo.remove(undo.size()-1));
}
}
final void enableDrawing() { mDrawingEnabled = true; }
final void disableDrawing() { mDrawingEnabled = false; }
public void changeBrushColor(int color){
paint.setColor(color);
}
public void changeBrushSize(int size){
paint.setStrokeWidth(size);
}
}
here is the drawing
and when I add image clears the drawing
I want to keep the drawn path alongside the image.
I'm trying to draw circle that follow my finger over the screen.
it's done but if I move finger fast, circle move isn't smooth.
customview.java
public customview(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
paint = new Paint();
paint.setAntiAlias(true);
paint.setARGB(255, 255, 255, 255);
}
Context context;
public static float x=100, y=100;
private Paint paint;
public static boolean drew = false;
RadialGradient radialGradient;
#Override
protected void onDraw(Canvas canvas) {
if(drew){
int ringColors[] = { 0xFF15afda, 0x0015afda };
radialGradient = new RadialGradient(x, y,100, ringColors, null,
TileMode.CLAMP);
paint.setShader(radialGradient);
canvas.drawCircle(x, y, 200, paint);
}
}
public static void setDrew(boolean drew){
customview.drew = drew;
}
public static void setXT(float x){
customview.x = x;
}
public static void setYT(float y){
customview.y = y;
}
touch event( in touch event I change x and y that shows the position of finger and set invalidate until view redraw a circle in new position )
#Override
public boolean onTouch(View v, MotionEvent event) {
dispatchGenericMotionEvent(event);
// TODO Auto-generated method stub
MotionEvent ev = event;
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
customview.setXT(ev.getRawX());
customview.setYT(ev.getRawY());
customview.setDrew(true);
(findViewById(R.id.touch)).invalidate();
break;
case MotionEvent.ACTION_UP:
customview.setDrew(false);
(findViewById(R.id.customview)).invalidate();
break;
default:
break;
}
return true;
}
How to Draw a rectangle (Using Shape Resources) at the touched point(like coordinates 28,87). I have Created a shape Like This.
android:shape="rectangle" >
<solid
android:color="#color/transparent"/>
<stroke
android:width="3dp"
android:color="#color/green" />
This rectangle I want to Draw at the touch point on the image.
You can draw a shape on a view in the onDraw() method of that view. There is no method available for drawing a shape drawable on a view canvas.
And you don't have to use a shape drawable for drawing a rectangle. You can draw a rectangle using canvas.drawRect() method.
Here is a code for this:
public class MyView extends View{
float x,y;
Bitmap bmp;
Paint mPaint;
float width = 100.0f;
float height = 50.0f;
boolean touched = false;
public MyView (Context context)
{
super(context);
x = y = 0;
mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Style.STROKE);
}
#Override
protected void onDraw (Canvas canvas)
{
canvas.drawColor(Color.WHITE);
if(touched)
{
canvas.drawRect(x, y, x+width, y+height, mPaint);
}
}
#Override
public boolean onTouchEvent (MotionEvent event)
{
touched = true;
//getting the touched x and y position
x = event.getX();
y = event.getY();
invalidate();
return true;
}
}
#kam' solution needs an update I guess. Everything that was in the constructor must be in init() method and the constructors must be overwritten 3 times.
public class MyView extends View {
private float xDown = 0,yDown = 0, xUp = 0, yUp = 0;
Paint mPaint;
boolean touched = false;
public MyView(Context context) {
super(context);
init(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw (Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
if(touched) {
canvas.drawRect(xDown, yDown, xUp, yUp, mPaint);
}
}
#Override
public boolean onTouchEvent (MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
xDown = event.getX();
yDown = event.getY();
xUp = 0;
yUp = 0;
break;
case MotionEvent.ACTION_MOVE:
xUp = event.getX();
yUp = event.getY();
touched = true;
break;
case MotionEvent.ACTION_UP:
xUp = event.getX();
yUp = event.getY();
touched = true;
break;
}
invalidate();
return true;
}
}