ExpandableListView, changing group view on click - android

I'm using an ExpandableListView widget. When a group view is clicked, in addition to displaying the child views, I also want to make a change to the group view. I have added an OnClickListener to the group views in the adapter's getGroupView() method, but when one of them is clicked, the changes to the group view are made but the child views don't display - it's as if the OnClickListener overrides the showing of the child views. Is there a call I can add to the OnClickListener to display the children? Any other thoughts are welcome...
EDIT: Here is the code for the getGroupView() method, with the OnClickListener added. I don't think any of the other code is relevant given that it works fine when the OnClickListener is commented out
#Override
public View getGroupView(int position, boolean arg1, View convertView,
ViewGroup arg3) {
if (convertView == null){
convertView = getLayoutInflater().inflate(R.layout.questionbox, null);
}
TextView summary = (TextView)convertView.findViewById(R.id.questionHl);
final TextView asker = (TextView)convertView.findViewById(R.id.asker);
TextView numviews = (TextView)convertView.findViewById(R.id.numViews);
TextView numAnswers = (TextView)convertView.findViewById(R.id.numAnswers);
final TextView fullText = (TextView)convertView.findViewById(R.id.fullText);
summary.setText(q.get(position).summary);
asker.setText("by " + q.get(position).author);
numviews.setText(Integer.toString(q.get(position).views));
numAnswers.setText(Integer.toString(q.get(position).numAnswers));
fullText.setText(q.get(position).fullText);
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
if (fullText.getVisibility() == 8){
fullText.setVisibility(1);;
}
else if (fullText.getVisibility() == 0){
fullText.setVisibility(8);
}
}
});
return convertView;
}

I think it's because your onclicklistener returns true when it should return false. True means that you fully handled the event, so then it is not forwarded back to the expandable list view. But I'm taking a blind guess here, since you didn't show any code :D
Edit: Some onclick methods return a boolean, some don't, so I might be completely wrong. Could you show us your code?

Related

How to update view from an onClickListener inside getView of a CustomAdapter in Android

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);
}
});
}

getView() method CHANGE listview items positions

I have a listview with a checked textview and two textviews,however, my getView method keeps changing the listview items while scrolling, the values and checkbox states are both saved into sqlite database. I tried every possible solution and spent 4 hours trying to fix that.
Any help appreciated.The only solution that worked was setting convertview to null at beginning of getView() which lags the listview.
GOAL:to make listview display items properly without changing its positions randomly.
Final working code for anyone in need:
#Override
public View getView( final int position, View convertView, ViewGroup parent) {
viewHolder = null;
if(convertView == null){
convertView = inflater.inflate(R.layout.sin_item,null);
viewHolder = new HolderCo();
viewHolder.box = (CheckBox)convertView.findViewById(R.id.coco);
viewHolder.subject = (TextView)convertView.findViewById(R.id.subject_com);
viewHolder.date = (TextView)convertView.findViewById(R.id.date_co);
convertView.setTag(viewHolder);
}
else{
viewHolder = (HolderCo)convertView.getTag();
}
viewHolder.position = position;
viewHolder.box.setText(list.get(viewHolder.position).getWhats());
viewHolder.subject.setText(list.get(viewHolder.position).getSubject());
if(list.get(viewHolder.position).isSelected()) {
viewHolder.box.setOnCheckedChangeListener(null);
viewHolder.box.setChecked(true);
viewHolder.box.setPaintFlags(viewHolder.box.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}else{
viewHolder.box.setOnCheckedChangeListener(null);
viewHolder.box.setChecked(false);
viewHolder.box.setPaintFlags(viewHolder.box.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
}
if(dator.equals("d"))
viewHolder.date.setText(list.get(viewHolder.position).getDay()+"/"+list.get(viewHolder.position).getMonth()+"/"+list.get(viewHolder.position).getYear());
if(dator.equals("m"))
viewHolder.date.setText(list.get(viewHolder.position).getMonth()+"/"+list.get(viewHolder.position).getDay()+"/"+list.get(viewHolder.position).getYear());
if(dator.equals("y"))
viewHolder.date.setText(list.get(viewHolder.position).getYear()+"/"+list.get(viewHolder.position).getMonth()+"/"+list.get(viewHolder.position).getDay());
viewHolder.box.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(buttonView.isChecked()) {
list.get(position).setSelected(true);
db.updateState(list.get(position),true);
buttonView.setPaintFlags(buttonView.getPaintFlags()| Paint.STRIKE_THRU_TEXT_FLAG);
if(PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("add_mark_dialog",true))
buttonView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dialoging(viewHolder.position);
}
});
}else{
buttonView.setOnClickListener(null);
list.get(position).setSelected(false);
db.updateState(list.get(position), false);
buttonView.setPaintFlags(buttonView.getPaintFlags()&(~Paint.STRIKE_THRU_TEXT_FLAG));
}
}
});
return convertView;
}
By doing this:
viewHolder.box.setTag(position);
viewHolder.date.setTag(position);
viewHolder.subject.setTag(position);
you set the tags to the views to the first position they were created with.
So when getView() is called with non-null convertView (previously recycled), the tags in its viewHolder still point to that position.
Move these setTag() calls outside if(), to set new position to recycled view.
BTW I would rather replace all this with
viewHolder.position = position; // outside if()
and using it everywhere you use (Integer)x.getTag()
UPDATE: Also you have to do this:
viewHolder.box.setOnCheckedChangeListener(null);
before this:
viewHolder.box.setChecked(...);
Because otherwise it can trigger previous listener which most likely you don't want.
You're updating the view conditionally with if conditions. You need to provide corresponding else blocks where you reset the view to their default values.
For example,
if(dator.equals("d"))
viewHolder.date.setText(...);
if(dator.equals("m"))
viewHolder.date.setText(...);
if(dator.equals("y"))
viewHolder.date.setText(...);
needs to be something like
if(dator.equals("d"))
viewHolder.date.setText(...);
else if(dator.equals("m"))
viewHolder.date.setText(...);
else if(dator.equals("y"))
viewHolder.date.setText(...);
else
viewHolder.date.setText("some default value");
Similarly reset defaults in viewHolder.box.setPaintFlags().
The reason is that ListView views are recycled. Recycled views are not in their pristine state like they were immediately after inflation. Instead they will be in a state they were before they were recycled, possibly containing data from the list row previously using that view.

