I am facing issue in multiselect listview and issue i am facing was when i select the first item in the listview last item also checked while scrolling and this issue happens only if i have more that 10+ items in listview. I don't know what's wrong in my code and could any one suggest me a proper solution to avoid this problem.Thanks in advance
private class MultipleChoiceClickListener implements
ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
if (questionCount > 0) {
ImageView imgTick = (ImageView) view.findViewById(R.id.imgTick);
int len = lvMultiSelect.getCount();
checked = lvMultiSelect.getCheckedItemPositions();
for (int i = 0; i < len; i++) {
if (i == position) {
if (checked.get(i)) {
lvMultiSelect.getChildAt(i).setBackgroundColor(
getResources().getColor(
R.color.listitem_onclick));
imgTick.setImageResource(R.drawable.tick_img);
} else {
lvMultiSelect.getChildAt(i).setBackgroundColor(
getResources().getColor(
R.color.default_list_background));
imgTick.setImageResource(R.drawable.not_checked_img);
}
}
}
}
Create an object to store the position of all tags something like Integer array.
In your onItemClick, try this:
checked = view.getTag();
and once you select an item, do view.setTag(positions);. But nevertheless I suggest you, switch to RecyclerView if this codebase is new / you are doing it from scratch.
I have done this using RecyclerView Multiselect using Contextual Action Mode. Check this.
Related
I'm using a ListView with sections. I have a list of ArrayAdapters, one for each section.
I need to determine the right adapter on onItemClick to launch a method in the adapter.
How do I do get a reference to the the right adapter?
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id)
{
adapterList.get(x).toggleBought(position);
//need to determine x (the x'th adapter in the list
//or somehow get a reference to the adapter instead of adapterList.get(x)
}
and here's the code where the adapterlist is created:
adapterList = new ArrayList<CartRowAdapter>(); //list of ArrayAdapters
oListAdapter = new CartListAdapter(getActivity()); //BaseAdapter
for (int i = 0; i < cartLists.size(); i++)
{
adapterList.add(new CartRowAdapter(getActivity(), R.layout.cart_row,
cartLists.get(i)));
oListAdapter.addSection(cartLists.get(i).get(0).getAisle(),
adapterList.get(i));
}
listView.setAdapter(oListAdapter);
The parent AdapterView is passed in as a parameter to onItemClick() and it has getAdapter().
I ended up fixing this by writing an ugly hack.
I had another by having the item position off since each section header also counted.
Also for the adapters the first item on each adapter is position 0, so I'm calculating that as well.
Although it's an ugly hack, at least it works perfectly for what I'm doing. i is the number I was looking for.
int i = 0;
int sizes = 0;
while (true)
{
sizes += cartLists.get(i).size();
position -= 1;
if (position < sizes)
{
position -= sizes;
position += cartLists.get(i).size();
break;
}
else
{
i++;
}
}
I'm trying to implement my own StickyGridHeadersBaseAdapter, my current source code here - http://paste.org.ru/?11jrjh, and I use it like
ModeAdapter adapter = new ModeAdapter(this);
modeGridView.setAdapter(adapter);
Problems which I have is that
1) I have no idea how to call notifyDataSetChanged() for this adapter, so I can't change items
2) And implementation of AdapterView.OnItemClickListener (http://paste.org.ru/?mvgt7b) works strange
Mode mode = (Mode) adapter.getItem(position);
returns null for items with 1st and 2nd positions, item on 3rd position is actual 1st item in adapter.
Where is my fault here?
One more question is why I can't cast adapterView.getAdapter() in my OnItemClickListener to my ModeAdapter class. What if I want to call notifyDataSetChanged() here?
I didn't find any examples for custom implementation of StickyGridHeadersBaseAdapter here.
Thanks in advance.
I had the same issue as you 2):
after the first header i got the item of the previous row, after the second header got the item of two rows up, etc...
The reason is the following:
StickyHeadersGridView:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mOnItemClickListener.onItemClick(parent, view, mAdapter.translatePosition(position).mPosition, id);
}
The position is corrected. so in your onItemClick you get the corrected value of position.
If than you request the item with: Mode mode = (Mode) adapter.getItem(position);
you get StickyGridHeadersBaseAdapterWrapper.getItem(int pos)
#Override
public Object getItem(int position) throws ArrayIndexOutOfBoundsException {
Position adapterPosition = translatePosition(position);
if (adapterPosition.mPosition == POSITION_FILLER || adapterPosition.mPosition == POSITION_HEADER) {
// Fake entry in view.
return null;
}
return mDelegate.getItem(adapterPosition.mPosition);
}
In StickyGridHeadersBaseAdapterWrapper.getItem() position gets corrected for the second time which causes the wrong item to be returned...
I added a work-around:
In StickyHeadersBaseAdapterWrapper I added:
public Object getItemAtDelegatePosition(int pos) {
return mDelegate.getItem(pos);
}
And I use this in onItemClick:
Mode item = (Mode) ((StickyGridHeadersBaseAdapterWrapper)parent.getAdapter()).getItemAtDelegatePosition(position);
An easier way to get an item would be:
StickyGridHeadersBaseAdapterWrapper wrapper = (StickyGridHeadersBaseAdapterWrapper) parent.getAdapter();
Mode item = (Mode ) wrapper.getWrappedAdapter().getItem(position);
I have a listView that contains lots of elements i.e. we have to scroll down to see all the elements. Now what i want to do is, click all the listView elements. How can I do that. Right now,I am using the following code but it doesn't scroll automatically. Please help.
ListView l = solo.getCurrentListViews().get(0);
assertNotNull("No list views!", l);
assertTrue("No items in list view!", l.getChildCount() > 0);
// Get the last list item
View v = l.getChildAt(l.getChildCount());
System.out.println("getChildCount: " + l.getChildCount());
int i = 1;
while (i <= l.getChildCount()) {
solo.clickInList(i);
solo.goBack();
i++;
}
I have previously used these helper functions in a slightly different state to handle most of what we need with listviews:
public View getViewAtIndex(final ListView listElement, final int indexInList, Instrumentation instrumentation) {
ListView parent = listElement;
if (parent != null) {
if (indexInList <= parent.getAdapter().getCount()) {
scrollListTo(parent, indexInList, instrumentation);
int indexToUse = indexInList - parent.getFirstVisiblePosition();
return parent.getChildAt(indexToUse);
}
}
return null;
}
public <T extends AbsListView> void scrollListTo(final T listView,
final int index, Instrumentation instrumentation) {
instrumentation.runOnMainSync(new Runnable() {
#Override
public void run() {
listView.setSelection(index);
}
});
instrumentation.waitForIdleSync();
}
With these your method would be:
ListView list = solo.getCurrentListViews().get(0);
for(int i=0; i < list.getAdapter().getCount(); i++){
solo.clickOnView(getViewAtIndex(list, i, getInstrumentation()))
}
It looks like your code, as currently implemented, is only considering the visibile list items when controlling the loop and handling the clicking. It's important to note the behavior of two things:
First, there's a concept called view recycling in Android that helps conserve memory when dealing with ListViews. Only the views that are currently on screen are created, and once they scroll off the screen they'll be repopulated with new data. Therefore, calling methods like getChildCount and getChildAt on a ListView will only perform these operations on the visible items. To find information about the data that populates the list, you can call methods such as getCount() or getItem() on the ListView's adapter.
Second, the clickInList() method is 1-indexed, relative to the current position of the list, and can only be used for visible items. As far as I know, it will never scroll your list automatically. This means that calling clickInList(2) when at the top of the list will click the second item, but then calling clickInList(2) again when the 30th item is at the top of the list will click the 32nd.
Knowing these two things, your solution will need to consider all of the list data and perhaps have a bit more precision when making clicks. Here's how I'd rewrite your while loop to ensure you'll be able to handle every item on the list, hope this helps:
ListAdapter adapter = l.getAdapter();
for(int i=0; i < adapter.getCount(); i++)
{
//Scroll down the list to make sure the current item is visible
solo.scrollListToLine(l, i);
//Here you need to figure out which view to click on.
//Perhaps using adapter.getItem() to get the data for the current list item, so you know the text it is displaying.
//Here you need to click the item!
//Even though you're in a list view, you can use methods such as clickOnText(), which might be easier based on how your adapter is set up
solo.goBack();
}
It should help you(not tested):
public void clickAllElementsOnListView(int index) {
ListView listView = solo.getCurrentListViews().get(index);
count = listView.getAdapter() != null ? listView.getAdapter().getCount() : 0;
for (int i = 0; i < count; i++) {
scrollListToLine(listView, i);
solo.clickInList(1, index);
solo.goBack();
}
}
protected void scrollListToLine(final ListView listView, final int line) {
getInstrumentation().runOnMainSync(new Runnable() {
public void run() {
listView.setSelection(line);
}
});
}
I create the below code:
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v, int position,
long id) {
for (int i = 0; i < mListView.getCount(); i++) {
View callLogView = mListView.getChildAt(i);
mRelativeLayout = (LinearLayout)callLogView.findViewById(R.id.myShow);
if(i == position){
if(mRelativeLayout.getVisibility() == View.GONE){
mRelativeLayout.setVisibility(View.VISIBLE);
}
else{
mRelativeLayout.setVisibility(View.GONE);
}
}else{
mRelativeLayout.setVisibility(View.GONE);
}
}
}
});
I want to realize a function like when i click one item of Listview, it will show a view, and the other items of Listview will be hidden. But mListView.getChildAt(i) will have the null pointer after exceed mListView.getChildCount().
How to solve this? Thanks in advance!
AdapterView.getCount() returns the number of data items, which may be larger than the number of visible views, that's why you are getting null pointer exception, because you are trying to find views which do not exist in the current visible ListView items.
To solve this issue you will first need to find the first visible item in the ListView using getFirstVisiblePosition() and the last visible item using getLastVisiblePosition(). Change the for loop condition as:
int num_of_visible_view=mListView.getLastVisiblePosition() -
mListView.getFirstVisiblePosition();
for (int i = 0; i < num_of_visible_view; i++) {
// do your code here
}
you can not implement this in onItemClick.
As you can access only visible child not all child.
What you can do is on onItemClick
you can send the position in adapter
and then set the logic there in getView too change view
and update the adapter in listview, or notify for changes.
I have a list view full of items, after the users selects an item it lights up, and then it goes back to normal. Is there a way to make it so that when the user selects an item in my ListView it stays selected, and highlighted?
Apparently the "disappearing selection" is by design; it's something called "touch mode". I read through that document and still I have no idea why they thought it was a good idea. My guess is that, since Android was originally designed for small-screen devices, they expected that you would fill the screen with a list and then, when the user clicks an item, move to a new list on a different screen. Thus, the user wouldn't be aware that Android lost track of the selected item.
But this behavior is quite annoying if, for example, you want the user to select an item and then show information about that item on the same screen. If the selection disappears, how is the user supposed to know what they clicked (assuming of course that users have the attention span of a goldfish)?
One possible solution is to change all the list items into radio buttons. I don't really like that solution because it wastes screen real estate. I'd rather just use the background color to show which item is selected. I have seen one solution so far but it is not quite complete or general. So here's my solution:
1. In your XML layout file
Go to your ListView element and the following attribute: android:choiceMode="singleChoice". I'm not entirely sure what this does (by itself, it doesn't allow the user to select anything) but without this attribute, the code below doesn't work.
2. Define the following class
It is used to keep track of the selected item, and also allows you to simulate pass-by-reference in Java:
public class IntHolder {
public int value;
public IntHolder() {}
public IntHolder(int v) { value = v; }
}
3. Put the following code somewhere
I'll assume you put it in your Activity, but it could go in any class really:
static void setListItems(Context context, AdapterView listView, List listItems, final IntHolder selectedPosition)
{
setListItems(context, listView, listItems, selectedPosition,
android.R.layout.simple_list_item_1,
android.R.layout.simple_spinner_dropdown_item);
}
static void setListItems(Context context, AdapterView listView, List listItems, final IntHolder selectedPosition,
int list_item_id, int dropdown_id)
{
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> list, View lv, int position, long id) {
selectedPosition.value = position;
}
});
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(context, list_item_id, listItems) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = super.getView(position, convertView, parent);
if (selectedPosition.value == position)
itemView.setBackgroundColor(0xA0FF8000); // orange
else
itemView.setBackgroundColor(Color.TRANSPARENT);
return itemView;
}
};
adapter.setDropDownViewResource(dropdown_id);
listView.setAdapter(adapter);
}
This code does two things: it attaches your list items (e.g. List<String>) to your ListView, and it overrides ArrayAdapter.getView() with some code that changes the background of the selected item.
4. Use that code to set up your list
For example:
ListView _list;
IntHolder _selectedItem = new IntHolder(-1); // nothing selected at first
#Override
protected void onCreate(Bundle savedInstanceState) {
...
_list = (ListView)findViewById(R.id.list);
List<String> items = Arrays.asList("Item 1", "Item 2", "Item 3");
setListItems(this, _list, items, _selectedItem);
}
That's all! The above assumes you want single selection. With some small modifications to getView(), you could support multi-selection too, I guess, but you should probably use checkboxes instead.
Warning: this solution needs further development. If the user uses arrow keys or buttons to select an item, that item will not be selected from the IntHolder's perspective. If the user presses the unlabeled button (what's the name of that button? "Enter"?) then the item will become "officially" selected but then you have another problem because if the user uses the arrow keys again, it will sort of look like two items are selected. Leave a comment if you figure out how to keep the "internal selection" in the IntHolder synchronized with the "keyboard selection" or whatever it's called. What is it called, anyway?
There is an attribute in ListView called listSelector:
Drawable used to indicate the currently selected item in the list.
http://developer.android.com/reference/android/widget/AbsListView.html#attr_android:listSelector
EDIT after Stan comment
To ensure that a ListView stays selected, you should
① Set the view's attribute choiceMode via xml or programmatically.
② Use an adapter that uses views which implement Checkable interface, like CheckedTextView (inside simple_list_item_single_choice layout).
File TestActivity.java
public class TestActivity extends Activity {
private static final int SINGLE_CHOICE = android.R.layout.simple_list_item_single_choice;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
String[] items = {"test 1", "test 2", "test 3"};
ListAdapter adapter = new ArrayAdapter<String>(this, SINGLE_CHOICE, items);
ListView list = (ListView) findViewById(R.id.testList);
list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
list.setAdapter(adapter);
}
}
Here a simpler solution than Qwertie's:
Do not rely on given selection mechanism. Do it yourself.
View mSelectedItemView = null; //class member variable
View mTouchedItemView = null; //class member variable
ListView v = (ListView) getActivity().findViewById(R.id.listView);
// select on click
v.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter,
View clickedViewItem, int position, long id) {
if (mSelectedItemView != null)
selectedItemView.setBackgroundColor(Color.WHITE);
clickedViewItem.setBackgroundColor(Color.YELLOW);
mSelectedItemView = clickedViewItem;
}
});
// highlight on touch
v.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v instanceof ListView) {
ListView listView = (ListView) v;
// Find the child view that was touched (perform a
// hit test)
Rect rect = new Rect();
int childCount = listView.getChildCount();
int[] listViewCoords = new int[2];
v.getLocationOnScreen(listViewCoords);
int x = (int) event.getRawX() - listViewCoords[0];
int y = (int) event.getRawY() - listViewCoords[1];
View child;
for (int i = 0; i < childCount; i++) {
child = listView.getChildAt(i);
child.getHitRect(rect);
if (rect.contains(x, y)) {
View touchedView = child;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
touchedView
.setBackgroundColor(Color.RED);
mTouchedItemView = touchedView;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
mTouchedItemView
.setBackgroundColor(Color.WHITE);
}
}
}
}
return false;
}
});
Also this method only deals with clicks and will not work if the user uses the arrow keys.
Disclaimer: De-highlighting after touch does not work reliably.
Credits for the touching part go to ozik.dev:
Get Item from ListView only with OnTouchListener
just add this to your listview layout
android:listSelector="#drawable/selector_expandable_listview"
android:drawSelectorOnTop="true"
Use a Selector.XML File and this code:
//SetOnClickListner to catch Events
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
view.setSelected(true);
}
});
Just add this to your ListView:
android:listSelector="#color/my_color"
This answer is working try this one
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long arg3)
{
for(int a = 0; a < parent.getChildCount(); a++)
{
parent.getChildAt(a).setBackgroundColor(Color.TRANSPARENT);
}
view.setBackgroundColor(Color.GREEN);
}