Scrolling ListView - android

In listView I marked a line selected by a particular color, scrolling down the list and going back up - a marked line becomes colored even though there is still a mark (I did a test for it). Have you encountered such a phenomenon and if so how can you handle it.
'''
public class CustomDesignList extends AppCompatActivity {
ArrayList<Fruit> list;
ListView listView;
FruitAdapter adapter;
String[] fruitNames= {"apple", "apricot", "banana", "cherry", "coconut", "grapes",
"kiwi","mango", "melon","orange", "peach","pear",
"pineapple","strawberry", "watermelon"};
int[] imageResourceArray= {R.drawable.apple,R.drawable.apricot,R.drawable.banana,
R.drawable.cherry,R.drawable.coconut,R.drawable.grapes,
R.drawable.kiwi,R.drawable.mango,R.drawable.melon,R.drawable.orange,
R.drawable.peach,R.drawable.pear,R.drawable.pineapple,
R.drawable.strawberry,R.drawable.watermelon};
int[] arrCounter=new int[15];// מערך מונים לסימון ברשימה
int sum=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_design_list);
for(int i = 0; i< arrCounter.length;i++)
arrCounter[i] = 0;
listView=findViewById(R.id.lvCustom);
list=new ArrayList<>();
for(int i=0; i<fruitNames.length;i++)
list.add(new Fruit(fruitNames[i],
(int)((Math.random() * (100 - 10 + 1)) + 10),
imageResourceArray[i]));
//Connect all data to all elements in the list
//Layout where we defined what one element in the list would look like
adapter=new FruitAdapter(this,R.layout.my_custom_list,this.list);
//Connect the full list of data to xml
this.listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
arrCounter[position]++;
if( arrCounter[position] %2==1)
{
view.setBackgroundColor(Color.parseColor("#EBBEF3"));
sum+=list.get(position).getFruitWeight();//
}
else
{
view.setBackgroundColor(Color.TRANSPARENT);
sum-=list.get(position).getFruitWeight();//
}
}
});
}'''

You have a bunch of problems here.
1)You shouldn't be using ListView these days. RecyclerView replaced it most of a decade ago.
2)In a ListView click handler, you can't change anything in the UI. If you do and you don't really know what you're doing, you're going to screw it up. Like you did here. Instead of changing the UI, you should change the data in the adapter, and then tell the view to reload new data from the adapter. Anything else and you're going to have problems when the views recycle themselves on scroll.
I'd really look at changing to recycler view, but at a minimum you need to change how your click handler works completely so that it doesn't make any UI changes directly and only changes the data.

Related

Change Specific TextView Color in Listview

