Edmund Rojas
Edmund Rojas

Reputation: 6606

Android viewholder class wide scope

I have a list adapter using a viewholder pattern, my problem is I need to update a TextView from the viewholder in onpostexecute() of an AsyncTask contained within the same class, but this always returns a nullpointer on the TextView, how can give my textviews from the viewholder enough scope that I can change them in teh AsyncTask? I will include a code example below, thanks in adcvance

public class ListAdapter extends BaseAdapter {


TextView tvA1, tvA2;

Integer submitId;
String submitQuestion;

public ListAdapter() {

  ....

}

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

    friendsViewHolder holder = null;


    if (convertView == null) {

        holder = new friendsViewHolder();

        convertView = inflater.inflate(R.layout.list_item_status, null);


        holder.tvA1 = (TextView) convertView
                .findViewById(R.id.tvA1);
        holder.tvA2 = (TextView) convertView
                .findViewById(R.id.tvA2);
        holder.btQ1 = (LinearLayout) convertView
                .findViewById(R.id.btConfirm);
        holder.btQ2 = (LinearLayout) convertView
                .findViewById(R.id.btDeny);



        convertView.setTag(holder);
    } else {
        holder = (friendsViewHolder) convertView.getTag();
    }


    tvA1 = holder.tvA1;
    tvA2 = holder.tvA2;

    holder.btQ1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            submitId = id;
            submitQuestion = q1;

            new UpdateAnswer().execute();


        }
    });

    holder.btQ2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            submitId = id;
            submitQuestion = q2;

            new UpdateAnswer().execute();

        }
    });

    return convertView;


}

static class friendsViewHolder {

    TextView tvA1, tvA2;
    LinearLayout btQ1, btQ2;

}

private class UpdateAnswer extends AsyncTask<Void, Void, ArrayList<Object>> {


    @Override
    protected ArrayList<Object> doInBackground(Void... params) {

        return apiHelper.submitAnswer(submitId, submitQuestion);

    }

    @Override
    protected void onPostExecute(ArrayList<Object> answers) {
        super.onPostExecute(answers);

        if (answers != null) {

            Integer a1 = (Integer) answers.get(1);
            Integer a2 = (Integer) answers.get(2);

            //Error here
            tvA1.setText(a1);
            tvA1.setVisibility(View.VISIBLE);

            //error here
            tvA2.setText(a2);
            tvA2.setVisibility(View.VISIBLE);


        }

    }
}
}

Upvotes: 1

Views: 186

Answers (2)

Inverce
Inverce

Reputation: 1496

Lets change few things here and there :)

Lets make friendsViewHolder public, also i need to add that in java it is somehow custom to name classes with names starting with cammel case simply put FirstLetterOfEeachWordIsBig :)

Lets add constructor to Update task :)

private class UpdateAnswer extends AsyncTask<Void, Void, ArrayList<Object>> {
    int submitId;
    String submitQuestion;
    friendsViewHolder holder;

    public UpdateAnswer (int submitId, String submitQuestion, friendsViewHolder holder) {
        this.submitId = submitId;
        this.submitQuestion = submitQuestion;
        this.holder = holder;
    }

On click lets change to

  final frendsViewHolder finalHolder = holder;
  holder.btQ1.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        new UpdateAnswer(id, q1, finalHolder).execute();
    }
});

We are no longer in need of global TvA1, tvA2, submitId, submitQuestion;

And lastly we need to edit onPostExecute, hovewer i think that want be so hard since you have holder value forwarded :)

        holder.tvA1.setText(a1);
        holder.tvA1.setVisibility(View.VISIBLE);

        holder.tvA2.setText(a2);
        holder.tvA2.setVisibility(View.VISIBLE);

In java you can create constructor to allow passing additional data to object, we use this to set id, question and holder for our task, and for passing that information to other object we should'nt be using global values (they can be changed by every process), think about situation when your call "apiHelper.submitAnswer(submitId, submitQuestion)" would take long to proceed (half a second for example), then user click within half a second on answer in first and answer c on 3 question, by passing values globally you cant determine wich answer to accept :)

Cheers :)

Upvotes: 1

Karan
Karan

Reputation: 2130

Java is pass by value, but you can send reference of an Object in the value. I have used the above, and sent reference of holder to AsyncTask.

public class ListAdapter extends BaseAdapter {


TextView tvA1, tvA2;

Integer submitId;
String submitQuestion;

public ListAdapter() {

  ....

}

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

    friendsViewHolder holder = null;


    if (convertView == null) {

        holder = new friendsViewHolder();

        convertView = inflater.inflate(R.layout.list_item_status, null);


        holder.tvA1 = (TextView) convertView
                .findViewById(R.id.tvA1);
        holder.tvA2 = (TextView) convertView
                .findViewById(R.id.tvA2);
        holder.btQ1 = (LinearLayout) convertView
                .findViewById(R.id.btConfirm);
        holder.btQ2 = (LinearLayout) convertView
                .findViewById(R.id.btDeny);



        convertView.setTag(holder);
    } else {
        holder = (friendsViewHolder) convertView.getTag();
    }


    holder.btQ1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            submitId = id;
            submitQuestion = q1;

            new UpdateAnswer(holder).execute();


        }
    });

    holder.btQ2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            submitId = id;
            submitQuestion = q2;

            new UpdateAnswer().execute();

        }
    });

    return convertView;


}

static class friendsViewHolder {

    TextView tvA1, tvA2;
    LinearLayout btQ1, btQ2;

}

private class UpdateAnswer extends AsyncTask<Void, Void, ArrayList<Object>> {
     friendsViewHolder holder;

    UpdateAnswer(friendsViewHolder holder)
    {
        this.holder = holder;
    }

    @Override
    protected ArrayList<Object> doInBackground(Void... params) {

        return apiHelper.submitAnswer(submitId, submitQuestion);

    }

    @Override
    protected void onPostExecute(ArrayList<Object> answers) {
        super.onPostExecute(answers);

        if (answers != null) {

            Integer a1 = (Integer) answers.get(1);
            Integer a2 = (Integer) answers.get(2);

            //Error here
            holder.tva1.setText(a1);
            holder.tva1.setVisibility(View.VISIBLE);

            //error here
            holder.tva2.setText(a2);
            holder.tva2.setVisibility(View.VISIBLE);


        }

    }
}
}

Upvotes: 1

Related Questions