Android: ListView.getChildAt() throws null - android

Sometimes, when I call goToLast() it throws me a null exception in vista=lista.getChildAt(), it happens when the list is full, I dont know why I have this code:
private void goToLast() {
lista.post(new Runnable() {
#Override
public void run() {
lista.setSelection(mensajes.getCount() - 1);
View vista = lista.getChildAt(mensajes.getCount() - 1);
TextView txtMensaje = (TextView)vista.findViewById(R.id.txtMensajeLista);
txtMensaje.setBackgroundColor(Color.RED);
}
});
}

You should log lista.getChildCount(), see how many children the ListView has. ListView recycles views, which means it only hold limit number of views.
So if you want to get the last view, you should do something like this
private void goToLast() {
lista.post(new Runnable() {
#Override
public void run() {
lista.setSelection(mensajes.getCount() - 1);
// Figure out the last position of the view on the list.
int lastViewIndex = lista.getChildCount() - 1;
View vista = lista.getChildAt(lastViewIndex);
TextView txtMensaje = (TextView)vista.findViewById(R.id.txtMensajeLista);
txtMensaje.setBackgroundColor(Color.RED);
}
});
}

ListView.getChildAt() position is different to the position in your adapter. ListView recycles its views, so if you have more items in your adapter, than can fit on the screen it will not create views for all of those, but only the ones that are visible. If you want to update an item in the ListView you need to update it in the adapter and call notifyDataSetChanged()

Related

How to get specific position view in RecyclerView?

I have a recyclerView. One of recyclerView items has an onClick method. In this method, I setVisibility for one Item to Visible(default is gone), also get the position and add it to an arrayList.
onClick :
public void onClickImageView(ImageView imageView, int position) {
imageView.setVisibility(View.VISIBLE);
positionArrayList.add(position);
}
Before i go to another activity, I want to the ImageView visibility be reset to default (gone) so I use an interface to send positionArrayList to the recyclerView activity to get views by position and reset ImageViews to default.
Interface :
public interface RecyclerViewMainActivityImp {
void resetImageViewToDefault(ArrayList<Integer> positionArrayList);
}
Where I use interface method in adapter :
public void OnClickNextActivity() {
Intent intent = new Intent();
intent.setClass(context, ClassActivity.class);
context.startActivity(intent);
recyclerViewInterface.resetImageViewToDefault(positionArrayList);
}
Activity :
#Override
public void resetImageViewToDefault(ArrayList<Integer> positionArrayList) {
for (int position = 0; position < positionArrayList.size(); position++) {
View row = binding.recyclerView.getLayoutManager().findViewByPosition(positionArrayList.get(position));
ImageView imageView = row.findViewById(R.id.imageView);
imageView.setVisibility(View.GONE);
}
}
When i scroll the recyclerView, and if some items are not in view anymore their views going to null so some positions in positionArrayList no longer have their view.
My problem is some views are null after scrolling and I don't know how to get access to them.
I searched on google and found some solutions but I could not solve my problem
Note : I have used data binding
there's workaround you can try for this,
create a function in your activity
function void updateRecyclerView(position){
yourArrayListForVisibility.set(position,'hide');
mAdapter.notifyDataSetChanged();
}
#Override
public void onResume(){
super.onResume();
mAdapter.notifyDataSetChanged();
}
call this function in your adapter before intent and in your adapter check for this arraylist value to hide or show content, always set default value to 'yourArrayListForVisibility' for every position
Any Activity that restarts has its onResume() method executed first.
So, call notifyDataSetChanged() of RcyclerView from onResume()
#Override
public void onResume(){
super.onResume();
mAdapter.notifyDataSetChanged();
}

How to highlight a item in recycler view by position android

