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
isabstract
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.