I want to run some Java code when the user clicks on this ToolbarButtonField in my BlackBerry app. I have the following code which is not working. Please tell me where I am wrong.
butHome = new ToolbarButtonField(new StringProvider("Home"));
butHome.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
System.out.println("Clicked...");
}
});
You can use:
ToolbarButtonField#invoke
Performs an action when this
ToolbarButtonField is clicked on if
Command has been set. A click is
defined as the following sequence of
touch events: TouchEvent.DOWN,
TouchEvent.CLICK, TouchEvent.UNCLICK
and TouchEvent.UP.
You're going to have to use that in conjuction with the Command framework. If that's not desirable, override ToolbarButtonField#touchEvent for a TouchEvent.UNCLICK event to execute the desired code.
public boolean touchEvent(TouchEvent message) {
if ( message.geEvent() == TouchEvent.UNCLICK ) {
// do what I want.
}
}
Try this:
butHome = new ToolbarButtonField(new StringProvider("Home")) {
protected boolean navigationClick(int status, int time) {
System.out.println("Clicked...");
return true;
}
});
Related
I am working with an attached behavior for logging user actions on a ScrollBar.
my code:
class ScrollBarLogBehavior : Behavior<ScrollBar>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
}
void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
...
var track = (Track)AssociatedObject.Template.FindName("PART_Track", AssociatedObject);
// ** HERE is the problem: track is null ! **
...
}
How can I detect that the template has loaded and I can find the Track?
(when I call AssociatedObject.Template.LoadContent() the result containt the requested Track, so it i a matter of timing and not a matter of wrong template or naming)
Override the method OnApplyTemplate
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var textBox = Template.FindName("PART_Textbox", this) as TextBox;
}
I did not find any good way to detect when the template was loaded. However, I did find a way to find the Track:
in OnAttached() - register to Scroll event fo the ScrollBar (this can only happen after the entire template is loaded, of course):
protected override void OnAttached()
{
base.OnAttached();
_scrollHandler = new ScrollEventHandler(AssociatedObject_Scroll);
AssociatedObject.AddHandler(ScrollBar.ScrollEvent, _scrollHandler, true);
}
When handling the Scroll event, remove registration and find the Thumb:
void AssociatedObject_Scroll(object sender, ScrollEventArgs e)
{
var track = (Track)AssociatedObject.Template.FindName("PART_Track", Associated
if (track == null)
return;
AssociatedObject.RemoveHandler(ScrollBar.ScrollEvent, _scrollHandler);
// do my work with Track
...
}
If I understand correctly, you wish to create an attached behavior that will reference a template part after the ScrollBar has been loaded.
The following should work:
internal static class ScrollBarLogBehavior
{
public static readonly DependencyProperty LogUserActionProperty = DependencyProperty.RegisterAttached(
"LogUserAction",
typeof(bool),
typeof(ScrollBarLogBehavior),
new UIPropertyMetadata(default(bool), LogUserActionChanged));
public static bool GetLogUserAction(DependencyObject obj)
{
return (bool)obj.GetValue(LogUserActionProperty);
}
public static void SetLogUserAction(DependencyObject obj, bool value)
{
obj.SetValue(LogUserActionProperty, value);
}
public static void LogUserActionChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
if (s is ScrollBar scrollBar)
{
scrollBar.Loaded += OnScrollBarLoaded;
}
}
private static void OnScrollBarLoaded(object sender, RoutedEventArgs e)
{
if (sender is ScrollBar scrollBar)
{
if (scrollBar.Template != null)
{
// I'm not sure, but the `name` in the following method call might be case sensitive.
if (scrollBar.Template.FindName("PART_Track", scrollBar) is Track track)
{
// do work with `track` here
}
}
}
}
}
where you would "attach" the behavior in your XAML with:
<ScrollBar guiControls:ScrollBarLogBehavior.LogUserAction="True">
<!-- more here -->
</ScrollBar>
BE ADVISED: this implementation completely ignores the bool value that is being set for LogUserAction
I am working on ListView section, in this, the user can search the content by name and directly move at the first element of List via pressing a keyboard button. Like, if you press button B from (right vertical manager) it will scroll the list and move focus to first record of B.
The code is working fine in simulator but it's not working on Touch device - I have BB 9380 Curve.
here is the code for :
LabelField a = new LabelField("A" , FOCUSABLE)
{
protected void paint(Graphics graphics)
{
graphics.setColor(0xC4C4C4);
super.paint(graphics);
}
protected boolean navigationClick(int status, int time)
{
//fieldChangeNotify(1);
injectKey(Characters.LATIN_CAPITAL_LETTER_A);
injectKey(Characters.LATIN_CAPITAL_LETTER_A);
return true;
}
};
private void injectKey(char key)
{
try
{
searchList.setFocus();
KeyEvent inject = new KeyEvent(KeyEvent.KEY_DOWN, key, 0);
inject.post();
/*inject.post();*/
} catch (Exception e) {
Log.d("In injectKey :: :: :: "+e.toString());
MessageScreen.msgDialog("In Inject Key "+e.toString());
}
}
Alternate Solution
I would recommend a different strategy for this. Instead of trying to simulate key press events, I would define one method that handles a keypress of a certain letter, or a touch click on that same letter's LabelField.
Source: blackberry.com
So, you can have code that handles key presses by using
protected boolean keyChar( char character, int status, int time )
{
// you might only want to do this for the FIRST letter entered,
// but it sounds like you already have the keypress handling
// the way you want it ...
if( CharacterUtilities.isLetter(character) )
{
selectLetter(character);
return true;
}
return super.keyChar( character, status, time );
}
and then also handle touch events:
LabelField a = new LabelField("A" , FOCUSABLE)
{
protected void paint(Graphics graphics)
{
graphics.setColor(0xC4C4C4);
super.paint(graphics);
}
protected boolean navigationClick(int status, int time)
{
char letter = getText().charAt(0);
selectLetter(letter);
return true;
}
};
then, simply define a method that takes in one character, and scrolls to the start of that part of the list:
private void selectLetter(char letter);
Key Injection
If you really, really want to simulate key presses, though, you might try changing the code so that it injects two events: key down, and then key up (you're currently injecting two key down events). This might be causing problems.
injectKey(Characters.LATIN_CAPITAL_LETTER_A, true);
injectKey(Characters.LATIN_CAPITAL_LETTER_A, false);
with
private void injectKey(char key, boolean down)
{
try
{
searchList.setFocus();
int event = down ? KeyEvent.KEY_DOWN : KeyEvent.KEY_UP;
KeyEvent inject = new KeyEvent(event, key, 0);
inject.post();
} catch (Exception e) { /** code removed for clarity **/
}
}
Additional Note
For UIs, I like to trigger events on the key up, or unclick events. I think this makes a better experience for the user. So, you could replace keyChar() with keyUp() and navigationClick() with navigationUnclick() if you want to do this.
I am trying to set a common listener for an Customized button and Bitmap field.I am able to reach in listener but not able to differentiate between two fields.
private class MeaningsDetailsPageListner implements FieldChangeListener{
public void fieldChanged(Field field, int arg1) {
Dialog.alert("Hi");
if(field == bField){
Dialog.alert("Image Clicked");
}else if(field == wordBtn){
Dialog.alert("Button Clicked!!");
}
}
}
In following code wordBtn is my customised button and other is BitmapField.I am getting Hi alert but not able to differentiate further.
Any help would be appreciated.
Although I see what you're trying to do, you're better off adding a FieldChangeListener to each Field individually as an anonymous class. This way you don't have to worry about casting your Field to the correct type when testing for equality inside fieldChanged.
ButtonField b = new ButtonField("Hello!");
b.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
Dialog.alert("Button clicked");
}
});
Here is some of the code in my midlet:
the addKeyListener method presents an error as the function is not recognized.
import net.rim.device.api.system.KeyListener;
import net.rim.device.api.ui.Keypad;
public class PhraZApp extends javax.microedition.midlet.MIDlet implements ActionListener{
public PhraZApp {
addKeyListener (new KeyPadListener());
}
protected void keyPressed(int key) {
System.out.println(key);
}
public void actionPerformed(ActionEvent evt) {
System.out.println(evt.getKeyEvent());
}
public final class KeyPadListener implements KeyListener {
public boolean keyChar(char key, int status, int time) {
return false;
}
public boolean keyDown(int keycode, int time) {
if (Keypad.KEY_ESCAPE == Keypad.key(keycode)) {
System.out.println("key: " + keycode);
return true;
}
//let the system to pass the event to another listener.
return false;
}
public boolean keyUp(int keycode, int time) {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean keyRepeat(int keycode, int time) {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean keyStatus(int keycode, int time) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
The keyPressed action is not heard by any of those listeners.
Ive been told to add the keylistner to a GUI component, but none that I try it with accept it.
Furthermore, one of the possible issues is that the addKeyListener method is not declared, but in that case I don't know how to declare it.
If i change extends javax.microedition.midlet.MIDlet to extends UiApplication, the addKeyListener becomes accepted but the entire midlet falls to a RuntimeErrorException.
How can I get my Midlet to hear the escape key? I have searched through many forums and none of the suggestions have worked so far.
Thanks in advance.
You need to create a LWUIT Command and assign it to the parent form using the setBackCommand method. You can handle the command event like you handle every other command in LWUIT. E.g. through a command listener or even just by subclassing it and overriding actionPerformed(ActionEvent).
Thanks to Shai pointing me in the right direction, I solved it.
Here is how I did it.
Command backCommand = new Command("",Keypad.KEY_ESCAPE);
form.setBackCommand(backCommand);
then
public void actionPerformed(ActionEvent evt) {
if (evt.getCommand().getId() ==Keypad.KEY_ESCAPE){
//execution code
}
I didn't try, but if I had included text in the command I imagine it would appear as such when I push the menu button. The important thing is that I finally got the MIDlet to hear out the escape button after MANY hours of trying and searching for solutions.
when i am pressing the back button a pop screen is displayed which shows three button save, discard and cancel button i don't want this screen to be popped up. is this possible.
Thanks in advance
The default behaviour of the back button is to save changes for dirty screens. Rewrite the onClose() method to overwrite the default behaviour.
public boolean onClose() {
int choice = Dialog.ask(Dialog.D_YES_NO, "¿Do you want to exit?", Dialog.YES);
if (choice == Dialog.YES) {
//write a close() routine to exit
close();
}
return true;
}
You return true because you managed the ESC button pressed event. Review the Screen class docs.
You can also change the default behaviour of the ESC button rewriting the keyChar method as follows:
protected boolean keyChar(char character, int status, int time) {
if (character == Keypad.KEY_ESCAPE) {
onClose();
return true;
}
return super.keyChar(character, status, time);
}
close() should be somenthing like:
public void close() {
System.exit(0);
}
Override the onSavePrompt method. Then that screen will not come. Actually that popup screen will come only when something is changed on your screen. So it will ask you for the appropriate action.
protected boolean onSavePrompt() {
return true;
}
Skip the saving prompt with it
protected boolean onSavePrompt() {
return false;
}
Override onClose() method like this:
public boolean onClose() {
close();
return true;
}
you will not get that annoying alert message.