Iam having a tree viewer in a view. I have a implemented a listener to the tree content provider. When some data changes, the tree is notified. But when iam trying to refresh my treeviewer, an error message comes and show that the tree is disposed. Whats the issue with my refreshing action and why the tree gets disposed.
Here i a snippet of my code.
getChildren()
{
resource.addListener(this);
}
public void dataChangeListener(changeddata)
{
tree.refresh(changedata,true); // Tree shows as disposed.
}
Regards,
Girish
All updates to the UI need to be done within the UI thread. You can do that with the following code. You should also check to see if the tree has been disposed before calling refresh.
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
if (!tree.getTree().isDisposed()) {
tree.refresh(changedData, true);
}
}
});
Related
I have a Spring Boot project with Vaadin integration (v14). I want my application to do some background operation and represent the results on the Vaadin-based frontend. For this I have a view which is a Polymer template generated with Vaadin Designer (.js) and connected to a Java companion class. To this view I've just simply added a button initialized with the following listener:
_btnMyTriggerButton.addClickListener(event -> {
CompletableFuture<Void> c = CompletableFuture.supplyAsync(() -> {
for (int i = 0; i < 5; i++)
{
try
{
System.out.println("Waiting");
Thread.sleep(1000);
UI.getCurrent().access(() -> {
Notification.show("Waiting");
});
}
catch (InterruptedException e)
{
}
}
return "Waiting over. Greet!";
}).thenAccept(s -> {
System.out.println(s);
UI.getCurrent().access(() -> {
Notification.show(s);
});
});
I'm trying to access the UI as the documentation says. However when this is being executed it only reaches the first "Waiting", then stops. If I remove the UI interaction (Notification.show()) the output is printed to the backend as desired, but not when attempting do any interaction on the UI..
The structure of my java companion class is the following:
#Tag("my-view")
#JsModule("./my-view.js")
#Route("")
#Push(PushMode.AUTOMATIC)
public class MyView extends PolymerTemplate<MyView.MyModel>
{
#Id("trigger-button")
private Button _btnMyTriggerButton;
MyView() {
// listener initialization code described above
}
public interface MyModel extends TemplateModel
{
}
}
Do I miss something to achieve asynchronous behavior on this webpage?
Any help is appreciated.
The problem in this case is that the first callback terminates with a NullPointerException because UI.getCurrent() returns null when it's run from a background thread. CompletableFuture will just ignore the exception unless you explicitly handle it (e.g. using handle instead of thenAccept) or block on the result.
You can fix this by adding UI ui = UI.getCurrent(); in the beginning of the click listener and then referencing ui in both the supplyAsync and thenAccept callbacks instead of using UI.getCurrent() there.
I am using Android Studio 3
I am following this article to learn how to use Google Recaptcha in Android Studio.
Installed the package using this: implementation 'com.google.android.gms:play-services-safetynet:12.0.1'
API keys are also registered.
I saw there is onClick event handler but where is it mentioned about rendering the recaptcha?
Update 1
When I wrote the button click code as mentioned in the link...I got a complication error: inconvertible types cannot cast anonymous android.view.view.onclicklistener to java.util.concurrent.executor
Code as asked in comment
btn_Login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View view) {
SafetyNet.getClient(this).verifyWithRecaptcha("")
.addOnSuccessListener((Executor) this,
new OnSuccessListener<SafetyNetApi.RecaptchaTokenResponse>() {
#Override
public void onSuccess(SafetyNetApi.RecaptchaTokenResponse response) {
// Indicates communication with reCAPTCHA service was
// successful.
String userResponseToken = response.getTokenResult();
if (!userResponseToken.isEmpty()) {
// Validate the user response token using the
// reCAPTCHA siteverify API.
}
}
})
.addOnFailureListener((Executor) this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
if (e instanceof ApiException) {
// An error occurred when communicating with the
// reCAPTCHA service. Refer to the status code to
// handle the error appropriately.
ApiException apiException = (ApiException) e;
int statusCode = apiException.getStatusCode();
} else {
}
}
});
}
});
I used below code and everything is work fine now.
Make sure to implement Executor in the activity
btn_Login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View view) {
SafetyNet.getClient(Activity.this).verifyWithRecaptcha("")
.addOnSuccessListener((Activity) MyActivity.this,
new OnSuccessListener<SafetyNetApi.RecaptchaTokenResponse>() {
#Override
public void onSuccess(SafetyNetApi.RecaptchaTokenResponse response) {
// Indicates communication with reCAPTCHA service was
// successful.
String userResponseToken = response.getTokenResult();
if (!userResponseToken.isEmpty()) {
// Validate the user response token using the
// reCAPTCHA siteverify API.
}
}
})
.addOnFailureListener((Activity) MyActivity.this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
if (e instanceof ApiException) {
// An error occurred when communicating with the
// reCAPTCHA service. Refer to the status code to
// handle the error appropriately.
ApiException apiException = (ApiException) e;
int statusCode = apiException.getStatusCode();
} else {
}
}
});
}
});
According to the article, in your button click handler you must call the method SafetyNet.getClient(this).verifyWithRecaptcha(...) to show reCAPTCHA and handle success or error. Passing this, you give the SDK handle to your current view which should be shown after solving reCAPTCHA. Most probably the rendering will be done by the SDK itself given that it’s a part of the OS. And most probably it will be full-screen in a separate top-level view blocking access to your app before solving the riddle.
You should try to implement it in your app as described in the article and see how it goes. Then you can ask a more specific question.
EDIT: You combined 2 techniques in your code: copy-pasting the code from Google and implementing anonymous class from it. So the problem you asked in the comment is that using (Executor) this in line 5 refers now not to your View (as it was there in the original tutorial) but to the instance of the anonymous interface implementation new View.OnClickListener() that you created. Ypu can refer to this answer to see how it can be implemented not interfering with already complex reCAPTCHA code.
using rxBindings im trying to slow down a click event but i would like to know what the parameter is need.
For example, here is a call i am doing on a imageview. So ImageView v;
RxView.clicks(v)
.throttleFirst(400, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe(new Consumer<Object>() {
#Override
public void accept(#io.reactivex.annotations.NonNull Object v) throws Exception {
showBottomSheet(getAdapterPosition());
}
});
but im im not sure what the parameter in accept should be ?
I was expecting i would get the view here but when i try changing the type to View i get an error of no such method.
If you look at the source code of the Observable generate using RxView.clicks(), you will see that when the click happens, the following code is triggered:
observer.onNext(Notification.INSTANCE);
that is defined in the library, as:
public enum Notification {
INSTANCE
}
It is just a convenient way for indicating that the event happened, it doesn't carry any extra information.
I need to recreate new page instance on every page load (also when user pressed Back button).
So I overrided OnBackKeyPress method:
protected override void OnBackKeyPress(CancelEventArgs e)
{
base.OnBackKeyPress(e);
if (NavigationService.CanGoBack) {
e.Cancel = true;
var j = NavigationService.RemoveBackEntry();
NavigationService.Navigate(j.Source);
NavigationService.RemoveBackEntry();
}
}
The problem is that I can't handle case when user press back button to close CustomMessageBox dialog. How can I check it? Or is there any way to force recreation of page instance when going back through history state?
Why do you need to recreate the page instance? If you are simply trying to re-read the data to be displayed, why not put the data loading logic into OnNavigatedTo()?
Assuming that is what you are actually trying to achieve, try something like this...
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// don't do your data loading here. This will only be called on page creation.
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
LoadData();
base.OnNavigatedTo(e);
}
MyViewModel model;
async void LoadData()
{
model = new MyViewModel();
await model.LoadDataAsync();
}
}
If you also have specific logic that you need to run on first construction of the page vs. on a back key navigation, check the NavigationMode property of the NavigationEventArgs object that gets passed to OnNavigatedTo.
if(e.NavigationMode == NavigationMode.New)
{
//do what you need to do specifically for a new page instance
}
if (e.NavigationMode == NavigationMode.Back)
{
// do anything specific for back navigation here.
}
Ha, in the near thread, i have opposite question :)
What about MessageBox - it depends, which one are you using. It can be custom message box, for example. Anyway, try to check MessageBox.IsOpened (or alternative for your MessageBox) in your OnBackKeyPress().
Another solution is to use OnNavigatedTo() of the page you want to be new each time.
Third solution: in case you works with Mvvm Light, add some unique id in ViewModel getter, like
public MyViewModel MyViewModel
{
get
{
return ServiceLocator.Current.GetInstance<MyViewModel>((++Uid).ToString());
}
}
This would force to recreate new ViewModel each time, so you'd have different instance of VM, so you would have another data on the View.
I have a list of items that goes to another page, That page is hooked up to a view model. In the constructor of this view model I have code that grabs data from the server for that particular item.
What I found is that when I hit the back button and choose another item fromt hat list and it goes to the other page the constructor does not get hit.
I think it is because the VM is now created and thinks it does not need a new one. I am wondering how do I force a cleanup so that a fresh one is always grabbed when I select from my list?
I faced the same issue, that's how i solved it.
Have a BaseView class, override OnNavigatedTo
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigatedToCommand != null && NavigatedToCommand.CanExecute(null))
NavigatedToCommand.Execute(null);
}
add DependencyProperty.
public static readonly DependencyProperty NavigatedToCommandProperty =
DependencyProperty.Register("NavigatedToCommand", typeof(ICommand), typeof(BaseView), null);
public ICommand NavigatedToCommand
{
get { return (ICommand)GetValue(NavigatedToCommandProperty); }
set { SetValue(NavigatedToCommandProperty, value); }
}
On the necessary pages, add to xaml (and, of course, inherit BaseView )
NavigatedToCommand="{Binding OnNavigatedToCommand}"
In the ViewModel, make command itself
public RelayCommand OnNavigatedToCommand
{ get { return new RelayCommand(OnNavigatedTo); } }
and implement method you want to call to update list
public async void OnNavigatedTo()
{
var result = await myDataService.UpdateMyList();
if (result.Status == OK)
MyList = result.List;
}
So, now, every time you navigate to page with list, inside of overriden OnNavigatedTo(), a NavigatedToCommand would be executed, which would execute OnNavigatedToCommand (which you set in xaml), which would call OnNavigatedTo, which would update your list.
A bit messy, but MVVM :)
EDIT: What about cleanings, they can be done in OnNavigatedFrom(), which works the same. Or OnNavigatingFrom(), which also can be useful in some cases.