0

I'm trying to emulate a simple chat view using two layouts, one for sent and one for received message. When i try to scroll, a NullPointerException is thrown. Not sure why this happens. Can someone find the problem.

public class DisplayMessageAdapter extends ArrayAdapter<Message> {

Context context;
int sentResource, rcvdResource;
ArrayList<Message> messages = null;

public DisplayMessageAdapter(Context context, int sentResource, int rcvdResource, ArrayList<Message> messages) {
    super(context, sentResource, rcvdResource, messages);
    this.context = context;
    this.sentResource = sentResource;
    this.rcvdResource = rcvdResource;
    this.messages = messages;
}

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

    View row = convertView;
    MessagesHolder holder = null;

    Message message = messages.get(position);


    if(message.sent != null)
    {

        if(row == null)
        {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = inflater.inflate(sentResource, parent, false);

            holder = new MessagesHolder();
            holder.sent  = (TextView) row.findViewById(R.id.sent_message);
            row.setTag(holder);
        }

        else
        {
            //Log.i("holder-not-null","holder-not-null");
            holder = (MessagesHolder) row.getTag();
        }

        holder.sent.setText(message.sent);
    }
    else
    {
        if(row == null)
        {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = inflater.inflate(rcvdResource, parent, false);

            holder = new MessagesHolder();
            holder.received  = (TextView) row.findViewById(R.id.received_message);
            row.setTag(holder);
        }

        else
        {
            //Log.i("holder-not-null","holder-not-null");
            holder = (MessagesHolder) row.getTag();
        }

        holder.received.setText(message.received);

    }

    return row;
}

static class MessagesHolder
{
    TextView sent;
    TextView received;

} }

sent_message and received_message layouts

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
      <TextView 
        android:id="@+id/sent_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:textColor="#000000"
        android:background="@drawable/sent"
        android:layout_marginTop="4dp"
        android:layout_marginBottom="4dp"
        android:textSize="15sp"
         />

</RelativeLayout>

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

      <TextView
          android:layout_alignParentRight="true"
          android:id="@+id/received_message"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:background="@drawable/rcvd"
          android:layout_marginRight="20dp"
          android:textColor="#000000"
          android:layout_marginTop="4dp"
          android:layout_marginBottom="4dp"
          android:textSize="15sp"
           />

</RelativeLayout>

Logcat

12-23 13:46:04.996: E/AndroidRuntime(8131): FATAL EXCEPTION: main
12-23 13:46:04.996: E/AndroidRuntime(8131): java.lang.NullPointerException
12-23 13:46:04.996: E/AndroidRuntime(8131):     at com.example.swipetext.DisplayMessageAdapter.getView(DisplayMessageAdapter.java:58)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.widget.AbsListView.obtainView(AbsListView.java:2608)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.widget.ListView.makeAndAddView(ListView.java:1852)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.widget.ListView.fillUp(ListView.java:718)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.widget.ListView.fillGap(ListView.java:655)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.widget.AbsListView.trackMotionScroll(AbsListView.java:6592)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3706)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.widget.AbsListView.onTouchEvent(AbsListView.java:4556)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.View.dispatchTouchEvent(View.java:7817)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2435)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2159)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2441)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2174)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2441)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2174)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2441)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2174)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2441)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2174)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2326)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1612)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.app.Activity.dispatchTouchEvent(Activity.java:2494)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2274)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.View.dispatchPointerEvent(View.java:8025)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4659)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4547)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4148)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4198)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4167)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4252)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4175)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4309)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4148)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4198)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4167)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4175)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4148)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6293)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6231)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6202)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6373)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:188)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:177)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:6346)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:6392)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:791)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.Choreographer.doCallbacks(Choreographer.java:591)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.Choreographer.doFrame(Choreographer.java:559)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:777)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.os.Handler.handleCallback(Handler.java:730)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.os.Handler.dispatchMessage(Handler.java:92)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.os.Looper.loop(Looper.java:137)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at android.app.ActivityThread.main(ActivityThread.java:5419)
12-23 13:46:04.996: E/AndroidRuntime(8131):     at java.lang.reflect.M

Code that calls the adapter

public void populateView(ArrayList<Message> allMessages)
{
    DisplayMessageAdapter adapter = new DisplayMessageAdapter(getApplicationContext() , R.layout.sent_message_layout, R.layout.received_message_layout, allMessages);
    listview = (ListView) findViewById(R.id.messageview);
    listview.setAdapter(adapter);
    listview.setSelection(listview.getAdapter().getCount()-1);
}

