I have an interesting situation if someone can see what i'm doing wrong? I have a class called TrackView below. It essentially opens up a hole where you touch the screen to reveal a hidden background only at that touch point. This all works great when I call it from setContentView(new TrackView(this)). Graphics are fast and fluid. If I use the exact same TrackView class in the manner below which is started from a service(why i'm using WindowManager), graphics are drawn very poorly and slow. The structure of the view hierarchy in each case is identical. I've also tried SurfaceView and the problem did not go away.
public class OverlayTesting
{
private WindowManager wm;
private LayoutInflater li;
private RelativeLayout rl;
private View v;
private TrackView tv;
public OverlayTesting(Context context)
{
wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
li = LayoutInflater.from(context);
v = li.inflate(R.layout.activity_overlay, null);
rl = (RelativeLayout) v.findViewById(R.id.relativeLayout);
tv = new TrackView(context);
rl.addView(tv);
}
public void show()
{
wm.addView(rl, new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
}
private class TrackView extends View
{
Bitmap bitmapBase;
Bitmap bitmapDefault;
Bitmap bitmapUsed;
Canvas canvasBase;
Canvas canvasDefault;
Canvas canvasUsed;
Paint paint1;
Paint paint2;
int displayHeight;
int displayWidth;
int X = -100;
int Y = -100;
public TrackView(Context context)
{
super(context);
Display display = wm.getDefaultDisplay();
Point point = new Point();
display.getSize(point);
displayWidth = point.x;
displayHeight = point.y;
// create base background
bitmapBase = Bitmap.createBitmap(displayWidth, displayHeight, Bitmap.Config.ARGB_8888);
canvasBase = new Canvas(bitmapBase);
canvasBase.drawColor(Color.WHITE);
// create overlays and masks
bitmapDefault = Bitmap.createBitmap(displayWidth, displayHeight, Bitmap.Config.ARGB_8888);
canvasDefault = new Canvas(bitmapDefault);
canvasDefault.drawColor(Color.BLACK);
bitmapUsed = Bitmap.createBitmap(displayWidth, displayHeight, Bitmap.Config.ARGB_8888);
canvasUsed = new Canvas(bitmapUsed);
// paint structures here
}
#Override
public boolean onTouchEvent(MotionEvent ev)
{
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
X = (int) ev.getX();
Y = (int) ev.getY();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
X = (int) ev.getX();
Y = (int) ev.getY();
invalidate();
break;
}
return true;
}
#Override
public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
// refresh overlay with default bitmap and open new hole at XY
// refresh overlay code here
// refresh core view canvas showing only area at XY
// draw canvas code here
}
}
}
Related
I'm trying to draw a circle where I touch so I'm using the OnTouchListener to get the x,y coordinates.
But when I run the code on the emulator the circle is not drawn where I clicked.
public class MainActivity extends AppCompatActivity {
Canvas canvas;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//------------------
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getRealMetrics(displaymetrics);
int screenWidth = displaymetrics.widthPixels;//480
int screenHeight = displaymetrics.heightPixels;//1920
Bitmap bg = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bg);
RelativeLayout ll = (RelativeLayout) findViewById(R.id.RelativeLayoutCells);
ll.setBackgroundDrawable(new BitmapDrawable(bg));
ll.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
float xf = event.getRawX();
float yf = event.getRawY();
Log.d("=", String.valueOf(x) + " " + String.valueOf(xf));
Paint fillpaint= new Paint();;
fillpaint.setColor(Color.RED);
fillpaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(xf, yf, 5, fillpaint);
canvas.drawCircle(x, y, 15, fillpaint);
}
Button button = (Button) findViewById(R.id.button);
button.setText("ghj");
return false;
}
});
}
}
You should not use event.getRawX(). The returned value is completly raw and not adjusted to your view. If the canvas is not scaled, use event.getX() instead. It makes a difference because of what it returns. getX returns x adjusted to custom views but getRawX is based on the screen which causes inaccuracy.
I am developing an app where the user can mark a damage icon on a car. The car here is basically an image and the damage icon is also an image. I am able to draw an image, where the user touches the car, using this code:
public class TestActivity extends Activity {
private Button btn_save, btn_resume;
private ImageView iv_canvas;
private Bitmap baseBitmap;
private Canvas canvas;
private Paint paint;
public Bitmap bt;
public Bitmap ct;
boolean ismove = false;
private static final int SWIPE_MIN_DISTANCE = 50;
int i = 0;
private float x = 0;
public HashMap<String, String> testholdmap = new HashMap<String, String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// The initialization of a brush, brush width is 5, color is red
paint = new Paint();
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
iv_canvas = (ImageView) findViewById(R.id.iv_canvas);
iv_canvas.setOnTouchListener(touch);
bt = BitmapFactory.decodeResource(getResources(),
R.drawable.damage_mark_r_ic);
}
private View.OnTouchListener touch = new OnTouchListener() {
// Coordinate definition finger touch
float startX;
float startY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
// Press the user action
case MotionEvent.ACTION_DOWN:
// The first drawing initializes the memory image, specify the
// background is white
if (baseBitmap == null) {
baseBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.b_ferrari_f152_000_0000).copy(
Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(baseBitmap);
}
// Recording began to touch point coordinate
startX = event.getX();
startY = event.getY();
break;
// The user's finger on the screen of mobile action
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
float stopX = event.getX();
float stopY = event.getY();
if (startX == stopX) {
canvas.drawBitmap(bt, startX, startY, paint);
// The pictures to the ImageView
iv_canvas.setImageBitmap(baseBitmap);
} else if (startX - stopX > 50 || stopX - startX > 50) {
baseBitmap = null;
}
break;
default:
break;
}
return true;
}
};
}
My problem is figuring out how to remove this icon if it already drawn. I want it to function this way: I draw a damage icon on x,y position, and if the user clicks on x,y position again, then this damage icon will be removed.
How can I achieve this?
Use this:
canvas.drawColor(0, Mode.CLEAR);
or
overlayBitmap.eraseColor(Color.TRANSPARENT);
This sets an existing Bitmap to all transparent.
My solution would be like follows :
Every time the user click on image - store the damge bounds (x,y,w,h) in list
and Draw it on the canvas
If user click again at the same damage image - remove it from list and redraw(clean canvas - Canvas.drawColor(Color.WHITE)) everything again : the car and remaining images in the list
I am working on a drawing app. And I already create a draw view that allow user to draw on the view.
The problem is , when I draw on it , it exceed the area of the image (please refer to the picture, the yellow line is exceeed the actual photo area), how can I stick the canvas size to the actual image ?
And how the imageview be zoomable? That means when user click on pen button, it draw, when user click again then it become zoom function.
Thanks. The zoom function can be tackle later and the problem of exceed area need to fix first
Here is the custom draw view in xml
<com.example.tool.DrawView
android:id="#+id/draw"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Here is the java
public class DrawView 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 DrawView(Context context) {
super(context);
init();
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DrawView(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;
}
}
And I call it by:
DrawView pic = findViewById(R.id.draw);
pic.setImageBitmap(bitmap);
Thanks a lot for helping
Calculate the displayed image size first and then clamp the eventX and eventY to the bound of the displayed image. Code as below:
int imageViewH=imageView.getMeasuredHeight();//height of imageView
int imageViewW =imageView.getMeasuredWidth();//width of imageView
int drawableH =imageView.getDrawable().getIntrinsicHeight();//original height of underlying image
int drawableW=imageView.getDrawable().getIntrinsicWidth();//original width of underlying image
int displayH, displayW; // the shown height and width of the picture.
int leftX, rightX, topY, bottomY; // the shown edges of the picture.
if (imageViewH/drawableH <= imageViewW/drawableW){
displayW = drawableW*imageViewH/drawableH;//rescaled width of image within ImageView
displayH = imageViewH;
leftX = (imageViewW - displayW)/2; // left edge of the displayed image.
rightX = leftX + displayW; // right edge of the displayed image.
topY = 0; // top edg
bottomY = displayH; // bottom edge.
}else{
displayH = drawableH*imageViewW/drawableH;//rescaled height of image within ImageView
displayW = imageViewW;
leftX = 0; // left edge of the displayed image.
rightX = displayW; // right edge of the displayed image.
topY = (imageViewH - displayH)/2; // top edg
bottomY = topY + displayH; // bottom edge.
}
//TODO: clamp the eventX and eventY to the bound of leftX, rightX, topY, bottomY
Note: the code should be used only after your ImageView has its measured height and a drawable.
You can set a width of your DrawView programmatically to ImageView's width, if in ImageView's properties there's a true attribute adjustViewBounds.
I have a class Panel which overrides onDraw() method as below.I have two images mentioned in canvas.drawBitmap(),as of now their position is fixed. Is it possible for me to move these two images from bottom to top on my emulator? Code is:
class Panel extends View {
public Panel(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
Bitmap image1 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
canvas.drawColor(Color.CYAN);
canvas.drawBitmap(Image1, 10, 10, null);
Bitmap Image2 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
canvas.drawBitmap(Image2, 100, 100, null);
}
}
Re-write your code like this canvas.drawBitmap(Image1, x, y, null);.
And write a thread to change the value of x or y over time.
Once you change the value of x or y, please call invalidate in your Panel.java to redraw the view.
use variables in onDraw call for values of x,y where you want to draw. I modified your code to redraw itself to new coordinates after 10 seconds of first draw call. It will give you an idea to use variables inside draw call to dynamically change where you draw. Hope I have answered your question
class Panel extends View {
public Panel(Context context) {
super(context);
}
Handler handler = new Handler() {
public void handleMessage(Message msg) {
x1 = 20;
y1 = 20;
x2 = 120;
y2 = 120;
invalidate();
}
}
private int x1 = 10;
private int y1 = 10;
private int x2 = 100;
private int y2 = 100;
private boolean drawAfterTenSecs = false;
#Override
public void onDraw(Canvas canvas) {
Bitmap image1 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
canvas.drawColor(Color.CYAN);
canvas.drawBitmap(Image1, x1, y1, null);
Bitmap Image2 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
canvas.drawBitmap(Image2, x2, y2, null);
if(!drawAfterTenSecs) {
handler.sendEmptyMessageDelayed (-1, 10000)
drawAfterTenSecs = true;
}
}
}
My basic goal is to subtract a Path from a predetermined region (also created with a Path) in Android 1.6.
Is there anyway to get Region.setPath to treat the path passed to it the same way the canvas treats a stroked path (which is what my users are seeing)?
I understand that the Style is used for Painting to the canvas, but I need the behavior to reflect what is drawn on canvas and the path being drawn is Stroked.
The behavior works find when adding shapes to the path (path.addRect, path.addCircle), but not when using lineTo, quadTo, cubeTo. My only other option is to compose a path of nothing but rects or circles.
public class ComplexRegions extends Activity {
static Display display;
/** Code Sample for StackOverflow */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
display = getWindowManager().getDefaultDisplay();
setContentView(new SampleView(this));
}
private static class SampleView extends View {
Path orig_path, finger_path;
Paint dirty_paint, fingerpaint, temp_paint;
Region orig_region, clip, fingerregion, tempregion;
Canvas c;
int h, w;
public float mX, mY, dx, dy;
boolean b;
public SampleView(Context context) {
super(context);
fingerregion = new Region();
tempregion = new Region();
orig_region = new Region();
clip = new Region();
orig_path = new Path();
finger_path = new Path();
dirty_paint = new Paint();
dirty_paint.setStyle(Paint.Style.STROKE);
dirty_paint.setColor(Color.BLUE);
dirty_paint.setStrokeWidth(5);
dirty_paint.setStrokeCap(Paint.Cap.ROUND);
dirty_paint.setStrokeJoin(Paint.Join.ROUND);
dirty_paint.setAntiAlias(false);
fingerpaint = new Paint();
fingerpaint.setColor(Color.GREEN);
fingerpaint.setStrokeWidth(10);
fingerpaint.setStyle(Paint.Style.STROKE);
fingerpaint.setStrokeCap(Paint.Cap.ROUND);
fingerpaint.setStrokeJoin(Paint.Join.ROUND);
fingerpaint.setAntiAlias(false);
temp_paint = new Paint();
temp_paint.setColor(Color.RED);
temp_paint.setStrokeWidth(1);
temp_paint.setStyle(Paint.Style.STROKE);
temp_paint.setStrokeCap(Paint.Cap.ROUND);
temp_paint.setStrokeJoin(Paint.Join.ROUND);
temp_paint.setAntiAlias(false);
w = display.getWidth();
h = display.getHeight();
clip.set(0, 0, w, h);
orig_path.addRect(w*0.25f, h*0.55f, w*0.65f, h*0.65f, Path.Direction.CW);
orig_region.setPath(orig_path, clip);
}
#Override
protected void onDraw(Canvas c) {
c.drawPath(orig_path, dirty_paint);
c.drawPath(finger_path, fingerpaint);
//following line shows that finger_path is
//behaving as though fill and stroke is on
//after being passed to the region class
c.drawPath(tempregion.getBoundaryPath(), temp_paint);
invalidate();
}
//L T R B
#Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
private void touch_start(float x, float y) {
finger_path.moveTo(x, y);
//finger_path.addCircle(x, y, 25, Path.Direction.CCW);
//when addCircle is only Path method used on path the result is fine
}
private void touch_move(float x, float y){
finger_path.lineTo(x, y);
}
private void touch_up() {
//*PROBLEM* Seems like setPath forces finger_path to default to fill and stroke
fingerregion.setPath(finger_path, clip);
//set tempregion to the region resulting from this Op
tempregion.op(orig_region, fingerregion, Region.Op.DIFFERENCE);
//check if the resulting region is empty
if(tempregion.isEmpty()){
for(int i = 0;i<100;i++)
Log.e("CR", "Region Completely Covered.");
}
}
}
}