Android: setImageResource in adapter changing image 3 positions later - android

I am using AndroidSwipeableCardStack to do a tinder like interface with a 5 star rating system. When I click on a button that is meant to change the image. The image changes three cards later not in the current card.
This is the project I used: https://github.com/wenchaojiang/AndroidSwipeableCardStack
I assume it is to do with it pre-loading the next card as a recyclerview normally does but I am really not sure.
This my adapter below, you can see at the bottom I call setimageresource on the one star rating image button onclicklistner. I set a Log.d and the button click is registering immediately upon click but the change in the image resource appears only after 3 cards have been swiped.
Edit: So I noticed that regardless of the project I imported, my adapter is only extending ArrayAdatper. So I guessed that the problem might be in the getView method and that I might need to override something else (not sure). But the important point, I checked the position in getView by logging the position and sure enough it said position 0,1,2,3 without anything being swiped away. This is analogous to the offset of the setImageResource so I believe they are interlinked but unfortunately that doesn't bring me any closer to an answer.
Thanks for your help.
public class SongPreviewCardsDataAdapter extends ArrayAdapter<SongDatabaseMappingAdapter> {
public SongPreviewCardsDataAdapter(Context context, int resource) {
super(context, resource);
}
ImageButton oneStarRating;
#Override
public View getView(int position, final View contentView, ViewGroup parent) {
// Initialise Song Views
SongDatabaseMappingAdapter item = getItem(position);
TextView songName = (TextView) (contentView.findViewById(R.id.songNameTextView));
songName.setText(item.getSongTitle());
com.mikhaellopez.circularimageview.CircularImageView songImage = (CircularImageView) contentView.findViewById(R.id.songImageView);
String ImageURL = (item.getPictureURL());
Picasso
.with(this.getContext())
.load(ImageURL)
.into(songImage);
// Initialise Rating Buttons
oneStarRating = (ImageButton) contentView.findViewById(R.id.ratingButton1);
// Create OnClickListners for Ratings Buttons
oneStarRating.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
oneStarRating.setImageResource(R.drawable.starfull);
Log.d("Star", "Star Clicked");
}
});
return contentView;
}
}

getView can be called at any time on any item, so if you want something to stay set on a view, you have to model it.
I don't know if your SongDatabaseMappingAdapter class is something you can change or not, but I'll assume you can change it.
Add a variable to your item class, like boolean mOneStar. Getter/setter left as an exercise for the reader.
In getView() make your item final so you can refer to it in the onClick callback:
final SongDatabaseMappingAdapter item = getItem(position);
Use the variable in getView to set up your view:
oneStarRating = (ImageButton) contentView.findViewById(R.id.ratingButton1);
oneStarRating.setImageDrawable(null); // clear out recycled value
if (item.isOneStar()) {
oneStarRating.setImageResource(R.drawable.starfull);
}
In your OnClickListener, set the property on the item and call notifyDataSetChanged():
oneStarRating.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("Star", "Star Clicked");
item.setOneStar(true);
notifyDataSetChanged();
}
});
The main point is: Never change a view from an event handler. Always change the item from the event handler then call adapter.notifyDataSetChanged(), and always change your view in getView() based on the item.

Related

OnClickListener for Buttons in GridView

