Instant value change handler on a GWT textbox - events

I would like to update a text field instantly when typing in a GWT TextBox. My problem is that ValueChangeEvent and ChangeEvent handlers only fire when the TextBox loses focus. I thought about using the KeyPressEvent but then nothing would happen when performing a copy paste with the mouse.
What's the simplest way to do that ?

You could catch the ONPASTE event and manually fire a ValueChangeEvent. Something like this:
public void onModuleLoad() {
final Label text = new Label();
final ExtendedTextBox box = new ExtendedTextBox();
box.addValueChangeHandler(new ValueChangeHandler<String>() {
#Override
public void onValueChange(ValueChangeEvent<String> event) {
text.setText(event.getValue());
}
});
box.addKeyUpHandler(new KeyUpHandler() {
#Override
public void onKeyUp(KeyUpEvent event) {
text.setText(box.getText());
}
});
RootPanel.get().add(box);
RootPanel.get().add(text);
}
private class ExtendedTextBox extends TextBox {
public ExtendedTextBox() {
super();
sinkEvents(Event.ONPASTE);
}
#Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
switch (DOM.eventGetType(event)) {
case Event.ONPASTE:
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
#Override
public void execute() {
ValueChangeEvent.fire(ExtendedTextBox.this, getText());
}
});
break;
}
}
}
Tested on firefox 3.6.1.

As a general solution, what works for me (thx to gal-bracha comment):
Generally, GWT does not have classes to handle input event (described here
and here). So we need to implement it by ourselves:
Handler class:
import com.google.gwt.event.shared.EventHandler;
public interface InputHandler extends EventHandler {
void onInput(InputEvent event);
}
Event class:
import com.google.gwt.event.dom.client.DomEvent;
public class InputEvent extends DomEvent<InputHandler> {
private static final Type<InputHandler> TYPE = new Type<InputHandler>("input", new InputEvent());
public static Type<InputHandler> getType() {
return TYPE;
}
protected InputEvent() {
}
#Override
public final Type<InputHandler> getAssociatedType() {
return TYPE;
}
#Override
protected void dispatch(InputHandler handler) {
handler.onInput(this);
}
}
Usage:
box.addDomHandler(new InputHandler() {
#Override
public void onInput(InputEvent event) {
text.setText(box.getText());
}
},InputEvent.getType());
It works on every TextBox value change including pasting using context menu. It does not react on arrows, ctrl, shift etc...

This has been a major issue for me in the past. The keyupHandler wont work because the copy paste requires a second key press on the paste option which does not fire the event. the best i have been able to do is use the old changelistener not ideal but it does work.

I prefer use Elements than Widgets so this my way to handler.
Element input = Document.get().getElementById("my-input");
DOM.sinkBitlessEvent(input, "input");
DOM.setEventListener(input, event -> GWT.log("Event!"));

Why not use combination of both KeyUpHandler and a ChangeHandler on the TextBox?
Should take care of immediate feedback on each keystroke as well as copy paste case as well.