After change

public class DisplayMessageAdapter extends ArrayAdapter<Message> {

Context context;
int sentResource, rcvdResource;
ArrayList<Message> messages = null;

public DisplayMessageAdapter(Context context, int sentResource, int rcvdResource, ArrayList<Message> messages) {
    super(context, sentResource, rcvdResource, messages);
    this.context = context;
    this.sentResource = sentResource;
    this.rcvdResource = rcvdResource;
    this.messages = messages;
}

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

    View row = convertView;
    MessagesHolder holder = null;

    Message message = messages.get(position);


    if(message.sent != null)
    {

        if(row == null)
        {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = inflater.inflate(sentResource, parent, false);

            holder = new MessagesHolder();
            holder.data  = (TextView) row.findViewById(R.id.sent_message);
            row.setTag(holder);
        }

        else
        {
            //Log.i("holder-not-null","holder-not-null");
            holder = (MessagesHolder) row.getTag();
        }

        holder.data.setText(message.sent);
    }
    else
    {
        if(row == null)
        {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = inflater.inflate(rcvdResource, parent, false);

            holder = new MessagesHolder();
            holder.data  = (TextView) row.findViewById(R.id.received_message);
            row.setTag(holder);
        }

        else
        {
            //Log.i("holder-not-null","holder-not-null");
            holder = (MessagesHolder) row.getTag();
        }

        holder.data.setText(message.received);

    }

    return row;
}

static class MessagesHolder
{
    TextView data;


}

}

brainfreak
  • 137
  • 1
  • 13

3 Answers3

1

You forgot to override the following methods

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return messages.size();
    }

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

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }
Rahul Matte
  • 1,151
  • 2
  • 23
  • 54
  • don't needed in `ArrayAdapter`, its must be override in `BaseAdapter` – Shayan Pourvatan Dec 23 '13 at 08:41
  • Ok..@Shayanpourvatan then on what basis the getView() method get called? – Rahul Matte Dec 23 '13 at 08:44
  • i'm not sure but i think so with super constructor.`super(context, sentResource, rcvdResource, messages);` – Shayan Pourvatan Dec 23 '13 at 08:46
  • @RBM no need to override `getCount` since op has `super(context, sentResource, rcvdResource, messages);` – Raghunandan Dec 23 '13 at 08:48
  • @Raghunandan http://stackoverflow.com/questions/18868194/android-xml-layout-for-a-listview-with-different-items Here, I haven't seen any super call like super(context, sentResource, rcvdResource, messages); – Rahul Matte Dec 23 '13 at 08:54
  • @RBM that has nothing to with what you have posted. Besides i din't post full code. So you are assuming super call?? Looks like op wants to set receive text when he receives messages and set sent text when he is sending text. something like chat with listview. – Raghunandan Dec 23 '13 at 08:57
0

The null pointer exception may be caused at these places-

holder.sent.setText(message.sent);
 holder.received.setText(message.received);

It is because, first time you are inflating your row layouts based on messages.sent.Suppose in first case it is a sent message so you inflate row layout for sent message, initialize the sent text view of the MessagesHolder.When you start scrolling, the views are not null so they are not redrawn, so at this time a recieved message comes in the position previously occupied by a sent message, it tries to set text on textView holder.recived , which has not been initialized previously and results in a null pointer exception.

Solution- Use just one row layout for both cases and one text view member in MessagesHolder class.

nikvs
  • 1,090
  • 5
  • 9
  • you mean like this? http://stackoverflow.com/questions/20737222/android-custom-listview-changes-while-scrolling – brainfreak Dec 23 '13 at 09:06
  • No. same exception comes up. – brainfreak Dec 23 '13 at 09:25
  • use single row layout xml for both cases having a plain text view.Set the background(or any other parameters for differentiating sent and received message) of the textview programatically(inside getview based on the condition for sent or received message).Hope you got it. – nikvs Dec 23 '13 at 09:25
0

Temproary Solution :

The problem seems to be in the holder and it's use. It works now if i dont use the holder class for performance boost and do the naive implementation. Although i'm not sure how to use the holder properly, if one needs performance boost.

brainfreak
  • 137
  • 1
  • 13