I have a GridView adapter displaying a grid of Buttons. Now I want to set up an OnClickListener for my buttons but of course they don't have their own R.id I can access as they are added to the grid via the adapter, rather than a layout.xml.
I tried to use OnItemClickListener as follows:
m_onItemClickListener = new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
switch(pos) {
case MyConstants.POS_OF_BUTTON_1:
// Do stuff...
break;
case MyConstants.POS_OF_BUTTON_2:
// Do stuff...
break;
}
}
};
But to my understanding you can't use a clickable or focusable item with OnItemClickListener. How do I get round this? Thanks!
There are more elegant ways to do this whole thing (starting from using a RecyclerView with a GridLayoutManager instead of a GridView), but if you're looking for the quick and easy solution to use with what you already have, this is what you can do:
First of all, you should set some ID on your buttons, they don't have to come from R.id (although it would be preferable if you inflated the views from a layout, with an ID defined there, and used a ViewHolder).
Worst case, you can just define constants in your adapter for the IDs you want to use for each kind of button (e.g. static final int DELETE_BUTTON = 1;), and then set these IDs on the buttons manually, in code.
Then you can pass a simple OnClickListener (not OnItemClickListener), which handles clicks of all these different buttons in a single item, to your adapter, and make the adapter set the listener on each of these buttons, for each of the item views in the grid.
You will also need to set the position of the item as a tag on the button view itself, so that when the click happens, you can determine for which item the click happened.
Sample code as follows:
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View v) {
Object tag = v.getTag();
if (!(tag instanceof Integer)) {
// Show error message or just throw an exception.
}
int position = (Integer) tag;
// We get the item at this position, to know which one to use
Item item = adapter.getItem(position);
switch (v.getId()) {
case DELETE_BUTTON:
// Delete stuff here
break;
case EDIT_BUTTON:
// Edit stuff here
break;
...
}
}
};
adapter.setOnClickListener(listener);
Then, in the getView method of the adapter, you need to set this listener on each of the buttons and also set the position of the item as a tag on the buttons. This way, you will be able to figure out to which item the button belongs to, in the listener code above.
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
...
deleteButton.setId(DELETE_BUTTON);
deleteButton.setOnClickListener(listener);
deleteButton.setTag(i);
...
}
In general, I sincerely urge you to also look into the ViewHolder pattern, and RecyclerView and GridLayoutManager when you have time. Most of this will translate there as well.
EDIT
In order to make multiple Views clickable/focusable inside a list/grid item, you need to set the descendantFocusability attribute to blocksDescendants on the root view of the item, either simply in the XML, or in code via:
viewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);

ImageButton setEnabled in Adapter

I am trying to set one button to enabled and a different one to disabled once it is clicked on the listView. There are 2 buttons and I am trying to change them in the onClick in the getView function of my adapter.
Here is the onCLick of one of them.
holder.upButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("Event", "Up");
View parentRow = (View) v.getParent();
ImageButton uButton = (ImageButton) parentRow.findViewById(R.id.upImageView);
ListView listView = (ListView) parentRow.getParent();
final int position = listView.getPositionForView(parentRow);
if (activity.equals("Host"))
{
} else {
}
uButton.setEnabled(false);
}
});
thank you
If you try to change the view inside an onClick() function, it will not work.
You didn't post a lot of code, so I will just describe the correct code.
First, your adapter needs to model the enabled state of the two buttons. In other words, for each list item you need two booleans, one for each button.
In your view binding method, you need to access the two booleans and set the enabled state of each button based on its model boolean value.
In your onClick(), you need to flip the state of the two booleans and call notifyDataSetChanged() to refresh the list with the new button states.

Items ImageButton drawable change upon click in ListView seems to change for 2 items at a time

I know this question has been asked before and got an understanding roughly of whats happening but i cant seem to find a solution.
In my Custom List Adapter and inside public View getView(int position, View convertView, ViewGroup parent) { ive setup a click function for the items ImageButton.
final ImageButton bookmark = (ImageButton)
convertView.findViewById(R.id.bookmarkthis);
bookmark.setTag(position);
bookmark.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
bookmark.setImageResource(R.drawable.ic_bookmarked);
bookmark.setTag(R.drawable.ic_bookmarked);
}
});
The Imagebutton is a clear star and when Clicked it changes that same drawable to a yellow star. It works Ok.
Problem is when i click Item in Position 0 Star Icon to make yellow , it also changes for item 8 further down the list not in View Yet. if i click position 1 changes also for position 9 and so on.
I had a look around and researched the issue and even tried a holder for the Imagebutton but no go. Something is preventing the drawable change for the ImageButton to its correct Position only.
Thanks
Solution is to initially Set a flag item in the array for each Item. Then onClick set the flag to true just for that item. Then in the getView its just a case of an if statement to check the flag as the Items get Cycled.
//in array creation
items.setFlag("false");
//in getView as you set Text and what ever get the Flag state
String flag = m.getFlag();
//check the flag state and take action in this case change the icon accordingly
if (Objects.equals(flag, "true")) {
bookmark.setImageResource(R.drawable.ic_bookmarked);
bookmark.setTag(R.drawable.ic_bookmarked);
}
else {
bookmark.setImageResource(R.drawable.ic_bookmark);
}
// and in the click function
#Override
public void onClick(View v) {
bookmark.setImageResource(R.drawable.ic_bookmarked);
m.setFlag("true");
}
You need to reset your bookmark image resource & tag values a non-null convertView is passed in to getView. Your onClick handler is setting them to these values:
bookmark.setImageResource(R.drawable.ic_bookmarked);
bookmark.setTag(R.drawable.ic_bookmarked);
So you'll need to reset them to the default values when showing a new list item.
ListView reuses the views that fall out of range. You don't reset the "bookmark" icon when reusing the views (when you get a non-null convertView). Make sure to always reset all properties of your views to the correct values, and you won't have a problem.

