1

In my app user will select player from various team. on top of the screen there is count which show total no. of players selected and on each team row there is count which will show total no. of player selected from that team.

My am facing unexpected behaviour by ArrayAdapter.

Problem:-

  • why getView() method called five time for each element in array list.
  • only once player count(on Team row) update works, after that player count not work
  • when I select expand team then count on team row get reset.

Code:

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<LinearLayout
    android:id="@+id/lLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#fa8765"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:padding="15dp" >

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="left"
        android:text="@string/selected" />

    <TextView
        android:id="@+id/tViewNoOfSelectionOnTop"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="right"
        android:textStyle="bold"
        android:text="0" />
</LinearLayout>

<ListView
    android:id="@+id/lView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/lLayout" >
</ListView>

parent.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#9999ff"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp" >

<TextView
    android:id="@+id/tViewTeamName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:text="Team Name" />

<TextView
    android:id="@+id/tViewNoOfSelectionFrmPatent"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:text="0" />
</RelativeLayout>

child.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f1fbf2"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp" >

<TextView
    android:id="@+id/tViewPlayerName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:text="Player Name" />

<ImageView
    android:id="@+id/iViewCheckUnCheck"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:src="@drawable/un_mark" />
</RelativeLayout>

MainActivity.java

public class MainActivity extends Activity {
private Context context;
private ListView lView;
private ArrayAdapter<Team> aAdapterTeam;
private List<Team> listTeam;
private TextView tViewNoOfSelectionOnTop;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    context = MainActivity.this;

    tViewNoOfSelectionOnTop = (TextView) findViewById(R.id.tViewNoOfSelectionOnTop);
    lView = (ListView) findViewById(R.id.lView);
    listTeam = new ArrayList<Team>();
    loadData();

    aAdapterTeam = new TeamAdapter(context, 0, listTeam, tViewNoOfSelectionOnTop);

    lView.setAdapter(aAdapterTeam);
}

private void loadData() {
    Team team = new Team("Team A", 0);
    Team[] teamPlayers = new Team[5];
    teamPlayers[0] = new Team("Player A1", 1, team);
    teamPlayers[1] = new Team("Player A2", 1, team);
    teamPlayers[2] = new Team("Player A3", 1, team);
    teamPlayers[3] = new Team("Player A4", 1, team);
    teamPlayers[4] = new Team("Player A5", 1, team);
    team.addPlayers(teamPlayers);
    listTeam.add(team);

    team = new Team("Team B", 0);
    teamPlayers = new Team[4];
    teamPlayers[0] = new Team("Player B1", 1, team);
    teamPlayers[1] = new Team("Player B2", 1, team);
    teamPlayers[2] = new Team("Player B3", 1, team);
    teamPlayers[3] = new Team("Player B4", 1, team);
    team.addPlayers(teamPlayers);
    listTeam.add(team);

    team = new Team("Team C", 0);
    teamPlayers = new Team[3];
    teamPlayers[0] = new Team("Player C1", 1, team);
    teamPlayers[1] = new Team("Player C2", 1, team);
    teamPlayers[2] = new Team("Player C3", 1, team);
    team.addPlayers(teamPlayers);
    listTeam.add(team);
}
}

TeamAdapter.java