I am working on an application where I need a spinner if the user chooses some values from the spinner then I need to smooth scroll for that particular position and highlight the item of that position . I have done something for that I have a method that will highlight the position if the view is visible if the view is not visible it will not highlight. So, I am facing some issues like this.
This is the place where I get the position from the spinner
versesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
final int possition = (int) versesSpinner.getSelectedItemId();
recyclerView.smoothScrollToPosition(possition);
setBackgroundColor(possition);
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
And this is the method where I highlight the item
public void setBackgroundColor(int position) {
for (int i = 0; i < versesList.size(); i++) {
View view = mLayoutManager.findViewByPosition(i);
try {
if (view != null) {
if (i == position)
view.setBackgroundColor(Color.parseColor("#504cbee7"));
else
view.setBackgroundColor(Color.WHITE);
}
} catch (Exception e) {}
}
}
Here the problem is while calling the setBackground() method before the smooth scroll method reaches that particular item.
The highlighting will be done only if the view is visible and if the view is not null.
Please tell me if there is any other way is there to achieve this else please tell me how can I find out if the smooth scroll to position has reached the particular item
I hope it make sense.
You are trying to change the background of item before it has completed the scrolling. Use a handler to schedule that task after some time.
recyclerView.postDelayed(new Runnable(){
public void run(){
setBackgroundColor(possition);
}
},1000);
You might consider having a loom at my answer here to check how to highlight the items in your RecyclerView on demand. Now if you want a smooth scroll to the position highlighted just add smoothScrollToPosition(position) right after calling the notifyDataSetChanged() function.
So the highLightTheListItems() function might look like this which I'm referring from my answer there.
public void highLightTheListItems() {
// Modify your list items.
// Call notifyDataSetChanged in your adapter
yourAdapter.notifyDataSetChanged();
// Now smooth scroll to the position you want.
}
Hope that helps.

RecyclerView notifyDataSetChanged IllegalStateException

I wana create mobile application that uses RecyclerView with pagination that loads each time from dataBase 10 items, then when the list reaches the bottom I load 10 other items, so I used this metho to be notified if I reached the end of the list :
public boolean reachedEndOfList(int position) {
// can check if close or exactly at the end
return position == getItemCount() - 1;
}
and I used this function to load items :
#Override
public void onBindViewHolder(Info holder, int position, List<Object> payloads) {
if (reachedEndOfList(position)) {
Log.d("reachedEnd", "true");
this.autoCompletesTmp = getTmp();
notifyDataSetChanged();
}
}
getTmp() update the list of items with another 10 items, but i get this exception when I reached the bottom of the list:
java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
I had the same problem a while ago:
This helped:
Handler handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
//Do something like notifyDataSetChanged()
}
};
handler.post(r);

Update a listview item row but on scroll the modifications don't remain

I have a ListView in an Android Activity and a custom adapter for that listview.
I want to be able to edit a row item and update that row instantly. This works, the modifications of the row is seen But, on scroll i loose all data.
This is my Asynk task from where i get the data and update the list row item:
/**
*
*/
public class EditNewsFeedPostAsyncTask extends AsyncTask<Void, Void, Boolean> {
public Activity context;
public String content;
public int rowPosition;
public ListView listView;
public TextView decriptionTxt;
#Override
protected Boolean doInBackground(Void... params) {
try {
token = Utils.getToken(context);
if (token != null) {
....
// {"status":"true"}
if (result != null) {
....
}
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
#Override
protected void onPostExecute(final Boolean success) {
if (success) {
updateListView(rowPosition, content);
}
}
public boolean updateListView(int position, String content) {
int first = listView.getFirstVisiblePosition();
int last = listView.getLastVisiblePosition();
if (position < first || position > last) {
return false;
} else {
View convertView = listView.getChildAt(position - first);
decriptionTxt.setText(content);
listView.invalidateViews();
return true;
}
}
private void updateView(int index, TextView decriptionTxt) {
View v = listView.getChildAt(index - listView.getFirstVisiblePosition());
if (v == null)
return;
decriptionTxt.setText(content);
listView.invalidateViews();
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onCancelled() {
}
}
What am i missing? shouldn't the data be persistent?
Thx
You must update the object in your listView adapter, not only the views!
after scrolling, the getView method inside your list's adapter will call and you will return the default view for that.
if you want to change that item permanent, you should update your data set and call notifyDataSetChanged on your adapter.
Make sure you're updating the data, not just the view.
When you modify the row, are you changing the underlying data or just the view? If just the view...
You're probably running into ListView recycling issues. This answer has a great explanation. Basically, ListViews are about efficiency in displaying views based on data, but are not good for holding new data on screen. Every time a ListView item is scrolled out of view, its View is recycled to be used for the item that just scrolled into view. Therefore, if you put "hi" in an EditText and then scroll it off screen, you can say goodbye to that string.
I solved this in my app by ditching ListView altogether and using an array of LinearLayouts (probably a clunky approach, but I had a known list size and it works great now). If you want to continue using a ListView, you'll have to approach it from a "data first" perspective. Like I said, ListViews are great at showing info from underlying data. If you put "hi" in an EditText and simultaneously put that string in the underlying data, it would be there regardless of any scrolling you do. Updating onTextChanged might be cumbersome, so you could also let each row open a dialog in which the user enters their data which then updates the underlying dataset when the dialog closes.
These are just some ideas, based on some assumptions, but editing views in a ListView is, in general, not very in line with how ListViews work.

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

Categories

Resources