Skip to content

Commit

Permalink
Feat fragment form (#27)
Browse files Browse the repository at this point in the history
Form can now be created from a Fragment
* Centralize Form lifecycle in a FormManager class, which greatly simplify creation of a Form in Activities and Fragments
* Add fragment form in sample app
  • Loading branch information
aveuiller authored and dkharrat committed Sep 28, 2016
1 parent 932a535 commit 31437f9
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 89 deletions.
Original file line number Diff line number Diff line change
@@ -1,65 +1,32 @@
package com.github.dkharrat.nexusdialog;

import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.os.Bundle;
import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams;
import android.support.v4.app.FragmentActivity;

/**
* <code>FormActivity</code> is provides a default Activity implementation for using NexusDialog. It provides simple APIs to quickly
* create and manage form fields. If you'd like the Activity to be based on <code>AppCompatActivity</code>, you can use
* {@link FormWithAppCompatActivity}
*/
public abstract class FormActivity extends FragmentActivity {
private static final String MODEL_BUNDLE_KEY = "nd_model";
private FormController formController;
public abstract class FormActivity extends FragmentActivity implements FormInitializer {
private FormManager formManager;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.form_activity);
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_HIDDEN);

formController = new FormController(this);
initForm();

FragmentManager fm = getSupportFragmentManager();
FormModel retainedModel = (FormModel) fm.findFragmentByTag(MODEL_BUNDLE_KEY);

if (retainedModel == null) {
retainedModel = formController.getModel();
fm.beginTransaction().add(retainedModel, MODEL_BUNDLE_KEY).commit();
}
formController.setModel(retainedModel);
recreateViews();
formManager = new FormManager(this, this, R.id.form_elements_container);
}

/**
* Reconstructs the form element views. This must be called after form elements are dynamically added or removed.
*/
protected void recreateViews() {
ViewGroup containerView = (ViewGroup) findViewById(R.id.form_elements_container);
formController.recreateViews(containerView);
}

/**
* An abstract method that must be overridden by subclasses where the form fields are initialized.
*/
protected abstract void initForm();

/**
* Returns the associated form controller
*/
public FormController getFormController() {
return formController;
return formManager.getFormController();
}

/**
* Returns the associated form model
*/
public FormModel getModel() {
return formController.getModel();
return getFormController().getModel();
}

protected void recreateViews() {
formManager.recreateViews();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.github.dkharrat.nexusdialog;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
* <code>FormFragment</code> provides a default Fragment implementation for using NexusDialog.
*/
public abstract class FormFragment extends Fragment implements FormInitializer {
private FormManager formManager;

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.form_activity, null);
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
formManager = new FormManager(this, this, R.id.form_elements_container);
}

public FormController getFormController() {
return formManager.getFormController();
}

public FormModel getModel() {
return getFormController().getModel();
}

protected void recreateViews() {
formManager.recreateViews();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.dkharrat.nexusdialog;

/**
* Provides method inflating the form initial content.
*/
public interface FormInitializer {
/**
* An abstract method that must be overridden
* by subclasses where the form fields are initialized.
*/
void initForm(FormController controller);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.github.dkharrat.nexusdialog;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.view.ViewGroup;
import android.view.WindowManager;

/**
* Manage a form lifecycle inside a {@link FragmentActivity} or {@link Fragment}.
*/
public class FormManager {
private static final String MODEL_BUNDLE_KEY = "nd_model";

private final FragmentActivity enclosing;
private final FormInitializer initializer;
private final FormController formController;
private final int formResourceID;

/**
* Create a <code>FormManager</code> for a {@link FragmentActivity}.
*
* @param enclosing The activity in which the form should be displayed.
* @param initializer The form initialization sequence.
* @param formResourceID Identifier of the view containing the form elements.
*/
public FormManager(FragmentActivity enclosing, FormInitializer initializer, int formResourceID) {
this.enclosing = enclosing;
this.initializer = initializer;
formController = new FormController(enclosing);
this.formResourceID = formResourceID;
initialize();
}

/**
* Create a <code>FormManager</code> for a {@link Fragment}.
*
* @param enclosing The activity in which the form should be displayed.
* @param initializer The form initialization sequence.
* @param formResourceID Identifier of the view containing the form elements.
*/
public FormManager(Fragment enclosing, FormInitializer initializer, int formResourceID) {
this(enclosing.getActivity(), initializer, formResourceID);
}

public FormController getFormController() {
return formController;
}

/**
* Initialize the Form as requested in the given {@link FormInitializer} implementation,
* and manage its retained model.
*/
private void initialize() {
enclosing.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

initializer.initForm(formController);

FragmentManager fm = enclosing.getSupportFragmentManager();
FormModel retainedModel = (FormModel) fm.findFragmentByTag(MODEL_BUNDLE_KEY);

if (retainedModel == null) {
retainedModel = formController.getModel();
fm.beginTransaction().add(retainedModel, MODEL_BUNDLE_KEY).commit();
}
formController.setModel(retainedModel);
recreateViews();
}

/**
* Reconstructs the form element views.
* This must be called after form elements are dynamically added or removed.
*/
public void recreateViews() {
ViewGroup containerView = (ViewGroup) enclosing.findViewById(formResourceID);
formController.recreateViews(containerView);
}
}
Original file line number Diff line number Diff line change
@@ -1,64 +1,31 @@
package com.github.dkharrat.nexusdialog;

import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams;

/**
* <code>FormWithAppCompatActivity</code> is provides a default Activity implementation for using NexusDialog. If you'd
* like the Activity to be based on the standard Android <code>Activity</code>, you can use {@link FormActivity}
*/
public abstract class FormWithAppCompatActivity extends AppCompatActivity {
private static final String MODEL_BUNDLE_KEY = "nd_model";
private FormController formController;
public abstract class FormWithAppCompatActivity extends AppCompatActivity implements FormInitializer {
private FormManager formManager;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.form_activity);
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_HIDDEN);

formController = new FormController(this);
initForm();

FragmentManager fm = getSupportFragmentManager();
FormModel retainedModel = (FormModel) fm.findFragmentByTag(MODEL_BUNDLE_KEY);

if (retainedModel == null) {
retainedModel = formController.getModel();
fm.beginTransaction().add(retainedModel, MODEL_BUNDLE_KEY).commit();
}
formController.setModel(retainedModel);
recreateViews();
formManager = new FormManager(this, this, R.id.form_elements_container);
}

/**
* Reconstructs the form element views. This must be called after form elements are dynamically added or removed.
*/
protected void recreateViews() {
ViewGroup containerView = (ViewGroup) findViewById(R.id.form_elements_container);
formController.recreateViews(containerView);
}

/**
* An abstract method that must be overridden by subclasses where the form fields are initialized.
*/
protected abstract void initForm();

/**
* Returns the associated form controller
*/
public FormController getFormController() {
return formController;
return formManager.getFormController();
}

/**
* Returns the associated form model
*/
public FormModel getModel() {
return formController.getModel();
return getFormController().getModel();
}

protected void recreateViews() {
formManager.recreateViews();
}
}
1 change: 1 addition & 0 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<activity android:name=".ComplexForm"/>
<activity android:name=".SimpleExample"/>
<activity android:name=".CustomFormActivity"/>
<activity android:name=".FragmentFormActivity"/>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import android.text.InputType;
import android.view.*;

