I faced with a weird problem. I have an android application and it contains activity that includes ListViewAdapter. Each element contains a checkbox.
And I've the following code -
public View getView(final int position, View convertView, ViewGroup parent) {
CheckBox check = (CheckBox) view.findViewById(R.id.myCheckBox);
if(check.getTag() == null) {
check.setTag(position);
check.setOnCheckedChangeListener(myCheckChangList);
}
check.setChecked(holders.get(position).isChecked);
}
private OnCheckedChangeListener myCheckChangList = new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
holders.get((Integer) buttonView.getTag()).isChecked = isChecked;
}
};
And when I scroll my listview down my OnCheckedChangeListener fires and reset my checkbox if it was set. Why does it happend? I excpeted OnCheckedChangeListener will be triggered when I click checkbox manually or call setChecked programmatically but it does when i scroll down too. Why?
Karn is correct, it is getting recycled, so when scrolling, it generates the view and:
check.setChecked(holders.get(position).isChecked);
makes it so your listener is called every time you scroll because you set your listener to listen when the check is changed. Change your setOnCheckedChangeListener to setOnClickListener.
When you scroll getView method is called due to view recycling behavior of adapter. and setChecked method will call which triggers OnCheckedChangeListener.
Related
I have a GridView in my activity. I am having 2 elements in the GridView. One is an ImageView and the other is a TextView.
I want to perform an action when clicking the ImageView only, but I want this to happen in the activity and not in the GridView adapter.
I handle the ImageView click in the getView() of my adapter, but I do not want it that way. I want to handle it in the activity when calling:
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this, items));
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
//THIS WORKS FINE
String string = ((TextView) v.findViewById(R.id.text)).getText().toString();
Log.d("string",string);
//THE PROBLEM OCCURS HERE
ImageView capture = (ImageView) v.findViewById(R.id.capture);
capture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
}
});
//THE PROBLEM OCCURS HERE
The action is supposed to happen the first time I click the ImageView, but it only happens on the second click and further.
This is where I am stuck with the issue. I want this action to occur on the first click and not on the second click.
You can see the code block-
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
String string = ((TextView) v.findViewById(R.id.text)).getText().toString();
Log.d("string",string);
//THE PROBLEM OCCURS HERE
ImageView capture = (ImageView) v.findViewById(R.id.capture);
capture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
}
Your imageview capture is adding any action after gridview is being clicked. So, after clicking first time it is executing and then setting click listener to imageview and then next click its executing its onClick block. So better, handle imageview click event inside adapter.
You can call method inside your activity from adapter class but you should implement setOnClickListener inside your adapter class.
with the help of interface, you can do that do one thing create an interface and implement it on your activity and get imageview click on the adapter and here initiate the interface and you will get the click inside the activity
I think the problem is that on the first click you just set OnClickListener on your imageView, and so its onClick() method is not getting called. On the second click though, as the listener is already set, onClick() is invoked.
Instead of setting new Listener to your imageView each time an item in the grid clicked (which does not make any sense by the way), you should do either of these:
If imageView in each item has to be handled the same way, set OnClickListener to the imageView in the adapter, when creating a view.
If not, pass the interface for handling imageView clicks to the constructor of the adapter, and then, in the activity, implement this interface when creating an adapter.
Create a method in you activity.
Now, in adapter, onClick call that method using
((Activityname)context).methodname(parameters);
I've a question about ListView and listeners.
Suppose i have a listview in my app. Each item of it contains a checkbox.
And i've the following code:
public View getView(final int position, View convertView, ViewGroup parent) {
CheckBox checkbox = (CheckBox)v.findViewById(R.id.checkbox);
checkbox.setOnCheckedChangeListener(new OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// some code
}
});
}
As you can see I set setOnCheckedChangeListener every time when getView method is called.
So is it right way to set listener? Or should I set it once? Is it bad in terms of performance? Or it doesn't matter? And if I set the listener multiple times whether it will overwrite previous listener or will i have multiple listeners for this event?
Each listener that you set will overwrite the previous one (if any). Setting a listener, or anything else, will consume a super small amount of time, however here you're also creating a new anonymous class which will take longer.
For the maximum performance, I would make a few adjustments:
Use convertViews to cache the views
Use ViewHolder pattern to avoid multiple findViewById calls
Use a single listener for all checkboxes and set it only on the cached views (if convertView == null)
Save the position of the item that the CheckBox is located in as the CheckBox's tag
Here's an example:
private static class ViewHolder {
CheckBox checkBox;
}
private CompoundButton.OnCheckedChangeListener mCheckListener = new CompoundButton
.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.e("TAG", "CheckBox position: " + buttonView.getTag());
}
};
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
/* Inflate the layout here */
holder = new ViewHolder();
holder.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
/* Find other views in you layout here */
holder.checkBox.setOnCheckedChangeListener(mCheckListener);
// Set the holder as tag, so you can fetch it later for re-use
convertView.setTag(holder);
} else {
// Fetch the holder
holder = (ViewHolder) convertView.getTag();
}
// Set the checkbox position
holder.checkBox.setTag(position);
return convertView;
}
If you're not familiar with using convertViews and the ViewHolder pattern you should watch The World of ListView that explains it all.
Is it bad in terms of performance? Or it doesn't matter?
It does not matter. The setter just assign the object.
And if I set the listener multiple times whether it will overwrite
previous listener or will i have multiple listeners for this event?
you will have just one for each instance of the checkbox upon you called setOnCheckedChangeListener. The ListView is particular case due of its recycling mechanism
i currently create an app that needs an custom listview. Everything with my listview is fine, but now i neet to know, how to set an onClickListener to an view, defined in my list_row.xml. i just want the onclicklistener on the whole item, and on this one inner view. I attach a picture to demonstrate my problem, because it is so hard to describe >.<
Picture (dropbox): https://www.dropbox.com/s/72xdxuwz47vl7s5/problem.png
I need a function that is called when clicking into the view [my Problem] indicates. its an ImageView filled with an image.
Here's something I've done before that seems pretty similar to what you want to accomplish.
First, you declare an onItemClickListener for your ListView. This will handle standard list item taps (that is, taps inside a list item but outside the inner view region that you're concerned about). You can do this in a variety of places in your code, but onCreate() is a common one.
Example:
mListView.setOnItemClickListener( new OnItemClickListener() {
#Override
public void onItemClick( AdapterView<?> parent, View view, int position, long id ) {
// Handle standard list item tap
// ...
}
} );
Then, you can just declare whatever onClickListeners you need for your inner view(s) inside your adapter's getView() method to handle click/tap events on your inner view.
Example:
#Override
public View getView( int position, View convertView, ViewGroup parent ) {
LinearLayout itemView;
// Inflate layout XML, etc.
// ...
// Find subviews in layout
ImageView innerView = (ImageView) itemView.findViewById( R.id.myInnerViewId );
// ...
// Set up onClickListener for inner view
innerView.setOnClickListener( new OnClickListener() {
#Override
public void onClick( View v ) {
// Handle inner view tap
// ...
}
} );
// ...
}
To set an OnClickListener in each row simply extend your current Adapter and override the getView() method. In there you can define specific listeners as you normally would.
This is discussed in great detail in this Google Talk by Romain Guy.
I want to set the background color of a specific item in the listview.
My listview is generated by ArrayAdapter using a ArrayList.
I have a specific item in the listview that I plan to change the background color.
I know the item's position in the list.
This is my code for generating the listview.
respondMessageListView = (ListView) findViewById(R.id.respondMessageListView);
respondMessageListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, autoRespondMessages.getMessages()));
Thank you!
[edit]
According to this post, using setSelection makes no effect if is used in onCreate(), the work around is "remove the method onAttachedToWindow in PullToRefreshListView". I am not quite understanding the solution. May I ask how should I accomplish this? I am a subclass of Activity, so I cannot subclass any other class anymore.
You will have to subclass ArrayAdapter and override the getView(...) method. For simplicity's sake you could just call through to the base class implementation and set the background color for the returned View.
Edit:
The following example colors the items' backgrounds alternating black and white.
private class MyAdapter extends ArrayAdapter {
...
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
v.setBackgroundColor(position % 2 == 0 : 0xff000000, 0xffffffff);
}
}
This code is for the when you select the listitem.
Try this code...
listview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> myAdapter, View myView, int pos, long mylng) {
if( pos == 1) {
// to change the listview background
listview.setBackgroundColor(getResources().getColor(R.color.your_color_id));
// to change the selected item background color
myView.setBackgroundColor(getResources().getColor(R.color.your_color_id));
}
}
});
Good luck.
I've searched around for a solution to my problem but couldn't find one so here it is.
I have a arraylist of data that i display using a combination of textview and checkbox. This is done using custom adapter which basically inflates it row by row. Everything up to this point is fine until i want to retrieve the checked data.
What i read from other people is that their checked boxes becomes unchecked when scrolled due to the recycling of the view. For me the state of the checkboxes remain but i don't know how to retireve them without scrolling it. When i check a box it is "check" on the UI but actaully it is not recognized until i scroll the checked item out of view and back in again.
Currently i use a global arraylist to populate with list.get(position).getName() if the list.get(position).isSelected() == true. But you see this arraylist remains empty unless i scroll the item out of view and back again. the same applies when the boxes are unchecked, they remain in the arraylist until i scroll. What i have in mind is a submit button that takes all the checkbox's current state when it is clicked then construct the arraylist.
any idea how to create such thing?
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) {
LayoutInflater inflator = context.getLayoutInflater();
view = inflator.inflate(R.layout.qlist, null);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.text = (TextView) view.findViewById(R.id.label);
viewHolder.checkbox = (CheckBox) view.findViewById(R.id.check);
viewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Model element = (Model) viewHolder.checkbox.getTag();
//
if(isChecked){
selctionCount++;
}
else if (!isChecked){
selctionCount--;
}
if(selctionCount > 2)
{
Toast.makeText(context, "error, you checked more than 2!", Toast.LENGTH_LONG).show();
element.setSelected(false);
}
else
{
element.setSelected(buttonView.isChecked());
}
System.out.println(selctionCount);
}
});
view.setTag(viewHolder);
viewHolder.checkbox.setTag(list.get(position));
}
else {
view = convertView;
((ViewHolder) view.getTag()).checkbox.setTag(list.get(position));
}
ViewHolder holder = (ViewHolder) view.getTag();
holder.text.setText(list.get(position).getName());
//This is the problem here! the selected arraylist (global) is empty even when i check the boxes,
//it only populates when i scroll the checked boxes out of view and back in! helphelp!
holder.checkbox.setChecked(list.get(position).isSelected());
if (list.get(position).isSelected() == true){
selected.add(list.get(position).getName());
}
else if (list.get(position).isSelected() == false){
selected.remove(list.get(position).getName());
}
return view;
}
}
I have already messed up with the similar problem. You can initially call a function in your constructor which initialize the states of your checkboxes as false and save all states in your arraylist. Whenever checkbox will be clicked just check the box and save its state in boolean arraylist.
Later you can retrieve your states from arraylist.
If you havn't understand then let me know I will try to send some lines of code to help you...
Here is a little code for your help:
I will create a boolean arraylist.
private ArrayList<Boolean> itemChecked = null;
Then I will set states of all checkboxes as false in my constructor:
for (int i=0; i < no_of_elements.size(); i++) {
itemChecked.add(i, false);
}
Set the actual checked state when checkbox clicked:
holder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
itemChecked.set(position, isChecked);
}
});