I try to draw bitmap in touch points when touch move i need to draw the bitmap in newly added position not redraw the whole points again how to achieve that.
i try the following
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:{
break;
}
case MotionEvent.ACTION_MOVE:{
drawPanel.setPaths(new PointF(event.getX(),event.getY()));
drawPanel.invalidate();
break;
}
case MotionEvent.ACTION_UP:{
break;
}
}
return true;
}
And
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);Z
if(point != null) {
for (PointF pointF : points)
canvas.drawBitmap(shape, pointF.x, pointF.y, paint);
}
}
i know its draw again the loop. I need continue to draw the newly position only how to do that?
onDraw needs to draw the entire view. So there is no way to draw only the new touches in onDraw.
What you can do is create a memory bitmap, and draw new touches to the bitmap in onTouch. Then draw the bitmap to the screen in onDraw. Since the bitmap will never be cleared, you only have to add touches to it.
Related
I have a code in my custom view, where if I touch, a circle is drawn. When I move the circle follow along. Code as below
// override onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (circleBounds != null) {
canvas.drawArc(circleBounds, 360, 360, false, mPaint);
}
}
// when ACTION_DOWN start touch according to the x,y values
private void startTouch(float x, float y) {
mPath.moveTo(x, y);
mX = x;
mY = y;
circleBounds.set(mX - mRadius, mY - mRadius, mX + mRadius, mY + mRadius);
invalidate();
}
//override the onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(x, y);
break;
case MotionEvent.ACTION_MOVE:
startTouch(x, y);
break;
}
return true;
}
When I move slowly, the movement is smooth. But when I move fast, one could feel the flickering. I guess invalidate() happens many time, causing the flickering. Is there a way to make it smoother (reduce the flickering on the drawn circle)?
Invalidate is not your issue, the system has double buffering, and you should not see a flicker.
From your code, I can think of 2 way to improve:
in onStartTouch you call mPath.moveTo(). It is not clear why you need mPath, but this path will keep growing and growing and will need more memory
canvas.drawArc is rendered by by CPU (it creates a bitmap and draws the arc into it on every call to onDraw). It would be faster to render yourself the circle once into a bitmap, and draw that bitmap in onDraw. If you keep using the same bitmap, it will be loaded once to the GPU and drawing will be much faster
I know this question is old, but in case someone else has same problem, the solution is to use postInvalidate() instead of the invalidate() in the onTouchEvent method, which will make it much more fluid.
As we receive many events in onTouchEvent, it's more effective to put invalidate call into events queue for future processing instead of doing it immediately.
public class MyView extend FrameLayout implement OnTouchListener{
public MyView(params){
init();
}
private void init(){
ImageView imageView = new ImageView();
imageView.setImageResource(R.mipmap.logo);
addView(imageview)
imageView.setOnTouchListener(this);
}
private boolean onTouch(params){
//some logic to control
//use imageView.setLayoutParams() instead of invalidate();
}
}
I want to let the user to draw lines on the image and user can erase tline they draw. So far I can draw lines on the custom ImageView created as below. However, I tried a lot of method and all of them draw black line when I selected erase mode. I want the part after earse still is the Image I set and implement the erase just like drawing lines. How can I do this?
public class PaintView : ImageView
{
private Paint _Paint = new Paint();
private Paint ErasePaint= new Paint();
public Bitmap _Bmp;
private Canvas _Canvas = null;
private PointF _StartPt = new PointF();
private PointF _EndPt = new PointF();
public int status=1; //1 draw, 0 erase
public PaintView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
public void Initialize()
{
_Paint = new Paint ();
_Paint.Color = new Color (255, 255, 255);
_Paint.StrokeWidth = 5;
_Paint.StrokeCap = Paint.Cap.Round;
int alpha = 3;
_Bmp = Bitmap.CreateScaledBitmap(_Bmp,_Bmp.Width*alpha, _Bmp.Height*alpha, false);
_Canvas = new Canvas(_Bmp);
this.SetImageBitmap(_Bmp);
}
override public bool OnTouchEvent(MotionEvent e)
{
if (status == 1) {
switch (e.Action)
{
case MotionEventActions.Down:
_StartPt.Set(e.GetX() - 1, e.GetY() - 1);// for just a tapping
DrawLine(e);
break;
case MotionEventActions.Move:
DrawLine(e);
break;
case MotionEventActions.Up:
DrawLine(e);
break;
}
}
if (status == 0) {
switch (e.Action)
{
case MotionEventActions.Down:
_StartPt.Set(e.GetX() - 1, e.GetY() - 1);// for just a tapping
eraseCanvas(e);
break;
case MotionEventActions.Move:
eraseCanvas(e);
break;
case MotionEventActions.Up:
eraseCanvas(e);
break;
}
}
return true;
}
private void DrawLine(MotionEvent e)
{
_EndPt.Set(e.GetX(), e.GetY());
_Canvas.DrawLine(_StartPt.X, _StartPt.Y, _EndPt.X, _EndPt.Y, _Paint);
_StartPt.Set(_EndPt);
Invalidate();
}
private void eraseCanvas(MotionEvent e)
{
_EndPt.Set(e.GetX(), e.GetY());
ErasePaint.Color = Color.Transparent;
ErasePaint.StrokeWidth = 5;
ErasePaint.SetXfermode (new PorterDuffXfermode (PorterDuff.Mode.Clear));
_Canvas.DrawLine(_StartPt.X, _StartPt.Y, _EndPt.X, _EndPt.Y, ErasePaint);
_StartPt.Set(_EndPt);
Invalidate();
}
}
Well then, the way I do it in my app is to set the bitmap I draw on as a background to the canvas View on initialization - this should make the line go transparent after the drawing operation is done. I am not really familiar with Xamarin but in Android Java I just go something like:
BitmapDrawable bd = new BitmapDrawable(bmp);
canvas.setBitmapDrawable(bd); //or this.setBitmapDrawable(bd);
after instantiating the Canvas. It's because the xfer mode clear scrapes off everything showing View beneath the Canvas, so the line stays black. I believe you should adapt it to your code fairly easy.
If it works as I described (the line is black during drawing, and then poof - goes transparent), then to make it transparent right away, the way to go for me was to turn off hardware acceleration when drawing with Mode.Clear, again, Android Java:
boolean bool = "rubber".equals(tool);
if (bool) {
this.setLayerType(LAYER_TYPE_SOFTWARE, canvasPaint); //or canvas.setLayer...
}
//drawing stuff
if (bool) {
this.setLayerType(LAYER_TYPE_HARDWARE, canvasPaint);
}
Good luck, let me know how did it go or if you need some clarification or something. :)
i have a picture of a head and its on a transparent background and i want to get if the head is clicked not the area behind his head. this is what i had for the entire image not the image withing the box. pretend the grey part is the transparent part. i want nothing to happen when that area is clicked. only his body.
link to picture->link to picture
stage.addListener(new ClickListener()
{
#Override
public boolean touchDown(InputEvent event, float x, float y, int a , int b)
{
if(box.equals(stage.hit(x,y,false)))
{
box.setPosition(100, 100);
point.play();
score++;
}
return true;
}
});
So, I have a map, that I want to be able to draw rectangles on to highlight an area. When the mouse is released a permanent rectangle is drawn on the map that persists until the mouse is dragged again to start the creation of a new rectangle. While the mouse is being dragged the rectangle outline should be created as it goes along.
The persisting rectangle is removed when the mouse is reclicked which given the application would mean that a new drag event would be started.
What happens is that the first rectangle is painted correctly and everything is fine, but subsequent rectangles that are currently being dragged have the corner cut off (image link at bottom).
If I click and then wait for the image to repaint before I start dragging then this problem does not exist, as well as if I sleep the thread before beginning to draw the rectangle in onMouseDragged so that it has time to repaint.
I'd like a more elegant solution than those to allow the screen to repaint before the rectangle created in onMouseDragged is shown on the screen. So what is the best way to get the repaint to complete without zapping part of the drawn rectangle?
Note that despite the look of the outline, the persisting rectangle that gets drawn is correct.
This is what the rectangle looks like
public void onMousePressed(MapMouseEvent ev)
{
startPos = new Point(ev.getPoint());
drawer.removeDrawings(pane.getMapContent());
pane.repaint();
pane.setIgnoreRepaint(true);
}
public void onMouseDragged(MapMouseEvent ev)
{
super.onMouseDragged(ev);
if (enabled) {
ensureGraphics();
if (dragged)
{
// because we're in XOR mode, this has the effect of erases the previous rectangle
graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
}
else
{
}
rect.setFrameFromDiagonal(startPos, ev.getPoint());
graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
dragged = true;
}
}
public void onMouseReleased(MapMouseEvent ev)
{
super.onMouseReleased(ev);
if (dragged) {
ensureGraphics();
dragged = false;
drawer drawable = new drawer();
drawable.drawRectangle((Graphics2D) parentComponent.getGraphics().create(), rect.x, rect.y, rect.width, rect.height, pane.getMapContent());
graphics.dispose();
graphics = null;
pane.setIgnoreRepaint(false);
pane.repaint();
}
}
You need to maintain a List<Rectangle> and add the current Rectangle to the list on mouseReleased(). GraphPanel illustrates the basic mouse handling. In your case, simply render the image, any existing rectangles and the current rectangle.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// render background
g.drawImage(image, 0, 0, this);
// render existing rectangles
for (Rectangle r : list) {
r.draw(g);
}
// render the current dragged rectangle
if (selecting) {
g.setColor(Color.darkGray);
g.drawRect(mouseRect.x, mouseRect.y,
mouseRect.width, mouseRect.height);
}
}
In the example, selecting is a boolean value that controls whether a new rectangle is beng drawn or an existing selection is being dragged to a new location.
As all rendering must occur on the EDT, I doubt that you need (or want) a new thread.
My problem is when I'm using scroller and draw canvas on it. It will scroll smoothly but when I draw bitmap + canvas the scroller scroll very slow means it is heavy to scroll thats why it scroll very slow fashion.
How to solve this problem?
I have no experience with OpenGL nor accelerometer, but swipe (called fling in Android's API) is not hard to achieve. First thing you need when making such a custom View, is implementing a GestureDetector and call its onTouchEvent() in your view's onTouchEvent()
GestureDetector mGD = new GestureDetector(getContext(),
new SimpleOnGestureListener() {
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// beware, it can scroll to infinity
scrollBy((int)distanceX, (int)distanceY);
return true;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float vX, float vY) {
mScroller.fling(getScrollX(), getScrollY(),
-(int)vX, -(int)vY, 0, (int)mMaxScrollX, 0, (int)mMaxScrollY);
invalidate(); // don't remember if it's needed
return true;
}
#Override
public boolean onDown(MotionEvent e) {
if(!mScroller.isFinished() ) { // is flinging
mScroller.forceFinished(true); // to stop flinging on touch
}
return true; // else won't work
}
});
#Override
public boolean onTouchEvent(MotionEvent event) {
return mGD.onTouchEvent(event);
}
While OnGestureListener.onScroll() calls directly View.scrollBy(), for the onFling() method you'll need a Scroller.
Scroller is a simple object that, as reference says, encapsulates scrolling. It can be used for continuous scrolling or to react to flings. Scroller.fling() begin a "simulation" of fling scroll inside itself, and by watching it you can copy its smoothness with a continuous redrawing animation:
#Override
protected void onDraw(Canvas canvas) {
// ....your drawings....
// scrollTo invalidates, so until animation won't finish it will be called
// (used after a Scroller.fling() )
if(mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
}
}
that is, until animation is running, calculate the point we reached and scroll there.
As a last note: remember to return true in your OnGestureListener.onDown(), even if you don't want to do anything on down, or it won't work.
And be careful, because Scroller in Android 2.2 has a bug for which the fling animation will not actually end even if it reaches the limits you passed as arguments (yet computed offset respects them, so it won't actually move).