In an android ListView, how can I iterate/manipulate all the child views, not just the visible ones?
Asked Answered
M

4

10

The code below does NOT change the text of all of a ListView's rows because getChildCount() does not get all of a ListView's rows, but just the rows that are visible.

for (int i = 0; i < listView.getChildCount(); i++)
{
    View v = listView.getChildAt(i);
    TextView tx = (TextView) v.findViewById(R.id.mytext);
    tx.setTextSize(newTextSize);
}

So, what SHOULD I do?

Is there code for getting a notification when a ListView's row becomes visible, so I can set its text size then?

Magdala answered 22/1, 2010 at 1:37 Comment(1)
Store the text size in your adapter or ListActivity and set the text size in your adapter's getView method.Tallage
R
9

List13 from the API Demos does something similar using OnScrollStateChanged. There may be a better way, though:

public void onScrollStateChanged(AbsListView view, int scrollState) {
    switch (scrollState) {
    case OnScrollListener.SCROLL_STATE_IDLE:
        mBusy = false;

        int first = view.getFirstVisiblePosition();
        int count = view.getChildCount();
        for (int i=0; i<count; i++) {
            TextView t = (TextView)view.getChildAt(i);
            if (t.getTag() != null) {
                t.setText(mStrings[first + i]);
                t.setTag(null);
            }
        }

        mStatus.setText("Idle");
        break;

. . .

EDIT BY Corey Trager:

The above definitely pointed me in the right direction. I found handling OnScrollListener.onScroll worked better than onScrollStateChanged. Even if I removed the case statement in onScrollSgtaetChanged and handled every state change, some text wasn't getting resized. But with onScroll, things seem to work.

So, my seemingly working code looks like this:

public void onScroll(AbsListView v, int firstVisibleItem, int visibleCount, int totalItemCount)
{
    ListView lv = this.getListView();
    int childCount = lv.getChildCount();

    for (int i = 0; i < childCount; i++)
    {
        View v = lv.getChildAt(i);
        TextView tx = (TextView) v.findViewById(R.id.mytext);
        tx.setTextSize(textSize);
    }
}
Returnee answered 22/1, 2010 at 1:54 Comment(4)
This will probably work but it will also be very very very costly. You're doing the same work on every scroll event, of which you can receive many (and not necessarily only when a new item becomes visible.)Quadrant
@Romain - "costly" only matters if there is some effect that the user himself can see. And there isn't. Computers are faster than you give them credit for and they don't mind the hard work. Hey, think of any sort of paint program that reacts to every mouse movement event by extending the visible stroke.Magdala
No they're not. We are dealing with phones and everything you do costs battery. There is absolutely NO REASON to be a lazy programmer and waste battery. The reason why Android is usable on phones is because we spend a lot of time worrying about performance and battery life. We still have lots to do but your point of view is certainly flawed. There are situations where there's no need to worry much about performance (button click for instance), but doing what you do for every pixel scrolled is just stupid.Quadrant
@Romain - Ok, ok, you convinced me that I SHOULD care, so now I DO. So, what SHOULD my code do? @RickNotFred's code, which we got from a SDK sample app, did NOT work. That is, some text got changed, but as I scrolled other items into view, they had not gotten the message. What works?Magdala
Q
15

In a ListView the only children are the visible ones. If you want to do something with "all the children," do it in the adapter. That's the best place.

Quadrant answered 22/1, 2010 at 6:21 Comment(4)
"all the children" means the TextViews. I'm changing their font-size. Are the TextViews children of the adapter or children of the listview itself?Magdala
They're children of the ListView.Quadrant
this is the right answer. i chose to override getView in my custom adapter and perform work needed per item to be displayed. also if trigger your text changes from onClick event, call invalidateViews() on List View. Works nicely! Thanks Romain!Eulau
@Romain, do you have any examples of this?Discrepant
R
9

List13 from the API Demos does something similar using OnScrollStateChanged. There may be a better way, though:

public void onScrollStateChanged(AbsListView view, int scrollState) {
    switch (scrollState) {
    case OnScrollListener.SCROLL_STATE_IDLE:
        mBusy = false;

        int first = view.getFirstVisiblePosition();
        int count = view.getChildCount();
        for (int i=0; i<count; i++) {
            TextView t = (TextView)view.getChildAt(i);
            if (t.getTag() != null) {
                t.setText(mStrings[first + i]);
                t.setTag(null);
            }
        }

        mStatus.setText("Idle");
        break;

. . .

EDIT BY Corey Trager:

The above definitely pointed me in the right direction. I found handling OnScrollListener.onScroll worked better than onScrollStateChanged. Even if I removed the case statement in onScrollSgtaetChanged and handled every state change, some text wasn't getting resized. But with onScroll, things seem to work.

So, my seemingly working code looks like this:

public void onScroll(AbsListView v, int firstVisibleItem, int visibleCount, int totalItemCount)
{
    ListView lv = this.getListView();
    int childCount = lv.getChildCount();

    for (int i = 0; i < childCount; i++)
    {
        View v = lv.getChildAt(i);
        TextView tx = (TextView) v.findViewById(R.id.mytext);
        tx.setTextSize(textSize);
    }
}
Returnee answered 22/1, 2010 at 1:54 Comment(4)
This will probably work but it will also be very very very costly. You're doing the same work on every scroll event, of which you can receive many (and not necessarily only when a new item becomes visible.)Quadrant
@Romain - "costly" only matters if there is some effect that the user himself can see. And there isn't. Computers are faster than you give them credit for and they don't mind the hard work. Hey, think of any sort of paint program that reacts to every mouse movement event by extending the visible stroke.Magdala
No they're not. We are dealing with phones and everything you do costs battery. There is absolutely NO REASON to be a lazy programmer and waste battery. The reason why Android is usable on phones is because we spend a lot of time worrying about performance and battery life. We still have lots to do but your point of view is certainly flawed. There are situations where there's no need to worry much about performance (button click for instance), but doing what you do for every pixel scrolled is just stupid.Quadrant
@Romain - Ok, ok, you convinced me that I SHOULD care, so now I DO. So, what SHOULD my code do? @RickNotFred's code, which we got from a SDK sample app, did NOT work. That is, some text got changed, but as I scrolled other items into view, they had not gotten the message. What works?Magdala
D
0

Not so sure about the performace effect but u can try this.

//declare the list
ListView list = (ListView)findViewById(R.id.yourListID);

// in my case i have spinners and made a test just to know if this works
 Spinner spnWhatever;
 View view;
 int i =0;


       do{

           view =list.getAdapter().getView(i,null,null);
           //get the spinners from each row
           spnWhatever =(Spinner)view.findViewById(R.id.spnTarifas);

        i++;

       }while(list.getAdapter().getCount() !=i);
Donley answered 25/4, 2016 at 11:16 Comment(0)
C
0
//Create layout file containing views you want to read input from
//layout/gen_edt_layout.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parLay"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/edtGName"
        android:layout_width = "match_parent"
        android:layout_height ="wrap_content"
        android:hint="Name"
        android:inputType="text" />


    <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/edtGIdPass"
        style="@style/edt"
        android:hint="Id number"
        android:inputType="number" />


</LinearLayout>

//Create Empty Activity -> MultipleInput

//layout/activity_multiple_input.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".OpenAccount">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">


      <ListView
            android:id="@+id/inputLView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="60dp" />

    </LinearLayout>

    <Button
        android:layout_width="wrap_content
        android:layout_height="wrap_content"
        android:text="Read"
        android:onClick="readInput"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent>

</androidx.constraintlayout.widget.ConstraintLayout>

public class MultipleInput extends AppCompatActivity {

    //Declare and intialize arrays to store input fields as public modifiers
    ArrayList<TextInputEditText> edtNameArr = new ArrayList<>();
    ArrayList<TextInputEditText> edtIdsArr = new ArrayList<>();

    //Intialize arrays to store input values as public modifiers
    ArrayList<String> arr0 = new ArrayList<>();
    ArrayList<String> arr1 = new ArrayList<>();

    ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {


        listView = findViewById(R.id.listView);

        edtNameArr.clear();
        edtIdsArr.clear();

        arr0.clear();
        arr1.clear();

        //set number of group fields to be generated
        int members = 4;

        //initialize class passing fields number and context
        edtAdapter = new EdtAdapter(members, getApplicationContext());

        //Set retured fields in listView
        listView.setAdapter(edtAdapter);


    }

    public class EdtAdapter extends BaseAdapter {

        class ViewHolder {

            TextInputEditText edtGName, edtGIdPass;
        }

        public Context context;

        ArrayList<EdtClass> edtArrayList;
        int members;

        EdtAdapter(int members, Context context) {

            this.members = members;
            this.context = context;

        }

        @Override
        public int getCount() {
            return members;
        }

        @Override
        public Object getItem(int position) {
            return position;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(final int position, View contentView, ViewGroup parent) {

            View rowView = contentView;

            if (rowView == null) {

                viewHolder = new ViewHolder();

                LayoutInflater inflater = getLayoutInflater();
                rowView = inflater.inflate(R.layout.gen_edt_layout, parent, false);
                viewHolder.edtGName = rowView.findViewById(R.id.edtGName);
                viewHolder.edtGIdPass = rowView.findViewById(R.id.edtGIdPass);

                viewPosition = position;

                //loop adding input fields in arrays
                for (int i = 0; i < members; i++) {
                    edtNameArr.add(viewHolder.edtGName);
                    edtIdsArr.add(viewHolder.edtGIdPass);
                }


            } else {
                viewHolder = (ViewHolder) contentView.getTag();
            }

            return rowView;

        }

    }


    //Read input when button clicked
    public void readInput(View view){


        for(int edt = 0; edt<edtNameArr.size(); edt++){

            String name = Objects.requireNonNull(edtNameArr.get(edt).getText()).toString();

            if(!arr0.contains(name)) {
                arr0.add(name);
            }

            String ids = Objects.requireNonNull(edtIdsArr.get(edt).getText()).toString();

            if(!arr1.contains(ids)) {
                arr1.add(ids);
            }

        }

        StringBuilder name = new StringBuilder();
        StringBuilder idno = new StringBuilder();

        //loop through arrays with input, reading and appending values in StringBuilder
        for (int m = 0; m < arr0.size(); m++) {

            name.append(arr0.get(m)).append(",");

            idno.append(arr1.get(m)).append(",");

        }

        //Show input values in logcat/Toast
        Log.e("Names ", name.toString());
        Log.e("Ids ", idno.toString());
        Toast.makeText(MultipleInput.this, "Names "+ name.toString(), Toast.LENGTH_LONG).show();

    }
Cassidy answered 17/2, 2020 at 7:45 Comment(1)
While this code may provide a solution to the question, it's better to add context as to why/how it works. This can help future users learn, and apply that knowledge to their own code. You are also likely to have positive feedback from users in the form of upvotes, when the code is explained.Grenadine

© 2022 - 2024 — McMap. All rights reserved.