I am trying to animate a set of images using AnimationDrawable. The user should have the ability to pause/resume the animation on a click of a button. I am doing so, using setVisible(boolean visible, boolean restart). Not completely sure if I understand the documentation correctly, but every time pause is pressed, the animation starts from the beginning.
Is there any way to resume the animation from the same frame?
From AnimationDrawable API:
...If restart is false, the drawable will resume from the most recent frame
Just to be clear:
activityAnimation.setVisible(false, false); Should stop the animation.
activityAnimation.setVisible(true, false); Should resume the animation from the same frame (it doesn't).
Here is my code:
The animation start() is called in onWindowFocusChanged.
The animation and the buttons are created in onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content);
ImageView activityImage = (ImageView) findViewById(R.id.activity_image);
activityImage.setBackgroundResource(R.drawable.anim);
activityAnimation = (AnimationDrawable) activityImage.getBackground();
....
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ImageButton b = (ImageButton) v;
if ((Integer)b.getTag() == R.drawable.pause) {
b.setImageResource(R.drawable.play);
b.setTag(R.drawable.play);
activityAnimation.setVisible(false, false);
} else {
b.setImageResource(R.drawable.pause);
b.setTag(R.drawable.pause);
activityAnimation.setVisible(true, false);
}
}
});...
}
This is the content of the XML file
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="#drawable/one" android:duration="1000" />
<item android:drawable="#drawable/two" android:duration="1000" />
<item android:drawable="#drawable/three" android:duration="1000" />
<item android:drawable="#drawable/four" android:duration="1000" />
</animation-list>
I have noticed several of these old posts, but non had the answer.
How to reset AnimationDrawable (The other side of this issue)
How do I pause frame animation using AnimationDrawable? [Closed]
To pause and resume AnimationDrawable, simply customize it and handle with only one variable.
class CustomAnimationDrawable1() : AnimationDrawable() {
private var finished = false
private var animationFinishListener: AnimationFinishListener? = null
private var isPause = false
private var currentFrame = 0
fun setFinishListener(animationFinishListener: AnimationFinishListener?) {
this.animationFinishListener = animationFinishListener
}
interface AnimationFinishListener {
fun onAnimationFinished()
}
override fun selectDrawable(index: Int): Boolean {
val ret = if (isPause) {
super.selectDrawable(currentFrame)
} else {
super.selectDrawable(index)
}
if (!isPause) {
currentFrame = index
if (index == numberOfFrames - 1) {
if (!finished && ret && !isOneShot) {
finished = true
if (animationFinishListener != null) animationFinishListener!!.onAnimationFinished()
}
}
}
return ret
}
fun pause() {
isPause = true
}
fun resume() {
isPause = false
}
}
Since I needed a timer on the screen as well, the answer in my case was simply to create a runnable replace the ImageView background every second.
Here is the general idea:
Create the runnable, ImageView and few helpers:
ImageView exImage = null;
long startTime = 0;
long pauseTime = 0;
long pauseStartTime = 0;
List<Integer> animList = new ArrayList<>();
int animIt = 0;
Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {
#Override
public void run() {
long millis = System.currentTimeMillis() - startTime - pauseTime;
double dSecs = (double) (millis / 100);
int pace = 10;
if (dSecs % pace == 0.0 && !animList.isEmpty()) {
animIt = (animIt == animList.size() - 1) ? 0 : animIt + 1;
exImage.setBackgroundResource(animList.get(animIt));
}
timerHandler.postDelayed(this, 100);
}
};
In OnCreate bind the pause and play buttons, fire the timer and set the first ImageView
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_exercise);
// Set the image view
exImage = (ImageView) findViewById(R.id.image_zero);
exImage.setBackgroundResource(R.drawable.fe_0);
// Start the timer
timerHandler.postDelayed(timerRunnable, 0);
// Bind the buttons
ImageButton pp = (ImageButton) findViewById(R.id.button_play_pause_toggle);
pp.setImageResource(R.drawable.pause);
pp.setTag(R.drawable.pause);
pp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ImageButton pp = (ImageButton) v;
//Pause
if ((Integer) pp.getTag() == R.drawable.pause) {
pauseStartTime = System.currentTimeMillis();
timerHandler.removeCallbacks(timerRunnable);
} else { // Resume
pauseTime += System.currentTimeMillis() - pauseStartTime;
timerHandler.postDelayed(timerRunnable, 0);
}
}
});
}
Related
I have a collection of Views able to move down the screen and subsequently reverse but now as I try to move a CardView it will go down but not back up.
When the User clicks a button, it prompts the collection of Views to drop down/fade away and when the User clicks the button again they return. In the space created by the collection of Views vanishing, a CardView drops down. I have the CardView to drop down but when the User clicks the button, the collection of views return but not the CardView - instead it just vanishes. Cheers!
MainActivity (NB: in OnCreate())
...
final View blankCard = findViewById(R.id.child_card_template);
blankCard.setVisibility(blankCard.INVISIBLE);
...
ImageButton btn = (ImageButton) findViewById(R.id.imageLeft);
btn.setOnClickListener(new View.OnClickListener() {
int click_Animation_Type = 0;
View viewTest = findViewById(R.id.child_card_template);
View viewCard = viewTest;
#Override
public void onClick(View view) {
switch(click_Animation_Type) {
case 0:
moveSecondRow();
moveCardViewLayout();
click_Animation_Type = 1;
break;
case 1:
rewindSecondRow();
rewindCardViewLayout();
click_Animation_Type = 0;
break;
}
}
public void moveCardViewLayout() {
View viewTest = findViewById(R.id.child_card_template);
ObjectAnimator viewTestMovi = ObjectAnimator.ofFloat(viewTest, "translationY", 0f, 258f);
if(viewTest.getVisibility()==View.INVISIBLE) {
viewTestMovi.setDuration(250);
viewTestMovi.start();
viewTest.setVisibility(viewTest.VISIBLE);
}
}
public void rewindCardViewLayout() {
View viewTest = findViewById(R.id.child_card_template);
ObjectAnimator viewTestMoviReverse = ObjectAnimator.ofFloat(viewTest, "translationY", 258f, 0f);
if (viewTest.getVisibility() == View.VISIBLE) {
viewTestMoviReverse.setDuration(250);
viewTestMoviReverse.start();
viewTest.setVisibility(viewCard.INVISIBLE);
//THERE'S NO ANIMATION, JUST TURNING INVISIBLE.
//Registers View is visible.
}
}
public void moveSecondRow() {
for (int i = 0; i < list.size(); i++) {
ObjectAnimator bottomRow = ObjectAnimator.ofFloat(list.get(i), "translationY", 0f, 300f);
bottomRow.setDuration(250);//set duration
Animation fadeOutAnimation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_out_anim);
bottomRow.start();//start animation
list.get(i).startAnimation(fadeOutAnimation);
list.get(i).setVisibility(View.GONE);
}
}
public void rewindSecondRow() {
for (int i = 0; i < list.size(); i++) {
ObjectAnimator bottomRow = ObjectAnimator.ofFloat(list.get(i), "translationY", 300f, 0f);
bottomRow.setDuration(300);//set duration
Animation fadeInAnimation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_in_anim);
bottomRow.start();//start animation
list.get(i).setVisibility(View.VISIBLE);
list.get(i).startAnimation(fadeInAnimation);
}
}
});
every time click_Animation_Type starts with 0. Put click_Animation_Type = 0; layer below the setContentView.
I am practising on a simple Android Game where a round button is randomly placed on the screen when the user taps on it..
it works fine but i want to speedify the process of placing the button so that the game gets harder for user...
here is the Code I'm using -
public class GameWindow extends Activity implements View.OnClickListener {
static int score;
private Timer t;
private int TimeCounter = 29;
private boolean canMove = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
////Remove title screen for activty.
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_game_window);
moveButton();
endonTimeOver();
}
public void endonTimeOver(){
////Activity timer for 60 seconds.
final TextView timer = (TextView) findViewById(R.id.seconds);
t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
////Set string to timer.
#Override
public void run() {
// TODO Auto-generated method stub
runOnUiThread(new Runnable() {
public void run() {
timer.setText(String.valueOf(TimeCounter)); // you can set it to a textView to show it to the user to see the time passing while he is writing.
TimeCounter = TimeCounter - 1;
}
});
}
}, 1000, 1000); // 1000 means start from 1 sec, and the second 1000 is do the loop each 1 sec.
new Timer().schedule(new TimerTask(){
public void run() {
GameWindow.this.runOnUiThread(new Runnable() {
public void run() {
startActivity(new Intent(GameWindow.this, Finished.class));
}
});
}
}, 30000);
}
////Move button.
private void moveButton()
{
if(!canMove){ return; }
runOnUiThread(
new Runnable()
{
#Override
public void run()
{
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Button button = (Button)findViewById(R.id.button);
Random r = new Random();
int startX = width/2;
int startY = height/2;
if(score==0){
button.setX(startX);
button.setY(startY);
}
else {
int x = r.nextInt(width - 210);
int y = r.nextInt(height - 200);
button.setX(x);
button.setY(y);
}
}
}
);
}
////Display score
public void displayScore(int score) {
TextView scoreView = (TextView) findViewById(R.id.score);
scoreView.setText(String.valueOf(score));
}
#Override
public void onClick(View v) {
MediaPlayer mp = MediaPlayer.create(this, R.raw.buttonsound);
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
mp.release();
}
});
mp.start();
score = score + 1;
displayScore(score);
switch (v.getId()) {
case (R.id.button): {
moveButton();
}
}
}
public static int getScore(){
return score;
}}
Use global variables for values that don't change:
findViewById is slow
Creating new Random every time is not necessary
getting the window parameter every time is not necessary either
You seem to be starting a new thread and telling it to runonui , this might be slowing you down , try this :
private void moveButton()
{
if(!canMove){ return; }
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Button button = (Button)findViewById(R.id.button);
Random r = new Random();
int startX = width/2;
int startY = height/2;
if(score==0){
button.setX(startX);
button.setY(startY);
}
else {
int x = r.nextInt(width - 210);
int y = r.nextInt(height - 200);
button.setX(x);
button.setY(y);
}
}
the computation itself doesn't seem to heavy so no need for another thread , you can do it on the main thread , if you're calling from oncreate that means you're already on main thread , this might give you the answer if i understood the question , try it
I'm developing a little app, the idea is that the user clicks a buton in 15 seconds, there's textview which counts how many clicks he does. Now I want to add a restart button, but I want to show it after 15 seconds. Do you guys have any idea how to do that? Here's my code:
final TextView textic = (TextView) findViewById(R.id.textView2);
Typeface fac=Typeface.createFromAsset(getAssets(),"fonts/fipps.otf");
textic.setTypeface(fac);
final int oldscore = getSharedPreferences("myPrefs", MODE_PRIVATE).getInt("highscore", 0);
count = new CountDownTimer(15000, 1000) { // MOVED UP
public void onTick(long millisUntilFinished) {
int seconds = (int) ((millisUntilFinished / 1000));
textic.setText("Time Left: " + millisUntilFinished / 1000);
}
public void onFinish() {
String message;
textic.setText("Time's Up!");
buttonCount.setEnabled(false);
if (clicks > oldscore) {
getSharedPreferences("myPrefs", MODE_PRIVATE).edit().putInt("highscore", clicks).commit();
}
}
};
final TextView textView = (TextView) findViewById(R.id.clicks);
Typeface face=Typeface.createFromAsset(getAssets(),"fonts/fipps.otf");
textView.setTypeface(face);
buttonCount = (ImageButton) findViewById(R.id.button);
buttonCount.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
clicks++;
textView.setText("" + clicks);
TextView textVie = (TextView) findViewById(R.id.topScoreView);
Typeface fa=Typeface.createFromAsset(getAssets(),"fonts/fipps.otf");
textVie.setTypeface(fa);
textVie.setText("Best: " + oldscore);
if(!started){
count.start(); // START COUNTDOWN TIMER
started = true;
timerProcessing = true;
}
}
});
}
}
Could someone please help me, what should I do?
just add the button in your layout like your textview and set it unvisible.Set it visible when time's out(in onfinish()).
This is my code. I used this to hide a layout after 5 seconds,, use this. Hope this will help u
private void HideLayout()
{
swiper=(RelativeLayout)findViewById(R.id.llSwiper);
header=(LinearLayout)findViewById(R.id.llHeader);
swiper.postDelayed(new Runnable()
{
public void run()
{
if(!swiper.isPressed())
{
swiper.setVisibility(View.GONE);
}
else
{
HideLayout();
}
}
}, 5000);
}
I'm learning android development, and what I'm trying to do is to have a label that counts down from 40 minutes, and when it reaches 0 it would stop counting and do something else. This is my code:
#Override
protected void onStart() {
super.onStart();
count = 2400;
final Timer t = new Timer();//Create the object
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
minLeft = (int) Math.floor(count / 60);
secLeft = count - minLeft * 60;
counter = minLeft + ":" + secLeft;
TextView tv = (TextView) findViewById(R.id.timer);
Log.i(MainActivity.TAG,minLeft+", "+secLeft+", "+counter);
tv.setText(counter);
count--;
if (minLeft <= 0 && secLeft <= 0) {
t.cancel();
count = 2400;
onFinish();
}
}
}, 1000, 1000);
}
But, when I go to that activity by clicking a button in the main activity, the label has the text "Timer" (its original text), and after a few seconds the app crashes with CalledFromWrongThreadException, but the line that causes the problem seems to be the one where I set the text of the TextView.
Please help, thanks in advance.
Your scheduled task runs on the background thread.
And you try to set the text to the textview from this background thread.
However in Android all view related operations have to be done on the main thread.
That is way in your scheduled task you have to use something like:
#Override
protected void onStart() {
super.onStart();
count = 2400;
final Timer t = new Timer();//Create the object
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
minLeft = (int) Math.floor(count / 60);
secLeft = count - minLeft * 60;
counter = minLeft + ":" + secLeft;
// the name of your actual activity
MyActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
TextView tv = (TextView) findViewById(R.id.timer);
Log.i(MainActivity.TAG,minLeft+", "+secLeft+", "+counter);
tv.setText(counter);
}
});
count--;
if (minLeft <= 0 && secLeft <= 0) {
t.cancel();
count = 2400;
onFinish();
}
}
}, 1000, 1000);
}
Please also note, that this code can be written more elegantly, without all/so many anonymous classes, but it should do the trick.
I would like to set the size and color of the text at random during onCreate method
Here is my code:
private TextView start;
private boolean isClicked;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
start = (TextView) findViewById(R.id.tvStart);
isclick = false;
Random r = new Random();
while (isclick = false)
{
start.setTextSize(r.nextInt(50));
start.setTextColor(Color.rgb(r.nextInt(256), r.nextInt(256),
r.nextInt(256)));
}
}
This code of mine doesn't work.
During onCreate I want the text size and color continuously and randomly changing.
It works for me :( let me know is there is a problem )
private boolean isclick;
Handler handler ;
private TextView start;
private boolean isClicked;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
start = (TextView) findViewById(R.id.tvStart);
handler = new Handler();
isclick = false;
new Thread(new Runnable() {
#Override
public void run() {
while (isclick == false)
{
handler.post(new Runnable() {
#Override
public void run() {
Random r = new Random();
start.setTextSize(r.nextInt(50));
start.setTextColor(Color.rgb(r.nextInt(256), r.nextInt(256),
r.nextInt(256)));
}
});
Log.w("DEBUG","Text View value : "+ start.getText().toString());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
you need to use a handler sins your are modifying UI widgets
this article may help you .
Here is some code that works on a TextView called x
//create random value between 0 and 70
int random = (int)Math.ceil(Math.random()*70);
x.setTextSize((float)random);
int red = (int)Math.ceil(Math.random()*255);
int green = (int)Math.ceil(Math.random()*255);
int blue = (int)Math.ceil(Math.random()*255);
Color randomcolor = new Color();
if (red < 16){
hexred = "0" + Integer.toHexString(red);
}else {
hexred = Integer.toHexString(red);
}
if (green < 16){
hexgreen = "0" + Integer.toHexString(green);
}else {
hexgreen = Integer.toHexString(green);
}
if (blue < 16){
hexblue = "0" + Integer.toHexString(blue);
}else {
hexblue = Integer.toHexString(blue);
}
String color = "#" + hexred + hexgreen + hexblue;
x.setTextColor(randomcolor.parseColor(color));
Continuasly changing is not recommended however. If you want to make it continuasly change color, chance is that the update of the color and size are to slow that nothing is displayed. Also it might happen that the entire XML layout is not loaded because of the calculations on the xml.
if you want your text randomly changing continously, use thread.
put your "while" action. inside run method, and give some delay on it.
I think its because of a missing = in the isclick = false if clause. It should be isclick==false.