OnClickListeners not consistently firing - android

I have a listview where each list item can contain 1 or more clickable images. Clicking them starts an activity that shows a full screen version of the image.
The following code (slightly simplified) sets up the OnClickListeners within my GetView() implementation, for each image in each ListItem. This worked perfectly.
ImageView imageView = (ImageView)convertView.findViewById(R.id.image_res);
imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ActivityLauncher.startPhotoViewerActivity(mContext, item);
}
});
However, to improve performance, I decided to store each view in memory after constructing it the first time. I do this instead of using convertView because there are 4-5 different types of Views and they are sometimes dynamically constructed in code hence making re-use difficult.
As soon as I start storing the views in memory and then returning the already constructed views during subsequent calls to getView() based on the position, the performance significantly improves but the OnClickListeners start behaving funky.
Specifically, now when a given image is tapped, the OnClickListener for it may fire immediately or not at all. In the cases where it does not fire immediately, it may fire as soon as one starts scrolling. If this doesn't trigger the OnClickListener to run, clicking back (to quit the activity) may trigger it. If this doesn't trigger it, then closing all activities by pressing BACK) and then re-entering the app will trigger all the queued OnClickListeners. In one case I clicked 25 times on various images with no result. Then I closed all the activities and then clicked the application icon once again which opened all 25 image previews.
Any idea as to what is causing this? What is interesting to me is that the OnClickListeners are obviously set correctly because they do open the photo viewer activity at some point in time. The wrong image is never opened and there is never a case where they NEVER fire. The problem is that they may fire immediately or much later.
Is it not recommended to save the Views for each list item and return them based on position from within getView() like I am doing? Any help is greatly appreciated.

Watch the talk on ListViews given at Google I/O. At 13:45, they specifically warn against caching views and returning them. If one does this, it can lead to strange behavior, which is exactly what I am experiencing. Given this, the question is moot.
http://www.youtube.com/watch?v=wDBM6wVEO70
Hope this helps someone in the future avoid the same pitfalls. Caching views is NOT RECOMMENDED!!!

Related

Edit button in listview item depending on other listview items

