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();
}
}
Related
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.
So I am using onDraw in a custom View class to draw shapes on a RelativeLayout + TableLayout all that works fine, and I have another class that I use to draw lines from Point A to Point B etc. example below:
My Goal:
If I drag my finger from Point A (objectA) to Point B(objectB), how can I delete those 2 View Objects from the canvas? I added a method:
objectA.delete();
objectB.delete();
that should delete both A and B when I drag my finger through MotionEvent, but it only deletes one and not the other, so i am thinking it's not retroactive?
Codes below:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
/// get the child that corresponds to the first touch
DotView objectA = getChildForTouch((TableLayout) v, x, y);
return true;
case MotionEvent.ACTION_MOVE:
///used the x - y to get the object for every other shape the user`S finger passes over.
DotView objectB = getChildForTouch((TableLayout) v, x, y);
/// just update positions
line.setCoords(mStartX, mStartY, (int) x, (int) y);
objectA.delete(); ///Delete first shape
objectB.delete(); ///Delete second shape
break;
case MotionEvent.ACTION_UP:
///Gets the last shape where the user released their fingers
endView = getChildForTouch((TableLayout) v, x, y);
break;
Delete method inside the: DotView extends View class:
private static class DotView extends View {
private static final int DEFAULT_SIZE = 100;
private Paint mPaint = new Paint();
private Rect mBorderRect = new Rect();
private Paint mCirclePaint = new Paint();
private int mRadius = DEFAULT_SIZE / 4;
public DotView(Context context) {
super(context);
mPaint.setStrokeWidth(2.0f);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.RED);
mCirclePaint.setColor(Color.CYAN);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.parseColor("#0099cc"));
mBorderRect.left = 0;
mBorderRect.top = 0;
mBorderRect.right = getMeasuredWidth();
mBorderRect.bottom = getMeasuredHeight();
canvas.drawRect(mBorderRect, mPaint);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,
mRadius, mCirclePaint);
}
public void delete(){
mPaint.setColor(Color.TRANSPARENT);
}
}
Just something simple to fake delete the circles
Thanks in advance guys.. more codes can be provided if need be.
Edit: If I can accomplish this in any other way please feel free to share. (Draw circles on a Grid and delete the ones I used my finger to draw my line over)
First of all try changing your delete() method to this:
public void delete(){
mPaint.setColor(Color.TRANSPARENT);
invalidate();
}
You need to let your View know that you want it to redraw itself (that's why the invalidate() call).
So, I have created a texture, and then a sprite.
On my render() method, I am check for user input. If the user has touched/clicked, then I want my sprite to rotate 90 degrees ONCE.
Right now the rotation works. However, it rotates multiple times per click!
How can I make it rotate only once per touch? I have a feeling that I might have to use delta time, and that occurs because the render method is being called frequently, but I don't know how to fix it... Thanks!
public class MyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture img;
Sprite sprite;
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
sprite = new Sprite(img);
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
sprite.draw(batch);
batch.end();
if (Gdx.input.isTouched()) {
rotateRight();
}
}
private void rotateRight() {
sprite.setRotation(sprite.getRotation() - 90);
}
}
Right now you are polling input inside of your render method. Polling simply checks the status of the input (is it touched or not) and does not care for any actual "event" occurred.
Instead of this you need to look for input events via event handling as this will give you access to the actual event of the screen being touched or untouched. You can do this by implementing InputProcessor which will give you access to override a bunch of touch event methods so you can execute your rotate method only on the event of the touch.
Example:
public void update()
{
Gdx.input.setInputProcessor(new InputProcessor() {
#Override
public boolean TouchDown(int x, int y, int pointer, int button) {
if (button == Input.Buttons.LEFT) {
rotateRight();
return true;
}
return false
}
});
}
Don't forget to call your update() method from your render method. I put it in a separate function just for readability and not to clog up your rendering code.
You can also have your class implement InputProcessor and override the methods in your class so you do not have to do it inline as I did above.
if (Gdx.input.justTouched() && Gdx.input.isTouched()) {
rotateRight();
}
when i touch finger on sprite, score increses randomly not by fix rate i mean that i set score
to increase 50 each time but when i scroll on sprite sometimes it increases by 100 sometimes by 150 and 200;
mHardware1[active] = new Sprite(pX, pY, samsung,
this.getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
float X, float Y) {
if (pSceneTouchEvent.isActionMove()) {
scene.detachChild(mHardware1[active]);
score+50;
}
it works when i use isActionDown in place of isActionMove but m working a game like fruit ninja in which i need to scroll finger on screen that why i cant use isActionDown
You can use a SurfaceScrollDetector, which detects when the user is sliding his finger across the screen. It has the following events associated with it:
onScrollStarted
onScroll
onScrollFinished
I guess you could increase by 50 when you reach the onScrollFinished.
I am currently using the SurfaceScrollDetector for a project, but I haven't used it in a way you are asking about, so I can't say forsure if it will work as expected.
Here is one of the examples that uses it (in addition to the PinchZoomDetector):
https://github.com/nicolasgramlich/AndEngineExamples/blob/GLES2/src/org/andengine/examples/PinchZoomExample.java
Scene updates happen on a different thread to UI events, so possibly multiple queued touchscreen events are getting through before the event handler realises that the sprite has been detached.
Set a local boolean variable to prevent this, e.g.:
boolean touchProcessed = false;
mHardware1[active] = new Sprite(pX, pY, samsung,
this.getVertexBufferObjectManager());
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
float X, float Y) {
if (!touchProcessed & pSceneTouchEvent.isActionMove()) {
touchProcessed = true;
scene.detachChild(mHardware1[active]);
score += 50;
}
}
Note that you can use: mHardware1[active].detachSelf() instead of scene.detachChild(...).
Also note that you should be detaching in the sprite in the update thread, thus:
boolean touchProcessed = false;
mHardware1[active] = new Sprite(pX, pY, samsung,
this.getVertexBufferObjectManager());
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
float X, float Y) {
if (!touchProcessed & pSceneTouchEvent.isActionMove()) {
touchProcessed = true;
score += 50;
engine.runOnUpdateThread(new Runnable() {
#Override
public void run() {
mHardware1[active].detachSelf();
}
});
}
}
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).