Android: Determine which View clicked within ListView row

I have a ListView with lots of icons which may be clicked to change the state of their respective row items. I realise that I can create onClick handlers for all of them but I would like to a generic means of identifying which icon (View) has been clicked. (e.g. determine View at touch coords x,y?
Any idea how I can do that?
I am getting row clicks with the below handler:
lvClickListener = new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick( AdapterView<?> arg0, View arg1, int position, long arg3 )
...
On each row I have 5 ImageViews.
I assume all rows are being inflated with the same layout and you are using the holder pattern (http://developer.android.com/training/improving-layouts/smooth-scrolling.html).
This way, you can have a single OnClickListener to "listen" for any of your ImageView (or whatever they are). You only need to set the listener in the momento you actually inflate the view (not when you reuse it), and then set for all ImageView in the same row the row position as the Tag, so, then in the onClick method, you can chech to which row they belong to (by the tag) and which ImageView is it (by the id)
class MyAdapter extends BaseAdapter implements OnClickListener
[...]
#Override
public View getView (int position, View convertView, ViewGroup parent)
{
MyHolder holder = null;
if(convertView != null)
{
holder = new MyHolder();
convertView = inflater.inflate(...)
holder.imageView1 = (ImageView)convertView.findViewById(R.id.[...] )
holder.imageView1.setOnClickListener(this);
holder.imageView2 = (ImageView)convertView.findViewById(R.id.[...] )
holder.imageView2.setOnClickListener(this);
[...]
convertView.setTag(holder);
}
else
{
holder = (HyHolder)convertView.getTag();
[...]
}
holder.imageView1.setTag(position);
holder.imageView2.setTag(position);
[...]
return convertView;
}
#Override
public void onClick(View v)
{
int id = v.getId();
Integer position = (Integer)v.getTag();
[...]
}
Of course you can then optimize this, put the views in an array or whatever or put the OnClickListener outside the Adapter (in the activity, or an instance variable), but this is the basic idea.
As a note, you should check what happens with the onItemClickListener, I'm not pretty sure, but it may cause problems intercepting touches (or it may no, I don't know), but if you're not going to use the whole row click, then you can remove it
You can do this in multiple ways, You can tag each ImageView to your AsyncTask(or whatever) you use. Or, refer https://github.com/square/picasso

How can I handle onListItemClick(...) based on what view inside the row was clicked?

Question
I have a listView inside a DialogFragment and I want to fire certain callbacks only when certain particular items inside a row are fired. How can I do that?
Basically, I want to do something like this
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final int viewId = view.getId();
if ((viewId == R.id.textView1) || (viewId == R.id.textView2)) {
// do something...
}
which I can't. Read further if you don't know why.
What I tried
I tried to look into the documentation, but the OnItemClickListener callback doesn't offer as a parameter the exact clicked view (the View you can see in the signature is the whole row).
Also, I tried to set a simple onClick callback on the single view in the adapter, but this overrides the listSelector and other behavior a list should have. Reading in the documentation, I found it's explicitly written that we should set callbacks via the onListItemClick(...) method (not via onClick(...)), so I'm looking for a way to do that, using this method, not to override any default list behavior.
I was trying to get this done by working on the xml. To my surprise, I found that if I set a view android:clickable property to true, the onListItemClick callback won't fire (I thought it was the opposite),
so a partial solution would be to set to android:clickable=true every view in the row apart from the one I want to fire the callback, but that is not a solution because if the user clicks where there is padding or white space, the callback will fire. Also, I found that if I set the parent of the row's view to android:clickable=true and the child views I want to handle with the callback to android:clickable=false, this won't work, because apparently the property is not overwritten.
EDIT Sorry for the really bad title this question had before, I didn't even noticed I submitted the question.
new Answer, hope I understood now :)
In your adapters getView, attach an OnClickListener to any view in your layout you want to fire. (more pseudocode)
public class Adapter extends ArrayAdapter<XYZ> {
private int resource;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null) convertView = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(this.resource, parent, false);
((Button)convertView.findViewById(R.id.YOUR_BUTTON_IN_LAYOUT)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
DOSTUFF();
}
});
return convertView ;
}
}
old Answer:
The position indicates where you are in the list (pseudocode).
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, final int position,long arg3) {
YOUR_ITEM_BACKED_BY_ADAPTER item = listView.getItemAtPosition(position);
if(item==THE_FIRST_ITEM_IN_LIST) doSomething();
else if(item == THE_LAST_ITEM_IN_LIST) doSomethingElse();
}
});
You can set listeners for other views inside the adapter's getView
public class MyAdapter extends ArrayAdapter<MyItem> implements View.OnClickListener {
public View getView(int position, View convertView, ViewGroup parent) {
// setup the converView inflating it, for simplicity I've removed that code
MyItem item = getItem(position);
text1 = (TextView)convertView.findViewById(R.id.text1);
text2 = (TextView)convertView.findViewById(R.id.text2);
text1.setOnClickListener(this);
// pass the item to use when clicked
text1.setTag(item);
text2.setOnClickListener(this);
text2.setTag(item);
}
public void onClick(View v) {
MyItem item = v.getTag();
switch (v.getId()) {
case R.id.text1:
download(item);
break;
case R.id.text2:
upload(item);
break;
}
}
}
Instead of hardcoding action (eg download) inside the adapter you can pass to it an interface and for example the calling activity can implement that interface

