Josh Josh - 2 months ago 6
Android Question

Android - getListView and setListAdapter error with NullPointerException when using custom layout

I have a layout like this

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff">

<com.ftni.core.ui.ActionBar
android:id="@+id/actionbar"
style="@style/ActionBar"/>

<TextView android:id="@+id/list_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Title"
android:textStyle="bold"
android:textColor="#000000"
android:textSize="18sp"
android:padding="3px"/>

<ListView android:id="@id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawSelectorOnTop="false"/>
</LinearLayout>


and in my listview (code that was working before I changed the layout)

private void buildListView()
{
ListView lv = getListView();

registerForContextMenu(lv);

lv.setTextFilterEnabled(true);

lv.clearChoices();

setListAdapter(new UserListAdapter(SuspendedUsersActivity.this, R.layout.useritem, users));

lv.setOnItemClickListener(clickListener);
}


I tried moving the call to
setListAdapter
first, but I still get the NullPointerException. Here's the logcat

FATAL EXCEPTION: main
java.lang.NullPointerException
at android.app.ListActivity.setListAdapter(ListActivity.java:267)
at com.myapp.backoffice.users.SuspendedUsersActivity.buildListView(SuspendedUsersActivity.java:140)
at com.myapp.backoffice.users.SuspendedUsersActivity.access$0(SuspendedUsersActivity.java:138)
at com.myapp.backoffice.users.SuspendedUsersActivity$2.handleMessage(SuspendedUsersActivity.java:194)
at android.os.Handler.dispatchMessage(Handler.java:99)


I have a feeling that what is happening is that the default ID I was told is correct (
@id/android:list
) is not correct for the default list view.

EDIT:

Here are more details about how I have this set up.

First, I have an inherited activity to ensure the user is authenticated. When I inherit directly from this class, all works fine.

public class ProtectedListActivity extends ListActivityBase {
boolean isAuthenticated = false;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Thread validationThread = new Thread()
{
@Override
public void run()
{
try
{
isAuthenticated = UserService.ValidateToken();
}
catch (FTNIServiceException e)
{
//eat it
}
finally
{
if (!isAuthenticated)
{
startActivity(new Intent(ProtectedListActivity.this, SignInActivity.class));
finish();
}
}
}
};

validationThread.start();
}
}


Then, I extend that one step further to wrap my default action bar setup into a base class.

public class ListWithActionBarActivity extends ProtectedListActivity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public void onContentChanged()
{
ActionBar actionBar = (ActionBar)findViewById(R.id.actionbar);
if (actionBar != null)
{
actionBar.setOnTitleClickListener(new OnClickListener() {
public void onClick(View v) {
startActivity(new Intent(ListWithActionBarActivity.this, SelectSiteActivity.class));
finish();
}
});

SiteModel site = PreferencesHelper.getSite();

actionBar.setTitle(site.Name + " (" + site.Abbreviation + ")");
actionBar.addAction(new IntentAction(ListWithActionBarActivity.this,
new Intent(ListWithActionBarActivity.this, MainMenuActivity.class),
R.drawable.ic_title_home_default));
}
}

public static Intent createIntent(Context context) {
Intent i = new Intent(context, MainMenuActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
return i;
}

protected Intent createShareIntent() {
final Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "Shared from the ActionBar widget.");
return Intent.createChooser(intent, "Share");
}
}


Then, because I have 2 lists of users separated by status (suspended or active) I was attempting to wrap an addition to the action bar in a base class.

public class UserBase extends ListWithActionBarActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
//setContentView(R.layout.queue);
super.onCreate(savedInstanceState);

ActionBar actionBar = (ActionBar)findViewById(R.id.actionbar);

actionBar.addAction(new UserStatusSelectorAction(UserBase.this));

}
}


and finally, we have my activity. I've omitted a little code, but I left most of it so you could see how the data is retrieved through another thread while a loading screen is shown, and then the listview is built.

public class SuspendedUsersActivity extends ListWithActionBarActivity implements Runnable{
ProgressDialog progress;
ArrayList<UserModel> users;
int position;

@Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.queue);
super.onCreate(savedInstanceState);

TextView title = (TextView)findViewById(R.id.list_title);
title.setText("Suspended Users");

progress = ProgressDialog.show(SuspendedUsersActivity.this, "", "Loading...", true);

Thread thread = new Thread(SuspendedUsersActivity.this);
thread.start();
}

private void buildListView()
{
ListView lv = getListView();

//registerForContextMenu(lv);

lv.setTextFilterEnabled(true);

lv.clearChoices();

setListAdapter(new UserListAdapter(SuspendedUsersActivity.this, R.layout.useritem, users));

lv.setOnItemClickListener(clickListener);
}

private OnItemClickListener clickListener = new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
UserModel user = users.get(position);

SuspendedUserAction action = new SuspendedUserAction(SuspendedUsersActivity.this, user.UserId);
action.performAction(view);
}
};

@Override
public void run() {
// TODO Auto-generated method stub
SiteModel site = PreferencesHelper.getSite();

try
{
users = UserService.GetSuspendedUsers(site.SiteId);
}
catch (FTNIServiceException e)
{
// TODO Auto-generated catch block
Message message = new Message();
message.what = ActivityBase.RESULT_ERROR;
message.obj = e.getMessage();

handler.sendMessage(message);
return;
}

handler.sendEmptyMessage(ActivityBase.RESULT_DONE);
}

private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch(msg.what)
{
case ActivityBase.RESULT_SUCCESS:
progress.dismiss();
startActivity(new Intent(SuspendedUsersActivity.this, SelectSiteActivity.class));
finish();
break;
case ActivityBase.RESULT_DONE:
buildListView();
ApplicationController app = (ApplicationController)getApplication();
app.setSuspendedUsersChanged(false);
progress.dismiss();
break;
case ActivityBase.RESULT_ERROR:
progress.dismiss();
new AlertDialog.Builder(SuspendedUsersActivity.this)
.setMessage(msg.obj.toString())
.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
//do nothing
arg0.dismiss();
}
})
.show();
break;
}
}
};
}


It works with ProtectedListActivity when I do not set a content view, but everything else it fails on, whether or not I set the content view and comment out the actionbar stuff.

Answer

I changed the listview to explicitly give it an id like this

 <ListView android:id="@+id/list_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_weight="1"
     android:drawSelectorOnTop="false"/>

And then I changed my code to this

private void buildListView()
{
    ListView lv = (ListView)findViewById(R.id.list_view);

    lv.setTextFilterEnabled(true);

    lv.clearChoices();

    lv.setAdapter(new UserListAdapter(SuspendedUsersActivity.this, R.layout.useritem, users));

    lv.setOnItemClickListener(clickListener);
}

And now it works. For some reason that default ID for the listview doesn't work.

Comments