Just saw this question. Because I was facing the similar problem.
Did some hack and it worked for me.
You can use KeyUpHandler but use it with additional if block that checks
for length of textbox. If length of text box is > 0, do your thing.
Ex:
textBox.addKeyUpHandler(new KeyUpHandler() {
#Override
public void onKeyUp(KeyUpEvent keyUpEvent) {
if (textBox.getText().length() > 0) {
//do your stuff`enter code here`
}
}

Related

Toast is shown every time when device is rotate

In my Android app I use AAC.
Here my activity:
public class AddTraderActivity extends AppCompatActivity {
AddTraderViewModel addTraderViewModel;
private static final String TAG = AddTraderActivity.class.getName();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AddTraderActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.add_trader_activity);
binding.setHandler(this);
init();
}
private void init() {
ViewModelProvider viewViewModelProvider = ViewModelProviders.of(this);
addTraderViewModel = viewViewModelProvider.get(AddTraderViewModel.class);
Observer<String> () {
#Override
public void onChanged (String message){
Debug.d(TAG, "onChanged: message = " + message);
Toast.makeText(AddTraderActivity.this, message, Toast.LENGTH_LONG).show();
}
});
}
public void onClickStart() {
EditText baseEditText = findViewById(R.id.baseEditText);
EditText quoteEditText = findViewById(R.id.quoteEditText);
addTraderViewModel.doClickStart(baseEditText.getText().toString(), quoteEditText.getText().toString());
}
}
Here my ViewModel:
public class AddTraderViewModel extends AndroidViewModel {
private MutableLiveData<String> messageLiveData = new MutableLiveData<>();
private static final String TAG = AddTraderViewModel.class.getName();
public AddTraderViewModel(#NonNull Application application) {
super(application);
}
public void doClickStart(String base, String quote) {
Debug.d(TAG, "doClickStart: ");
if (base.trim().isEmpty() || quote.trim().isEmpty()) {
String message = getApplication().getApplicationContext().getString(R.string.please_input_all_fields);
messageLiveData.setValue(message);
return;
}
}
public LiveData<String> getMessageLiveData() {
return messageLiveData;
}
}
So when I click on button on Activity call method onClickStart()
If any fields is empty the show toast. In the activity call method:
onChanged (String message)
Nice. It's work fine.
But the problem is, when I rotate the device in the activity method onChanged(String message) is called AGAIN and as result show toast. This happened on every rotation.
Why?
This is the expected behaviour. If you want to avoid this you must set message = "" and keep an empty check before showing the toast.
A better way to use it is something like Event Wrapper or SingleLiveEvent
Highly recommend you to read this article. This explains why you are facing this and what are your options in detail.

How to handle the setText("Some Text") event on a disabled TextBox

I want to fire an event when setting a value in a TextBox with setText("something")
What I tried so far
w.addChangeHandler(new ChangeHandler() {
#Override
public void onChange(ChangeEvent event) {
isChanged = true;
}
});
And I tried also to have a custom a TextBox that listen to past event
class CustomTextBox extends TextBox implements HasHandlers {
private HandlerManager handlerManager;
public CustomTextBox() {
super();
handlerManager = new HandlerManager(this);
sinkEvents(Event.ONPASTE);
}
#Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
switch (event.getTypeInt()) {
case Event.ONPASTE:{
fireEvent(new TextChangeEvent());
break;
}
default:
// Do nothing
}
}
#Override
public void fireEvent(GwtEvent<?> event) {
handlerManager.fireEvent(event);
}
public HandlerRegistration addTextChangeEventHandler(TextChangeEventHandler handler) {
return handlerManager.addHandler(TextChangeEvent.TYPE, handler);
}
}
With no success, any suggestions would be appreciated.
setText won't fire events.
Add a ValueChangeHandler<String> to your TextBox, then use setValue("something", true) to fire a ValueChangeEvent<String> to all handlers while setting the new value, whether the box id disabled or not.

ViewPagerIndicator not snapping or displaying pager contents all of a sudden