I would like to start by saying if you can think of a better title for this problem, feel free to change it since I have no clue how to explain this in a short way.
So here is my problem:
For the application I am trying to make I have these schedules, one schedule for today, and one for upcoming days. Both of these are a listview inside a fragment.
(I used those fragments to create tabs to seperate the two.)
Each game (let's call them games because calling them activities would be confusing) on the schedule has a status, and here is where the annoying part comes. I have been asked to check if any game has been started, and if so I need to disable the buttons to start any other game than the one that is already ongoing.
EDIT: The main problem is that I cannot use findViewById on the listview item because apparently it is a null object reference
I made a little "paint"ing to give you more of a graphical representation.
So long story short, I need a way to check the status inside of every listview item inside of the listview inside of the fragment inside the activity to use said status to disable every button except for the one of the ongoing game.
Little side note: If no games have been started yet, all buttons are enabled.
I hope this made sense.
If you want some code to go with this, feel free to ask but be warned, I am making this inside a testing app so a lot of useless testing data and sloppy code.
EDIT:
This is where I am stuck in a more clear example:
The start buttons are enabled but should be disabled.
Scrolling further down the list, there is a started 'game' and right below it, a game with the same status as in the previous picture where the button is disabled as it should be.
This is because the "isStartable" boolean in my code goes to false after the game with status "start" has passed and the following items are disabled.
When I scroll back up, it is how it should be, the items are all disabled but I need them to be like this when the listview gets filled. Any way to refresh the data in this listview and taking the "isStartable" boolean with it?
This is what it looks like after I scroll back up.
create a model class for your listview data items. Set a boolean variable in model class like isGameStarted. Set that variable as per your result.Then in your listview adapter, put a condition as below
if(isGameStarted){
holder.startButton.setEnable(true);
else
holder.startButton.setEnable(false);

Randomly refreshing thumb in android ListView

I'm building my first app based on material from http://javatechig.com/video/json-feed-reader-in-android.
Everything goes quite ok so far, but there is a bug in thumbnails. When I scroll list, random thumb images quick "refresh" with random different thumb and back to original one. Any suggestion why it behaves like that?
You should take a look at the following page:
http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
The part Handle Concurrency explains everything:
Common view components such as ListView and GridView introduce another
issue when used in conjunction with the AsyncTask as demonstrated in
the previous section. In order to be efficient with memory, these
components recycle child views as the user scrolls. If each child view
triggers an AsyncTask, there is no guarantee that when it completes,
the associated view has not already been recycled for use in another
child view. Furthermore, there is no guarantee that the order in which
asynchronous tasks are started is the order that they complete.
So you are right. This is actually a bug of this sample which hasn't got a proper load/display logic.

Click button and that button waits for second button on activity to be pushed. Game board. How to accomplish?

I wasn't exactly sure how to briefly paraphrase my question in the title, so please forgive me as I never post questions until now. I am new to Android/Java for starters, as the main language I have used so far is C++. My question is that I have a game board layout (similar to checkers/chess). When it is the user’s turn, they are to click the piece they want to move on the board and then the blank location they would like to move it to. How can this be accomplished? Up until this point I have implemented onClickListeners that never rely on another button in the activity to be clicked afterwards and wait for the user to do so.
Brief information on my project currently (unsure if needed):
I am currently using an ImageButtons array (of size 36) and a two dimensional integer array to hold the information of each of the buttons, as they are displayed in a GridLayout in a 6 X 6 fashion. In my MainActivity class I have implemented the OnClickListener and created a switch statement in onClick() for each of the button ids.
I am not sure how much more information is needed on my code for help or if it is completly irrelevant. I tried looking on the internet before choosing to ask finding nothing. It is always possible though I was not correctly phrasing my issue. Thank you to everyone in advance!!! :)
Without any code it is hard to say what you are doing wrong/right. But if you have the images stored in an Array then when they click on one image that can be put into a variable which is used to place on the View that they click on next. If you have a more precise question then please post relevant code and error messages from what you have tried, if any. Hope this helps
after a user click's a piece store that info in a member variable like a current piece and then on second click check if the previous is not null, the current clicked space is empty and start your animation by posting to a runnable implementation. this is the simple logic part. where are you having problem exactly
To optimize your code to change images what you can do is custom animations on the imageview. You can change the position of the image with animation without having to create or store an image. (If you use images bitmap a lot you have a very good chance of running into OutOfMemory exceptions and this will happen on top end phones very easily
How to Move a Button and Change the Button Image
I would put a single Button on every GridView position.
Then, I would use button.setBackground() and button.setVisibility() to display and hide the buttons with various images. In this way, the buttons will appear to move, but you are really just displaying a different button.
I recommend this because it is easier to change the visibility and image properties of a button than it is to actually move the button, although both methods are possible.
You will maintain a 1 dimensional array of images[n] and a 2 dimensional array of buttons[6,6].
As an example, suppose you want to move image[5] from grid position (1,2) to position (3,4):
// Hide the button at (1,2);
button[1,2].setVisibility(Button.INVISIBLE);
// Display the button at (3,4) with image #5.
button[3,4].setBackground(image[5]);
button[3,4].setVisibility(Button.VISIBLE);
Additionally, if your button images are stored in your resources, you could efficiently use button.setBackgroundResource(R.drawable.image-5-id);
The instructions above discuss how to move a Button, but now how to trigger one Button to be moved and then trigger the location into which to move the Button.
To accomplish this, you will have to define two states, such as:
private final static int STATE_PICK_BUTTON = 0;
private final static int STATE_PICK_LOCATION = 1;
private int state;
Initialize state = STATE_PICK_BUTTON;
When the system is in the first state STATE_PICK_BUTTON, all button
presses identified in your onClick() function memorize a grid
position to move from, and in some cases transition the system into
the second state: STATE_PICK_LOCATION.
When the system is in the second state STATE_PICK_LOCATION, all
button presses actually move the button from the memorized grid
position to the grid position of this button press.
Of course, you will have to do all sorts of error checking to make
sure you are allowed to move a button before triggering the state
transition.
Finally, the above suggestion may not work, because it may be impossible to click an invisible button. If this is the case, instead of changing the visibility of Buttons in empty grid locations, leave all the buttons Button.VISIBLE and use a fully transparent Button image for the buttons representing empty grid spaces.

Adapter "holding on" to view when I animate it

I'm using a custom adapter for a GridView, and feeding it an ArrayList of my custom data class.
The idea is, when the user presses (+) on an item, its number goes up, and when they press (-), it goes down.
This has worked perfect with the following code:
Activity:
//playerLayouts is the ArrayList of my Player class.
playerLayouts.get(position).changeLife(-1); //All this does is decrement life by 1.
adapter.notifyDataSetChanged();
Adapter:
holder.textLife = (TextView) row.findViewById(R.id.player.text_life);
...
holder.textLife.setText(Integer.toString(player.life));
So this works great. I press plus, it goes up, I press minus, it goes down. All is well.
But, if I want to put a little "-1" popup in the view that shows that 1 has been subtracted, and I animate the view, a problem arises.
...
//textLifeChange is a different TextView in the layout. Also, I'm using NineOldAndroids.
textLifeChange.setText(Integer.toString(player.lifeChange));
animate(holder.textLifeChange).setDuration(2000).alpha(0); //There are a few more animations in addition to this one.
Basically, whenever the animation is playing (which is a full two seconds), the view doesn't do any more updates.
For example, say "life" = 20. I hit (-), and now it says 19, and my little popup shows "-1" and fades out. Then, just ONE second later, I hit (-1) again. Nothing happens. The "life" TextView still says 19, and the popup just keeps playing its animation with no change. So I wait another second, and the animation is finished. NOW if I press (-) again, it updates properly again.
Interestingly, if I log my setText method, I see that it is updating with the correct number, and getView is being called, but it's just.. not updating visibly.
What's wrong?
I daresay that your animation is hogging UI Thread, and thus prevents changes in other parts of interface. This asnwer shoud help you:
Android: Running thread preventing animation from starting

Android Gallery View and adapter getView gets called many time

I have a Gallery, which has an adapter connects to it. In the getView method, i have a custom layout, so that I can i have image and caption displayed together
The image is downloaded from an URL, which is done asynchously, and working as expected.
Currently i make each item to fill the screen, so i only have one item display at a time, basically i want to make it like a slide show.
let me be clear, currently i have an activity, and it only has one View, which is a Gallery.
problem occurs when I am swiping, the image bounces and stays at the same image. i need to swipe many times, hard, and long swipe, then i can get to next image.
i put a debug message in my custom adapter in the getView(), it seems getView is getting called many times (4 times), and position being passed is either the current position or the previous one, which explains why i am stuck at the same screen.
if i remove the remote downloading image part, or just use a static image form the phone, i don't have any more issues, in fact, the getView only gets called once, with correct position.
i am very frustrated, not sure what the problem is, could it be because i am downloading image asynchously, which will cause the image to update which causes getView to get called again to redraw itself?
i am not sure..
please help
Unfortunately this is a bug with gallery. Listviews will scroll nicely regardless of data being updated asynchronously. However, the gallery is just not coded up to par with the listview
When the gallery tries to update a visible view (due to your image loading callback) this view will "snap" back to the focused position. If you are changing the view in any way when it is scrolling it will snap. This is likely why you have to scroll hard to get away from the current view. It is trying to perform a callback on your view and only scrolling fast will prevent the callback from occurring before you move away from that view.
I've reported this bug a while ago here:
Android Issues
There are a few workarounds posted in there you can try if you are set on using a Gallery.
Unfortunately it hasn't gained attention from the Android developers.
It seems the issue is caused with views being set to "wrap_content" and the gallery having to remeasure/redraw its views
I have since migrated away from using the gallery and instead use a ViewPager. It is much easier to manage and you don't have to worry about this problem. This has been a known problem with the Gallery since the gallery was first introduced. I have no idea if this was fixed in any of the newer Android versions (3.x/4.x). As of 2.3.7 it is not fixed.
At first you should note: Any AdapterView (Gallery, ListView etc) doesn't guarantee that each time getView() method is called it will pass the same View instance parameter. It only guarantees that each view will have the same type (see Adapter.getIntemViewType() method docs)
So, when you start image downloading you should only specify position of the element. Then after the image has been downloaded you should bind specified ImageView with downloaded image in Adapter.getView() method call.
Take a look to the ImageDownloader from the Android samples here
The other approach is to use WeakHashMap in order to contain map of adapter views to its positions. I can provide you with code samples if you need.

Categories

Resources