As soon as you start writing apps that require user input such as a web form, you will wonder "Are there any form processing libraries" for Android? You will then google that question. And you will probably find a handful of them.
There is a general principle for handing form fields which most of these frameworks would have implemented. This general approach has the following key points.
Use inputType attribute in layout xml files
Android has a built-in capacity to categorize fields when you put them the layout files. Here is an example
<EditText android:id="@+id/email"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
/>
Notice the use of inputType as a textEmailAddress. This specification will allow android to appropriately allow or disallow based on what is permitted in this field. There are a number of input types that you could make use of. If you have eclipse you can press ctrl-space and you will see all of the available types
Use TextView.setError()
Android also provides a nice mechanism to set an error message on a text view field if you realize there is a problem.
This view will look like this when you do setError()
What is the problem then?
consider the following button click function of an activity that requires a number of input fields
public void signupButtonClick(View v)
{
//get the following fields
userid
password1
password2
email
//Validate the fields
But how?
//call a signup function
signup(userid,password1,email);
}
Before calling the signup() you have to validate the fields. Doing so for each field whether it is required and what other rules need to apply is laborious and could quickly become a spaghetti.
Code up a validator framework
The you typically solve thsi problem is you can write validators and attach them to each field. You can do this through metadata java annotations as done in a number of these frameworks or or attach those validators to the fields through code. Once you have these fields with validators attached to them, you can treat them as a set of validators adn each the whole set one time and proceed with taking the action only if the validation is successful. The validation step would also have used the setError() to indicate messages for each invalid field. Now you can write the above code like this
MyActivity extends FormActivity
{
//Keep local pointers to your fields that you need
EditText userid;
EditText password1;
EditText password2;
EditText email
//This is a callback from the base class FormActivity
@Override
protected void initFields()
{
//use findViewById() to populate the local fields
//attach validators
//Attach a suitable validator to the field
Field useridField = new Field(userid,required=true);
userid.addValueValidator(
new MinMaxValidator(5,10,"Must be between 5 and 10"));
//Add the field to the form
addValidator(useridField);
//or
addValidator(new Field(password1));//default required validator
addValidator(new Field(password2));//default required validator
addValidator(new Field(email));//default validator
//add a compound validator
addValidator(new PasswordRule(password1,password2));
}
//button click
public void signupButtonClick(View v)
{
//Form Activity will have a single method to do this
if (validateForm() == false)
{
alert("Sorry fill in the indicated fields");
}
//All good to go
//Read the following fields
userid
password1
password2
email
//call a signup function
signup(userid,password1,email);
}
}
Here is what the FormActivity looks like
Here is the source code for the FormActivity and its supporting classes.
public interface IValidator {
public boolean validate();
}
public interface IValueValidator
{
boolean validateValue(String value);
String getErrorMessage();
}
public class Field
implements IValidator
{
private TextView control;
private boolean required = true;
private ArrayList<IValueValidator> valueValidatorList
= new ArrayList<IValueValidator>();
public Field(TextView tv)
{
this(tv, true);
}
public Field(TextView tv, boolean inRequired)
{
control = tv;
required = inRequired;
}
@Override
public boolean validate()
{
String value = getValue();
if (StringUtils.invalidString(value))
{
//in valid string
if (required)
{
warnRequiredField();
return false;
}
}
for(IValueValidator validator: valueValidatorList)
{
boolean result = validator.validateValue(getValue());
if (result == true) continue;
if (result == false)
{
//this validator failed
String errorMessage = validator.getErrorMessage();
setErrorMessage(errorMessage);
return false;
}
}//eof-for
//All validators passed
return true;
}//eof-validate
private void warnRequiredField()
{
setErrorMessage("This is a required field");
}
public void setErrorMessage(String message)
{
control.setError(message);
}
public String getValue()
{
return this.control.getText().toString();
}
}//eof-class
public class PasswordFieldRule implements IValidator
{
private TextView password1;
private TextView password2;
public PasswordFieldRule(TextView p1, TextView p2)
{
password1 = p1;
password2 = p2;
}
@Override
public boolean validate()
{
String p1 = password1.getText().toString();
String p2 = password2.getText().toString();
if (p1.equals(p2))
{
return true;
}
//They are not the same
password2.setError("Sorry, password values don't match!");
return false;
}
}//eof-class
public class FormActivity
extends Activity
{
//Called back by setContentView
protected abstract void initializeFormFields();
@Override
public void setContentView(int viewid) {
super.setContentView(viewid);
initializeFormFields();
}
//List of validators
private ArrayList<IValidator> ruleSet = new ArrayList<IValidator>();
//Ability to add validators
public void addValidator(IValidator v)
{
ruleSet.add(v);
}
//Run the validations
public boolean validateForm()
{
boolean finalResult = true;
for(IValidator v: ruleSet)
{
boolean result = v.validate();
if (result == false)
{
finalResult = false;
}
//if true go around
//if all true it should stay true
}
return finalResult;
}
}//eof-class
Conclusion
Of course this is a very basic idea. You can copy this code and extend it to suit your needs. This took me an hour to write. So I am sure you will need more. You can even coopt the code for annotations if you like. Or extend the idea using delegation instead of an inheritable FormActivity. This later approach will allow you to have differetn rule sets for different submit buttions.
satya - 3/8/2013 1:51:42 PM
See this link for the research that lead to this framework
See this link for the research that lead to this framework
You might find at this link if there are more complete frameworks/libraries that suit your needs better.
satya - 3/8/2013 1:55:10 PM
Some of you might benefit from this base activity class
Adjust this class to your needs. And have your form activity extend this class. This class lets you do common alerts, toasts, and progress dialogs much more easily. I haven't cleaned up this code at all. So morph this to suit your most common needs.
public abstract class BaseActivity extends Activity
implements IReportBack
{
//private variables set by constructor
private static String tag=null;
private ProgressDialog pd = null;
public BaseActivity(String inTag)
{
tag = inTag;
}
public void reportBack(String message)
{
reportBack(tag,message);
}
public void reportTransient(String message)
{
reportTransient(tag,message);
}
public void reportBack(String tag, String message)
{
Log.d(tag,message);
}
public void reportTransient(String tag, String message)
{
String s = tag + ":" + message;
Toast mToast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
mToast.show();
reportBack(tag,message);
Log.d(tag,message);
}
public boolean invalidString(String s)
{
return !validString(s);
}
public boolean validString(String s)
{
if (s == null)
{
return false;
}
if (s.trim().equalsIgnoreCase(""))
{
return false;
}
return true;
}
public void gotoActivity(Class activityClassReference)
{
Intent i = new Intent(this,activityClassReference);
startActivity(i);
}
//Utility functions
public void turnOnProgressDialog(String title, String message)
{
pd = ProgressDialog.show(this,title,message);
}
public void turnOffProgressDialog()
{
pd.cancel();
}
public void alert(String title, String message)
{
AlertDialog alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
alertDialog.show();
}
}//eof-class