I'm trying to set a Listener for my custom views that I put in a LinearLayout. Those views are created by code depending on a certain files or data, so I don't know the number of views there is. The OnClickListener is being set, but onClick() is never called:
#Override
protected void onResume() {
super.onResume();
try {
setupBackEnd();
} catch (IOException e) {
Toast.makeText(this, "Error Reading or Writing data in Storage, Try to restart the App", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
Toast.makeText(DayActivity.this, "Child count: " + linearList.getChildCount(), Toast.LENGTH_SHORT).show();
for (int i = 0; i < linearList.getChildCount(); i++) {
linearList.getChildAt(i).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(DayActivity.this, "clicked", Toast.LENGTH_SHORT).show();
}
});
}
}
This is my code. To make sure the child views are already there I showed a Toast and it gave me "Child Count: 5" but "clicked" Toast never shows up. I made sure that My custom Views are clickable (The childs of linearlist). But the listener won't work for some reason. Anyone knows why?
Edit: After I tried to debug the code I got this error:
10-27 18:52:26.309 17630-17630/osm_cave.timecave E/AndroidRuntime: FATAL EXCEPTION: main
Process: osm_cave.timecave, PID: 17630
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy#672721e is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:679)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.widget.Toast$TN.handleShow(Toast.java:459)
at android.widget.Toast$TN$2.handleMessage(Toast.java:342)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
c
This error won't be shown when I launch the app without debugging it only appears when Debugging with breakpoints. This error is supposed to mean I'm trying to use a context that no longer exist but I'm not so what's the problem here??
It looks like your toast message is running into an error.
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.widget.Toast$TN.handleShow(Toast.java:459)
at android.widget.Toast$TN$2.handleMessage(Toast.java:342)
at android.os.Handler.dispatchMessage(Handler.java:102)
If I remember correctly, the new object that you are creating of type View.OnClickListener (since it is anonymous) cannot access the DayActivity.this attribute. Instead you should get the activity from the view.
public void onClick(View view) {
Activity activity = (Activity) view.getContext()
Toast.makeText(activity, "clicked", Toast.LENGTH_SHORT).show();
}
While there's nothing wrong with using a Toast message to test for an event occurring, I would recommend simplifying to just using a log message. Log messages aren't context sensitive are unlikely to fail except if you provide a null message to it.
If Log messages are not working, the next troubleshooting step should be to examine the custom views and view hierarchy. If any clickable transparent views are in front of the custom views, they would intercept the click events. Double check that the view itself is clickable as well.
Related
I have two classes TrapCard and EquipmentCard, from which I want to make an object and pass this object through activities. I put a string and a int variable into my parcelable object. The int variable is a reference to an .svg image. And then I'm trying to get the reference from the object to load the .svg image in another activity. For TrapCard this works. But when I'm doing the excat same thing for EquiptmentCard my app crashes with E/Zygote: isWhitelistProcess - Process is Whitelisted
Constructor from EquiptmentCard:
public EquipmentCard(String cardName){
this.cardName = cardName;
switch (cardName){
case "Rope": cardPicRes = R.drawable.ic_rope;
break;
}
}
And here I'm trying to get the reference and load the image
final EquipmentCard equipCard = new EquipmentCard(cardName);
returnIntent.putExtra("card",equipCard);
returnIntent.putExtra("cardType","E");
runOnUiThread(new Runnable() {
#Override
public void run() {
imgViewCard.setImageDrawable(getResources().getDrawable(equipCard.getCardPicRes(), getApplicationContext().getTheme()));
}
});
Here same thing for TrapCard, this works
final TrapCard trapCard = new TrapCard(cardName);
returnIntent.putExtra("card",trapCard);
returnIntent.putExtra("cardType","F");
runOnUiThread(new Runnable() {
#Override
public void run() {
imgViewCard.setImageDrawable(getResources().getDrawable(trapCard.getCardPicRes(), getApplicationContext().getTheme()));
}
});
I'm doing this within the receiveDetections function from the Google mobileVisionAPI
with debugging I can see that my code runs to this line
imgViewCard.setImageDrawable(getResources().getDrawable(equipCard.getCardPicRes(), getApplicationContext().getTheme()));
and then crashes with the mentioned error in logcat.
I put this in try and catch but there is no error I receive. Thank you in advance for your time and have a nice day.
UPDATE1: I set the image in EquipmentCard to ic_spider1 and it worked. I have no clue whats wrong with ic_rope.
UPDATE2: Now i get an exception android.content.res.Resources$NotFoundException: Drawable xxx.xxx:drawable/ic_rope with resource ID #0x7f060072 but i have no clue why. I added this the same way i added spider. Delete and adding again don't work.
UPDATE3: Alright. It seems that my ic_rope.xml has a large string inside. I tried to rebuild my Project and then it shows the message string too large to encode using UTF-8. If i delete the ic_rope.xml i can rebuild without any errors.
Try replacing
imgViewCard.setImageDrawable(getResources().getDrawable(equipCard.getCardPicRes(), getApplicationContext().getTheme()));
with:
setImageResource(R.id.your_image_id)
here is the problem. I am using a custom component from someone else called a "Fancy showcase view". It focuses on buttons in my activity on highlights them with a text as a tutorial through the app. I am starting the first message, and when the user dissmises this by clicking anywhere in the activity, the next button is supposed to be highlighted. Unfortunately, the component, which otherwise is perfect, doesnt have a listener implemented like "OnDismis" of the first tutorial view so the next could start. Just putting both into code one after the other skips the second one. It also tried working with lifecycle methods, such as OnFocuseChanged() but even after the tutorial gets dismissed, this method isnt called a second time. What would you guys say is the best way to handle this? Here is what is NOT working:
try
{
new FancyShowCaseView.Builder(this) // if this crashes, we need clean rebuild
.Title(title1)
.TitleStyle(0, (int)GravityFlags.Center | (int)GravityFlags.Center)
.Build()
.Show();
}
catch (Exception e)
{
Toast.MakeText(this, "There was an error ... " + e, ToastLength.Short).Show();
}
try
{
new FancyShowCaseView.Builder(this) // if this crashes, we need clean rebuild
.Title("TEST")
//.TitleStyle(0, (int)GravityFlags.Center | (int)GravityFlags.Center)
.FocusOn(txtL)
.Build()
.Show();
}
catch (Exception e)
{
Toast.MakeText(this, "There was an error ... " + e, ToastLength.Short).Show();
}
The second one doesnt show up. There are no event handlers and I cannot make use of lifecycle methods. Click counting wouldnt work either, since the user might click on the activity while it is loading so hard coded values arent a good option either. Any ideas?
Thanks:)
Use the FancyShowCaseQueue to control the sequence.
You add individual FancyShowCaseViews to it and when you "Show()" the queue, each FancyShowCaseView happens in the order in-which you added them to the queue.
Example:
var fancyView1 = new FancyShowCaseView.Builder(this)
.Title("StackOverflow 1")
.FocusOn(button1)
.Build();
var fancyView2 = new FancyShowCaseView.Builder(this)
.Title("StackOverflow 2")
.FocusOn(button1)
.Build();
var fancyQueue = new FancyShowCaseQueue()
.Add(fancyView1)
.Add(fancyView2);
fancyQueue.Show();
I am using a Xamarin.Android binding library of FancyShowCaseView, but you can review the Java-based examples are in the sample app in the repo, ie:AnimatedActivity.java
Also you can implement the ME.Toptas.Fancyshowcase.IDismissListener interface:
public void OnDismiss(string p0)
{
//
}
public void OnSkipped(string p0)
{
//
}
And use that implementation on each of your FancyShowCaseViews:
var fancyView2 = new FancyShowCaseView.Builder(this)
.Title("StackOverflow 2")
.FocusOn(button2)
.DismissListener(this)
.Build();
I am working on an Android app which will support the Ice cream sandwich API but work on older devices such as running android 2.1.
I'm doing this by doing a check of what the current API version is, and if its post ice cream sandwich call one activity and then if its anything below, call a different activity.
I am allowing the user to perform a search, when it gets the results it then clears ArrayList and then adds the items from the search back in, and then calls the arrayadapter.notifydatasetchanged. This code I've copied and pasted from the ICS version into the pre ICS version, the ICS works fine but on the pre ics version the list view doesn't get updated. Below is the code that I have.
public void performSearch(ArrayList<Spanned> searchPasswords)
{
if (searchPasswords.size() > 0)
{
btnClearSearch.setVisibility(View.VISIBLE);
passwords.clear();
for (int i = 0; i < searchPasswords.size(); i++)
{
passwords.add(searchPasswords.get(i));
}
passwordArrayAdapter.notifyDataSetChanged();
btnClearSearch.setOnClickListener(mBtnClearSearch);
common.showToastMessage(searchPasswords.size() + " result(s) found", Toast.LENGTH_LONG);
}
else
{
common.showToastMessage("No search results found", Toast.LENGTH_LONG);
}
}
I've debugged this function so it is definetely calling it, and its displaying the toast messags with saying how many results were found, but the list view doesn't get changed.
Update
I have just made a discovery which is a bit confusing. As a test in the function that does the initial loading of the list view I manually create a new search ArrayList<Spanned> and pass this to the same PerformSearch(ArrayList<Spanned>) that I am having the problem with and this works without any problems.
The problem with the performSearch not updating the ListView only seems to happen when it is being called from onActivityResult. I know that onActivityResult is working fine as when the performSearch function is called it then prints out there is 1 result(s) found so its definitely got data just the list view doesn't get refreshed from the onActivityResult calling the performSearch function.
You can try:
passwordArrayAdapter = new WHAT_EVER_ADAPTER_THIS_IS(searchPasswords, extra_constructor_params);
YOUR_LIST_VIEW.setAdapter(passwordArrayAdapter)
instead of:
passwordArrayAdapter.notifyDataSetChanged();
I had a similar issue. Neither notidfyDataSetChanged() nor setAdapter() worked.
Finally I figured out a solution which may not be as efficient as using notifyDataSetChanged() but it works.
/**
* Updates courses' list adapter.
*/
private void updateListAdapter()
{
final ArrayAdapter a = (ArrayAdapter) coursesList.getAdapter();
runOnUiThread(new Runnable()
{
public void run()
{
populteCoursesList();
a.clear();
for (MainScreenListModel model : listModels)
{
a.add(model);
}
a.notifyDataSetChanged();
}
});
}
courseList is my custom list. populateCoursesList() gets the data from db and listModels is a Vector of models for list view.
As you can see I have to clear the adapter and fill it once again.
I think your mistake is to forget to clear the adapter, if you try it like this it should work:
public void performSearch(ArrayList<Spanned> searchPasswords)
{
if (searchPasswords.size() > 0)
{
btnClearSearch.setVisibility(View.VISIBLE);
passwordArrayAdapter.clear();
for (int i = 0; i < searchPasswords.size(); i++)
{
passwordArrayAdapter.add(searchPasswords.get(i));
}
passwordArrayAdapter.notifyDataSetChanged();
btnClearSearch.setOnClickListener(mBtnClearSearch);
common.showToastMessage(searchPasswords.size() + " result(s) found", Toast.LENGTH_LONG);
}
else
{
common.showToastMessage("No search results found", Toast.LENGTH_LONG);
}
}
passwords.clear();
for (int i = 0; i < searchPasswords.size(); i++)
{
passwords.add(searchPasswords.get(i));
}
passwordArrayAdapter.notifyDataSetChanged();
As per the code, u r modifying the list(passwords), but is it the same instance that is being used by the adapter (passwordArrayAdapter). If not then notifyDataSetChanged() wont work.
From my understanding, you can either set the list again with a new instance of passwordArrayAdapter.
ur_list_view.setAdapter(new PasswordArrayAdapter(newly_formed_password_list, and other parameters));
or use getter and setter to update the list(passwords) and then call notifyDataSetChanged()
I dont know much from only the code snippet you've pasted but from what I see there is no persistent link between passwords and the passwordsArrayAdapter....passwordsArrayAdapter initialises itself with the elements of passwords and passwordsArrayAdapter will not know when passwords changes. To add new items you should call passwordsArrayAdapter.add(searchPasswords).Dont bother calling notifyDataSetChanged() explicitly.
public void performSearch(ArrayList<Spanned> searchPasswords)
{
if (searchPasswords.size() > 0)
{
btnClearSearch.setVisibility(View.VISIBLE);
passwordArrayAdapter.clear();
passwordArrayAdapter.setNotifyOnChange(true);
for (int i = 0; i < searchPasswords.size(); i++)
{
passwordArrayAdapter.add(searchPasswords.get(i));
}
btnClearSearch.setOnClickListener(mBtnClearSearch);
common.showToastMessage(searchPasswords.size() + " result(s) found", Toast.LENGTH_LONG);
}
else
{
common.showToastMessage("No search results found", Toast.LENGTH_LONG);
}
}
I wouldnt think there'd be a relation with the onActivityResult bit -- the above code should fix it.
Call notifyOnChange(false) before clearing and adding the new entries. Otherwise, the clear() and every add() internally trigger a call to notifyDataSetChanged(), which, as I experienced, confuses the ListView. Keep your explicit notifyDataSetChanged(), which also resets the internal notifyOnChange flag to true again.
Thanks everyone for your help and suggestions, I've managed to find out what the problem was.
In my OnResume method for the activity I call a function called populateListArray which retrieves all of the information from the database and lists in the ListView.
When the activity was returned it was calling the OnResume method so after it got the results it then reset the list view back with all the original content.
To get round it I added a variable called performingSearch which I initialise to false. When I perform the search I set this to true and in the onResume, if the variable is true I don't do anything and after the perform search function has finished I set the variable back to false again.
Thanks everyone again for your help
I'm trying to use a Toast message inside a listener method, but I get an error saying something like: Toast is not applicable for the argument.. I don't understand this and can't solve this problem without some help? Thanks!
// Button 1
button_1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//text_1.setText("New text for first row!"); // Change text
Toast.makeText(this, "You have clicked on number 1", Toast.LENGTH_LONG).show();
if(controlImage_1)
{
image_1.setImageResource(R.drawable.android_1b); // Change image
controlImage_1 = false;
}
else
{
image_1.setImageResource(R.drawable.android_1a); // Change image
controlImage_1 = true;
}
//Toast.makeText(this, "Your download has resumed.", Toast.LENGTH_LONG).show();
}
});
Try it:
Toast.makeText(**YourClassName.this**, "You have clicked on number 1", Toast.LENGTH_LONG).show();
Without * =)
Toast.makeText(this, "You have clicked on number 1", Toast.LENGTH_LONG).show();
Your this in this statement refers to the View.OnClickListener you created. Read more on anonymous inner classes.
Use MyActivity.this instead
You need to pass in the Context of the current Activity as the first argument. You can't just say this in this case because it is not referring to the application context. You can either create a variable in your on create and use that context or just do this...
Toast.makeText([CLASS_NAME].this, "You have clicked on number 1", Toast.LENGTH_LONG).show();
Replace [CLASS_NAME] with the class that is extending Activity
Try getApplicationContext() as first argument for Toast.makeText or MyActivity.this
here in your code , this refers to the View
Also, if button_1 needs to have onclick listener by default and you are using API > 7,its nice to define onclick="myclickfunction" in the layout itself. Cleans up your code and easy to modify...
Because you are using it in the onClick Listener method, you cannot use only this as the first parameter.
Toast.makeText(ClassName.this, "You have clicked on number 1", Toast.LENGTH_LONG).show();
Use your classname.this as the first parameter.
I have a button that sends message - however the onClick feature in the onClickListener seems to give me a few errors when I tell it to disable the button when click, AND when I set toast to it. Oh, and the error underlines 'else' saying syntax error. (Other than that the code works fine and sends the SMS)
public void onClick(View v)
{
messageinfo mi = new messageinfo();
String message = txtMessage.getText().toString();
if (message.length()>0)
sendSMS(mi.SMSNO(), smsmessage);
Toast.makeText(getBaseContext(), "sending", Toast.LENGTH_SHORT).show();
myButton.setEnabled(false);
else
Toast.makeText(getBaseContext(), "enter your message", Toast.LENGTH_SHORT).show();
}
Is there an easy solution to this?
Through formatting your code I've noticed that it would appear that your if statement is failing.
You need to have { }s around the resulting code from an if statement, eg:
if(true){
line 1;
line 2;
line 3;
}
else {
line 1;
line 2;
line 3;
}
The only time you can miss out the { }s is when you've only got ONE statement following an if. This is always a bad idea however, its not much effort just to put in some { }s, and means that issues like this won't ever happen.
Clarification
The code in your question is running the if and if it matches it runs THE NEXT LINE. Then the code carries on OUTSIDE of the if clause (because of missing brackets)so then will run the next two lines REGARDLESS of the outcome of the if statement.
Then it encounters the else which its not expecting. Its supposed to be after either { .. } or directly after the FIRST statement after the if, so that's why it fails at the else.