Vala GTK+. Issue with customized widget - events

I need to create a gtk.Entry which accepts only numbers. but I can't overwrite the key_press_event event in an heredited class. It only works if I use the original Entry by means of connect function.
What am I doing wrong?
using Gtk;
public class NumberEntry : Entry {
public void NumberEntry(){
add_events (Gdk.EventMask.KEY_PRESS_MASK);
}
//With customized event left entry editing is not possible
public override bool key_press_event (Gdk.EventKey event) {
string numbers = "0123456789.";
if (numbers.contains(event.str)){
return false;
} else {
return true;
}
}
}
public class Application : Window {
public Application () {
// Window
this.title = "Entry Issue";
this.window_position = Gtk.WindowPosition.CENTER;
this.destroy.connect (Gtk.main_quit);
this.set_default_size (350, 70);
Grid grid = new Grid();
grid.set_row_spacing(8);
grid.set_column_spacing(8);
Label label_1 = new Label ("Customized Entry, useless:");
grid.attach (label_1,0,0,1,1);
//Customized Entry:
NumberEntry numberEntry = new NumberEntry ();
grid.attach(numberEntry, 1, 0, 1, 1);
Label label_2 = new Label ("Working only numbers Entry:");
grid.attach (label_2,0,1,1,1);
//Normal Entry
Entry entry = new Entry();
grid.attach(entry, 1, 1, 1, 1);
this.add(grid);
//With normal Entry this event works well:
entry.key_press_event.connect ((event) => {
string numbers = "0123456789.";
if (numbers.contains(event.str)){
return false;
} else {
return true;
}
});
}
}
public static int main (string[] args) {
Gtk.init (ref args);
Application app = new Application ();
app.show_all ();
Gtk.main ();
return 0;
}

The key_press_event of the superclass is no longer being called. You need to call the base class and return true when you have consumed the key.
public override bool key_press_event (Gdk.EventKey event) {
string numbers = "0123456789.";
if (numbers.contains(event.str)){
return base.key_press_event (event);
} else {
return true;
}
}
If you return false in a signal, this can be passed to an alternate handler, but only if you use connect and not override the signal method.

Related

Code Error. The modifier public is not valid. Why?

