我想对多个EditText设置监听事件,但看起来好像一次只能绑定一个资源,我知道OnClick方法可以绑定多个资源id,但EditText 好像不行,我不确定想问一下各位,英文不好请见谅
I'd like to set up listening events for multiple EditTexts, but it looks like I can only bind one resource at a time. I know the OnClick method can bind multiple resource ids, but EditText doesn't seem to work. I'm not sure I want to ask you, but I'm sorry for the English.
#OnTextChanged(**R.id.et_account ,R.id.et_password** , callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterTextChanged(Editable s) {
if (TextUtils.isEmpty(etPassword.getText().toString())) {
ivCleanPassword.setVisibility(View.GONE);
iv_eye.setVisibility(View.GONE);
} else {
ivCleanPassword.setVisibility(View.VISIBLE);
iv_eye.setVisibility(View.VISIBLE);
}
if (TextUtils.isEmpty(etAccount.getText().toString())) {
ivCleanAccount.setVisibility(View.GONE);
} else {
ivCleanAccount.setVisibility(View.VISIBLE);
}
}
Technically it’s possible:
#OnTextChanged({R.id.edittext1, R.id.edittext2})
but it’s a bit problematic because it’s hard to determine which view triggered the call.
For discussion on this topic see https://github.com/JakeWharton/butterknife/issues/672
Related
This gets called when a button is clicked
#Override
public void onFavoriteIconClicked() {
viewModel.isFavoriteExist(test.getId()).observe(getViewLifecycleOwner(), new Observer<Boolean>() {
#Override
public void onChanged(Boolean aBoolean) {
viewModel.isFavoriteExist(test.getId()).removeObserver(this);
if (aBoolean) {
binding.addToFavorite.setImageResource(R.drawable.non_fav);
viewModel.delete(test);
} else if (getActivity() != null) {
Test test2 = new Test(test.getId(), test.getName());
viewModel.insert(test2);
binding.addToFavorite.setImageResource(R.drawable.fav);
}
}
});
}
If the test object exists in the Favorite database, I have to delete it. After deleting, it calls this again (since it observed a chane) and inserts it again.
It keeps looping infinitely. Is there a better way to implement this or stop this?
It seems like some business logic has entered your view (Activity) class.
Since LiveData & Room are meant to be used when receiving updates about Database changes is needed, and your use of the DB is not requiring constant updates, I would suggest going with a more direct approach.
First, Remove the use of LiveData from your Database. Use simple return values.
Your view (Activity/Fragment) can then tell the view model that a button was clicked.
#Override
public void onFavoriteIconClicked() {
viewModel.onFavoriteClicked()
}
The view will observe the view model in order to receive the correct icon to show.
Something like:
viewModel.favoriteIcon.observe(getViewLifecycleOwner(), new Observer<Integer>() {
#Override
public void onChanged(Integer iconResId) {
binding.addToFavorite.setImageResource(iconResId)
}
}
Now the viewModel can handle the logic (or better add a Repository layer - See Here)
Upon click, Check if entry exist in DB.
If exists: remove it from DB and set favoriteIcon value:
favoriteIcon.setValue(R.drawable.non_fav)
If doesn't exist: Add it to DB and set favoriteIcon value.
favoriteIcon.setValue(R.drawable.fav)
For a good tutorial about using Room & LiveData - as well as doing so using the View/ViewModel/Repository pattern, check this link
Is there a switch/flag that allows to do this? I spent hours finding those but couldn't find anything that works. The other thing I'm planning to do is intercept the cefRequest by adding my own CefRequestHandler, examine the resource type and if it matches RT_IMAGE, cancel the request. Everything seems easy except the part when I have to cancel a request. How do I stop/block/cancel a cefRequest? I probably should not be doing it this way but it doesn't work anyway:
public class CefClientRequestHandler extends CefRequestHandlerAdapter {
#Override
public boolean onBeforeResourceLoad(CefBrowser cefBrowser, CefFrame cefFrame, CefRequest cefRequest) {
if (cefRequest.getResourceType().equals(CefRequest.ResourceType.RT_IMAGE)) {
cefRequest.setURL("");
}
return false;
}
// more overides
}
Any ideas?
So here's a hack that works. The trick is to change the Request Method to HEAD, and because HEAD requests aren't returned the body, images won't be part of the response.
public class CefClientRequestHandler extends CefRequestHandlerAdapter {
#Override
public boolean onBeforeResourceLoad(CefBrowser cefBrowser, CefFrame cefFrame, CefRequest cefRequest) {
if (cefRequest.getResourceType().equals(RT_IMAGE)) {
cefRequest.setMethod("HEAD");
}
return false;
}
// other overridden methods here...
}
I believe that this approach should be avoided mainly because of the following two reasons:
Changing the method from GET to HEAD does not prevent CEF from making the request to the server. The overhead of opening a connection and handling a request is still there which makes it slower than simply blocking the request.
I'm not sure if images won't be displayed if they are available from browser cache. Currently, I don't know of any methods to test this. Suggestions are welcome.
Edit 1:
Changing URL didn't work in the example I posted in the question because I was passing an empty String as the new URL. If we set the URL to some address that is not an "active" domain name (e.g. https://absolutegarbage-sdjdjfbskdfb.com), the request for that resource fails immediately:
#Override
public boolean onBeforeResourceLoad(CefBrowser cefBrowser, CefFrame cefFrame, CefRequest cefRequest) {
if (cefRequest.getResourceType().equals(CefRequest.ResourceType.RT_IMAGE)) {
cefRequest.setURL("https://yghjbnbk.com");
System.out.println("LOL!");
}
return false;
}
As you can probably guess, this still is not the best solution. Please post an answer or comment if someone has found a better solution.
Edit 2: Finally I have a clean working solution, thanks to user amaitland. We just have to pass a command line switch while setting the CefAppHandler. We can do that by overriding the method onBeforeCommandLineProcessing like this:
CefApp.addAppHandler(new CefAppHandlerAdapter(null) {
#Override
public void onBeforeCommandLineProcessing(String s, CefCommandLine cefCommandLine) {
cefCommandLine.appendSwitch("disable-image-loading");
}
#Override
public void stateHasChanged(CefApp.CefAppState state) {
if (state == CefApp.CefAppState.TERMINATED) System.exit(0);
}
});
I have an application which consists mainly in a JList being displayed on the screen. I would like that everytime I make a change to the AbstractListModel(adding or removing items to the list) , to somehow notify the app that changes have been made and modify the JFrame's title to something like frame_title[unsaved]. After I would save the app, the [unsaved] tag would go away.
I think maybe using the observer/observable technique would do the job but I am not sure how to do it. Maybe there is something much more appropriate to my problem? I am new to java new to java so that is why I came here asking for help. Thanks.
UPDATE : I can't really use the Observer-pattern in my case because I am already extending the AbstractListModel class.
Use this:
AbstractListModel model = ...;
model.addListDataListener(new ListDataListener() {
public void intervalAdded(ListDataEvent e) {
}
public void intervalRemoved(ListDataEvent e) {
}
public void contentsChanged(ListDataEvent e) {
}
});
I have a textbox and one suggestbox. I attach a value change and key up handler to the text box such that whatever the user types (or pastes) into the text box is echo-ed inside the suggestbox. I can get the suggestbox to display the suggestion list by calling showSuggestionList on each value change and key up event.
Now, how do I get the suggestbox to automatically choose the first item in the suggestion list?
One of the methods I tried is to programatically simulate key presses, i.e
suggestBox.setFocus(true);
NativeEvent enterEvent = Document.get().createKeyPressEvent(false, false, false, false, KeyCodes.KEY_ENTER);
DomEvent.fireNativeEvent(enterEvent, suggestBox);
textBox.setFocus(true);
This doesn't work at all. The enter key isn't simulated. Another possible solution is to extend SuggestionBox.SuggestionDisplay, but I'm not too sure how to that. Any pointers appreciated.
Update: I'm still working on this and trying various methods.
Here, I tried to implement my own SuggestionDisplay by subclassing DefaultSuggestionDisplay and overriding getCurrentSelection() to make accessible from my class. This doesn't work either. Null is returned.
private class CustomSuggestionDisplay extends DefaultSuggestionDisplay {
#Override
protected Suggestion getCurrentSelection() {
return super.getCurrentSelection();
}
}
suggestBox.setAutoSelectEnabled(true);
textBox.addKeyUpHandler(new KeyUpHandler() {
public void onKeyUp(KeyUpEvent event) {
suggestBox.setValue(textBox.getText(), true);
suggestBox.showSuggestionList();
if (suggestBox.isSuggestionListShowing()) {
String s = ((CustomSuggestionDisplay) suggestBox.getSuggestionDisplay()).getCurrentSelection().getDisplayString();
Window.alert(s);
}
}
});
Here, I tried to attach a value change handler to the SuggestBox, and casting the event type to SuggestOracle.Suggestion. Again, null is returned.
suggestBox.addValueChangeHandler(new ValueChangeHandler<String>() {
public void onValueChange(ValueChangeEvent<String> event) {
String s = ((SuggestOracle.Suggestion) event).getDisplayString();
Window.alert(s);
}
});
Use suggesBox.setAutoSelectEnabled(true)
Here more info about the SuggestBox of GWT:
You could try using addSelectionHandler in conjunction with setAutoSelectEnabled to receive an event whenever a suggestion is selected. You could also have your Oracle send a message when it suggests something, or your Display send a message when it displays a list:
public class AutomaticallySelectingSuggestionDisplay extends SuggestBox.DefaultSuggestionDisplay {
#Override
protected void showSuggestions(SuggestBox box, Collection<? extends SuggestOracle.Suggestion> suggestions, boolean isDisplayHtml, boolean isAutoSelectEnabled, SuggestBox.SuggestionCallback callback) {
super.showSuggestions(box, suggestions, isDisplayHtml, isAutoSelectEnabled, callback);
fireValueChangeEventWithFirstSuggestion(suggestions);
}
}
This idea feels a little muddled to me, so I hope you can find a solution just using event handlers.
Hey all, I'm still relatively new to Java, and looking for a sanity check.
I've been studying this Java port of Cocos2D and noticed that the CCLayer class has built-in hooks to the Android native touch events. That's great, but what I'd really like is for objects like CCSprite to directly respond to touch events without having to listen for those events in the layer and iterate through all the children to find which ones happen to intersect the event's x/y coordinates. So I figured that this would be the perfect chance to test my understanding of how to set up some event handlers and make a subclass of CCSprite that actually listens for touches without needing to go through CCLayer to know about it. Furthermore, I wanted to be able to assign different behaviors to different CCSprite instances on an ad-hoc basis without explicitly subclassing further, much like Android Buttons don't need to be subclassed just to give them a handler for their touch events.
This is what I came up with on a first pass:
// My touch interface for all touchable CCNode objects.
package com.scriptocalypse.cocos2d;
public interface ITouchable {
boolean onCCTouchesBegan();
boolean onCCTouchesEnded();
boolean onCCTouchesMoved();
}
And now the class that uses the ITouchable interface for its callbacks...
public class CCTouchSprite extends CCSprite implements CCTouchDelegateProtocol {
protected ITouchable mTouchable;
public void setTouchable(ITouchable pTouchable){
mTouchable = pTouchable;
boolean enable = mTouchable != null;
this.setIsTouchEnabled(enable);
}
public void setIsTouchable(boolean pEnabled){
// code to enable and disable touches snipped...
}
/////
// And now implementing the CCTouchDelegateProtocol...
/////
public boolean ccTouchesBegan(MotionEvent event) {
Log.d("hi there", "touch me");
if(mTouchable != null){
mTouchable.onCCTouchesBegan();
}
return CCTouchDispatcher.kEventHandled; // TODO Auto-generated method stub
}
public boolean ccTouchesMoved(MotionEvent event) {
if(mTouchable != null){
mTouchable.onCCTouchesMoved();
}
return CCTouchDispatcher.kEventIgnored; // TODO Auto-generated method stub
}
public boolean ccTouchesEnded(MotionEvent event) {
Log.d("hi there", "not touch me");
if(mTouchable != null){
mTouchable.onCCTouchesEnded();
}
return CCTouchDispatcher.kEventIgnored; // TODO Auto-generated method stub
}
}
And finally, instantiate the class and implement the interface...
final CCTouchSprite sprite = new CCTouchSprite(tex);
sprite.setIsTouchEnabled(true);
sprite.setPosition(CGPoint.ccp(160,240));
sprite.setTouchable(new ITouchable(){
#Override
public boolean onCCTouchesBegan() {
Log.d("SWEET SUCCESS", "I got a touch through my interface!");
return true;
}
#Override
public boolean onCCTouchesEnded() {
Log.d("SWEET SUCCESS", "You stopped touching my interface!");
sprite.runAction(CCRotateBy.action(1, 360));
return false;
}
#Override
public boolean onCCTouchesMoved(){
Log.d("SWEET SUCCESS", "You moved the touch");
return false;
}
});
So all of this works. The subclass does successfully register with the Cocos2D touch dispatcher, which successfully calls those ccTouches functions and pass them MotionEvents, which in turn call my Interface functions if the interface has been instantiated.
Is this the "proper" way to do it (Define "it" as you see fit, ranging from using Interfaces to create event handlers to working with Cocos2D, to writing Java at all)? Are there drawbacks to this that I'm not aware of? Is this somehow worse for performance than iterating through all the CCNode objects that are children of CCLayer? If so, how can that possibly be?
I think you have got the basics for setting up a listener right. There are some things I would change though.
First, the setter setIsTouchable. It's weird. You need a listener object to pass touch events to right? So what is this setter going to do when you pass it true (as your example does)? You snipped the code, but setting a boolean field to true does not seem right here as it would put the sprite object in an inconsistent internal state. I would just drop that setter. The getter can just evaluate whether mTouchable is assigned or null.
Second, why limit yourself to one listener? Change mTouchable to mTouchables, being a list of ITouchables. Then change setTouchable to addTouchable and possibly add removeTouchable and clearTouchables methods. This way you can add multiple listeners for different behaviors having to respond to the same events. This is how most other event systems work as well. You then just change isTouchable to check whether the list is empty or not.
scriptoclypse... I really am not completely understanding your question, but you have not had any response and yes interfaces and events are very similar. At this level I can only respond in C#.