Android invalidate doesn't show intermediate animation - android

This is my code.
On swipe to the top is performed:
for(int it = 0;it<4;it++){
myBoard.step_up();
invalidate();
}
int ad = myBoard.merge_up();
invalidate();
for(int it = 0;it<4;it++){
myBoard.step_up();
invalidate();
}
step_up and merge_up methods change the values of myBoard. The onDraw method draws the screen according to the myBoard values. But i do not see intermediate results of movements! Just start postition and then end position, the same as in the following code:
for(int it = 0;it<4;it++){
myBoard.step_up();
}
int ad = myBoard.merge_up();
for(int it = 0;it<4;it++){
myBoard.step_up();
}
invalidate();
How to deal this problem? It is very important for me
Update:
The code from onCreate:
GraphicsView myview=new GraphicsView(this);
setContentView(myview);
GraphicsView entire code is too big. The part of it:
public GraphicsView(Context context) { super(context);
this.setOnTouchListener(new OnSwipeTouchListener(getApplicationContext()) {
public void onSwipeTop() {
//above code
}
//other methods
}
protected void onDraw(Canvas canvas)
{
//draw
}
}

Related

Android pause an observer when view is removed

I have two custom views (canvas views) and both of them have an observer which triggers an event on the screen and draw something on canvas. I am reusing the container and rendering the canvas in the same parent layout by removing other views. Right now, no matter what my both view observer values.
I have tried an dirty hack where I am passing a boolan to both views as true being one and other being false on switch which kinda work but I don't want that. Because it is just a hack. Can anyone tell me an android way to do it?
public class FakeViewOne extends View {
private EventViewModel eventViewModel;
private float l = 0.0f;
public FakeViewOne(Context context, View ParentView) {
super(context);
eventViewModel = new ViewModelProvider((ViewModelStoreOwner) getContext()).get(EventViewModel.class);
eventViewModel.getTrigger().observe((LifecycleOwner) getContext(), new Observer<Float>() {
#Override
public void onChanged(Float val) {
l = val;
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/*
* if l > 50 change background of a linearlayout
* draw a line on a canvas
* */
invalidate();
}
}
public class FakeViewTwo extends View {
private EventViewModel eventViewModel;
private float l = 0.0f;
public FakeViewTwo(Context context, View ParentView) {
super(context);
eventViewModel = new ViewModelProvider((ViewModelStoreOwner) getContext()).get(EventViewModel.class);
eventViewModel.getTrigger().observe((LifecycleOwner) getContext(), new Observer<Float>() {
#Override
public void onChanged(Float val) {
l = val;
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/*
* if l > 50 change background of a tile
*
* */
invalidate();
}
}
CODE Inside a Fragment where I am switch between those two FakeViews. How can I make those not observe values when they are not-rendered/inactive/removed from the view. I am using mycanvas.removeAllViews();.
final RelativeLayout mycanvas = view.findViewById(R.id.myCanvas);
gestureViewModel.getGestureType().observe(getActivity(), new Observer<String>() {
#Override
public void onChanged(#Nullable String s) {
assert s != null;
mycanvas.removeAllViews();
switch (s){
case "shake":
ShakeControl(view,myView,mycanvas);
break;
case "move":
MoveControl(view,myView,mycanvas);
break;
}
}
});
I have solved the problem with removing the observers on window detach in each FakeView.
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.d("Detached","Head");
if (eventViewModel != null && eventViewModel.getTrigger().hasObservers()) {
eventViewModel.getTrigger().removeObservers((LifecycleOwner) ctx);
}
}

Call a method from activity that extends View

I'm doing a genre of a bull's eye, but instead of use circles I'm using squares.
But the problem is:
Everything is done, the algorithm to generate the color the others squares is done.
But I implemented a button, and I made it to refresh the bull's eye.
The problem is that I can't make, need help.
This is the MainActivity, it's from here that I will detect the button click.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FrameLayout frame = (FrameLayout) findViewById(R.id.frame);
Draw draw = new Draw(this);
frame.addView(draw);
Button refresh = (Button) findViewById(R.id.refresh);
refresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
draw.onDraw(canvas);
frame.addView(draw);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
And this is the Draw activity, this is the one that I used to render the image.
public class Draw extends View {
public Draw(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
Paint prop = new Paint();
Random color = new Random();
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int oriwidth = 0;
int oriheight = 0;
for (int x = 0; x < 20; x++) {
int red = color.nextInt();
int green = color.nextInt();
int blue = color.nextInt();
prop.setARGB(0, red, green, blue);
canvas.drawRect(oriwidth += 10, oriheight += 10, width -= 10,
height -= 10, prop);
}
}
}
Can someone help me? Sorry for the english.
Is there any special reason why you're adding the Draw view programmatically during onCreate?
Try defining the Draw view in the layout xml itself. That should solve any issues with defining the width / height of the view (make sure to define width & height... try hard coded dimensions at first like 100dp by 100dp)
Once you've done that, make sure to capture your "draw" view as a member of the activity:
public class MainActivity extends Activity
{
private Draw mDraw;
Then, in your onCreate:
mDraw = (Draw)findViewById(R.id.myDrawId);
Then, in your button click listener, just call invalidate:
refresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDraw.invalidate();
}
});
Try to change with this portion of code:
public void onClick(View v) {
MainActivity.this.draw.onDraw(canvas);
MainActivity.this.frame.addView(draw);
}
You shouldn't call onDraw() directly. Try draw.invalidate().

Bumping the mole changing the picture but the mole not coming back

I made a game that you should bump the mole. I am using postDelayed to make the mole appear and disappear every few seconds.
The problem is that when I touch the the mole, it turns to the squeezed mole picture and the mole disappears (as it should), but when the mole comes back to the screen, it appears with the wrong picture (the squeezed one).
STAGE View Class
private Runnable everyThreeSeconds = new Runnable() {
public void run() {
moleView.getMole().moveMole();
isPop = !isPop;
postDelayed(everyThreeSeconds, (3000)) ;
}
};
public AllViews(Context context) {
super(context);
test = new Paint();
first = new First_Stage();
isPop=false;
post(everyThreeSeconds);
}
public void setViews(StageView mainView, MoleView moleView,
PointsView pointsView, TimerView timerView,ClubView clubView)
{
this.mainView = mainView;
this.moleView = moleView;
this.pointsView = pointsView;
this.timerView = timerView;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mainView.onDraw(canvas);
pointsView.onDraw(canvas);
timerView.onDraw(canvas);
clubPic=BitmapFactory.decodeResource(getResources(), R.drawable.clubdown);
canvas.drawBitmap(clubPic, this.x-39,this.y-20, null);
if (isPop){
moleView.onDraw(canvas);
}
invalidate();
}
Mole View Class
public MoleView(Context context, Mole mole) {
super(context);
this.mole=mole;
}
#Override
protected void onDraw(Canvas canvas) {
if (!bool){
molePic=BitmapFactory.decodeResource(getResources(), R.drawable.nest_full_mole);
canvas.drawBitmap(molePic, mole.getX(), mole.getY(), null);
}else {
molePic=BitmapFactory.decodeResource(getResources(), R.drawable.pow);
canvas.drawBitmap(molePic, mole.getX(), mole.getY(), null);
}
}
just added to the if condition an else that return the true back to false and it work
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mainView.onDraw(canvas);
pointsView.onDraw(canvas);
timerView.onDraw(canvas);
clubPic=BitmapFactory.decodeResource(getResources(), R.drawable.clubdown);
canvas.drawBitmap(clubPic, this.x-39,this.y-20, null);
if (isPop){
moleView.onDraw(canvas);
}else{
moleView.setBool(false);
}
invalidate();

why is invalidate not working?

I am trying to draw a rectangle where I am initially providing it values from Activity class and then trying to change its coordinates from a different class.. As far as I am concerned the logic is right, but the invalidate is not working.. I did substantial research but still cannot make it work..
RectangleActivity.java
public class RectangleActivity extends Activity {
private Point dStart = null;
private Point dEnd = null;
final private Handler handler = new Handler();
Check check = new Check(this);
Draw draw ;
Runnable mUpdate = new Runnable()
{
public void run()
{
Toast.makeText(getApplicationContext(), "update Called", Toast.LENGTH_LONG).show();
check.update();
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dStart = new Point(0,0);
dEnd = new Point(60,60);
draw = new Draw(this,dStart,dEnd);
setContentView(draw);
handler.postDelayed(mUpdate, 4000);
}
}
Check.java
public class Check
{
private Point start = new Point(80,80);
private Point end = new Point(150,150);
private Context context;
public Check(Context context)
{
this.context =context;
}
void update()
{
Log.d("Aditi", "Update Called");
Draw draw = new Draw(context);
draw.reDraw(start,end);
}
}
Draw.java
public class Draw extends View
{
private Paint paint = null;
private Point start = null;
private Point end = null;
public Draw(Context context)
{
super(context);
paint();
}
public Draw(Context context, Point start, Point end)
{
super(context);
this.start= start;
this.end = end;
paint();
}
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawRect(start.x,start.y,end.x,end.y,paint);
Log.d("Aditi", "OnDraw Called");
System.out.println("StartX in ondraw is: "+start.x);
System.out.println("StartY in ondraw is: "+start.y);
System.out.println("EndX in ondraw is: "+end.x);
System.out.println("EndY in ondraw is: "+end.y);
}
public void reDraw(Point Start, Point End)
{
Log.d("Aditi", "Redraw Called");
this.start = Start;
this.end = End;
invalidate();
Log.d("Aditi", "Invalidate Called");
System.out.println("StartX in redraw is: "+start.x);
System.out.println("StartY in redraw is: "+start.y);
System.out.println("EndX in redraw is: "+end.x);
System.out.println("EndY in redraw is: "+end.y);
}
public void paint()
{
paint= new Paint();
paint.setColor(Color.YELLOW);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
}
}
please feel free to point me out what am I doing wrong.. Thanks in advance..
I afraid if it do not work for you. Please try call the invalidate method onDraw()
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
invalidate();
}
In above problem, in onCreate() you took a view using draw = new Draw(this,dStart,dEnd);
and then used in setContentView(draw), but when update() called you again created an object of Draw by using Draw draw = new Draw(this,dStart,dEnd); and call draw.reDraw(start, end) on second draw object, so the instance used in setContentView() is not invalidated, And second draw view object get invalidated but it did not passed in setContentView(), so it is not visible on ui: your solution is just
void update() {
// Draw draw = new Draw(context); //remove this
draw.reDraw(start, end);
}
if you invalidate a view from a thread other than the app's main thread it will not work , you must call postInvalidate() instead of invalidate().

onClick hierarchy

I'm creating some little sprites with a bmp inside the constructor of another view as background. But when I click the sprites, that have their onClickListener, the view I'm clicking is the background view.
Some code:
My constructor:
public ToposGameView(Context context) {
super(context);
moles = new ArrayList<MoleSprite>();
setFocusable(true);
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.i(tag, "Click en GameView");
}
});
gameLoopThread = new GameLoopThread(this);
int id = 0;
for(int x = 0; x<3; x++){
for(int y = 0; y<4 ; y++){
MoleSprite mole = new MoleSprite(this, x*WIDTH/3, HEIGHT/6+y*HEIGHT/6, FRONT);
mole.setId(id);
id++;
moles.add(mole);
}
}
holder = getHolder();
holder.addCallback(new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
boolean retry=true;
gameLoopThread.setRunning(false);
while(retry){
try{
gameLoopThread.join();
retry=false;
}catch(InterruptedException i){
}
}
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}
});
}
Sprite's constructor:
public MoleSprite(ToposGameView gameView, int x, int y, int direction) {
super(gameView.getContext()); //TODO
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.bad1);
this.width = bmp.getWidth() / BMP_COLUMNS;
this.height = bmp.getHeight() / BMP_ROWS;
this.x=x;
this.y= y;
this.setClickable(true);
this.setFocusable(true);
this.setFocusableInTouchMode(true);
setOnClickListener(this);
setOnTouchListener(this);
//I don't know what else to do!
}
It's my first question, please ask me for more info.
Edit1:
public class MoleSprite extends View implements OnClickListener, OnTouchListener{
public class ToposGameView extends SurfaceView{
Your mixing Surface drawing with View drawing. Surfaces are great for rapid asynchronous redraws (such as games), but they are not View containers. That is, there is no addView method on SurfaceView that is required for events and layouts.
As such, you will need to implement onClick handling on your SurfaceView and manually implement the detection of which Sprite is being clicked (e.g., is the click event within the sprite's x/y/width/height bounding box).
Also, it doesn't make sense for MoleSprite to extend View if used this way. I expect you'll want to make your own Sprite base class to support the bounding boxes and drawing callbacks. You can also add the onClick(..) interface to the Spirte base class, but you'll probably also want to keep separate lists for drawable/moveable/clickable items to optimize your list iterations as you develop your game.
Good luck!
i don't know what your GameLoopThread is doing, but it should be calling your surface's onDraw method, and updatePhysics method.
the update physics will loop through your sprites and give them information, like the current game time, so they can move the proper distance, change the animation frame, or whatever.
for a sprite base class I use something like this (but mine handles animations, accelerations and other stuff):
public class Sprite {
private Rect mScreenRect;
public Sprite() {
// init some stuff here, if you need it
// especially, somewhere in your init functions
// set your bounding rect
this.mScreenRect = Rect(xl, yt, xr, yb);
}
setStuff(int thing) {
// like setX(), setY()
}
// here is the good stuff
public boolean isTouched(int x, int y) {
if(mScreenRect.contains(x, y))
return true;
else
return false;
}
// also include a function to draw the graphic
public void drawGraphic(Canvas canvas) {
canvas.drawBitmap(mBitmap, mScreenRect.left, mScreenRect.top, null);
}
}
then, in your surface view you need to use the:
#Override
public boolean onTouchEvent(MotionEvent event) {
// this setup will move the sprite on the screen
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(sprite.isTouched(event.getX(), event.getY()) {
// do your stuff
}
break;
case MotionEvent.ACTION_MOVE:
// handle the moving pointer
sprite.setCoords(event.getX(), event.getY());
break;
case MotionEvent.ACTION_UP:
// handle the exiting pointer
sprite.setCoords(event.getX(), event.getY());
break;
}
}
then in the surface view again:
#Override
public void onDraw(Canvas canvas) {
sprite.drawGraphic(canvas)
}
there are other posts that cover this in a more detailed manner, but this will get you started.

Categories

Resources