This is a wierd problem.
This will be a terrible question because I have little to no information.
About two days ago I had the ViewPagerAdapter working just fine. I could swipe and it would switch between views as defined by the adapter.
However, all of a sudden (not by itself, I'm sure I did something) the TitlePagerIndicator doesn't snap to the headings and doesn't display any content. By not snapping I mean that if I drag to the left, the title will sit at 3/4 of the screen instead of snapping to the side and displaying the next page (screenshot below).
I have debugged and instantiate item is called and a proper view is returned.
However, when I open the app I'm getting a lot of warnings like these:
VFY: unable to resolve virtual method 3015: Landroid/widget/LinearLayout;.getAlpha ()F
VFY: unable to resolve direct method 3011: Landroid/widget/LinearLayout;. (Landroid/content/Context;Landroid/util/AttributeSet;I)V
VFY: unable to resolve virtual method 2965: Landroid/widget/FrameLayout;.setAlpha (F)V
I'm assuming this is a problem with my imports, but everything compiles just fine, I have the ViewPagerIndicator as a library project, as well as Sherlock.
Here's my adapter code:
public class ViewPagerAdapter extends PagerAdapter implements TitleProvider {
private static String[] titles = new String[] {
"My Klinks",
"Received Klinks"
};
private final Context context;
public ViewPagerAdapter(Context context) {
this.context = context;
}
public String getTitle(int position) {
return titles[position];
}
#Override
public int getCount() {
return titles.length;
}
#Override
public Object instantiateItem(View pager, int position) {
TextView t = new TextView(context);
t.setText("WheeeE");
return t;
}
#Override
public void destroyItem(View pager, int position, Object view) {
((ViewPager) pager).removeView((TextView) view);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
#Override
public void finishUpdate(View view) {
}
#Override
public void restoreState(Parcelable p, ClassLoader c) {
}
#Override
public Parcelable saveState() {
return null;
}
#Override
public void startUpdate(View view) {
}
}
And here is my activity code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.main );
// set up the slidey tabs
ViewPagerAdapter adapter = new ViewPagerAdapter( this );
ViewPager pager = (ViewPager)findViewById( R.id.viewpager );
TitlePageIndicator indicator = (TitlePageIndicator)findViewById( R.id.indicator );
pager.setAdapter( adapter );
indicator.setViewPager( pager );
// set up the action bar
final ActionBar ab = getSupportActionBar();
ab.setBackgroundDrawable(getResources().getDrawable(R.drawable.ad_action_bar_gradient_bak));
}
If someone else gets the same problem:
In instantiateView: don't forget to attach your new View to the ViewPager:
#Override
public Object instantiateItem(View pager, int position) {
TextView t = new TextView(context);
t.setText("WheeeE");
((ViewPager)pager).addView(t);
return t;
}
The current version of instantiateItem gets a ViewGroup instead of a View, the solution should be the same.
Well after a couple days of banging my head against a wall I've come to the conclusion that my ViewPagerAdapter was the problem.
I simply created a dynamic fragment and created a subclass of FragmentPagerAdapter instead and now it works just fine...

Demultiplex / delegate for GWT event system, using enums