i Have Two Database
first one Contain All The Items, and the ListView Display it
and the second db contain the the Favorite item , [selected from the first database]
what i want is that when the listview display all the items
check if the item is already exist in Favoritelist then make that textview background RED for this item
i have this code that work fine
public static void ChangeMyFavoritesBG(){
for (int i = 0; i < Items.size(); i++) {
if(db.verification(Items.get(i).toString())){
try {
TextView favtextview = (TextView) listview.getChildAt(i-listview.getFirstVisiblePosition()).findViewById(R.id.item_name);
favtextview.setBackgroundResource(R.drawable.redcolor);
}catch (NullPointerException e) {
}}}}
db.verification check if item exist in favorites database
if true . then it should change the background of this item to red
this code work fine but only if i put it in button click
i need to make the code work automatically
but if i made it start automatically when the activity is loaded i get NullPointer Error
i guess because the function ChangeMyFavoritesBG(); work before the listview display items
any idea guys? and sorry for my bad english
Do this control inside the getView(int position, View convertView, ViewGroup parent) method of the Adapter used by the listView.
If your favorite is not currently visible in the ListView then getChildAt() will return null.
You are looping over all items in the list view and my guess is that it holds more items than can fit on the screen. When your favorite item is one of them then this fragment of your code
listview.getChildAt(i-listview.getFirstVisiblePosition())
will return null. And that will cause the NullPointerException when you call findViewById(R.id.item_name) on it.
Just add a check for null on the result of getChildAt(). If it is null then do nothing, if it is non-null then call the second part. This will protect against the exception when your favorite item is not on the screen, and will allow it to be colored red when your favorite is visible on the screen.
update
My apologies, I read to quickly and misunderstood your problem to be about the NullPointerException but you say that your code works fine when you call it from a button click handler but not when you call it automatically at start-up.
You are right, the ListView does not yet have any items loaded when you are still in onCreate(). You can add a delay before running you code. The following works for me:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
// initialize the ListView with data for the list items. (I'm using a string array in this
// example. You are loading it from a database table, but that is the same in principle.)
ListAdapter adapter = new ArrayAdapter<String>(this, R.layout.item_list, R.id.item_name, Items);
ListView listview = (ListView) findViewById(R.id.listview);
listview.setAdapter(adapter);
// ask the system to wait before setting the background color of the favorite item so that
// the ListView has time to load the items.
final int DELAY_IN_MILLISECONDS = 100;
listview.postDelayed(new Runnable() {
#Override
public void run() {
ChangeMyFavoritesBG();
}
}, DELAY_IN_MILLISECONDS);
}
As you can see in the above example, after initializing the ListView, you ask the system to wait 100 milliseconds before calling ChangeMyFavoritesBG(). Hopefully that is enough time to load the items from the database into the ListView. If it is not enough time then you can, of course, use a longer delay.
The alternative
The above should work, but to be honest I would not write it this way. The above code is very brittle because it depends on the timing of how long it takes to load the items. I recommend that you put your background coloring into a customized adapter.
Because you want the items displayed in a customized way -- you want them to have a red background when it is the favorite one -- you should use a customized adapter. Override the bindView() function to make the background red when it is the favorite one or give it a normal background when it is not the favorite.
I don't know how you currently get the items from the database into your ListView, but inheriting from SimpleCursorAdaptor would work pretty well.
public class FavoritesItemAdapter extends SimpleCursorAdapter {
public FavoritesItemAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
super.bindView(view, context, cursor);
// read the name from the database
int nameColumnIndex = cursor.getColumnIndexOrThrow("name");
String name = cursor.getString(nameColumnIndex);
// write the name to the TextView
TextView nameText = (TextView) view.findViewById(R.id.item_name);
nameText.setText(name);
// set the background to normal or to red, depending on if it is the favorite one or not
boolean isFavorite = db_verification(name);
if (isFavorite) {
nameText.setBackgroundResource(R.drawable.redcolor);
} else {
nameText.setBackgroundResource(android.R.color.transparent);
}
}
public boolean db_verification(String name) {
// this is a stub. You must use your own code here
return name.equals("the favorite one");
}
}
You can then throw away ChangeMyFavoritesBG() and initialize your ListView with the adapter in onCreate() like this.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
Cursor cursor = readItemsFromDatabase();
String[] from = new String[] { "name_column" }; // mapping from database column name ...
int[] to = new int[] { R.id.item_name }; // ... to View ID in the item's layout.
FavoritesItemAdapter adapter = new FavoritesItemAdapter(this, R.layout.item_list, cursor, from, to, 0);
ListView listview = (ListView) findViewById(R.id.listview);
listview.setAdapter(adapter);
}
Good luck!

Android list -- how to find the wanted item?

