• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Apps First Android App and a Null Bundle Object

I've gone through the Notepad Tutorials and they are all working on my virtual device. I am now stuck in the midst of trying to write my own Android app. If I've left out any information that will help you help me, please let me know.

My setup:
Windows 7 SP 1 64-bit
Eclipse Indigo Service Release 2
Android Development Tools 16.0.1.v201112150204-238534
Android Virtual Device Platform 2.3.3, API Level 10, CPU/ABI ARM (armeabi)

The app is about as simple as they come - you enter a number, it divides it by another number and displays the result.

Here is the code involved:

AndroidManifest.xml:
Code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.driving"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <application
        android:icon="@drawable/puttputt"
        android:label="@string/app_name" >
        <activity
            android:name=".DrivingTimeEstimatorActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".DrivingTimeEstimator" />
    </application>

</manifest>
/res/layout/main.xml:
Code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/input_miles" />
    <EditText
        android:id="@+id/miles"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="number"
        android:hint="@string/input_miles_hint" />
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/time_to_go" />
    <TextView
        android:id="@+id/time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    
    <Button
        android:id="@+id/go_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/go_button" />

</LinearLayout>
/res/menu/main_menu.xml:
Code:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/go"
        android:title="@string/go" />
    <item android:id="@+id/exit"
        android:title="@string/exit" />
</menu>
/res/values/strings.xml:
Code:
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="hello">Time to estimate some drive times!</string>
    <string name="app_name">DrivingTimeEstimator</string>
    
    <string name="input_miles">Miles you\'re driving:</string>
    <string name="input_miles_hint">Enter a number between 1 and 24,000</string>
    
    <string name="time_to_go">Time it\'ll take to go the parenthesized number of miles: </string>
    <string name="go_button">Go</string>
    
    <string name="go">Go</string>
    <string name="exit">Exit</string>

</resources>
/gen/com/android/driving/R.java (figured it might come in handy, even though it's auto-generated):
Code:
/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.android.driving;

public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
        public static final int puttputt=0x7f020001;
    }
    public static final class id {
        public static final int exit=0x7f060004;
        public static final int go=0x7f060003;
        public static final int go_button=0x7f060002;
        public static final int miles=0x7f060000;
        public static final int time=0x7f060001;
    }
    public static final class layout {
        public static final int main=0x7f030000;
    }
    public static final class menu {
        public static final int main_menu=0x7f050000;
    }
    public static final class string {
        public static final int app_name=0x7f040001;
        public static final int exit=0x7f040007;
        public static final int go=0x7f040006;
        public static final int go_button=0x7f040005;
        public static final int hello=0x7f040000;
        public static final int input_miles=0x7f040002;
        public static final int input_miles_hint=0x7f040003;
        public static final int time_to_go=0x7f040004;
    }
}
/src/com/android/driving/DrivingTimeEstimatorActivity:
Code:
package com.android.driving;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;

public class DrivingTimeEstimatorActivity extends Activity implements OnTouchListener {
    private static final int ACTIVITY_CALCULATE=0;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Button goButton = (Button) findViewById(R.id.go_button);
        
        goButton.setOnClickListener(new View.OnClickListener() {            
            @Override
            public void onClick(View view) {
                calculate();
            }
        });
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // TODO Auto-generated method stub
        return false;
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.go:
            calculate();
            return true;
        case R.id.exit:
            // TODO
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }
    
    private void calculate() {
        Intent i = new Intent(this, DrivingTimeEstimator.class);
        startActivityForResult(i, ACTIVITY_CALCULATE);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        Bundle extras = intent.getExtras();
        switch (requestCode) {
        case ACTIVITY_CALCULATE:
            break;
        }
    }
}
/src/com/android/driving/DrivingTimeEstimator:
Code:
package com.android.driving;

import android.app.Activity;
import android.os.Bundle;
import android.widget.EditText;

public class DrivingTimeEstimator extends Activity {
    
    private double miles;
    private double mph;
    private final int CONVERSION_FACTOR = 60;
    
    private EditText mMiles;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.main); // suggested by Kostas G
        mMiles = (EditText) findViewById(R.id.miles);
        String strMiles = mMiles.getText().toString();
        miles = Double.parseDouble(strMiles);
        
        mph = 60;
        
        String time = "";
        double hours = 0;
        double minutes = 0;
        
        hours = miles/mph;
        
        if (miles%mph != 0) {
            double hoursInt = Math.floor(hours);
            double leftover = hours - hoursInt;
            minutes = leftover * CONVERSION_FACTOR;
            double minutesInt = Math.floor(minutes);
            
            time = hoursInt + " hours and " + minutesInt + " minutes";
        } else {
            time = hours + " hours";
        }
        
        time = getPrettyText(time);
    }

    private String getPrettyText(String time) {
        time = "It will take you " + time;
        time += " to go " + miles + " miles doing " + mph + " mph";
        time = time.replace(".0", "");
        return time;
    }

}
The problem:
By the time DrivingTimeEstimator's onCreate() method gets executed, the Bundle object (savedInstanceState) is null (which I'm guessing is what leads to mMiles being null, which throws a NullPointerException and kills everything). Does anyone have any ideas as to why? Thanks in advance. :)
 
The savedInstanceState is null, because this is the first time you run the activity. That makes sense and isn't the root of NullPointerException.

The problem is that you haven't called setContentView in the onCreate method. You can't call findViewById before setting the view:

Code:
setContentView(R.layout.main);
 
Thanks, Kostas - I'd thought from the tutorials that setContentView was to be called in the onCreate method of the class that extends an Activity.

I've added the line you suggested to the main class' onCreate method (and updated the code in my original post to reflect that), and mMiles no longer comes through as null.

Its text does come through as an empty string, however, despite my having entered a number into the input field.

All the same, I appreciate your help. :)
 
This is because the onCreate method is only called once, before you enter some text in the field. You need read the string after you have written it. There is an addTextChangeListener event you can use.
 
Back
Top Bottom