public class TeamAdapter extends ArrayAdapter<Team>{
private Context context;
private int resourceId;
private List<Team> listTeam;
private TextView tViewNoOfSelectionOnTop;
private int countSelectionOnTop = 0;
//  private int pos = 0;
//  private Team rowData = null;
private TextView tViewNoOfSelectionFrmPatent = null;
private int rowCount = 0;

public TeamAdapter(Context context, int resourceId,
        List<Team> teams, TextView tViewNoOfSelection) {
    super(context, resourceId, teams);
    this.context = context;
    this.resourceId = resourceId;
    this.listTeam = teams;
    this.tViewNoOfSelectionOnTop = tViewNoOfSelection;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    rowCount++;

    final int pos = position;
    final Team rowData = listTeam.get(pos);
//      pos = position;
//      rowData = listTeam.get(pos);
    System.out.println("Row Count(Position): " + position + ", pos: " + pos + ", Row Count2: " + rowCount);

    LayoutInflater inflater = ((Activity) context).getLayoutInflater();
    if (rowData.getType() == Team.TYPE_Team) {
        convertView = inflater.inflate(R.layout.parent, parent, false);
    } else if (rowData.getType() == Team.TYPE_Player) {
        convertView = inflater.inflate(R.layout.child, parent, false);
    }

    if (rowData.getType() == Team.TYPE_Team) {
        System.out.println("Type Team");
        TextView tViewTeamName = (TextView) convertView.findViewById(R.id.tViewTeamName);
        TextView tViewNoOfSelectionFrmPatent = (TextView) convertView.findViewById(R.id.tViewNoOfSelectionFrmPatent);

        tViewTeamName.setText(rowData.getStrName());
        rowData.settViewNoOfSelectionFrmPatent(tViewNoOfSelectionFrmPatent);
        convertView.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                rowData.toggleExpansion(TeamAdapter.this, pos);
            }
        });

    } else if (rowData.getType() == Team.TYPE_Player) {
        System.out.println("Type Player");
        final ImageView iViewCheckUnCheck = (ImageView) convertView.findViewById(R.id.iViewCheckUnCheck);
        TextView tViewPlayerName = (TextView) convertView.findViewById(R.id.tViewPlayerName);
        tViewNoOfSelectionFrmPatent = rowData.getParent().gettViewNoOfSelectionFrmPatent();
        System.out.println("TextView2 Id: " + tViewNoOfSelectionFrmPatent.getId());
        tViewPlayerName.setText(rowData.getStrName());

        if (rowData.isSelected()) iViewCheckUnCheck.setImageResource(R.drawable.mark);
        else iViewCheckUnCheck.setImageResource(R.drawable.un_mark);

        convertView.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                System.out.println("Player Clicked");
                rowData.toggleSelection();
                int a = Integer.valueOf(tViewNoOfSelectionFrmPatent.getText().toString());
                if (rowData.isSelected()) {
                    System.out.println("Player Selected");
                    a++;
                    countSelectionOnTop++;
                    iViewCheckUnCheck.setImageResource(R.drawable.mark);
                } else {
                    System.out.println("Player Unselected");
                    a--;
                    countSelectionOnTop--;
                    iViewCheckUnCheck.setImageResource(R.drawable.un_mark);
                }
                tViewNoOfSelectionOnTop.setText(String.valueOf(countSelectionOnTop));
                tViewNoOfSelectionFrmPatent.setText(String.valueOf(a));
            }
        });
    }
    return convertView;
}
}

Team.java

public class Team {
public static final int TYPE_Team = 0;
public static final int TYPE_Player = 1;

private String strName;
private boolean isSelected;
private Team[] players;
private Team parent;
private int type;
private boolean isExpanded = false;
private TextView tViewNoOfSelectionFrmPatent;


public Team(String strName, int type) {
    super();
    this.strName = strName;
    this.type = type;
}

public Team(String strName, int type, Team parent) {
    super();
    this.strName = strName;
    this.type = type;
    this.parent = parent;
}

public void toggleExpansion(TeamAdapter teamAdapter, int pos) {
    isExpanded = !isExpanded;
    if (isExpanded) {
        for (Team player : players) {
            pos++;
            teamAdapter.insert(player, pos);
        }
    } else {
        for (Team player : players) {
            teamAdapter.remove(player);
        }
    }
}

public void toggleSelection() {
    isSelected = !isSelected;
    /*for (Team player : parent.getPlayers()) {
        if (player.isSelected) {
            parent.isSelected = true;
        }
    }*/
}

public boolean hasChildren() {
    return players != null;
}

public void addPlayers(Team[] players){
    this.players = players;
}

private Team[] getPlayers(){
    return players;
}

public String getStrName() {
    return strName;
}

public boolean isSelected() {
    return isSelected;
}

public Team getParent() {
    return parent;
}

public void setParent(Team parent) {
    this.parent = parent;
}

public int getType() {
    return type;
}

public TextView gettViewNoOfSelectionFrmPatent() {
    return tViewNoOfSelectionFrmPatent;
}

public void settViewNoOfSelectionFrmPatent(
        TextView tViewNoOfSelectionFrmPatent) {
    System.out.println("TextView Id: " + tViewNoOfSelectionFrmPatent.getId());
    this.tViewNoOfSelectionFrmPatent = tViewNoOfSelectionFrmPatent;
}
}

LogCat:-