Android ListView: getTag() returns null

Hallo all,
I have a ListView which contains a Button in each line. The following code is part of the getView() Method
public View getView(final int position, View convertView, ViewGroup parent) {
View row = convertView;
TextView tv;
Button saveA_button;
EditText edittext;
FITB_ViewWrapper wrapper;
if (row == null) {
LayoutInflater li = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (ChooseMode_Act.modeInfo.equalsIgnoreCase("Training")) {
row = li.inflate(R.layout.exercise_for_training_fitb,parent, false);
}else {
row = li.inflate(R.layout.exercise_for_exam_fitb,parent, false);
}
wrapper=new FITB_ViewWrapper(row);
row.setTag(wrapper);
if (ChooseMode_Act.modeInfo.equalsIgnoreCase("Exam")) {
saveA_button=wrapper.getSaveAnswer_Button();
OnClickListener l=new OnClickListener() {
#Override
public void onClick(View v) {
Integer mp=(Integer)v.getTag();
Log.i("mp","my Position is: "+mp);
}
};
saveA_button.setOnClickListener(l);
}
}else {
wrapper=(FITB_ViewWrapper) row.getTag();
}
For my App i need to known to which item the Button belongs to, so i try to detect it. The code
Log.i("mp","my Position is: "+mp);
puts out a message: mp myPosition is: null
I can't understand, why do i get a "null" but not an Integer? How can i find out the Position of an Item in a ListView?
Thanks a lot.
Log.i("mp","my Position is: "+position);
you have the position already !
public View getView(final int position, View convertView, ViewGroup parent) {
The Views in a ListView are reused as you scroll up and down. Thus, setting values in getView often has unintented consequences, like the image that you meant to set for item number 5 appearing in item number 10, 15 and 20 also.
To avoid this, if you want to set properties in getView you need to make sure you set or unset them for each view.
I'm not sure what exactly you are trying to accomplish with your code, but it might help to move the setTag outside of the if statement, to make sure you are setting it each time that a view is used.

Categories

Resources