I am working on to update image of one button in a row inside custom listview. I have created a custom adapter class for the listview. On click of the button inside one row of listview I have open a custom dialog. Onclick of the OK button of dialog now I want t change the image of the button inside that listview.
Currently I have just made the static button inside the listview and passed the object of that button to the custom dialog method. I am using the simple listview not the fragments.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ItemInfoViewHolder viewHolder;
Log.d("Inside Get View", ""+position);
if (convertView == null) {
convertView = listInflater.inflate(R.layout.iteminfo_view_row, null);
viewHolder = new ItemInfoViewHolder();
viewHolder.itemSelection = (Button)convertView.findViewById(R.id.itemselectionTextview);
convertView.setTag(viewHolder);
}else{
viewHolder = (ItemInfoViewHolder)convertView.getTag();
}
try{
viewHolder.itemSelection.setId(itemsInfo.get(position).getId());
);
viewHolder.itemSelection.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("Item Id"+v.getId());
Toast.makeText(mContext, "Item Button Clicked", Toast.LENGTH_SHORT).show();
int itemId = v.getId();
SelectPopup selectPopup = new SelectPopup(mContext, R.style.myCoolDialog);
selectPopup.selectQuantity(itemsInfo,itemId,viewHolder.itemSelection);
}
});
}catch(Exception e){
e.printStackTrace();
}
return convertView;
}
Here viewHolder.itemSelection is the object of that button which I am passing to the custom dialog method. By this the image is update randomly on the listview on any row.
Please suggest me some better idea to do that.
Brief explanation:
An Adapter acts as a controller of your datamodel, in your case a List<itemsInfo>, and updates its assigned ListView accordingly inside the getView()-method.
Your goal is to change the visual state of the ListView whenever you do a certain action, a click. This is simply done by changing the datamodel behind the Adapter of your ListView whenever you perform your action and ask your ListView to redraw itself with help of the Adapter.
In code:
Add a field to your itemsInfo that will hold a certain state. This can be in any format or form but for now, let's use a boolean called isPressed.
In your getView(), your View will initialize itself depending on this pressed state.
viewHolder.itemSelection.whatevermethodyouwant(itemsInfo.get(position).isPressed)
When you want to change the state of your row, simply change the datamodel at the given position and update the ListView again.
viewHolder.itemSelection.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
itemsInfo.get(position).setPressed(whatever);
notifyDataSetChanged(); // This is a call for your Adapter and will refill the ListView
}
});
This is just pseudo code to explain the flow of events. Hope it will help you to improve your implementation.
Related
What do I want to achieve?
In the below SS, when user touches 'vote' button, these vertical progress bars (custom) will be set according to the voting percentages retrieved from server for that particular row.
What is the obstacle?
I have onClickListener inside getView of the CustomAdapter, and when I manipulate the ProgressBar instance (which is in ViewHolder Class), supposingly I want to see the updated ProgressBar on ONLY the one row of the listview that has triggered that action, but, I see every once 3 rows that I scroll down.
Example: I clicked first row, so first row has updated its progress bar, but 4th, 7th, 10th... rows are also updated EVEN IF I don't touch 'vote button'.
My Guessing
I think this problem is related to recycling the view, the weird number is 3 in this case but when I make rows smaller it goes '4', so that is the only clue I have.
SS & Codes
ScreenShot: bit.ly/sofscreenshot
Code:
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = mInflater.inflate(layout, null);
holder = new ViewHolder();
//some more initialization
holder.pb1 = (ProgressBar) convertView.findViewById(R.id.leftProgress);
holder.pb2 = (ProgressBar) convertView.findViewById(R.id.rightProgress);
holder.leftVoteButton = (Button) convertView.findViewById(R.id.leftButton);
holder.rightVoteButton = (Button) convertView.findViewById(R.id.rightButton);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.leftVoteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.pb1.setVisibility(View.VISIBLE);
holder.pb2.setVisibility(View.VISIBLE);
/Some codes...
holder.pb1.setProgress(50);
holder.pb2.setProgress(50);
}
});
}
private class ViewHolder {
//some more objects
ProgressBar pb1;
ProgressBar pb2;
Button leftVoteButton;
Button rightVoteButton;
}
All the answers and comments are appreciated, have a great day and thank you.
You're doing it wrong.
The problem is that you need to have a Model somewhere, and change its status. Then the view is updated regarding the model status.
For example, let's say that this is a "StackOverflow" app, and you have a list of answers. The user upvote the second answer. This means that the second element of the List is upvoted.
Now what?
When the adapter is going through your list of object it will "fire" the getView method for that position. Then you have to update that position according to your model. So, if the position is 1, the adapter is trying to show the second Answer, and you have to set the button to "upvoted". Otherwise you have to set it as "normal".
private List<Answer> answers;
public View getView(int position, View convertView, ViewGroup parent) {
// here get your view (or initialize it)
// get the matching answer
Answer answer = answers.get(position);
if(answer.isUpvoted()) {
holder.pb1.setVisibility(View.VISIBLE);
holder.pb2.setVisibility(View.VISIBLE);
} else {
holder.pb1.setVisibility(View.INVISIBLE);
holder.pb2.setVisibility(View.INVISIBLE);
}
holder.leftVoteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.pb1.setVisibility(View.VISIBLE);
holder.pb2.setVisibility(View.VISIBLE);
// not sure on how to get this answer here
// you probably have to go "upper" and manage the click from the ListView
answer.setUpvoted(true);
}
});
}
Im trying to implement instagram like listview , in each row there is like button and also comment box. the question is how can i achieve similar function in listview which means each row is specific and its not repeating for other rows , what i tries is , when a user clicks on button after some item , all other items are clicked! means the clicked item is repeating and its obvious i don't want this! how can i achieve instagram like row
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
viewHolder mHolder;
if(convertView==null){
mHolder = new viewHolder();
convertView = mInflator.inflate(R.layout.row_all_artist, parent, false);
mHolder.mArtistName = (TextView) convertView.findViewById(R.id.artist_name);
mHolder.mFollowers = (TextView) convertView.findViewById(R.id.artist_followers);
mHolder.mFollow = (Button) convertView.findViewById(R.id.artist_follow);
AppLogic.ChangeTextViewFont(mContext, mHolder.mArtistName);
convertView.setTag(mHolder);
} else{
mHolder = (viewHolder) convertView.getTag();
}
mHolder.mArtistName.setText(mItems.get(position).getArtistFullName());
mHolder.mFollowers.setText(mItems.get(position).getArtistFollowers());
mHolder.mFollow.setTag(R.id.artist_follow,mItems.get(position).getArtistID());
mHolder.mFollow.setOnClickListener((OnClickListener) mContext);
return convertView;
}
EDIT
my question is not about touching items or setting unlike listener or like this! the problem is when i click the button i change its background color , but this color is repeating on every 10 item on the list! there is a related question to me here : ListView repeated CheckBox behavior every 10th item erratically
public void onClick(final View v) {
if (!AppLogic.isLoggined(mShared)) {
AppLogic.ToastMaker(getApplicationContext(),
getResources().getString(R.string.msg_loginError));
startActivity(new Intent(FirstTimePage.this, Register.class)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
} else {
AppLogic.doFollow(FirstTimePage.this, v, mShared);
}
}
Prevent getting focus by the button.
Add this to the top element in your row_all_artist.xml custom view
android:descendantFocusability="blocksDescendants"
Create a new OnClickListener for every row, or check the View given in the argument at your current listener.
You are setting your mContext as the OnClickListener
mHolder.mFollow.setOnClickListener((OnClickListener) mContext);
You should just implement the ListView method setOnItemClickListener and then check for the position of the item that was clicked. Tags are also very helpful here.
Example: Adding an onclicklistener to listview (android)
I have been developing an android application and i am stuck in the middle.
The problem is i am using SimpleAdapter to do the adapter stuff and show items in the Listview and as far as i know i cannot override the getView() method of SimpleAdapter class to bound click listeners to the items.
There is a other way to handle click events of sub items like using XML, you can write in the XML like android:clickable="true"and android:onClick="clicklistenr", using this i can get the item but my problem is if i use this then i cannot get the position of the adapter which i need to get adapter item values and handle other tasks. So i am stuck here any help would be appreciable. thanks.
For example i have a ListView which contains one image, TextView, like Button, share Button in each of its items. And there is no way i can find that either its image or button clicked using setOnItemClickListener. So i need a way to handle click events of these sub items of a ListView, i am using SimpleAdapter.
Just call listView.setOnItemClickListener() with your implementation of the listener.
and use like
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
}
});
Where list=(ListView)findViewById(R.id.list); and list.setAdapter(your_adapter);
For More details Follow: http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/
Hope It Will Help You.. :)
You can use like
myListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
HashMap<String, Object> obj = (HashMap<String, Object>) adapter.getItem(position);
String result= (String) obj.get("name");
Log.d("Yourtag", name);
}
});
First of all your problem is how to handle the items of a custom listview.. not sub. Anyways...
If you are using SimpleAdapter , you may use getView().But if you are using SimpleAdapter you don't need to use the getView() as the it handles the mapping of data to your layout via the resource. For inforamtion check the SimpleAdapter in developer site.
Another thing is there is not mandatory for using any particular adapter. You can create any adapter class which will extends the BaseAdapter which will implement the getView().
Inside getView() you can able to inflate your custom layout(which contains the image,button etc..) to your view or convertview.something like:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
vi = convertView;
if (convertView == null)
vi = inflater.inflate(R.layout.yourcustomlayout, null);
//Initialize your custom views here
TextView someText= (TextView) vi.findViewById(R.id.tvSometext);
Button likeButton = (Button ) vi.findViewById(R.id.btnLike);
//put data or call onclicklistener or
//whatever you want to do with custom views
likeButton .setOnClickListener(...){
//on click
}
}
Ans simply call this through your constructor with some data and appropriate context.
Amir,
I am not sure you found the solution or not. You can do this way:
#Override
public View getView(int position, View view, ViewGroup viewGroup) {
//for each subitem, set click listener & set tag having position as
// value.
ImageView imv = (ImageView)view.findViewById(R.id.live_view);
imv.setTag(i);
imv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("INFO","Live view clicked");
//This will give you position in listview.
int position = (int) v.getTag();
}
});
}
I hope it will help you and others looking to find relevant answer.
May be there is some other solution too, but it will surely work.
I have a Custom ListView which has an ImageView and a TextView. and i implemented ListView.setOnItemSelectedListener();
But is these a way to make both the ImageView and TextView Clickable (Separately), I mean Click on ImageView must call ActivityA and Click on TextView must call ActivityB?
Yes you can do that inside the Adapter class itself. Just set the click listeners for ImageView and Textview in the Adapter class.
Yes ofcourse!
In your custom ListAdapter, you can set onClickListener like below:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if( row == null ){
LayoutInflater vi = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = vi.inflate(this.textViewResourceId, null);
}
row.findViewById(R.id.image_item).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
}
});
row.findViewById(R.id.text_item).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
}
});
}
Yes ofcourse you can achieve that. You can set onClickListener on them separately inside the adapter class and then set these buttons or textviews as not focusable if you want a different action to be done on clicking the whole list item, using onItemClickListener.
yourButton.setFocusable(false);
yourButton.setFocusableInTouchMode(false);
there are lots of example for the same
like this
point should keep
You need set the listener to each view in getView (don't create in
each time in get view just pass already created one or can pass this
and implement the listener in same adapter class)
make the view (like TextView ) clickable true
You 'll also required the row position so can use different logic
like get & Set tag or at view parant as in this link
I have got ListView with a custom Adapter. Every row contains clickable buttons and texts.
At the moment onClickListeners are set in the body of getView(), this is quite insane idea because this method is called very frequently. Inside every onClick function, I need access to the private data to call new activity with the bundle.
How can I move the onClick definition outside getView() routine?
When onClick will be called I need info about the position of the list element (to access private data) and which View was clicked (to start the correct Activity).
public View getView(int position, View convertView, ViewGroup parent) {
if(convertview == null) {
convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.photo_tweet_row, null);
}
TextView userTweet = (TextView) v.findViewById(R.id.feed_user_tweet_body);
RepliesButton repliesBtn = (RepliesButton) v.findViewById(R.id.feed_replies_button);
Status twitterDataItem = getItem(position);
if (twitterDataItem != null) {
if (userTweet != null) {
userTweet.setText(twitterDataItem.getText());
}
if (repliesBtn != null) {
repliesBtn.setText(" replies");
}
userTweet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), ProfileActivity.class);
intent.putExtra(getContext().getString(R.string.serializable_user), twitterDataItem.getUser());
getContext().startActivity(intent);
}
});
repliesBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if (!twitterDataItem.getComments().contentEquals("0")) {
Intent myIntent = new Intent(getContext(), RepliesToFeedActivity.class);
myIntent.putExtra("photoTweet", twitterDataItem);
getContext().startActivity(myIntent);
}
}
});
}
return convertView;
}
Your activitiy has to implement the OnClickListener, and move the implemented method up to the activity level.
Based on the view parameter of the method you are able to detect from which UI object the event came from (button, textview).
As for how to detect for which record/row in a listview. You have to set a custom tag on the buttons and textviews in the getView method, that you will read in the event via getTag this tag can be a custom object too if string is not enough. Probably/recommended way is to be the position in the adapter.
I am afraid that Pentium10 answer is not correct. If the checkbox is to be clickable and checkable independently from the list item itself, (say it is defined in the list item xml layout), then it will intercept the click event, and the list item (the list's OnItemClickListener) will not receive the click at all.
Therefore you must implement the onClickListener in the adapter itself - either in getView(), or have the adapter implement onClickListener - in this case the items must be tagged for the listener to know which item it is operating on.