04-13 10:15:22.948: I/System.out(2761): Row Count(Position): 0, pos: 0, Row Count2: 1
04-13 10:15:22.949: I/System.out(2761): Type Team
04-13 10:15:22.949: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:22.949: I/System.out(2761): Row Count(Position): 1, pos: 1, Row Count2: 2
04-13 10:15:22.949: I/System.out(2761): Type Team
04-13 10:15:22.949: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:22.949: I/System.out(2761): Row Count(Position): 2, pos: 2, Row Count2: 3
04-13 10:15:22.951: I/System.out(2761): Type Team
04-13 10:15:22.951: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.013: W/EGL_emulation(2761): eglSurfaceAttrib not implemented
04-13 10:15:23.013: W/OpenGLRenderer(2761): Failed to set EGL_SWAP_BEHAVIOR on surface 0xa5e45da0, error=EGL_SUCCESS
04-13 10:15:23.087: I/System.out(2761): Row Count(Position): 0, pos: 0, Row Count2: 4
04-13 10:15:23.087: I/System.out(2761): Type Team
04-13 10:15:23.088: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.088: I/System.out(2761): Row Count(Position): 1, pos: 1, Row Count2: 5
04-13 10:15:23.088: I/System.out(2761): Type Team
04-13 10:15:23.088: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.088: I/System.out(2761): Row Count(Position): 2, pos: 2, Row Count2: 6
04-13 10:15:23.088: I/System.out(2761): Type Team
04-13 10:15:23.088: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.089: I/System.out(2761): Row Count(Position): 0, pos: 0, Row Count2: 7
04-13 10:15:23.090: I/System.out(2761): Type Team
04-13 10:15:23.090: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.090: I/System.out(2761): Row Count(Position): 1, pos: 1, Row Count2: 8
04-13 10:15:23.090: I/System.out(2761): Type Team
04-13 10:15:23.090: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.090: I/System.out(2761): Row Count(Position): 2, pos: 2, Row Count2: 9
04-13 10:15:23.091: I/System.out(2761): Type Team
04-13 10:15:23.091: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.092: I/System.out(2761): Row Count(Position): 0, pos: 0, Row Count2: 10
04-13 10:15:23.092: I/System.out(2761): Type Team
04-13 10:15:23.093: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.093: I/System.out(2761): Row Count(Position): 1, pos: 1, Row Count2: 11
04-13 10:15:23.093: I/System.out(2761): Type Team
04-13 10:15:23.093: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.093: I/System.out(2761): Row Count(Position): 2, pos: 2, Row Count2: 12
04-13 10:15:23.093: I/System.out(2761): Type Team
04-13 10:15:23.093: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.148: I/System.out(2761): Row Count(Position): 0, pos: 0, Row Count2: 13
04-13 10:15:23.148: I/System.out(2761): Type Team
04-13 10:15:23.148: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.148: I/System.out(2761): Row Count(Position): 1, pos: 1, Row Count2: 14
04-13 10:15:23.149: I/System.out(2761): Type Team
04-13 10:15:23.149: I/System.out(2761): TextView Id: 2131099654
04-13 10:15:23.149: I/System.out(2761): Row Count(Position): 2, pos: 2, Row Count2: 15
04-13 10:15:23.149: I/System.out(2761): Type Team
04-13 10:15:23.149: I/System.out(2761): TextView Id: 2131099654

enter image description here

Vinit ...
  • 1,409
  • 10
  • 37
  • 66

2 Answers2

1

I think, you want to make a ListView where Teams are the parents and Players are the childs.

Quoting android engineer RomainGuy

This is not an issue, there is absolutely no guarantee on the order in which getView() will be called nor how many times.

I understand that as these assigned layout_height="wrap_content" the adapter enter many time to calculate the specific height.

You need put layout_height="match_parent" in ListView to avoid this behaviour.

P.S.

I have some observations in your code:

  • Why use ListView instead of ExpandableListView? This is a better option.
  • Implement Holder Pattern, is a good practice if you will work with ListView.
  • Don't implement View behavior in Entity beans. This generate unsupportable code in time.
  • In child.xml is better use a CheckBox instead of ImageView. Only need change the CheckBox view style.

I take your code and refactoring this, you can check on my GitHub https://github.com/cesardl/android-training

Community
  • 1
  • 1
Cesardl
  • 1,831
  • 1
  • 14
  • 18
  • @VinitVikash I made a mistake, I edited the answer, check please – Cesardl Apr 15 '15 at 19:31
  • @Cesardl...Thanks buddy...I downloaded code from github.. there is no change. And problem is till there. Only first time it update team count after that it not work. – Vinit ... Apr 16 '15 at 08:58
  • Only team class is there and this call have team array and team name will work as player name. Forget about Player class which I mention in my code. – Vinit ... Apr 16 '15 at 09:00
  • Hi @VinitVikash I upload last commit to GitHub. I think this have some interesting for you. – Cesardl Apr 23 '15 at 22:48
  • Hi @Cesardl... thanks buddy ... You have changed ArrayAdapter by Expandable ListView and very well you explained the code also ... Thanks once again ... But one question... We can't achieve this by using ArrayAdapter? And where I was wrong? I am storing reference of parent text view in parent class and updating when child get checked or unchecked. Why first time textview update and not update from second time. I was not done any mistake then why code not work as expected? – Vinit ... Apr 24 '15 at 10:49
  • I thinks is a bad use of arrayAdapter, this work fine with ListView. – Cesardl Apr 24 '15 at 17:11
0

Adapter's getView() is different against Activity's onCreate(). onCreate() is called only once on a Activity's life cycle, but getView() is called everytime when the list item in the ListView is shown. If a list item is hidden by scrolling up/down or other reason, and again show up, getView() is called. So it would be good to save data in another place.

In your code, rowCount++ in the getView() will not work properly. If you want a count of a ListView, use Adapter.getCount().

Tay Cleed
  • 69
  • 8
  • this is true, if there is no hidden data and content of list will fit into screen in a single short, even it will fit into half of the screen then why getView() method called five time for each row. And suppose data will not fit into one short then in that case also getView() method need to call only once for a row. But it calls this method more than one. – Vinit ... Apr 13 '15 at 10:43