I do have a list of possibly several thousands items (just testing with shorter list with about 200 items). The information is stored in SQLite, the ContentProvider, loader and SimpleCursorAdapter is used. The list is sorted lexicographically, and the android:fastScrollEnabled is used. The list scrolls smoothly -- no problem when one knows the exact name of the item.
Occasionally, I would like to find the items that contain some substring somewhere in the middle of their name. The `... LIKE "%wanted%" is a solution for me. However, I would like to give the user an incremental filtering -- i.e. the list content be updated during typing the substring. The reasoning is that it may not be neccessary to type many characters, and one should find the item as soon as possible. The goal is not to find one or none item. The goal is to filter the list so that manual scrolling be acceptable to overview the candidate items and select one of them by touch.
I have met the SearchView widget that looks nicely at the action bar. Anyway, reading something more about it in the doc, I am not sure if it is the right tool for me. Or if the recommended implementation is the one for me. (I am an android beginner, I am even not sure if I understand it well.)
Is it possible to use the widget for incremental filtering of the list in the same activity that has the SearchView in the Action Bar? Can you point me to some code that possibly shows how to implement the wanted behaviour?
Sample code to try out :
public class AndroidListViewFilterActivity extends Activity {
ArrayAdapter<String> dataAdapter = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Generate list View from ArrayList
displayListView();
}
private void displayListView() {
//Array list of countries
List<String> countryList = new ArrayList<String>();
countryList.add("Aruba");
countryList.add("Anguilla");
countryList.add("Netherlands Antilles");
countryList.add("Antigua and Barbuda");
countryList.add("Bahamas");
countryList.add("Belize");
countryList.add("Bermuda");
countryList.add("Barbados");
countryList.add("Canada");
countryList.add("Costa Rica");
countryList.add("Cuba");
countryList.add("Cayman Islands");
countryList.add("Dominica");
countryList.add("Dominican Republic");
countryList.add("Guadeloupe");
countryList.add("Grenada");
//create an ArrayAdaptar from the String Array
dataAdapter = new ArrayAdapter<String>(this,R.layout.country_list, countryList);
ListView listView = (ListView) findViewById(R.id.listView1);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
//enables filtering for the contents of the given ListView
listView.setTextFilterEnabled(true);
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
// When clicked, show a toast with the TextView text
Toast.makeText(getApplicationContext(),((TextView) view).getText(), Toast.LENGTH_SHORT).show();
}
});
EditText myFilter = (EditText) findViewById(R.id.myFilter);
myFilter.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
dataAdapter.getFilter().filter(s.toString());
}
});
}
}

Click all the list view elements while scrolling using robotium

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

How to apply android layout animation only to children above a certain index?

I have a ListView containing a series of notes.
Currently I use a layout animation to slide all the notes in from the side when the list first loads; this works perfectly.
However, I'm trying to figure out how to apply a layout animation only to list items below a certain point. Say I delete an item on the list: I'd like all items below it to shift up into the deleted note's old spot.
I've tried finding a way to customize the animation delays or interpolators by child index but haven't found anything appropriate for this location. Is there a way to do this using a custom layout animation (such as extending LayoutAnimationController) or would I have to do this low level and animate each view individually?
Any suggestions?
Create your animation and call it in your list OnItemClickListener. After that you might use the adapter's notifyDataSetChanged to refresh the list content.
In this example, I created a method called removeListItem with receives the row you want to remove and the position of that row in you list content array.
public class MainActivity extends ListActivity implements OnItemClickListener{
ArrayList<String> values;
ArrayAdapter<String> adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
values = generateMockData(50);
adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1, values);
setContentView(R.layout.activity_main);
getListView().setAdapter(adapter);
getListView().setOnItemClickListener(this);
}
private ArrayList<String> generateMockData(int number) {
ArrayList<String> result = new ArrayList<String>();
for(int i = 0; i < number; i++)
result.add(""+i+" "+ (int)Math.random() * 13);
return result;
}
private void removeListItem(View rowView, final int positon) {
Animation anim = AnimationUtils.loadAnimation(this,
android.R.anim.slide_out_right);
anim.setDuration(500);
rowView.startAnimation(anim);
new Handler().postDelayed(new Runnable() {
public void run() {
values.remove(positon);//remove the current content from the array
adapter.notifyDataSetChanged();//refresh you list
}
}, anim.getDuration());
}
public void onItemClick(AdapterView<?> arg0, View row, int position, long arg3) {
if(position == YOUR_INDEX) //apply your conditions here!
removeListItem(row,position);
}
I had a very similar problem and was able to find a solution with a simple View subclass that allows you to use a layout animation controller to animate only the views that you specify. Please see this link:
Can LayoutAnimationController animate only specified Views
In your layout xml, try adding:
<ListView
android:id="#android:id/list"
...
android:animateLayoutChanges="true"/>
This will automatically animate insertions and deletions from your listview.
If you have a custom animation, named anim_translate_left, use instead:
<ListView
android:id="#android:id/list"
...
android:animateLayoutChanges="#anim/anim_translate_left"/>
Source: Google API's

ListView stay selected?

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

Categories

Resources