I have a default ListView of items created and am wanting to give the user the ability to add their own item by clicking on the Floating Action Button. Once they hit OK on the dialog, I am given a null pointer exception specifically on the line itemAdapter.notifyDataSetChanged(). I have spent a few hours researching this now and can so no reason as to why this doesn't work. If I remove this line I get no error but the list does not update when an item is added to the ArrayList.
This appears to have been marked as duplicate but the cause of this is entirely different in Android from a standard Java application like the linked question. I am getting this error specifically because of the way Android activities and fragments interact.
MainActivity.java
package com.example.josh.project3;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.InputType;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private MainActivityFragment mainActivityFragment = new MainActivityFragment();
private String m_Text = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
if (fab != null) {
fab.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("Add Company");
final EditText input = new EditText(MainActivity.this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
builder.setView(input);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
m_Text = input.getText().toString();
String name = mainActivityFragment.getTitle(m_Text);
String desc = mainActivityFragment.getDescription(m_Text);
System.out.println(name);
System.out.println(desc);
mainActivityFragment.updateList(name, desc);
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
}
});
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private class CallWebTask extends AsyncTask<String, Void, String>
{
String change, companySymbol, biddingPrice;
protected String doInBackground(String... urls)
{
try {
String jsonData = mainActivityFragment.run(urls[0]);
JSONObject jsonObject = new JSONObject(jsonData);
JSONObject query = jsonObject.getJSONObject("query");
JSONObject results = query.getJSONObject("results");
JSONObject quote = results.getJSONObject("quote");
change = quote.getString("Change");
companySymbol = quote.getString("symbol");
biddingPrice = quote.getString("Bid");
}
catch (IOException e) {
e.printStackTrace();
}
catch(JSONException e)
{
e.printStackTrace();
}
return companySymbol + " " + change + " (" + biddingPrice + ")";
}
}
}
MainActivityFragment.java
package com.example.josh.project3;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.ListFragment;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.StrictMode;
import android.provider.Settings;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Console;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* A simple {@link Fragment} subclass.
*/
public class MainActivityFragment extends ListFragment {
OkHttpClient client = new OkHttpClient();
String desc;
public ArrayList<ListItems> items = new ArrayList<ListItems>();
public ListItemsAdapter itemAdapter;
public final static String EXTRA_TITLE = "com.example.josh.project3.title";
public final static String EXTRA_MESSAGE =
"com.example.josh.project3.message";
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
/*
//You can use a simple array like this to populate the list:
String [] values = new String [] {"Tennis", "Football", "Basketball", "Baseball",
"Vollyball", "Swimming"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
android.R.layout., values);
setListAdapter(adapter);
*/
//Here I am creating a list of objects from a class called ListItems using ArrayList
items.add(new ListItems("Yahoo", getDescription("YHOO")));
items.add(new ListItems("Google", getDescription("GOOG")));
items.add(new ListItems("Apple", getDescription("AAPL")));
items.add(new ListItems("Microsoft", getDescription("MSFT")));
items.add(new ListItems("Netflix", getDescription("NFLX")));
itemAdapter = new ListItemsAdapter(getActivity(), items);
setListAdapter(itemAdapter);
}
//this is to create a simple response when a list item is clicked.
@Override
public void onListItemClick(ListView l, View v, int position, long id){
super.onListItemClick(l, v, position, id);
Intent intent = new Intent(getActivity(), SelectedItemActivity.class);
ListItems item = (ListItems) getListAdapter().getItem(position);
intent.putExtra(EXTRA_TITLE, item.getTitle());
intent.putExtra(EXTRA_MESSAGE, item.getMessage());
startActivity(intent);
}
public String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
public String getDescription(String companySymbol)
{
try {
desc = new CallWebTask().execute("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20%28%22" +
companySymbol + "%22%29&format=json&env=store://datatables.org/alltableswithkeys").get();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
return desc;
}
public String getTitle(String companySymbol)
{
try {
desc = new CallWebTask().execute("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20%28%22" +
companySymbol + "%22%29&format=json&env=store://datatables.org/alltableswithkeys1").get();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
return desc;
}
public void updateList(String title, String desc)
{
ListItems listItem = new ListItems(title, desc);
items.add(listItem);
//itemAdapter.notifyDataSetChanged();
}
private class CallWebTask extends AsyncTask<String, Void, String>
{
String change, companySymbol, biddingPrice, name;
protected String doInBackground(String... urls) {
if (urls[0].endsWith("1")) {
urls[0] = urls[0].substring(0, urls[0].length() - 1);
try {
String jsonData = run(urls[0]);
JSONObject jsonObject = new JSONObject(jsonData);
JSONObject query = jsonObject.getJSONObject("query");
JSONObject results = query.getJSONObject("results");
JSONObject quote = results.getJSONObject("quote");
name = quote.getString("Name");
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return name;
} else {
try {
String jsonData = run(urls[0]);
JSONObject jsonObject = new JSONObject(jsonData);
JSONObject query = jsonObject.getJSONObject("query");
JSONObject results = query.getJSONObject("results");
JSONObject quote = results.getJSONObject("quote");
change = quote.getString("Change");
companySymbol = quote.getString("symbol");
biddingPrice = quote.getString("Bid");
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return companySymbol + " " + change + " (" + biddingPrice + ")";
}
}
}
}
ListItems.java
package com.example.josh.project3;
public class ListItems {
private String title, message;
public ListItems(String title, String message){
this.title = title;
this.message = message;
}
public String getTitle(){
return title;
}
public String getMessage(){
return message;
}
}
ListItemsAdapter.java
package com.example.josh.project3;
import android.content.Context;
import android.provider.ContactsContract;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
public class ListItemsAdapter extends ArrayAdapter<ListItems> {
public ListItemsAdapter(Context context, ArrayList<ListItems> items){
super(context, 0, items);
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
// Get the data item for this position
ListItems note = getItem(position);
// Check if an existing view is being reused, otherwise inflate a new view from// custom row layout
if (convertView == null) {
convertView =
LayoutInflater.from(getContext()).inflate(R.layout.row_item_layout, parent, false);
}
// Grab references of views so we can populate them with specific note row data
TextView itemTitle = (TextView) convertView.findViewById(R.id.listItemTitle);
TextView itemText = (TextView) convertView.findViewById(R.id.listItemBody);
// Fill each new referenced view with data associated with note it's referencing
itemTitle.setText(note.getTitle());
itemText.setText(note.getMessage());
// now that we modified the view to display appropriate data,
//return it so it will be displayed.
return convertView;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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"
android:fitsSystemWindows="true"
tools:context="com.example.josh.project3.MainActivity">
<fragment
android:name="com.example.josh.project3.MainActivityFragment"
android:id="@+id/MainActivityFrag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginTop="45dp"
tools:layout="@layout/row_item_layout" />
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_input_add" />
</android.support.design.widget.CoordinatorLayout>