import com.github.dkharrat.nexusdialog.FormController;
import com.github.dkharrat.nexusdialog.FormWithAppCompatActivity;
import com.github.dkharrat.nexusdialog.controllers.*;
import com.github.dkharrat.nexusdialog.controllers.SearchableSelectionController.SelectionDataSource;
Expand Down Expand Up @@ -36,7 +38,7 @@ public class ComplexForm extends FormWithAppCompatActivity {
private final Stack<String> addedElements = new Stack<String>();

@Override
protected void initForm() {
public void initForm(FormController formController) {
setTitle("Complex Form");

final FormSectionController section = new FormSectionController(this, "Personal Info");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.github.dkharrat.nexusdialog.sample;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.view.View;
import android.widget.Button;

import com.github.dkharrat.nexusdialog.FormController;
import com.github.dkharrat.nexusdialog.FormFragment;
import com.github.dkharrat.nexusdialog.controllers.CheckBoxController;
import com.github.dkharrat.nexusdialog.controllers.EditTextController;
import com.github.dkharrat.nexusdialog.controllers.FormSectionController;
import com.github.dkharrat.nexusdialog.controllers.SelectionController;
import com.github.dkharrat.nexusdialog.utils.MessageUtil;

import java.util.Arrays;

public class FragmentFormActivity extends FragmentActivity {
private static final String FORM_FRAGMENT_KEY = "nd_form";


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_activity);

FragmentManager fm = getSupportFragmentManager();
MyFormFragment formFragment;

Fragment retainedFragment = fm.findFragmentByTag(FORM_FRAGMENT_KEY);
if (retainedFragment != null && retainedFragment instanceof MyFormFragment) {
formFragment = (MyFormFragment) retainedFragment;
} else {
formFragment = new MyFormFragment();
fm.beginTransaction()
.add(R.id.fragment_container, formFragment, FORM_FRAGMENT_KEY)
.commit();
}
setSubmitAction(formFragment);
}

private void setSubmitAction(final MyFormFragment formFragment) {
Button submitButton = (Button) findViewById(R.id.submit_button);
submitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
formFragment.validate();
}
});
}

public static class MyFormFragment extends FormFragment {
public static final String FIRST_NAME = "firstName";
public static final String LAST_NAME = "lastName";
public static final String GENDER = "gender";
public static final String HOBBIES = "hobbies";

public boolean validate() {
getFormController().resetValidationErrors();
if (getFormController().isValidInput()) {
Object firstName = getModel().getValue(FIRST_NAME);
Object lastName = getModel().getValue(LAST_NAME);
Object gender = getModel().getValue(GENDER);
Object favColor = getModel().getValue(HOBBIES);

String msg = "First name: " + firstName + "\n"
+ "Last name: " + lastName + "\n"
+ "Gender: " + gender + "\n"
+ "Hobbies: " + favColor + "\n";
MessageUtil.showAlertMessage("Field Values", msg, getActivity());
} else {
getFormController().showValidationErrors();
}
return true;
}

@Override
public void initForm(FormController controller) {
Context ctxt = getContext();
FormSectionController section = new FormSectionController(ctxt, "Personal Info");
section.addElement(new EditTextController(ctxt, FIRST_NAME, "First name"));
section.addElement(new EditTextController(ctxt, LAST_NAME, "Last name"));
section.addElement(new SelectionController(ctxt, GENDER, "Gender", true, "Select", Arrays.asList("Male", "Female"), true));
section.addElement(new CheckBoxController(ctxt, HOBBIES, "You like", true, Arrays.asList("sport", "gaming", "relaxation", "development"), true));
controller.addSection(section);
}
}
}
Loading

0 comments on commit 31437f9

Please sign in to comment.