I'm working on application which gets an image and displays it into an image view. when the user tuches the screen I want the image covered by that circle (the circle is positioned were the screen is touched) to remain colored in that area after the screen is touched again.
I was able to draw the circle over the image but when the screen is touched again the image doesn't stay colored.
class DrawingView extends SurfaceView {
private final SurfaceHolder surfaceHolder;
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap bitmap1 = null;
private Bitmap bitmap2 = null;
private final Point node = new Point();
public DrawingView(Context context) {
super(context);
surfaceHolder = getHolder();
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
if (surfaceHolder.getSurface().isValid()) {
node.x= (int)event.getX();
node.y = (int)event.getY();
Canvas canvas = surfaceHolder.lockCanvas();
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.nic_eyes);
canvas.drawBitmap(bitmap1, 0, 0, null);
canvas.drawCircle(event.getX(), event.getY(), 50, paint);
bitmap2 = bitmap1.copy(bitmap1.getConfig(),true);
surfaceHolder.unlockCanvasAndPost(canvas);
photo = bitmap2;//photo is used for saving the modified image into galery
}
}
return false;
}
}
Any suggestions?
Related
I'm making an app where user will be able to click on part of the image and get a magnified version in the corner of WebView. I managed to make a Paint that would make a zoom version, but it displays wrong location, like there's some offset.
I know this question has been asked a lot of times and was already answered, but it appears non of those solutions helped.
Here's code I've used:
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
zoomPos = new PointF();
zoomPos.x = event.getX();
zoomPos.y = event.getY();
matrix = new Matrix();
mShader = new BitmapShader(MainActivity.mutableBitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint = new Paint();
mPaint.setShader(mShader);
outlinePaint = new Paint(Color.BLACK);
outlinePaint.setStyle(Paint.Style.STROKE);
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
zooming = true;
this.invalidate();
break;
case MotionEvent.ACTION_UP:
Point1 = true;
zooming = false;
this.invalidate();
break;
case MotionEvent.ACTION_CANCEL:
zooming = false;
this.invalidate();
break;
default:
break;
}
return true;
}
#Override
protected void onDraw(#NonNull Canvas canvas) {
super.onDraw(canvas);
if (zooming) {
matrix.reset();
matrix.postScale(2f, 2f, zoomPos.x, zoomPos.y);
mPaint.getShader().setLocalMatrix(matrix);
canvas.drawCircle(100, 100, 100, mPaint);
}
}
Technically it should draw a circle at upper-left corner and display zoomed image of area where my finger is, it draws a circle, but again, zoom is shifted.
Final result should look something like this:
MainActivity.java
public class MainActivity extends Activity {
static ImageView takenPhoto;
static PointF zoomPos;
Paint shaderPaint;
static BitmapShader mShader;
BitmapShader shader;
Bitmap bmp;
static Bitmap mutableBitmap;
static Matrix matrix;
Canvas canvas;
static Paint mPaint;
static Paint Paint;
static boolean zooming;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File file = new File(Environment.getExternalStorageDirectory() + "/Pictures/boxes.jpg");
String fileString = file.getPath();
takenPhoto = (ZoomView) findViewById(R.id.imageView1);
bmp = BitmapFactory.decodeFile(fileString);
mutableBitmap = bmp.copy(Bitmap.Config.ARGB_8888, true);
takenPhoto.setImageBitmap(mutableBitmap);
matrix = new Matrix();
mShader = new BitmapShader(mutableBitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint = new Paint();
mPaint.setShader(mShader);
zoomPos = new PointF();
Paint = new Paint(Color.RED);
}
}
ZoomView.java
public class ZoomView extends ImageView {
private PointF zoomPos;
PointF fingerPos;
private Paint paint = new Paint(Color.BLACK);
boolean zooming;
Matrix matrix;
BitmapShader mShader;
Paint mPaint;
Paint outlinePaint;
boolean Point1;
public ZoomView(Context context) {
super(context);
}
public ZoomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ZoomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
zoomPos = new PointF();
zoomPos.x = event.getX();
zoomPos.y = event.getY();
matrix = new Matrix();
mShader = new BitmapShader(MainActivity.mutableBitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint = new Paint();
mPaint.setShader(mShader);
outlinePaint = new Paint(Color.BLACK);
outlinePaint.setStyle(Paint.Style.STROKE);
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
zooming = true;
this.invalidate();
break;
case MotionEvent.ACTION_UP:
Point1 = true;
zooming = false;
this.invalidate();
break;
case MotionEvent.ACTION_CANCEL:
zooming = false;
this.invalidate();
break;
default:
break;
}
return true;
}
#Override
protected void onDraw(#NonNull Canvas canvas) {
super.onDraw(canvas);
if (zooming) {
matrix.reset();
matrix.postScale(2f, 2f, zoomPos.x, zoomPos.y);
mPaint.getShader().setLocalMatrix(matrix);
RectF src = new RectF(zoomPos.x-50, zoomPos.y-50, zoomPos.x+50, zoomPos.y+50);
RectF dst = new RectF(0, 0, 100, 100);
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
matrix.postScale(2f, 2f);
mPaint.getShader().setLocalMatrix(matrix);
canvas.drawCircle(100, 100, 100, mPaint);
canvas.drawCircle(zoomPos.x, zoomPos.y, 100, mPaint);
canvas.drawCircle(zoomPos.x-110, zoomPos.y-110, 10, outlinePaint);
}
if(Point1){
canvas.drawCircle(zoomPos.x, zoomPos.y, 10, paint);
}
}
}
EDIT:
As you can see new code is way better, still there is some offset - black dot - position of the finger.
Seems that the issue is with how you are using the matrix.
Now you are using the original image (1) as a shader which is then being post scaled up around a pivot point (2), which is like doing a zoom around a point (3) - but not centering the point (4) ! (For example, open google maps and zoom in on the map with your mouse - the point is zoomed around the pivot but the pivot is not centered)
What will be an easier way to achieve what you want is by using the Rect to Rect method. I.E. you want to take a small area from the original image (5) and draw it to a larger area (6) .
And here is a code sample:
RectF src = new RectF(zoomPos.x-50, zoomPos.y-50, zoomPos.x+50, zoomPos.y+50);
RectF dst = new RectF(0, 0, 200, 200);
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
Some more points:
Try not to create new object in the onTouch - it is being called many times and it is not good on performance . Instead, create once and reuse.
getAction() will have issues when there are more than one finger on screen since it is the action ID and the pointer ID. Instead use getActionMasked() and getActionIndex().
Do not use hardcoded values (50/100 in the sample code) - these are pixels and are not aware of screen density. Use scaled size like dp.
What I want to do is to draw an image when the touch is down and when it is moving the image should also move along with the finger and when the touch is up the image should remain in that position. Again if i put the finger down another copy of that same image should again appear in the touch down co-ordinates and the process continues.
I saw many tutorials and tried everything. I tried to save the image of the canvas when the finger is down and when it moves clear the screen with the image saved during the finger down but that did not work. Then, I tried to clear the canvas with black color and paste the image saved during finger down and then draw the new image that I want to appear but still it did not work properly. It causes flickering and sometimes when lift up my finger and then again put it down the last image gets erased.
This is my code:
public class renderView extends SurfaceView implements Runnable{
boolean isRunning=false;
SurfaceHolder holder;
Thread t=null;
Bitmap img;
Paint paint;
Canvas canvas2,canvas3;
Bitmap cache,backup,cache2;
float x,y;
boolean up,down,move;
boolean dummy;
public renderView(Context context) {
super(context);
isRunning=true;
holder = getHolder();
img = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
paint= new Paint();
x=y=0;
cache = Bitmap.createBitmap(600, 800, Config.ARGB_8888);
cache2 = Bitmap.createBitmap(600, 800, Config.ARGB_8888);
backup = Bitmap.createBitmap(600,800, Bitmap.Config.ARGB_8888);
canvas2 = new Canvas(cache);
canvas3 =new Canvas(cache2);
move=down=false;
up=true;
dummy=false;
}
#Override
public void run() {
Log.d("status","run");
while(isRunning){
if(!holder.getSurface().isValid()){
continue;
}
Canvas canvas1 = holder.lockCanvas();
canvas1.drawColor(Color.BLUE);
canvas1.drawBitmap(cache2,0,0,paint);
canvas1.drawBitmap(cache,0,0,paint);
holder.unlockCanvasAndPost(canvas1);
}
}
public void pause(){
isRunning = false;
while(true){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
t=null;
}
public void resume(){
isRunning=true;
t=new Thread(this);
t.start();
}
public void down(float posX, float posY) {
/*x=posX;
y=posY;*/
canvas2.drawBitmap(img,posX,posY,paint);
canvas3.drawBitmap(img,posX,posY,paint);
up=false;
}
public void move(float posX, float posY) {
move=true;
dummy=true;
canvas2.drawColor(Color.BLACK);
canvas2.drawBitmap(cache2, 0, 0,paint);
canvas2.drawBitmap(img,posX,posY,paint);
}
public void up(float posX, float posY) {
up=true;
move=false;
canvas2.drawColor(Color.BLACK);
canvas2.drawBitmap(cache2,0,0,paint);
canvas2.drawBitmap(img,posX,posY,paint);
}
}
I am creating an app to draw free shapes on the surface screen but i could only draw separated points my problem is . i want the points to be connected to each other when i draw them not lifting my finger from the screen . i mean as long as i am touching the screen draw.here's my code so far.
public class SurfaceViewActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new DrawingView(this));
}
class DrawingView extends SurfaceView {
private final SurfaceHolder surfaceHolder;
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private List<Point> pointsList = new ArrayList<Point>();
public DrawingView(Context context) {
super(context);
surfaceHolder = getHolder();
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (surfaceHolder.getSurface().isValid()) {
// Add current touch position to the list of points
pointsList.add(new Point((int)event.getX(), (int)event.getY()));
// Get canvas from surface
Canvas canvas = surfaceHolder.lockCanvas();
// Clear screen
canvas.drawColor(Color.BLACK);
// Iterate on the list
for(int i=0; i<pointsList.size(); i++) {
Point current = pointsList.get(i);
// Draw points
canvas.drawPoint(current.x, current.y, paint);
}
// Release canvas
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
return false;
}
}
}
you can use this function to draw smooth lines
public void drawBrzierLine(Canvas mCanvas, float xi, float yi, float xd, float yd) {
Point start = new Point((int) xi, (int) yi);
Point end = new Point((int) xd, (int) yd);
Path mPath = new Path();
mPath.reset();
mPath.moveTo(start.x, start.y);
mPath.quadTo(start.x, start.y, end.x, end.y);
mCanvas.drawPath(mPath, mPaint);
}
In onTouchEvent(MotionEvent event) you only handle ACTION_DOWN. So this code will only run when you press down on the screen. Use ACTION_MOVE instead.
http://developer.android.com/reference/android/view/MotionEvent.html
I am trying to use a mask to hide a part of a picture, depending on where the user touch the screen. Todo so, I followed a code sample provided by Cyril Mottier. What I did until now actually work : while clicking a part of my ImageView, all what I got above is hidden. The problem is it is hidden by black color, preventing what there is behind my ImageView from displaying.
Could anybody please provide me tips or tell me what I do wrong?
Here a screen of what I got at the time
Here is the main activity :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
final MaskedImageView iv = (MaskedImageView) findViewById(R.id.picture_on);
iv.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(MainActivity.class.getSimpleName(), "on touch called");
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
Point point = new Point();
display.getSize(point);
Rect bounds = iv.getDrawable().getBounds();
bounds.top = point.y
- (int) event.getY()
;
iv.setMask(bounds);
iv.invalidate();
return false;
}
});
}
And here the custom ImageView :
public class MaskedImageView extends ImageView {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap mMask;
public MaskedImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Prepares the paint that will be used to draw our icon mask. Using
// PorterDuff.Mode.DST_IN means the image that will be drawn will
// mask the already drawn image.
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
public MaskedImageView(Context context, AttributeSet attrs) {
super(context, attrs);
// Prepares the paint that will be used to draw our icon mask. Using
// PorterDuff.Mode.DST_IN means the image that will be drawn will
// mask the already drawn image.
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
public MaskedImageView(Context context) {
super(context);
// Prepares the paint that will be used to draw our icon mask. Using
// PorterDuff.Mode.DST_IN means the image that will be drawn will
// mask the already drawn image.
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
public void setMask(Rect rect) {
mMask = Bitmap.createBitmap(Math.abs(rect.right - rect.left),
Math.abs(rect.bottom - rect.top), Bitmap.Config.ALPHA_8);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
BitmapDrawable bd = (BitmapDrawable) getDrawable();
canvas.drawBitmap(bd.getBitmap(), 0, 0, null);
if (mMask != null) {
canvas.drawBitmap(mMask, 0, 0, mPaint);
}
canvas.restore();
}
}
Thank you for your answers
I try to erase parts of a bitmap in my Android application by using Porter-Duff Xfermodes.
I have a green background which is overlayed by a blue bitmap. When I touch the screen a "hole" in the overlaying bitmap is supposed to be created making the green background visible. Instead of a hole my current code produces a black dot.
Below is my code. Any ideas, what I am doing wrong here?
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new DrawView(this));
}
public class DrawView extends View implements OnTouchListener {
private int x = 0;
private int y = 0;
Bitmap bitmap;
Canvas bitmapCanvas;
private final Paint paint = new Paint();
private final Paint eraserPaint = new Paint();
public DrawView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
// Set background
this.setBackgroundColor(Color.GREEN);
// Set bitmap
bitmap = Bitmap.createBitmap(320, 480, Bitmap.Config.RGB_565);
bitmapCanvas = new Canvas();
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.drawColor(Color.BLUE);
// Set eraser paint properties
eraserPaint.setAlpha(0);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
eraserPaint.setAntiAlias(true);
}
#Override
public void onDraw(Canvas canvas) {
bitmapCanvas.drawColor(Color.BLUE);
bitmapCanvas.drawCircle(x, y, 10, eraserPaint);
canvas.drawBitmap(bitmap, 0, 0, paint);
}
public boolean onTouch(View view, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
invalidate();
return true;
}
}
Here is working code... may help somebody
public class ImageDemo extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Panel(this));
}
class Panel extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
Bitmap bitmap;
Canvas pcanvas;
int x = 0;
int y =0;
int r =0;
public Panel(Context context) {
super(context);
Log.v("Panel", ">>>>>>");
setFocusable(true);
setBackgroundColor(Color.GREEN);
// setting paint
mPaint = new Paint();
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setAntiAlias(true);
// getting image from resources
Resources r = this.getContext().getResources();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.mickey);
// converting image bitmap into mutable bitmap
bitmap = bm.createBitmap(295, 260, Config.ARGB_8888);
pcanvas = new Canvas();
pcanvas.setBitmap(bitmap); // drawXY will result on that Bitmap
pcanvas.drawBitmap(bm, 0, 0, null);
}
#Override
protected void onDraw(Canvas canvas) {
// draw a circle that is erasing bitmap
pcanvas.drawCircle(x, y, r, mPaint);
canvas.drawBitmap(bitmap, 0, 0,null);
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// set parameter to draw circle on touch event
x = (int) event.getX();
y = (int) event.getY();
r =20;
// At last invalidate canvas
invalidate();
return true;
}
}
}
First thought, I'm not sure if setting alpha to 0 on your erase paint object is a good idea. That might make the whole thing ineffective.
Also, you should always use Bitmap.Config.ARGB_8888 if you're dealing with alphas.
If you're having trouble with the PorterDuff stuff, though, I would suggest simplifying your approach to ONLY do that (temporarily). That will help you narrow down the part which isn't working. Comment out everything to do with touch and view updates.
Then you can single out what part of the drawing isn't working right. Set up your constructor like this:
DrawView()
{
/* Create the background green bitmap */
...
/* Create foreground transparent bitmap */
...
/* Draw a blue circle on the foreground bitmap */
...
/* Apply the foreground to the background bitmap
using a PorterDuff method */
...
}
onDraw()
{
/* Simply draw the background bitmap */
...
}
If you set things up like that, you should be able to tell how your PD method is affecting the green bitmap, and change things accordingly.
Here is another advancement for your solution ... See Demo example
public class MainActivity extends Activity {
Bitmap bp;
Canvas bitmapCanvas;
DrawView drawImg;
LinearLayout ln1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ln1 = (LinearLayout) findViewById(R.id.ln1);
drawImg = new DrawView(this);
ln1.addView(drawImg);
}
public class DrawView extends View implements View.OnTouchListener {
private int x = 0;
private int y = 0;
Bitmap bitmap;
Path circlePath;
Paint circlePaint;
private final Paint paint = new Paint();
private final Paint eraserPaint = new Paint();
public DrawView(Context context){
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
// Set background
this.setBackgroundColor(Color.CYAN);
bp = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
// Set bitmap
bitmap = Bitmap.createBitmap(320, 480, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas();
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.drawColor(Color.TRANSPARENT);
bitmapCanvas.drawBitmap(bp, 0, 0, null);
circlePath = new Path();
circlePaint = new Paint();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
// Set eraser paint properties
eraserPaint.setAlpha(0);
eraserPaint.setStrokeJoin(Paint.Join.ROUND);
eraserPaint.setStrokeCap(Paint.Cap.ROUND);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
eraserPaint.setAntiAlias(true);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, paint);
bitmapCanvas.drawCircle(x, y, 30, eraserPaint);
canvas.drawPath(circlePath, circlePaint);
}
public boolean onTouch(View view, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
bitmapCanvas.drawCircle(x, y, 30, eraserPaint);
circlePath.reset();
circlePath.addCircle(x, y, 30, Path.Direction.CW);
int ac=event.getAction();
switch(ac){
case MotionEvent.ACTION_UP:
Toast.makeText(MainActivity.this, String.valueOf(x), Toast.LENGTH_SHORT).show();
circlePath.reset();
break;
}
invalidate();
return true;
}
}
}
read more