I want to use animation on a Button that I created in my BlackBerry App. The animation works fine the first time when I click the button. On first click, the button starts animation (blinking). On second click the blinking stops. However, when I click the button again (third time), the blinking should start again. However, I get an error:
App Error 104 Uncaught: IllegalStateException
The code for creating the Button and adding animation is as follows:
final Bitmap image000 = Bitmap.getBitmapResource("panic.png");
final Bitmap image001 = Bitmap.getBitmapResource("panicon.png");
final Timer animationTimer = new Timer();
final BitmapField animationField = new BitmapField(image000,BitmapField.FOCUSABLE){
protected boolean navigationClick(int status, int time)
{
if(flag){
animationTask.cancel();
flag=false;
}else{
animationTimer.scheduleAtFixedRate(animationTask, 0, 100);
flag=true;
}
return true;
}
};
animationTask = new TimerTask() {
public void run() {
if(counter == 0){
animationField.setBitmap(image000);
}
if(counter == 1){
animationField.setBitmap(image001);
counter = -1;
}
counter++;
}
};
add(animationField);
EDIT: I debugged my code and the error occurs in the loop that starts the thread. Cancelling the thread seems fine. I am lost what is the issue. Please guide.
try this -
TimerTask animationTask;
BitmapField animationField;
final Bitmap image000 = Bitmap.getBitmapResource("panic.png");
final Bitmap image001 = Bitmap.getBitmapResource("panicon.png");
final Timer animationTimer = new Timer();
animationField = new BitmapField(image000,BitmapField.FOCUSABLE){
protected boolean navigationClick(int status, int time)
{
if(flag){
animationTask.cancel();
flag=false;
}else{
animationTask = new TimerTask() {
public void run() {
if(counter == 0){
animationField.setBitmap(image000);
}
if(counter == 1){
animationField.setBitmap(image001);
counter = -1;
}
counter++;
}
};
animationTask.run();
animationTimer.scheduleAtFixedRate(animationTask, 0, 100);
flag=true;
}
return true;
}
};
animationTask = new TimerTask() {
public void run() {
if(counter == 0){
animationField.setBitmap(image000);
}
if(counter == 1){
animationField.setBitmap(image001);
counter = -1;
}
counter++;
}
};
add(animationField);
Related
I can successfully work with the badge on my tabbar if i use it straight in my ViewWillAppear function but if i create a function where i try to control it then the badge does not appear.
This is the tabbedpaged renderer where I have to the function that changes the badge.
public override void ViewWillAppear(bool animated)
{
if (TabBar == null) return;
if (TabBar.Items == null) return;
var tabs = Element as TabbedPage;
if (tabs != null)
{
for (int i = 0; i < TabBar.Items.Length; i++)
{
UpdateItem(TabBar.Items[i], tabs.Children[i].Icon);
}
}
base.ViewWillAppear(animated);
}
private void UpdateItem(UITabBarItem item, string icon)
{
TabBar.UnselectedItemTintColor = UIColor.White;
}
public void UpdateBadge ()
{
var tabs = Element as TabbedPage;
if (tabs != null)
{
Device.BeginInvokeOnMainThread(() =>
{
var tab = TabBar.Items[3];
tab.BadgeValue = "New";
tab.BadgeColor = UIColor.Red;
});
}
}
Then I have another file where I handle a pushnotification and this is where I call the UpdateBadgefunction to both push a notification and also update the badge in the app.
void IPush.SendPush()
{
var notification = new UILocalNotification();
notification.SoundName = UILocalNotification.DefaultSoundName;
UIApplication.SharedApplication.PresentLocalNotificationNow(notification);
TabbedPage_Renderer tpr = new TabbedPage_Renderer();
tpr.UpdateBadge();
}
But as stated above this does not add the badge.
If I however add...
var tab = TabBar.Items[3];
tab.BadgeValue = "New";
tab.BadgeColor = UIColor.Red;
...inside the ViewWillAppear straight away it successfully shows an iconbadge when i start the app up but the idea is to control it so i can update the badge whenever i want.
We should not use the instance of the Renderer directly.
If you want to change the UI in the platform's renderer, we can try to define a BindableProperty in the forms. Then tell the renderer do some configuration when this property changed.
Firstly, define a BindableProperty in the page which you want to change its Badge like:
public static readonly BindableProperty BadgeTextProperty = BindableProperty.Create(nameof(BadgeText), typeof(string), typeof(MainPage), "0");
public string BadgeText {
set
{
SetValue(BadgeTextProperty, value);
}
get
{
return (string)GetValue(BadgeTextProperty);
}
}
Secondly, in the renderer, we can set the badge text when this property changed like:
for (int i = 0; i < TabBar.Items.Length; i++)
{
UpdateItem(TabBar.Items[i], tabs.Children[i].Icon);
//register the property changed event
tabs.Children[i].PropertyChanged += TabbarPageRenderer_PropertyChanged;
}
private void TabbarPageRenderer_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var page = sender as Page;
if (page == null)
return;
if (e.PropertyName == "BadgeText")
{
if (CheckValidTabIndex(page, out int tabIndex))
{
switch(tabIndex)
{
case 0:
UpdateBadge(TabBar.Items[tabIndex], (page as MainPage).BadgeText);
break;
case 1:
//Second Page, you can expand this switch depending on your tabs children
UpdateBadge(TabBar.Items[tabIndex], (page as SecondPage).BadgeText);
break;
default:
break;
}
}
return;
}
}
public bool CheckValidTabIndex(Page page, out int tabIndex)
{
tabIndex = Tabbed.Children.IndexOf(page);
return tabIndex < TabBar.Items.Length;
}
private void UpdateItem(UITabBarItem item, string icon)
{
TabBar.UnselectedItemTintColor = UIColor.White;
...//set the tabItem
}
private void UpdateBadge(UITabBarItem item, string badgeText)
{
item.BadgeValue = text;
item.BadgeColor = UIColor.Red;
}
At last, set the BadgeText in the forms when you want to update the badge.
So, i was able to make my code run, however i am having trouble with the highscore code. I am unable to use the bufferedreader and printwriter functions because for some reason that i am not understanding, they are not running. I want the program to compare the score to the highscore, and if the score is larger than the highscore, the highscore will be updated on a txt file. the reason the txt file is necessary is due to the fact that once the program closes, i need a method as to check for the previous highscore. I am really new to using processing and writing and reading txt files using programs, and none of the other sites and forums ive looked at have helped because they do not write the highscore variable onto a txt file. Please help, im ready to break my computer.
EM1.score = the score accumulated over the course of the program.
class High_Score {
int highscore = 0;
PrintWriter output; //imports the writer
BufferedReader reader; //imports the reader
int state = 0; //sets the varoable for the keyboard
void scoring() {
int score = EM1.score;
if (score > highscore) {
highscore = score;
}
textSize(30);
text("Your score is "+ score, 150, height/4);
text("The current highscore is "+highscore+".", 75, height/2);
text("Try to beat it.", 200, 450);
textSize(12);
text("Press esc to exit this page.", 225, 550);
}
void reader() {
importHS();
updateHS();
}
void updateHS() {
int score = EM1.score;
output = createWriter("highscore.txt"); //creates the file that will be
if (highscore < score) {
highscore = score;
output.print(highscore);
output.close();
}
}
void importHS() {
reader = createReader("highscore.txt"); //reads the current highscore
if (reader == null) {
highscore = 0;
return;
}
String line;
try {
line = reader.readLine();
}
catch (IOException e) {
e.printStackTrace();
line = null;
}
if (line != null) {
highscore = int(line);
println(highscore);
}
try {
reader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
void type() { //allows the screen to close is esc is pressed on the keyboard
state = key;
if (key == ESC) {
exit();
}
}
}
You're almost there. There are only a couple of things I see slightly off:
you're not calling flush() on the writer to writes the remaining data to the file
checking if (highscore < score) in updateHS() is preventing the write: you don't really need this anyway since scoring() already checks if the current score is a highscore and updates accordingly.
Here's most of your code tweaked to write/read from disk:
EM1Class EM1 = new EM1Class();
High_Score hs = new High_Score();
void setup(){
size(800,600);
EM1.score = 500;
hs.reader();
}
void draw(){
background(0);
hs.scoring();
hs.updateHS();
}
void keyPressed(){
if(keyCode == UP) EM1.score += 10;
if(keyCode == DOWN) EM1.score -= 10;
}
class EM1Class{
int score;
}
class High_Score {
int highscore = 0;
PrintWriter output; //imports the writer
BufferedReader reader; //imports the reader
int state = 0; //sets the varoable for the keyboard
void scoring() {
int score = EM1.score;
if (score > highscore) {
highscore = score;
}
textSize(30);
text("Your score is "+ score, 150, height/4);
text("The current highscore is "+highscore+".", 75, height/2);
text("Try to beat it.", 200, 450);
textSize(12);
text("Press esc to exit this page.", 225, 550);
}
void reader() {
importHS();
updateHS();
}
void updateHS() {
int score = EM1.score;
output = createWriter("highscore.txt"); //creates the file that will be
output.print(highscore);
output.flush();
output.close();
}
void importHS() {
reader = createReader("highscore.txt"); //reads the current highscore
if (reader == null) {
highscore = 0;
return;
}
String line;
try {
line = reader.readLine();
}
catch (IOException e) {
e.printStackTrace();
line = null;
}
if (line != null) {
highscore = int(line);
println(highscore);
}
try {
reader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
void type() { //allows the screen to close is esc is pressed on the keyboard
state = key;
if (key == ESC) {
exit();
}
}
}
There'a placeholder EM1Class there just so a sketch could compile.
I'm not sure the High_Score class should know about EM1 directly.
You might want to refactor the code a bit and make sure it's not as tightly coupled as it currently is since you're using classes anyway.
Also check out a Java style guide: it will help to write code that is consistent and neatly organised. It will surely pay off building the discipline to do so on the long run, but also immediately as you're code will be easier to scan/quickly read and deduce it's flow.
Another option is to use the Processing built-in XML and JSON functions which will make be easier to parse (and also have handy load/save functions).
int score = 0;
int highScore;
void setup(){
loadHighScore();
}
void draw(){
background(0);
text("score:"+score+"\nhighScore:"+highScore+"\n\nuse UP/DOWN\narrows",10,15);
}
void keyPressed(){
if(keyCode == UP) score += 10;
if(keyCode == DOWN) score -= 10;
if(score > highScore) highScore = score;
}
void loadHighScore(){
try{
JSONObject jsonScore = loadJSONObject("highScore.json");
highScore = jsonScore.getInt("highScore");
}catch(Exception e){
println("error loading/parsing highScore.json");
e.printStackTrace();
}
}
void saveHighScore(){
JSONObject jsonScore = new JSONObject();
jsonScore.setInt("highScore",highScore);
saveJSONObject(jsonScore,"highScore.json");
}
//save on close
void exit(){
saveHighScore();
super.exit();
}
I'm using this code to scroll programatically my pager
public void MoveNext(View view) {
pager.setCurrentItem(pager.getCurrentItem() + 1);
}
public void MovePrevious(View view) {
pager.setCurrentItem(pager.getCurrentItem() - 1);
}
The code work perfect but the transition is too fast. How can I introduce a delay so that the scrolling would be done more smoothly?
I believe there are two ways can achieve this goal, I've tried by ViewPager's fakeDrag() but which doesn't work perfect, luckly, another way does, by simulate touch motion event and use ObjectAnimator to specify the animation duration then make control the scroll speed become true.
public class ViewPagerActivity extends FragmentActivity
implements View.OnClickListener, Animator.AnimatorListener {
private ViewPager mViewPager;
private View btnTriggerNext;
private View btnTriggerPrev;
#Override
protected void onCreate(...) {
super...;
setContentView(R.layout.layout_xml);
mViewPager = findViewById(...);
btnTriggerNext = findViewById(R.id.btnTriggerNext);
btnTriggerNext.setOnClickListener(this);
btnTriggerPrev = findViewById(R.id.btnTriggerPrev);
btnTriggerPrev.setOnClickListener(this);
}
private boolean mIsInAnimation;
private long mMotionBeginTime;
private float mLastMotionX;
#Override
public void onClick(View v) {
if (mIsInAnimation) return;
ObjectAnimator anim;
if (v == btnTriggerPrev) {
if (!hasPrevPage()) return;
anim = ObjectAnimator.ofFloat(this, "motionX", 0, mViewPager.getWidth());
}
else if (v == btnTriggerNext) {
if (!hasNextPage()) return;
anim = ObjectAnimator.ofFloat(this, "motionX", 0, -mViewPager.getWidth());
}
else return;
anim.setInterpolator(new LinearInterpolator());
anim.addListener(this);
anim.setDuration(300);
anim.start();
}
public void setMotionX(float motionX) {
if (!mIsInAnimation) return;
mLastMotionX = motionX;
final long time = SystemClock.uptimeMillis();
simulate(MotionEvent.ACTION_MOVE, mMotionBeginTime, time);
}
#Override
public void onAnimationEnd(Animator animation) {
mIsInAnimation = false;
final long time = SystemClock.uptimeMillis();
simulate(MotionEvent.ACTION_UP, mMotionBeginTime, time);
}
#Override
public void onAnimationStart(Animator animation) {
mLastMotionX = 0;
mIsInAnimation = true;
final long time = SystemClock.uptimeMillis();
simulate(MotionEvent.ACTION_DOWN, time, time);
mMotionBeginTime = time;
}
// method from http://stackoverflow.com/a/11599282/1294681
private void simulate(int action, long startTime, long endTime) {
// specify the property for the two touch points
MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[1];
MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
pp.id = 0;
pp.toolType = MotionEvent.TOOL_TYPE_FINGER;
properties[0] = pp;
// specify the coordinations of the two touch points
// NOTE: you MUST set the pressure and size value, or it doesn't work
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
pc.x = mLastMotionX;
pc.pressure = 1;
pc.size = 1;
pointerCoords[0] = pc;
final MotionEvent ev = MotionEvent.obtain(
startTime, endTime, action, 1, properties,
pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0);
mViewPager.dispatchTouchEvent(ev);
}
private boolean hasPrevPage() {
return mViewPager.getCurrentItem() > 0;
}
private boolean hasNextPage() {
return mViewPager.getCurrentItem() + 1 < mViewPager.getAdapter().getCount();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}
cause it was simulating touch event, so please use a proper duration(less than 600ms will be nice) to do scrolling, when scroll in progress, put down finger would stop it and cause some bugs.
Change
pager.setCurrentItem(pager.getCurrentItem() + 1);
to
pager.setCurrentItem(pager.getCurrentItem() + 1,true);
This will call the method setCurrentItem(int item,boolean smoothScroll) which will scroll smoothly to the mentioned item instead of transitioning immediately
I continuously have this problem with all my most recent animation projects. Every time I run my animations, they never seem to be completely visible and full, but rather they blink and flash similar to a light bulb that is not fully screwed in ( i know, strange comparison but I can't think of what else it resembles). I feel like it must have something to do with my placement of repaint(); but I'm just not sure at this point. On a previous animation I made, the problem was that my "private BufferedImage offScr" variable wasn't set correctly, but viewing other programs similar to the one I am working on now, I don't see why that variable would be necessary. Thanks for all your help folks, and I apologize for my lack of knowledge in programming vocabulary.
Here is my program so far:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
public class DoveAnimator extends JFrame implements ActionListener {
private int DELAY = 40; // the delay for the animation
private final int WIDTH = 400; // the window width
private final int HEIGHT = 180; // the window height
private final int IMAGEAMT = 8;
private Image [] doveLeft = new Image[IMAGEAMT];
private Image [] doveRight = new Image[IMAGEAMT];
private int doveIndex = 0;
private boolean isRight = true;
private JPanel dovePanel;
private Image dove;
private JButton slowerButton = new JButton ("Slower");
private JButton fasterButton = new JButton ("Faster");
private JButton reverseButton = new JButton ("Reverse");
private JButton pauseResumeButton = new JButton (" pause ");
private Timer timer;
private int clicks = 2;
private boolean pause = false;
/** The constructor */
public DoveAnimator() {
MediaTracker track = new MediaTracker(this);
for (int i = 0; i < IMAGEAMT; ++i) {
doveLeft[i] = new ImageIcon("doves/ldove" + (i+1) + ".gif").getImage();
doveRight[i] = new ImageIcon("doves/rdove" + (i+1) + ".gif").getImage();
track.addImage(doveLeft[i],0);
track.addImage(doveRight[i],0);
}
// dove = doveRight[0];
//track.addImage(bkgImage,0);
// track.addImage(dove,0);
try {
track.waitForAll();
} catch ( InterruptedException e ) { }
dove = doveRight[0];
JPanel mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(WIDTH, HEIGHT));
setTitle ("Dove Animator");
dovePanel = new JPanel();
dovePanel.setPreferredSize(new Dimension(100, 125));
dovePanel.setBackground(Color.WHITE);
mainPanel.add(dovePanel);
JPanel buttonPanel = new JPanel();
buttonPanel.setPreferredSize(new Dimension(WIDTH, 40));
// button 1
slowerButton.addActionListener (this);
buttonPanel.add (slowerButton);
// button 2
fasterButton.addActionListener (this);
buttonPanel.add (fasterButton);
// button 3
reverseButton.addActionListener (this);
buttonPanel.add (reverseButton);
// button 4
pauseResumeButton.addActionListener (this);
buttonPanel.add (pauseResumeButton);
mainPanel.add(buttonPanel);
add(mainPanel);
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
setVisible (true);
pack();
timer = new Timer(DELAY,this); // setting timer delay
timer.start(); // start the timer
}
public void switchDove() {
++doveIndex;
if (doveIndex >= IMAGEAMT)
doveIndex = 0;
if (isRight)
dove = doveRight[doveIndex];
else
dove = doveLeft[doveIndex];
dove = (isRight) ? doveRight[doveIndex] : doveLeft[doveIndex];
}
/** Handler for button clicks and timer events */
public void actionPerformed (ActionEvent evt) {
if (evt.getSource() == slowerButton)
{
DELAY += 10;
}
else if (evt.getSource() == reverseButton)
{
if(evt.getSource() == reverseButton && isRight == true){
isRight = false;
}
else if(evt.getSource() == reverseButton && !isRight){
isRight = true;
}
}
else if (evt.getSource() == fasterButton)
{
DELAY -= 10;
if (DELAY <= 10 ){
DELAY = 10;
}
}
else if (evt.getSource() == pauseResumeButton)
{
if(evt.getSource() == pauseResumeButton && !pause){
pauseResumeButton.setText(" Resume ");
timer.stop();
pause = true;
}
else if(evt.getSource() == pauseResumeButton && pause == true){
pauseResumeButton.setText(" Pause ");
timer.start();
pause = false;
}
}
else if (evt.getSource() == timer)
{
drawAnimation();
switchDove();
repaint();
}
}
/** Draws the dove in the dovePanel */
public void drawAnimation() {
Graphics page = dovePanel.getGraphics();
page.drawImage(dove,0,0,Color.WHITE,null);
}
/** The main method */
public static void main (String [] args) {
new DoveAnimator();
}
}
Yep it seems to be one of your repaint() calls. Take out your repaint() method call here:
else if (evt.getSource() == timer)
{
drawAnimation();
switchDove();
repaint();
It's confusing the program because you are already switching the doves. That's my best guess. I ran it and it seems to work though. Cheers!
Also I just noticed that the way you are adjusting your timer will yeild you no results. You need to use the command: timer.setDelay(newDelay); you can also put arguments in the parenthesis like timer.setDelay(DELAY -= 10);
I have a code where I am calling photochooser in WP7 and I want to show a messagebox to user when the pic is more than 2Mb. When I try to do this, since the photochooser task is running in background, we start getting unhandled exceptions.
void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.ChosenPhoto != null)
{
ProgressBar.Visibility = Visibility.Visible;
image = _UploadImgeViewModel.ReadToEnd(e.ChosenPhoto);
if (image.Length < 16384)
{
BitmapImage bi = new BitmapImage();
bi.SetSource(e.ChosenPhoto);
UserSession.ProfileImage = bi;
Session.PreviousImage = bi;
UserSession.isImageChanged = true;
UserSession.image = image;
UserSession.Uploadimage = image;
NavigationService.Navigated += new NavigatedEventHandler(navigateCompleted);
}
else
{
ProgressBar.Visibility = Visibility.Collapsed;
UserSession.isImageChanged = false;
UserSession.ProfileImage = null;
Dispatcher.BeginInvoke(() => MessageBox.Show("The message"));
}
}
}
#endregion
This only shows the background job as resuming... and the msg box in foreground. and after a few seconds, the app crashes.
Can you please help me with this?
Cool. I got some idea to resolve this. Might not be a fix, but this way we can avoid this issue. Just add a button and do the validating process in the button click event. Since we can't display the message box when the navigation is in progress.
Below is the code:
void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.ChosenPhoto != null)
{
ProcessSelectedImage(e.ChosenPhoto);
}
}
private void ProcessSelectedImage(Stream stream)
{
if (stream != null)
{
bi.SetSource(stream);
UserSession.ProfileImage = bi;
UserSession.PreviousImage = bi;
image = ConvertToImage.ReadToEnd(stream);
UserSession.image = image;
UserSession.Uploadimage = image;
}
}
private void UploadImage_Click(object sender, RoutedEventArgs e)
{
if (image.Length < 16384)
{
UserSession.isImageChanged = true;
UserSession.image = image;
UserSession.Uploadimage = image;
NavigationService.Navigate(new Uri("/Views/EditMyProfile.xaml", UriKind.Relative));
}
else
{
UserSession.isImageChanged = false;
UserSession.ProfileImage = null;
UserSession.IsChangingProfilePicture = true;
MessageBox.Show(MessageContent.ImageUploadLengh);
}
}
Thanks
Kamal
You have 10 seconds to return to the foreground completely or your app will be killed. If you have a messagebox that can display here, you will fail certification (because user could not click anything for 10 seconds) -- you need to wait for the page to load.
A workaround for this if you need to show a MessageBox is to set a bool, and check it in the Page's Loaded event.
void photoChooserTask_Completed(object sender, PhotoResult e) { if (e.ChosenPhoto != null) { ProgressBar.Visibility = Visibility.Visible;
image = _UploadImgeViewModel.ReadToEnd(e.ChosenPhoto);
if (image.Length < 16384)
{
BitmapImage bi = new BitmapImage();
bi.SetSource(e.ChosenPhoto);
UserSession.ProfileImage = bi;
Session.PreviousImage = bi;
UserSession.isImageChanged = true;
UserSession.image = image;
UserSession.Uploadimage = image;
NavigationService.Navigated += new NavigatedEventHandler(navigateCompleted);
}
else
{
ProgressBar.Visibility = Visibility.Collapsed;
UserSession.isImageChanged = false;
UserSession.ProfileImage = null;
//set flag
UserSession.ImageTooBig = true;
}
}
}
#endregion
MyPage()
{
//make sure you attach Loaded Event if not already
Loaded += (s,e) =>
{
if (UserSession.ImageTooBig)
{
UserSession.ImageTooBig = false;
MessageBox.Show("Sorry, the image exceeds 2 MB");
}
};
}