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;
}
Related
I want to implement eraser in my paint app. But the code
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
draws black line on canvas. If I am changing the background color the canvas draws black on it too.
I have also tried using setLayerType() but it draws white on any color background.
// In constructor
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Below is the code for my PaintView.
public class PaintView extends View {
private Bitmap bitmapBackground,bitmapView;
private int backgroundColor;
private int brushSize;
private int eraserSize;
private float mX,mY;
private Canvas canvas=null;
private final int TOUCH_TOLERANCE=4;
private int paintColor;
private int modeStatus;
/*
1 for brush
2 for eraser
*/
private ArrayList<Paint> paints = new ArrayList<>();
private ArrayList<Path> paths = new ArrayList<>();
private int historyPointer=0;
public PaintView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initialise();
}
private void initialise() {
eraserSize=12;
brushSize=12;
backgroundColor= Color.WHITE;
paintColor = Color.BLACK;
modeStatus = 1;
paints.add(createPaint());
paths.add(new Path());
historyPointer++;
}
private float toPx(int brushSize) {
return brushSize*(getResources().getDisplayMetrics().density);
}
public void init(int width,int height) {
bitmapBackground=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
bitmapView=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
canvas=new Canvas(bitmapView);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(backgroundColor);
canvas.drawBitmap(bitmapBackground,0,0,null);
for (int i=0;i<historyPointer;i++) {
Path path = paths.get(i);
Paint paint = paints.get(i);
canvas.drawPath(path,paint);
}
}
private Paint createPaint() {
Paint paint = new Paint();
paint.setColor(paintColor);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
if (modeStatus==1) {
paint.setXfermode(null);
paint.setShader(null);
paint.setMaskFilter(null);
paint.setStrokeWidth(toPx(brushSize));
}
else {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setStrokeWidth(toPx(eraserSize));
}
return paint;
}
private Path createPath(float x,float y) {
Path path = new Path();
path.moveTo(x,y);
return path;
}
public void setBackgroundColor(int backgroundColor) {
this.backgroundColor=backgroundColor;
invalidate(); //Redraw
}
public void setBrushSize(int brushSize) {
this.brushSize=brushSize;
modeStatus=1;
}
public void setBrushColor(int color) {
paintColor=color;
}
public void setEraserSize(int eraserSize) {
this.eraserSize=eraserSize;
modeStatus=2;
}
public int getBrushSize() {
return this.brushSize;
}
public int getEraserSize() {
return this.eraserSize;
}
private void updateHistory(Path path) {
if (historyPointer==paths.size()) {
paths.add(path);
paints.add(createPaint());
historyPointer++;
}
else {
// For undo and redo
paths.set(historyPointer,path);
paints.set(historyPointer,createPaint());
historyPointer++;
for (int i=historyPointer,size=paths.size();i<size;i++) {
paths.remove(historyPointer);
paints.remove(historyPointer);
}
}
}
private Path getCurrentPath() {
return paths.get(historyPointer-1);
}
private Paint getCurrentPaint() {
return paints.get(historyPointer-1);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
// Case when finger touches the screen
case MotionEvent.ACTION_DOWN:
touchStart(x,y);
break;
// Case when finger moves on screen
case MotionEvent.ACTION_MOVE:
touchMove(x,y);
break;
// Case when finger is taken away from screen
case MotionEvent.ACTION_UP:
touchUp();
break;
default :
return false;
}
return true;
}
private void touchStart(float x, float y) {
mX=x;
mY=y;
updateHistory(createPath(x,y));
}
private void touchMove(float x, float y) {
float dx = Math.abs(x-mX);
float dy = Math.abs(y-mY);
Path path = getCurrentPath();
if (dx>=TOUCH_TOLERANCE || dy>=TOUCH_TOLERANCE) {
path.quadTo(x,y,(x+mX)/2,(y+mY)/2);
mX=x;
mY=y;
}
invalidate();;
}
private void touchUp() {
}
}
I got the answer after many research and the following worked.
Just save the canvas layer after setting the background color and at last restore to count.
The onDraw method is as follows -
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(backgroundColor);
canvas.drawBitmap(bitmapBackground,0,0,null);
int layerId = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG); // Line 1 added
for (int i=0;i<historyPointer;i++) {
Path path = paths.get(i);
Paint paint = paints.get(i);
canvas.drawPath(path,paint);
}
canvas.restoreToCount(layerId); // Line 2 added
}
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;
}
}
I'm trying to make an Activity in which the canvas color changes when you tap the canvas.
With the code I now have, I get this error:
Attempt to invoke virtual method 'void android.graphics.Canvas.drawRect(float, float, float, float, android.graphics.Paint)' on a null object reference
This is a part of my Activity code.
public class ColorActivity extends Activity {
private float x = 0;
private float y = 0;
public Canvas canvas;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_color);
setContentView(new MyView(this));
}
public class MyView extends View {
public MyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
setFocusableInTouchMode(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int x = getWidth();
int y = getHeight();
Paint paintCanvas = new Paint();
paintCanvas.setStyle(Paint.Style.FILL);
paintCanvas.setColor(Color.WHITE);
paintCanvas.setColor(Color.parseColor("#F44336"));
canvas.drawRect(0, 0, x, y, paintCanvas);
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Paint repaintCanvas = new Paint();
repaintCanvas.setStyle(Paint.Style.FILL);
repaintCanvas.setColor(Color.WHITE);
repaintCanvas.setColor(Color.parseColor("#2196F3"));
canvas.drawRect(0, 0, 100, 100, repaintCanvas);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
Log.d("LOG","Move");
invalidate();
break;
case MotionEvent.ACTION_UP:
Log.d("LOG", "Up");
invalidate();
break;
}
return super.onTouchEvent(event);
}
}
What am I doing wrong?
I think I know what is your problem. Invalidate calls again to the onDraw() method and it overwrites whatever you paint in the onTouchEvent method.
Try the following code:
public class ColorActivity extends Activity {
private float x = 0;
private float y = 0;
public Canvas canvas;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_color);
setContentView(new MyView(this));
}
public class MyView extends View {
Paint paintCanvas;
public MyView(Context context) {
super(context);
paintCanvas = new Paint();
paintCanvas.setStyle(Paint.Style.FILL);
paintCanvas.setColor(Color.WHITE);
paintCanvas.setColor(Color.parseColor("#F44336"));
// TODO Auto-generated constructor stub
setFocusableInTouchMode(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int x = getWidth();
int y = getHeight();
canvas.drawRect(0, 0, x, y, paintCanvas);
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
paintCanvas.setStyle(Paint.Style.FILL);
paintCanvas.setColor(Color.WHITE);
paintCanvas.setColor(Color.parseColor("#2196F3"));
invalidate();
break;
case MotionEvent.ACTION_MOVE:
Log.d("LOG","Move");
invalidate();
break;
case MotionEvent.ACTION_UP:
Log.d("LOG", "Up");
invalidate();
break;
}
return super.onTouchEvent(event);
}
}
Your Canvas is null. You will only get it inside onDraw(Canvas canvas) which is responsible for drawing on your Canvas. In your onTouchEvent, set some instance variables (such as color, positions etc.) and call invalidate(), then onDraw will be triggered and you can draw your Canvas with desired needs which you will get from the instance values you set in onTouchEvent.
I am working on a drawing board app. I would like to implement a "pen" that draw on the image.
And so far I have create a custom imageview:
public class CustomDraw extends ImageView {
private int color = Color.BLACK;
private float width = 4f;
private List<Holder> holderList = new ArrayList<Holder>();
private class Holder {
Path path;
Paint paint;
Holder(int color, float width) {
path = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(width);
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
}
}
public CustomDraw(Context context) {
super(context);
init();
}
public CustomDraw(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomDraw(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
holderList.add(new Holder(color, width));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Holder holder : holderList) {
canvas.drawPath(holder.path, holder.paint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
holderList.add(new Holder(color,width));
holderList.get(holderList.size() - 1).path.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
holderList.get(holderList.size() - 1).path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
invalidate();
return true;
}
public void resetPaths() {
for (Holder holder : holderList) {
holder.path.reset();
}
invalidate();
}
public void setBrushColor(int color) {
this.color = color;
}
public void setWidth(float width) {
this.width = width;
}
}
The problem is , how can I draw on the image actually but not the imageview? That means, I should not able to draw outside the image, and when I zoom the view, the content should be zoom accordingly. I have found and attempt using the extend drawable but it seems I can not draw at run time.
Thanks
You should possibly use SurfaceView instead. It is more advanced compared to ImageView.
Refer to this for drawing: http://examples.javacodegeeks.com/android/core/ui/surfaceview/android-surfaceview-example/
This is for zoom:
http://android-innovation.blogspot.co.nz/2013/07/how-to-implement-pinch-and-pan-zoom-on.html
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;
}
}