I am making a 2D game in Unity and am trying to make my moveable character stop every time dialogue appears on screen.
I am using the Fungus extension for my dialogue as I'm a newbie to coding. Every thing I try however I run in to problems.
My current issue is that the modifier 'public' is not valid for this item.
Anyone know how this can be fixed? I have attached the code below. I assume the issue is with the public void CantMove() and public void CanMove() lines.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float moveSpeed;
public Rigidbody2D theRB;
public float jumpForce;
private bool isGrounded;
public Transform groundCheckPoint;
public LayerMask whatIsGround;
private bool canDoubleJump;
private bool canMove = true;
private Animator anim;
private SpriteRenderer theSR;
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
theSR = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
if(!canMove)
{
theRB.velocity = new Vector2(0, 0);
}
else
{
theRB.velocity = new Vector2(moveSpeed * Input.GetAxis("Horizontal"), theRB.velocity.y);
}
public void CantMove()
{
canMove = false;
}
public void CanMove()
{
canMove = true;
}
//theRB.velocity = new Vector2(moveSpeed * Input.GetAxis("Horizontal"), theRB.velocity.y);
isGrounded = Physics2D.OverlapCircle(groundCheckPoint.position, .2f, whatIsGround);
if(isGrounded)
{
canDoubleJump = true;
}
if(Input.GetButtonDown("Jump"))
{
if (isGrounded)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
}
else
{
if(canDoubleJump)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
canDoubleJump = false;
}
}
}
if(theRB.velocity.x > 0)
{
theSR.flipX = true;
} else if(theRB.velocity.x < 0)
{
theSR.flipX = false;
}
anim.SetFloat("moveSpeed", Mathf.Abs( theRB.velocity.x));
anim.SetBool("isGrounded", isGrounded);
}
}
'''
Your problem is that your two functions defined for CanMove and CantMove are declared inside of the Update function body... which makes them locally scoped functions which means they can never have public access and can only be called from within the Update function itself.
Move these two functions outside of the Update function body like this...
void Update() {
...
}
public void CantMove() {
canMove = false;
}
public void CanMove() {
canMove = true;
}

Strange behaviour when trying to assign tags to instantiated unity UI game objects

When I call functions LikeFrame1(),LikeFrame2(),LikeFrame3() once (That is, click the button that call the function it for the first time) a random tag is assigned to the instantiated gameobject (The tag is coming from the other functions where I am doing the same thing, LikeFrame1(),LikeFrame2(),LikeFrame3() respectively)
When I click the button to call it a second time it will assign the proper tag.
I have tried to change where I call assigning the new tag in the function
Here is script with my functions that instantiate the Gameobject and assign the tags:
public class InventoryCreateButton : MonoBehaviour
{
public Button Button;
public void AddItemz1()
{
Button go = Instantiate(Button, new Vector3(0, 0, 0),
Quaternion.identity) as Button;
Button.tag = "poop";
go.transform.parent =
GameObject.Find("ButtonListContent1").transform;
Debug.Log("AddItemz1");
}
public void AddItemz2()
{
Button go = Instantiate(Button, new Vector3(0, 0, 0),
Quaternion.identity) as Button;
Button.tag = "poop2";
go.transform.parent =
GameObject.Find("ButtonListContent1").transform;
Debug.Log("AddItemz2");
}
I call them from this script like so:
public class ChooseFrame : MonoBehaviour
{
public GameObject currentInterObj = null;
public InteractionObject currentInterObjScript = null;
public Inventory inventory;
public InventoryCreateButton inventorycreateButton;
public InventoryButtonGetImage inventoryButtonGetImage;
public void LikeFrame1()
{
currentInterObj = frame1;
if (currentInterObj && _soundOn1)
{
inventorycreateButton.AddItemz1();
_soundOn1 = false;
Debug.Log(_soundOn1);
}
else
{
SoundOn1(); //method that can turn the sound on
_soundOn1 = true;
Debug.Log(_soundOn1);
}
}
// Update is called once per frame
void SoundOn1()
{
Destroy(GameObject.FindWithTag("poop"));
}
public void LikeFrame2()
{
currentInterObj = frame2;
if (currentInterObj && _soundOn2)
{
inventorycreateButton.AddItemz2();
_soundOn2 = false;
Debug.Log(_soundOn2);
}
else
{
SoundOn2(); //method that can turn the sound on
_soundOn2 = true;
Debug.Log(_soundOn2);
}
}
// Update is called once per frame
void SoundOn2()
{
Destroy(GameObject.FindWithTag("poop2"));
}
public void LikeFrame3()
{
currentInterObj = frame3;
if (currentInterObj && _soundOn3)
{
inventorycreateButton.AddItemz3();
_soundOn3 = false;
}
else
{
SoundOn3(); //method that can turn the sound on
_soundOn3 = true;
}
}
// Update is called once per frame
void SoundOn3()
{
Destroy(GameObject.FindWithTag("poop3"));
}
No error message and I would expect that the proper tag would be assigned to my instantiated gameobject upon calling the function the first time, not the second time.

How to implement TabLayout.IOnTabSelectedListener.OnTabUnselected with TabbedPage.ToolbarPlacement="Bottom" - Xamarin Forms?

I just recently used android:TabbedPage.ToolbarPlacement="Bottom". I used to have the following code:
void TabLayout.IOnTabSelectedListener.OnTabUnselected(TabLayout.Tab tab)
{
var playPage = Element.CurrentPage as NavigationPage;
if (!(playPage.RootPage is PhrasesFrame))
return;
var tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
var playTab = tabLayout.GetTabAt(4);
tab.SetText("Play");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
Anyone knows how can I implement this with ToolbarPlacement="Bottom" ? I have implemented both BottomNavigationView.IOnNavigationItemSelectedListener, BottomNavigationView.IOnNavigationItemReselectedListener but can't find any reference for UnselectedTab if there is any.
Edit:
Previous custom renderer using the default tab position and implementing TabLayout:
namespace Japanese.Droid
{
public class MyTabbedPageRenderer: TabbedPageRenderer, TabLayout.IOnTabSelectedListener
{
ViewPager viewPager;
TabLayout tabLayout;
bool setup;
public MyTabbedPageRenderer(Context context): base(context){ }
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// More codes here
}
void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
{
UpdateTab(tab);
}
void TabLayout.IOnTabSelectedListener.OnTabSelected(TabLayout.Tab tab)
{
UpdateTab(tab);
}
void TabLayout.IOnTabSelectedListener.OnTabUnselected(TabLayout.Tab tab)
{
var playPage = Element.CurrentPage as NavigationPage;
if (!(playPage.RootPage is PhrasesFrame))
return;
var tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
var playTab = tabLayout.GetTabAt(4);
tab.SetText("Play");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
void UpdateTab(TabLayout.Tab tab)
{
// To have the logic only on he tab on position 1
if (tab == null || tab.Position != 4)
{
return;
}
if (tab.Text == "Play")
{
tab.SetText("Pause");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_pause_outline_22);
App.pauseCard = false;
}
else
{
tab.SetText("Play");
tab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
}
}
}
Current custom renderer using the ToolbarPlacement="Bottom":
namespace Japanese.Droid
{
public class BottomTabPageRenderer : TabbedPageRenderer, BottomNavigationView.IOnNavigationItemSelectedListener, BottomNavigationView.IOnNavigationItemReselectedListener
{
public BottomTabPageRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
// More codes here
}
bool BottomNavigationView.IOnNavigationItemSelectedListener.OnNavigationItemSelected(IMenuItem item)
{
base.OnNavigationItemSelected(item);
UpdateTab(item)
}
void BottomNavigationView.IOnNavigationItemReselectedListener.OnNavigationItemReselected(IMenuItem item)
{
UpdateTab(item);
}
void UpdateTab(IMenuItem item)
{
var playTabId = 4;
var title = item.TitleFormatted.ToString();
if (item == null || item.ItemId != playTabId)
{
return;
}
if (item.ItemId == playTabId)
{
if (title == "Play")
{
item.SetTitle("Pause");
item.SetIcon(Resource.Drawable.ionicons_2_0_1_pause_outline_22);
App.pauseCard = false;
}
else
{
item.SetTitle("Play");
item.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
}
}
}
}
So now my problem is I don't have any idea how will I implement the TabLayout.IOnTabSelectedListener.OnTabUnselected in the new custom renderer.
There is no official stuff for OnTabReselected event for TabbedPage's bottom navigation or
BottomNavigationView because It doesn't use TabLayout.Tab for a start. Many overridden methods of TabbedPageRenderer not being called like SetTabIcon. If you are using IOnTabSelectedListener interface(As your first part of code) you have three methods to use.
void OnTabReselected(Tab tab);
void OnTabSelected(Tab tab);
void OnTabUnselected(Tab tab);
But when it comes to BottomNavigationView interface you have only two methods
void OnNavigationItemReselected
bool OnNavigationItemSelected
So we don't have built in OnTabUnselected method. Here you need to write custom code to make unseleted event.
I have tried this code without using custom renderer using 4 tabs pages & the xaml of tabbed written in MailPage.xaml file. First declare List<string> in App.xaml.cs file to store Title of all tabs
public static List<string> Titles {get;set;}
Add tabs pages title in above list from MainPage.xaml.cs file's OnAppearing method
protected override void OnAppearing()
{
for (int i = 0; i < this.Children.Count; i++)
{
App.Titles.Add(this.Children[i].Title);
}
}
Now go to your MyTabbedPage class in which is available in shared project.
public class MyTabbedPage : Xamarin.Forms.TabbedPage
{
string selectedTab = string.Empty;
string unSelectedTab = string.Empty;
bool isValid;
public MyTabbedPage()
{
On<Xamarin.Forms.PlatformConfiguration.Android>().SetToolbarPlacement(ToolbarPlacement.Bottom);
this.CurrentPageChanged += delegate
{
unSelectedTab = selectedTab;
selectedTab = CurrentPage.Title;
if (App.Titles != null)
isValid = true;
else
App.Titles = new List<string>();
if (isValid)
{
MoveTitles(selectedTab);
//Pass 0 index for tab selected & 1 for tab unselected
var unSelecteTabTitle = App.Titles[1];
//TabEvents(1); here you know which tab unseleted call any method
}
};
}
//This method is for to moving selected title on top of App.Titles list & unseleted tab title automatic shifts at index 1
void MoveTitles(string selected)
{
var holdTitles = App.Titles;
if (holdTitles.Count > 0)
{
int indexSel = holdTitles.FindIndex(x => x.StartsWith(selected));
holdTitles.RemoveAt(indexSel);
holdTitles.Insert(0, selected);
}
App.Titles = holdTitles;
}
}
Or you can make swith case like this
void TabEvents(int index)
{
switch (index)
{
case 0:
//Tab selected
break;
case 1:
//Tab unselected
break;
}
}
Few things I should mention that MainPage.xaml.cs file inheriting MyTabbedPage
public partial class MainPage : MyTabbedPage
Structure of MainPage.xaml file
<?xml version="1.0" encoding="utf-8" ?>
<local:MyTabbedPage
<TabbedPage.Children>
<NavigationPage Title="Browse">
</NavigationPage>
</TabbedPage.Children>
</local:MyTabbedPage>
Answer seems long but hope it help you.
As per G.Hakim's suggestion, I was able to do what I wanted to do by capturing the tab item I wanted to work on and do the necessary actions in BottomNavigationView.IOnNavigationItemSelectedListener.OnNavigationItemSelected.
namespace Japanese.Droid
{
public class BottomTabPageRenderer : TabbedPageRenderer, BottomNavigationView.IOnNavigationItemSelectedListener, BottomNavigationView.IOnNavigationItemReselectedListener
{
// same as above
bool BottomNavigationView.IOnNavigationItemSelectedListener.OnNavigationItemSelected(IMenuItem item)
{
base.OnNavigationItemSelected(item);
if(item.ItemId == 4 && item.TitleFormatted.ToString() == "Play")
{
item.SetTitle("Pause");
item.SetIcon(Resource.Drawable.ionicons_2_0_1_pause_outline_22);
App.pauseCard = false;
playTab = item;
}
if(item.ItemId !=4 && playTab.TitleFormatted.ToString() == "Pause")
{
playTab.SetTitle("Play");
playTab.SetIcon(Resource.Drawable.ionicons_2_0_1_play_outline_25);
App.pauseCard = true;
}
return true;
}
// same as above
}
}

Updating a datatable in Wicket

We are developing a new application in Wicket and have run into a small problem.
What we do:
1) create a new SortableDataProvider
2) create a new DefaultDataTablePagingInBottom
3) create a new WebMarkupContainer
4) add the DefaultDataTablePagingInBottom to the WebMarkupContainer
5) create a new AjaxCheckBox
6) in the onUpdate of the AjaxCheckBox, add the WebMarkupContainer to the AjaxRequestTarget
7) set the SortableDataProvider to a new SortableDataProvider (with the updated query)
8) DefaultDataTablePagingInBottom.replaceWith(new DefaultDataTablePagingInBottom - with the new provider).
What happends:
1) Click the checkbox -> nothing happends.
2) Click it again -> crash: "Last cause: This method can only be called on a component that has already been added to its parent.
WicketMessage: Method onRequest of interface org.apache.wicket.behavior.IBehaviorListener targeted at org.apache.wicket.ajax.markup.html.form.AjaxCheckBox$1#1a2fefd on component [ [Component id = checkBox]] threw an exception"
3) Click back in the browser -> the list i filtered with the new provider.
Any ideas?
---EDIT---
Here's some code.
1) In the constructor of the WebPage:
model = new Model(projectPlannerService);
provider = new SortableProjectDataProvider(model, (WebSession) getSession(), isChecked);
table = new DefaultDataTablePagingInBottom("table", columns, provider, 50);
listContainer = new WebMarkupContainer("wmc");
listContainer.add(table);
add(listContainer.setOutputMarkupId(true));
/*
* checkbox för filtrering
*/
AjaxCheckBox checkBox = new AjaxCheckBox("checkBox", new Model()) {
#Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(listContainer, "wmc");
isChecked = !isChecked;
provider = new SortableProjectDataProvider(model, (WebSession) getSession(), isChecked);
updateTable();
}
};
add(checkBox);
2) In updateTable():
table.replaceWith(new DefaultDataTablePagingInBottom("table", columns, provider, 50));
3) The SortableProjectDataProvider:
// Constructor
public SortableProjectDataProvider(IModel<?> model, WebSession webSession, boolean isChecked) {
this.model = model;
this.projectPlannerService = (ProjectPlannerService) model.getObject();
this.webSession = webSession;
setSort("customer", SortOrder.ASCENDING);
System.out.println("ischecked:" + isChecked);
if(!isChecked)
list = ((ProjectPlannerService) model.getObject()).findAllProjects();
else
list = ((ProjectPlannerService) model.getObject()).findAllActiveProjects();
System.out.println("size: " + list.size());
comparator = new ProjectComparator();
}
public Iterator<Project> iterator(int first, int count) {
Collections.sort(list, comparator);
if (first > list.size()) {
first = 0;
}
if (first + count > list.size()) {
return list.subList(first, list.size()).iterator();
} else {
return list.subList(first, first + count).iterator();
}
}
public IModel<Project> model(Project object) {
return new DetachableProjectModel((Project) object);
}
public int size() {
return list.size();
}
private class DetachableProjectModel extends LoadableDetachableModel {
private Long id;
#SpringBean
ProjectPlannerService projectPlannerService;
public DetachableProjectModel(Long id) {
Injector.get().inject(this);
if (id == null) {
throw new IllegalArgumentException();
}
this.id = id;
}
public DetachableProjectModel(Project project) {
this(project.getPk());
Injector.get().inject(this);
}
public int hashCode() {
return id.hashCode();
}
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj == null) {
return false;
} else if (obj instanceof DetachableProjectModel) {
DetachableProjectModel other = (DetachableProjectModel) obj;
return other.id == this.id;
}
return false;
}
protected Object load() {
return ((ProjectPlannerService) model.getObject()).findProjectById(id);
}
}
}
wicket:extend
-input wicket:id="checkBox" type="checkbox"- Show active -/input-
-div wicket:id="wmc"-
-table wicket:id="table"--/table-
-/div-
-/wicket:extend-
Thanks in advance!
/Andreas
By replacing the instance of your SortableProjectDataProvider with a new one you are making your life difficult. Instead of using the boolean isChecked in the constructor you could use an IModel<Boolean>. Assign the same instance of that model to your data provider and the check-box and you are done. No need to replace anything in onUpdate, add your listContainer to the AjaxRequestTarget and everything should just work...
e.g.
...
private IModel<Boolean> isCheckedModel = new Model<Boolean>(Boolean.FALSE);
...
provider = new SortableProjectDataProvider(model, (WebSession) getSession(), isCheckedModel);
...
AjaxCheckBox checkBox = new AjaxCheckBox("checkBox", isCheckedModel) {
#Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(listContainer);
}
};
...
It is almost never a good idea to replace such things with new ones in Wicket. Encapsulate what changes in a model and change / replace the model's object. Every object that has a reference to that model can see the updated value or change it as needed.
Hope this helps.
Try this:
Wrong: target.add(listContainer, "wmc");
Right: target.add(listContainer);
Wrong; table.replaceWith(new DefaultDataTablePagingInBottom("table", columns, provider, 50));
Right: DefaultDataTablePagingInBottom tmp = new DefaultDataTablePagingInBottom("table", columns, provider, 50);
table.replaceWith(tmp);
table = tmp;
(You replace the DefaultDataTablePagingInBottom but not your reference.)
//olle

Blackberry - Loading/Wait screen with animation

Is there a way to show "Loading" screen with animation in blackberry?
Options:
PME animation content
multithreading + set of images + timer/counter
standard rim api
some other way
Any of this?
Thanks!
Fermin, Anthony +1. Thanks to all, you gave me the part of answer.
My final solution:
1.Create or generate (free Ajax loading gif generator) animation and add it to project.
2.Create ResponseCallback interface (see Coderholic - Blackberry WebBitmapField) to receive thread execution result:
public interface ResponseCallback {
public void callback(String data);
}
3.Create a class to handle your background thread job. In my case it was http request:
public class HttpConnector
{
static public void HttpGetStream(final String fileToGet,
final ResponseCallback msgs) {
Thread t = new Thread(new Runnable() {
public void run() {
HttpConnection hc = null;
DataInputStream din = null;
try {
hc = (HttpConnection) Connector.open("http://" + fileToGet);
hc.setRequestMethod(HttpsConnection.GET);
din = hc.openDataInputStream();
ByteVector bv = new ByteVector();
int i = din.read();
while (-1 != i) {
bv.addElement((byte) i);
i = din.read();
}
final String response = new String(bv.toArray(), "UTF-8");
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
msgs.callback(response);
}
});
}
catch (final Exception e) {
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
msgs.callback("Exception (" + e.getClass() + "): "
+ e.getMessage());
}
});
}
finally {
try {
din.close();
din = null;
hc.close();
hc = null;
}
catch (Exception e) {
}
}
}
});
t.start();
}
}
4.Create WaitScreen (a hybrid of FullScreen and AnimatedGIFField with ResponseCallback interface):
public class WaitScreen extends FullScreen implements ResponseCallback
{
StartScreen startScreen;
private GIFEncodedImage _image;
private int _currentFrame;
private int _width, _height, _xPos, _yPos;
private AnimatorThread _animatorThread;
public WaitScreen(StartScreen startScreen) {
super(new VerticalFieldManager(), Field.NON_FOCUSABLE);
setBackground(
BackgroundFactory.createSolidTransparentBackground(
Color.WHITE, 100));
this.startScreen = startScreen;
EncodedImage encImg =
GIFEncodedImage.getEncodedImageResource("ajax-loader.gif");
GIFEncodedImage img = (GIFEncodedImage) encImg;
// Store the image and it's dimensions.
_image = img;
_width = img.getWidth();
_height = img.getHeight();
_xPos = (Display.getWidth() - _width) >> 1;
_yPos = (Display.getHeight() - _height) >> 1;
// Start the animation thread.
_animatorThread = new AnimatorThread(this);
_animatorThread.start();
UiApplication.getUiApplication().pushScreen(this);
}
protected void paint(Graphics graphics) {
super.paint(graphics);
// Draw the animation frame.
graphics
.drawImage(_xPos, _yPos, _image
.getFrameWidth(_currentFrame), _image
.getFrameHeight(_currentFrame), _image,
_currentFrame, 0, 0);
}
protected void onUndisplay() {
_animatorThread.stop();
}
private class AnimatorThread extends Thread {
private WaitScreen _theField;
private boolean _keepGoing = true;
private int _totalFrames, _loopCount, _totalLoops;
public AnimatorThread(WaitScreen _theScreen) {
_theField = _theScreen;
_totalFrames = _image.getFrameCount();
_totalLoops = _image.getIterations();
}
public synchronized void stop() {
_keepGoing = false;
}
public void run() {
while (_keepGoing) {
// Invalidate the field so that it is redrawn.
UiApplication.getUiApplication().invokeAndWait(
new Runnable() {
public void run() {
_theField.invalidate();
}
});
try {
// Sleep for the current frame delay before
// the next frame is drawn.
sleep(_image.getFrameDelay(_currentFrame) * 10);
} catch (InterruptedException iex) {
} // Couldn't sleep.
// Increment the frame.
++_currentFrame;
if (_currentFrame == _totalFrames) {
// Reset back to frame 0
// if we have reached the end.
_currentFrame = 0;
++_loopCount;
// Check if the animation should continue.
if (_loopCount == _totalLoops) {
_keepGoing = false;
}
}
}
}
}
public void callback(String data) {
startScreen.updateScreen(data);
UiApplication.getUiApplication().popScreen(this);
}
}
5.In the end, create Start screen to call HttpConnector.HttpGetStream and to show WaitScreen:
public class StartScreen extends MainScreen
{
public RichTextField text;
WaitScreen msgs;
public StartScreen() {
text = new RichTextField();
this.add(text);
}
protected void makeMenu(Menu menu, int instance) {
menu.add(runWait);
super.makeMenu(menu, instance);
}
MenuItem runWait = new MenuItem("wait", 1, 1) {
public void run() {
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
getFile();
}
});
}
};
public void getFile() {
msgs = new WaitScreen(this);
HttpConnector.HttpGetStream(
"stackoverflow.com/faq", msgs);
}
//you should implement this method to use callback data on the screen.
public void updateScreen(String data)
{
text.setText(data);
}
}
UPDATE: another solution naviina.eu: A Web2.0/Ajax-style loading popup in a native BlackBerry application
The basic pattern for this kind of thing is:
Have a thread running a loop that updates a variable (such as the frame index of the animated image) and then calls invalidate on a Field which draws the image (and then sleeps for a period of time). The invalidate will queue a repaint of the field.
In the field's paint method, read the variable and draw the appropriate frame of the image.
Pseudo code (not totally complete, but to give you the idea):
public class AnimatedImageField extends Field implements Runnable {
private int currentFrame;
private Bitmap[] animationFrames;
public void run() {
while(true) {
currentFrame = (currentFrame + 1) % animationFrames.length;
invalidate();
Thread.sleep(100);
}
}
protected void paint(Graphics g) {
g.drawBitmap(0, 0, imageWidth, imageHeight, animationFrames[currentFrame], 0, 0);
}
}
Note also here I used an array of Bitmaps, but EncodedImage lets you treat an animated gif as one object, and includes methods to get specific frames.
EDIT: For completeness: Add this to a PopupScreen (as in Fermin's answer) or create your own dialog by overriding Screen directly. The separate thread is necessary because the RIM API is not thread-safe: you need to do everything UI related on the event thread (or while holding the event lock, see BlackBerry UI Threading - The Very Basics
This is simple code for loading screen ....
HorizontalFieldManager popHF = new HorizontalFieldManager();
popHF.add(new CustomLabelField("Pls wait..."));
final PopupScreen waitScreen = new PopupScreen(popHF);
new Thread()
{
public void run()
{
synchronized (UiApplication.getEventLock())
{
UiApplication.getUiApplication().pushScreen(waitScreen);
}
//Here Some Network Call
synchronized (UiApplication.getEventLock())
{
UiApplication.getUiApplication().popScreen(waitScreen);
}
}
}.start();
If it's just an animation could you show an animated gif on a popup and close it when loading operation is complete?
Easiest way is probably to use the standard GaugeField, setting style GaugeField.PERCENT. This will give you a progress bar. Add this to a PopupScreen and it will sit on top of your content. Something like..
private GaugeField _gaugeField;
private PopupScreen _popup;
public ProgressBar() {
DialogFieldManager manager = new DialogFieldManager();
_popup = new PopupScreen(manager);
_gaugeField = new GaugeField(null, 0, 100, 0, GaugeField.PERCENT);
manager.addCustomField(_gaugeField);
}
Then have an update method which will use _gaugeField.setValue(newValue); to update the progress bar.
I normally have this called from whichever thread is doing the work (loading in your case, everytime an operation is complete the progress bar is updated.
I would suggest to take a look at this simple implementation. I liked this but never used it. May be helpful to you.
link text
ActivityIndicator is a good option if you are working with at least BB OS 6.0.
http://www.brighthub.com/mobile/blackberry-platform/articles/94258.aspx
http://docs.blackberry.com/en/developers/deliverables/17966/Screen_APIs_1245069_11.jsp

Resources