I'm trying to make an Android application with automatic form generation http://labs.makemachine.net/2010/04/android-form-generator/
I've been reading a lot of topic about the "InstantiationException" but none solve my problem.
Here is my Activity
package com.example.makemachine;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.LinearLayout.LayoutParams;
/**
* FormActivity allows you to create dynamic form layouts based upon a json schema file.
* This class should be sub-classed.
*
* @author Jeremy Brown
*/
public abstract class FormActivity extends Activity
{
public static String SCHEMA_KEY_TYPE = "type";
public static String SCHEMA_KEY_BOOL = "boolean";
public static String SCHEMA_KEY_INT = "integer";
public static String SCHEMA_KEY_STRING = "string";
public static String SCHEMA_KEY_PRIORITY = "priority";
public static String SCHEMA_KEY_TOGGLES = "toggles";
public static String SCHEMA_KEY_DEFAULT = "default";
public static String SCHEMA_KEY_MODIFIERS = "modifiers";
public static String SCHEMA_KEY_OPTIONS = "options";
public static String SCHEMA_KEY_META = "meta";
public static String SCHEMA_KEY_HINT = "hint";
public static final LayoutParams defaultLayoutParams = new LinearLayout.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
// -- data
protected Map<String, FormWidget> _map;
protected ArrayList<FormWidget> _widgets;
// -- widgets
protected LinearLayout _container;
protected LinearLayout _layout;
protected ScrollView _viewport;
// -----------------------------------------------
//
// parse data and build view
//
// -----------------------------------------------
/**
* parses a supplied schema of raw json data and creates widgets
* @param data - the raw json data as a String
*/
public FormActivity(){
super();
}
public void generateForm( String data )
{
_widgets = new ArrayList<FormWidget>();
_map = new HashMap<String, FormWidget>();
try
{
String name;
FormWidget widget;
JSONObject property;
JSONObject schema = new JSONObject( data );
JSONArray names = schema.names();
for( int i= 0; i < names.length(); i++ )
{
name = names.getString( i );
if( name.equals( SCHEMA_KEY_META ) ) continue;
property = schema.getJSONObject( name );
boolean toggles = hasToggles( property );
String defaultValue = getDefault( property );
int priority = property.getInt( FormActivity.SCHEMA_KEY_PRIORITY );
widget = getWidget( name, property );
if( widget == null) continue;
widget.setPriority( priority );
widget.setValue( defaultValue );
if( toggles ){
widget.setToggles( processToggles( property ) );
widget.setToggleHandler( new FormActivity.FormWidgetToggleHandler() );
}
if( property.has(FormActivity.SCHEMA_KEY_HINT)) widget.setHint( property.getString( FormActivity.SCHEMA_KEY_HINT ) );
_widgets.add( widget );
_map.put( name, widget );
}
} catch( JSONException e ) {
Log.i( "MakeMachine", e.getMessage() );
}
// -- sort widgets on priority
Collections.sort( _widgets, new PriorityComparison() );
// -- create the layout
_container = new LinearLayout( this );
_container.setOrientation( LinearLayout.VERTICAL );
_container.setLayoutParams( FormActivity.defaultLayoutParams );
_viewport = new ScrollView( this );
_viewport.setLayoutParams( FormActivity.defaultLayoutParams );
_layout = new LinearLayout( this );
_layout.setOrientation( LinearLayout.VERTICAL );
_layout.setLayoutParams( FormActivity.defaultLayoutParams );
initToggles();
for( int i = 0; i < _widgets.size(); i++ ) {
_layout.addView( ( View ) _widgets.get(i).getView() );
}
_viewport.addView( _layout );
_container.addView( _viewport );
setContentView( _container );
}
// -----------------------------------------------
//
// populate and save
//
// -----------------------------------------------
/**
* this method fills the form with existing data
* get the json string stored in the record we are editing
* create a json object ( if this fails then we know there is now existing record )
* create a list of property names from the json object
* loop through the map returned by the Form class that maps widgets to property names
* if the map contains the property name as a key that means there is a widget to populate w/ a value
*/
protected void populate( String jsonString )
{
try
{
String prop;
FormWidget widget;
JSONObject data = new JSONObject( jsonString );
JSONArray properties = data.names();
for( int i = 0; i < properties.length(); i ++ )
{
prop = properties.getString( i );
if( _map.containsKey(prop) ) {
widget = _map.get( prop );
widget.setValue( data.getString(prop) );
}
}
} catch ( JSONException e ) {
}
}
/**
* this method preps the data and saves it
* if there is a problem w/ creating the json string, the method fails
* loop through each widget and set a property on a json object to the value of the widget's getValue() method
*/
protected JSONObject save()
{
FormWidget widget;
JSONObject data = new JSONObject();
boolean success = true;
try{
for( int i = 0; i < _widgets.size(); i++ )
{
widget = _widgets.get(i);
data.put( widget.getPropertyName(), widget.getValue() );
}
} catch( JSONException e )
{
success = false;
Log.i( "MakeMachine", "Save error - " + e.getMessage() );
return null;
}
if( success ) {
Log.i( "MakeMachine", "Save success " + data.toString() );
return data;
}
return null;
}
// -----------------------------------------------
//
// toggles
//
// -----------------------------------------------
/**
* creates the map a map of values for visibility and references to the widgets the value affects
*/
protected HashMap<String, ArrayList<String>> processToggles( JSONObject property )
{
try{
ArrayList<String> toggled;
HashMap<String, ArrayList<String>> toggleMap = new HashMap<String, ArrayList<String>>();
JSONObject toggleList = property.getJSONObject( FormActivity.SCHEMA_KEY_TOGGLES );
JSONArray toggleNames = toggleList.names();
for( int j = 0; j < toggleNames.length(); j++ )
{
String toggleName = toggleNames.getString(j);
JSONArray toggleValues = toggleList.getJSONArray( toggleName );
toggled = new ArrayList<String>();
toggleMap.put( toggleName, toggled );
for( int k = 0; k < toggleValues.length(); k++ ) {
toggled.add( toggleValues.getString(k) );
}
}
return toggleMap;
} catch( JSONException e ){
return null;
}
}
/**
* returns a boolean indicating that the supplied json object contains a property for toggles
*/
protected boolean hasToggles( JSONObject obj ){
try{
obj.getJSONObject( FormActivity.SCHEMA_KEY_TOGGLES );
return true;
} catch ( JSONException e ){
return false;
}
}
/**
* initializes the visibility of widgets that are togglable
*/
protected void initToggles()
{
int i;
FormWidget widget;
for( i = 0; i < _widgets.size(); i++ ) {
widget = _widgets.get(i);
updateToggles( widget );
}
}
/**
* updates any widgets that need to be toggled on or off
* @param widget
*/
protected void updateToggles( FormWidget widget )
{
int i;
String name;
ArrayList<String> toggles;
ArrayList<FormWidget> ignore = new ArrayList<FormWidget>();
toggles = widget.getToggledOn();
for( i = 0; i < toggles.size(); i++ )
{
name = toggles.get(i);
if( _map.get(name) != null )
{
FormWidget toggle = _map.get(name);
ignore.add( toggle );
toggle.setVisibility( View.VISIBLE );
}
}
toggles = widget.getToggledOff();
for( i = 0; i < toggles.size(); i++ )
{
name = toggles.get(i);
if( _map.get(name) != null )
{
FormWidget toggle = _map.get(name);
if( ignore.contains(toggle) ) continue;
toggle.setVisibility( View.GONE );
}
}
}
/**
* simple callbacks for widgets to use when their values have changed
*/
class FormWidgetToggleHandler
{
public void toggle( FormWidget widget ) {
updateToggles( widget );
}
}
// -----------------------------------------------
//
// utils
//
// -----------------------------------------------
protected String getDefault( JSONObject obj ){
try{
return obj.getString( FormActivity.SCHEMA_KEY_DEFAULT );
} catch ( JSONException e ){
return null;
}
}
/**
* helper class for sorting widgets based on priority
*/
class PriorityComparison implements Comparator<FormWidget>
{
public int compare( FormWidget item1, FormWidget item2 ) {
return item1.getPriority() > item2.getPriority() ? 1 : -1;
}
}
/**
* factory method for actually instantiating widgets
*/
protected FormWidget getWidget( String name, JSONObject property )
{
try
{
String type = property.getString( FormActivity.SCHEMA_KEY_TYPE );
if( type.equals( FormActivity.SCHEMA_KEY_STRING ) ){
return new FormEditText( this, name );
}
if( type.equals( FormActivity.SCHEMA_KEY_BOOL ) ){
return new FormCheckBox( this, name );
}
if( type.equals( FormActivity.SCHEMA_KEY_INT ) )
{
if( property.has( FormActivity.SCHEMA_KEY_OPTIONS ) )
{
JSONObject options = property.getJSONObject( FormActivity.SCHEMA_KEY_OPTIONS );
return new FormSpinner( this, name, options );
}else{
return new FormNumericEditText( this, name );
}
}
} catch( JSONException e ) {
return null;
}
return null;
}
public static String parseFileToString( Context context, String filename )
{
try
{
InputStream stream = context.getAssets().open( filename );
int size = stream.available();
byte[] bytes = new byte[size];
stream.read(bytes);
stream.close();
return new String( bytes );
} catch ( IOException e ) {
Log.i("MakeMachine", "IOException: " + e.getMessage() );
}
return null;
}
}
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.makemachine"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.makemachine.MainActivity"
android:label="@string/app_name" >
</activity>
<activity
android:name="com.example.makemachine.FormActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Logcat Error
12-22 10:41:01.791: E/AndroidRuntime(2953): FATAL EXCEPTION: main
12-22 10:41:01.791: E/AndroidRuntime(2953): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.makemachine/com.example.makemachine.FormActivity}: java.lang.InstantiationException: can't instantiate class com.example.makemachine.FormActivity
12-22 10:41:01.791: E/AndroidRuntime(2953): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2106)
12-22 10:41:01.791: E/AndroidRuntime(2953): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
12-22 10:41:01.791: E/AndroidRuntime(2953): at android.app.ActivityThread.access$600(ActivityThread.java:141)
12-22 10:41:01.791: E/AndroidRuntime(2953): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
12-22 10:41:01.791: E/AndroidRuntime(2953): at android.os.Handler.dispatchMessage(Handler.java:99)
12-22 10:41:01.791: E/AndroidRuntime(2953): at android.os.Looper.loop(Looper.java:137)
12-22 10:41:01.791: E/AndroidRuntime(2953): at android.app.ActivityThread.main(ActivityThread.java:5041)
12-22 10:41:01.791: E/AndroidRuntime(2953): at java.lang.reflect.Method.invokeNative(Native Method)
12-22 10:41:01.791: E/AndroidRuntime(2953): at java.lang.reflect.Method.invoke(Method.java:511)
12-22 10:41:01.791: E/AndroidRuntime(2953): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
12-22 10:41:01.791: E/AndroidRuntime(2953): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
12-22 10:41:01.791: E/AndroidRuntime(2953): at dalvik.system.NativeStart.main(Native Method)
12-22 10:41:01.791: E/AndroidRuntime(2953): Caused by: java.lang.InstantiationException: can't instantiate class com.example.makemachine.FormActivity
12-22 10:41:01.791: E/AndroidRuntime(2953): at java.lang.Class.newInstanceImpl(Native Method)
12-22 10:41:01.791: E/AndroidRuntime(2953): at java.lang.Class.newInstance(Class.java:1319)
12-22 10:41:01.791: E/AndroidRuntime(2953): at android.app.Instrumentation.newActivity(Instrumentation.java:1054)
12-22 10:41:01.791: E/AndroidRuntime(2953): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2097)
12-22 10:41:01.791: E/AndroidRuntime(2953): ... 11 more
What is wrong in my code?
Your FormActivity
is abstract
and abstract classes cannot be instantiated.
Reading the comments from the generated code, it says "This class should be sub-classed". So extend the base FormActivity
and only declare the non-abstract, derived activity classes in your manifest.