Android listview toggle image by click, changes more than one image

I have a listview and on click I try to change an arrow image inside my listview adapter row.
This works fine, but I have the problem, that not only a single image changes. A few rows above and below the arrow images also change.
It changes the images in every 6th row.
the code for my onClick event is this:
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent,View view, int position, long id) {
if (row != null)
{
ImageView PfeilImage = (ImageView) row.findViewById(R.id.imageView1);
PfeilImage.setImageResource(R.drawable.pfeil);
}
ImageView PfeilImage2 = (ImageView) view.findViewById(R.id.imageView1);
PfeilImage2.setImageResource(R.drawable.pfeil_aktiv);
row = view;
getInfoById(position);
}
});
It would be nice if someone could help me with that problem.
thanks in advance
Frank
That's actually normal for how the ListView is setup since it is designed to not re-inflate Views, but instead re-use them; which is why you can't just change the image, you need to change the data it's pointing to.
The solution to this, inside your Adapter's getView() method, is to check the data and set the background image based on that.
You may want to create a new Class to hold your current data and the new image to use holder. ie.
Class ListViewItem
-int imageToUse
-Object whateverDataYouWereOriginallyUsing
and then set the image for that view based on ListViewItem.get(position).imageToUse
Now, when you're actually changing the data based on a user click, you'll need to call notifyDataSetChanged() on the adapter so that it will re-evaluate the UI based on the new DataSet and your getView() method
got it, many thanks.
in my Adapter Class I made a new int variable "myClickedPosition".
In my click event I set this variable and after this I set notifyDataSetInvalid to redraw the listview:
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> theAdapter,View view, int position, long id) {
LazyAdapter mAdapter = (LazyAdapter) theAdapter.getAdapter();
mAdapter.myClickedPosition=position;
mAdapter.notifyDataSetInvalidated();
getInfoById(position);
}
});
and in my getView function I check the clicked position:
if(myClickedPosition == position)
{
PfeilIcon.setImageResource(R.drawable.pfeil_aktiv);
} else {
PfeilIcon.setImageResource(R.drawable.pfeil);
}
Now all is working fine.

Different content for each Spinner item in the same Activity

How can I show different content after selecting an item from the Spinner?
I want to create a Spinner with locations of chain stores.
I want the spinner to be always there on top. The only thing that
changes to be the content under the spinner
Create a simple method in your activity to refresh the layout below the Spinner(which will remain untouched). That method will be called from a OnItemSelectedListener set on the Spinner. It would be something like this:
private void changeAdress(int newSelectedAdress) {
// The ImageView and the TextView will be already in the layout
ImageView map = (ImageView) findViewById(R.id.theIdOfTheImage);
// Set the image. You know the current address selected by the user
// (the newSelectedAddress int) so get it from the array/list/database
// where you stored it
// also set the image
}
Called the method above from the onItemSelected callback:
yourSpinnerRefference.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
changeAddress(position);
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
If this is not what you want, please explain.
Edit: Make two arrays to hold your data:
int[] images = {R.drawable.imag1, R.drawable.imag2 ..etc...};
//also for the text
String[] text = {"text1", "text2 ...etc...};
Then use those two arrays in the method I recommended above:
private void changeAdress(int newSelectedAddress) {
((ImageView)findViewById(R.id.mapView1)).setImageResource(images[newSelectedAddress]);
// assing an id to the TextView in your layout and do the same as above.
}
There is no need for multiple ImageView and TextViews.

Categories

Resources