I have an enum, say Fruits { Apple, Banana, Cherry }. I want to write a event subsystem for my application so that I can have the following pattern :
class AppleListener implements HasFruitPriceChangeListener<Apple> {
onFruitPriceChange(int newPrice) {
// do stuff
}
}
and a single listener, that can delegate tasks in the following format:
class FruitPriceListener {
public void onPriceChange(EnumMap<Fruits, Integer> pricePoints) {
// for each fruit, throw the event as
// FruitPriceChange.fire(Apple, newPrice);
}
}
Is there a way to do it in the above manner ? I would probably like to use ValueChangeEvent, but creating another 1 event and handler is also fine too. What I do not want to do is have event/class definitions for each item, like AppleFruitPriceChangeEvent, and so on.
You can use the EventBus for this things, which google suggested ( http://www.youtube.com/watch?v=PDuhR18-EdM ) Here how to use it.
Your globl Eventbus
public static SimpleEventBus bus = new SimpleEventBus();
Your change event:
import com.google.gwt.event.shared.GwtEvent;
import eyeweb.client.gwtMessages.JSPollingEntry;
public class EventModified extends GwtEvent<EventModifiedHandler> {
public final static Type<EventModifiedHandler> TYPE = new Type<EventModifiedHandler>();
private final Fruits fruits;
public final JSPollingEntry getPollingMessage(){
return fruits;
}
public EventModified(Fruits fruits) {
this.fruits = fruits;
}
#Override
public com.google.gwt.event.shared.GwtEvent.Type<EventModifiedHandler> getAssociatedType() {
return TYPE;
}
#Override
protected void dispatch(EventModifiedHandler handler) {
handler.onUpdateRecivde(this);
}
}
the handler for the event
package eyeweb.client.eventbus;
import com.google.gwt.event.shared.EventHandler;
public interface EventModifiedHandler extends EventHandler {
public void onUpdateRecivde(EventModified handler);
}
The event when something changes
EventBus.bus.fireEvent(new EventModified(fruite));
and the handler which gets the event
EventBus.bus.addHandler(EventModified .TYPE, new EventModifiedHandler() {
#Override
public void onMessageSend(EventSendData e) {
//... do stuff }
});
Well that sould be all ;)
Regards,
Stefan
So the solution I came up with was:
Create the enum, and associate a GwtEvent.Type with them:
enum Fruits {
Apple, Banana, Cherry;
public GwtEvent.Type getGwtEventType() {
return new GwtEvent.Type();
}
}
Create a new event.
class FruitPriceChangeEvent extends GwtEvent<?> {
private final Fruit fruit;
FruitPriceChangeEvent(Fruit fruitEnum) {
this.fruit = fruitEnum;
}
#Override
public GwtEvent.Type<?> getAssociatedType() {
return fruit.getGwtEventType();
}
// ... other stuff...
}
And then pass it through the whole event handler loop as #Stefan has mentioned. The beauty/hack of this approach is that the SimpleEventBus maintains a HashMap<GwtEvent.Type, List<HasHandlers>> from which to get the events, and everytime you create a new GwtEvent.Type it generates a unique hashcode (check the implementation for more details).
References:
http://grepcode.com/file/repo1.maven.org/maven2/com.google.gwt/gwt-servlet/2.1.1-rc1/com/google/gwt/event/shared/GwtEvent.java?av=f
http://grepcode.com/file/repo1.maven.org/maven2/com.google.gwt/gwt-servlet/2.1.1-rc1/com/google/gwt/event/shared/SimpleEventBus.java?av=f

screens in blackberry

how can i navigate screens on button click.
there are two buttons , previous and next.
when i click the previous button, a screan1 will be displayed and when i press next ,a screan2 will be displayed and so on.
There are a couple of different ways to do this. The UI library on a Blackberry will maintain a stack of screens for you so you can either:
1) when a button is pressed, create a new screen and push it onto the stack, then remove the old screen and let it be garbage collected; or
2) when a button is pressed, determine if a screen for that button already exists on the stack and move it to the front, or create a new one as above. In this case you don't have to remove the screens, but if you have a lot of complex screens you could run out of resources.
See: net.rim.device.api.ui.UiApplication.pushScreen(Screen screen) and net.rim.device.api.ui.UiApplication.popScreen(Screen screen)
Here's a simple implementation:
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
public class NavigateScreen extends MainScreen implements FieldChangeListener {
private ButtonField previousButton;
private ButtonField nextButton;
public NavigateScreen() {
add(new LabelField("Some content"));
previousButton = new ButtonField("Previous", ButtonField.CONSUME_CLICK);
previousButton.setChangeListener(this);
nextButton = new ButtonField("Next", ButtonField.CONSUME_CLICK);
nextButton.setChangeListener(this);
add(previousButton);
add(nextButton);
}
public void fieldChanged(Field field, int context) {
if (field == previousButton) {
UiApplication.getUiApplication().popScreen(NavigateScreen.this);
UiApplication.getUiApplication().pushScreen(new PreviousScreen());
}
if (field == nextButton) {
UiApplication.getUiApplication().popScreen(NavigateScreen.this);
UiApplication.getUiApplication().pushScreen(new NextScreen());
}
}
}
Hi I have created a SwitchScreenController class to make switching between screen very easly.
public class SwitchScreenController {
public void switchHomeScreen() {
}
public void switchToHomeScreen() {
//UiApplication.getUiApplication().pushScreen(new HomeScreen());
}
public void switchToProgressingScreen() {
}
public void switchToNextScreen(MainScreen targetScreen) {
UiApplication.getUiApplication().pushScreen(targetScreen);
}
public void switchToPreviousScreen() {
UiApplication.getUiApplication().popScreen(
UiApplication.getUiApplication().getActiveScreen());
}
}
you can use it any where like this.
new
public class SwitchScreenController {
public void switchHomeScreen() {
}
public void switchToHomeScreen() {
//UiApplication.getUiApplication().pushScreen(new HomeScreen());
}
public void switchToProgressingScreen() {
}
public void switchToNextScreen(MainScreen targetScreen) {
UiApplication.getUiApplication().pushScreen(targetScreen);
}
public void switchToPreviousScreen() {
UiApplication.getUiApplication().popScreen(
UiApplication.getUiApplication().getActiveScreen());
}
}
you can go back using
new SwitchScreenController().switchToPreviousScreen();
and to next screen using
new SwitchScreenController().switchToNextScreen(new NextScreen());

Resources