Hire the author: Dafa Faris MS
Introduction
As you may have noticed, there are a lot of popular online form creation platforms offering a modular creation process and ease of use. This makes a lot of parties that should’ve benefited more from a more native approach decide to use those online platforms instead. And the easy process of developing a native app is getting more and more non-beneficial to even casual creators.
Native implementation of a form inside your Android app, for example, can give you a lot of advantages. Be it implementing as an extra on your existing apps (preventing users getting out of your ecosystem), control & customization, a more professional approach, etc. Only beaten hard in the ease-of-creation aspect.
This project intends to set up the structure to modularly create your forms inside an Android app. Even if you’re not trying to implement one in Android, the methods & main idea can be taken elsewhere. Basically, any expansion or modification to the forms will only take place in the layout (.xml), and no further main program adjustment necessary. You can copy-paste the layout components, adjust some things there, and it’ll be functional — modularity achieved.
Glossary
Some new terms I learned while doing this project:
- HTTP Request – A packet of information that one computer sends to another computer to communicate; such as from you as a client, to a web-app as a server.
- Web App – A computer program that utilizes web browsers and web technology to perform tasks over the Internet.
Requirements
- Android Studio (used here is 4.0 Canary 2).
- Phone or AVD (Android Virtual Device) for testing.
- Google account.
No need to exactly match the version used here, those are just numbers to take in mind as there might be big differences between versions. The project file used in this tutorial is available in this GitHub repository.
Step-by-step
Without further ado, let’s start the steps to achieve this goal.
0 – Basics
0a – Starting The Project
Skip this step if you’re planning to implement this to an existing application project. This will guide you to some basics for creating your first Android Studio project. You may follow other step-by-step for this part, but make sure to enable “Use legacy android.support libraries” to be able to copy-paste XML (layout) files presented here. It allows you to use older XML syntax in the new Android Studio (you don’t need to do this in older versions).
Here are the parameters I use personally during the creation of a new project:
- Template: Scrolling Activity
- Name: Synced Form
- Package Name: com.arsenicbismuth.syncedform
- Language: Java
- Minimum SDK: API 21 – Android 5.0 (Lollipop)
- Use legacy android.support libraries
0b – The Files
The files you want to focus on are present on the project structure on the left window. Inside the java folder is the main program to be executed, if you’re following our initial project creation it’d be called ScrollingActivity (the extension is hidden, but it’s .java), or MainActivity if you use some of the other templates.
The layout for editing the components appear to the viewer/user is placed in the layout folder. Meanwhile, external resources for more organized value management are placed in values folder. And last but not least, the AndroidManifest up top for formal declaration such as permissions, package name, app name, etc.

For a detailed explanation, please always refer to the complete documentation already presented by the Android community.
1 – Main Program
The entire program is placed on a single activity. In my case, that’d be ScrollingActivity.java . You may want to use intent if you planned to add this to an existing app. The main idea here is that the program would search for certain components in the layout (including its children), get their data, grouping them all, and last send it to the Google Sheet.
As a result, you won’t need to modify anything in the program (.java file) every time you add a new input box/dialog. Thus you can modify the layout as if it’s a modular form. The input components covered here are EditText (your common editable box for numbers or text), RadioButton (single-choice), CheckBox (multi-choice), and EditText using DatePicker (calendar).
First off, the libraries used. Thanks to Android Studio, those aren’t necessary to put beforehand, as it can automatically import during the writing of your code. But just in case you’re confused about which is being used, since there might be multiple versions.
import android.app.DatePickerDialog;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.RadioButton;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
The first part of the main program would be the data compilation. The methods/subprograms are designed to get every input-able components in the layout. This process is already started during the initialization of the activity.
Below is the process during initialization. As you can see we’re trying to look for every child in our layout. To do this, any View components in the bottom of the tree (that is, no more children to that component) will be acquired. This way, any input-able components (such as EditText) encapsulated by layout grouper, organizer, or wrapper can still be obtained.
public class ScrollingActivity extends AppCompatActivity implements DatePickerDialog.OnDateSetListener {
// Contains every component IDs inside content_scrolling.xml
private static ArrayList<View> components = new ArrayList<View>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrolling);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ViewGroup main = findViewById(R.id.lay_main);
components = getChildren(main);
}
private ArrayList<View> getChildren(View v) {
// Anything with child(s) is a ViewGroup, end recursion if not
if (!(v instanceof ViewGroup)) {
ArrayList<View> viewArrayList = new ArrayList<View>();
viewArrayList.add(v);
return viewArrayList;
}
ArrayList<View> result = new ArrayList<View>();
// Loop inside current group, and compile results from every child
ViewGroup vg = (ViewGroup) v;
Log.i("ChildCount",String.valueOf(vg.getChildCount()));
for (int i = 0; i < vg.getChildCount(); i++) {
View child = vg.getChildAt(i);
ArrayList<View> viewArrayList = new ArrayList<View>();
viewArrayList.add(v);
viewArrayList.addAll(getChildren(child)); // Recursion
result.addAll(viewArrayList);
}
// Return to parent
return result;
}
The function will then return the list of those lowest components in the layout as a View and store it in the components variable.
Now to handle every radio button (single-choice ie. Male or Female) and checkbox (multi-choice), we add two methods below. The data stored here are a pair of view tag & its input into a Map (similar to a dictionary in Python).
Note that method onRadioClicked & onCheckClicked must be called when the respective components are clicked. Thus, they must be added into every respective RadioButton & CheckBox component in the layout. This will be explained later.
// Class variables for storing radioButton states
private Map<String, String> allRadio = new HashMap<>();
private Map<String, String> allCheck = new HashMap<>();
// Assigned to every RadioButton onClick parameter.
public void onRadioClicked(View view) {
// Check if button currently checked
boolean checked = ((RadioButton) view).isChecked();
// Tag naming format: group_pick. Ex: sex_female
String[] tag = view.getTag().toString().split("_");
String group = tag[0];
String pick = tag[1];
// Put all data
if(checked) {
allRadio.put(group, pick);
}
}
// Assigned to every check box onClick parameter.
public void onCheckClicked(View view) {
boolean checked = ((CheckBox) view).isChecked();
// Applies for every check, each must contains tag
if (checked) {
allCheck.put(view.getTag().toString(), "v");
} else {
allCheck.put(view.getTag().toString(), "");
}
}
Lastly for the data compilation part is DatePicker. It’s similar to the method above, but here the input component in the layout is using EditText. Hence it’ll be acquired by the getChildren method before, and requires no special case.
Here, every time the special EditText is clicked, it’d open a DatePicker dialog (in the form of a calendar) instead of manually inputting the data. Note you can modify the formatting by changing the date variable assignment (here we set it to day/month/year).
Do note for some reason the month is started from 0 (January) in my case, that’s why I added it by one.
EditText picked;
// Assigned to every date EditText onClick parameter.
public void showDateDialog(View view) {
picked = (EditText) view; // Store the dialog to be picked
DatePickerDialog datePickerDialog = new DatePickerDialog(
this, this,
Calendar.getInstance().get(Calendar.YEAR),
Calendar.getInstance().get(Calendar.MONTH),
Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
datePickerDialog.show();
}
@Override
public void onDateSet(DatePicker datePicker, int y, int m, int d) {
// If done picking date
String date = d + "/" + (m+1) + "/" + y;
picked.setText(date);
}
In the last part of the main program, we get all the data from EditText in getData method. After combining that data with the allRadio and allCheck which stores RadioButton and CheckBox information, we post the data into Google Sheet by using a simple HTTP POST command.
Here Google Script is used as an interface to your Google Sheet. Hence why the variable url MUST be edited to the one provided by your own Google Script project. It’ll be covered later.
private Map<String, String> getData() {
// Collect all input data
Map<String, String> result = new HashMap<>();
for (View comp : components) {
// Get every EditText's tag & text.
if (comp instanceof EditText) {
result.put(comp.getTag().toString(), ((EditText) comp).getText().toString());
}
}
return result;
}
// Assigned to the fab (floating action button) onClick parameter.
public void postSheet(final View view) {
// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="https://script.google.com/macros/s/MODIFY_YOURSELF";
// Collect all data to send
final Map<String, String> allData = getData();
allData.putAll(allRadio); // Combine with radio data
allData.putAll(allCheck); // Combine with check data
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Snackbar.make(view, "Response: " + response, Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Snackbar.make(view, "No response", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
}) {
@Override
protected Map<String, String> getParams() {
// Map<String, String> params = new HashMap<>();
return allData;
}
};
// Add the request to the RequestQueue.
queue.add(stringRequest);
}
For adding internet permission, we need to add this line to the AndroidManifest.xml, just between “package” & “<application” lines.
<uses-permission android:name="android.permission.INTERNET" />
That’s all for the program. We can now move on to the layout & the general procedure for adding a new component.
2 – Layout
Generally, it’s easier for you to just copy-paste the following XML code to the content_scrolling.xml file, you must enter code view beforehand by clicking one of the menus on the top right. This way, you can just modify & follow my custom procedure to add every component to be used as an input.

Here’s the code. We do always try to create everything as beautiful as possible. Hence visual aspect such as margin, padding, theme consistency, etc aren’t neglected.
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".ScrollingActivity"
tools:showIn="@layout/activity_scrolling">
<LinearLayout
android:id="@+id/lay_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:paddingTop="@dimen/text_margin">
<EditText
android:id="@+id/edit_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:layout_weight="1"
android:autofillHints=""
android:ems="10"
android:hint="@string/h_name"
android:inputType="textPersonName"
android:tag="name" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<EditText
android:id="@+id/edit_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:autofillHints=""
android:ems="10"
android:hint="@string/h_phone"
android:inputType="phone"
android:tag="phone"
android:textAlignment="viewStart" />
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/text_margin"
android:layout_marginRight="@dimen/text_margin"
android:layout_marginBottom="@dimen/option_margin"
android:layout_weight="1"
android:ems="10"
android:paddingLeft="4sp"
android:paddingRight="4sp"
android:text="@string/t_sex"
android:textAppearance="@style/TextAppearance.Design.Counter"
android:textColor="?android:attr/textColorHint" />
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/text_margin"
android:layout_marginRight="@dimen/text_margin"
android:orientation="horizontal"
android:paddingBottom="@dimen/text_margin">
<RadioButton
android:id="@+id/radio_male"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/text_margin"
android:layout_weight="1"
android:onClick="onRadioClicked"
android:tag="sex_male"
android:text="@string/t_male" />
<RadioButton
android:id="@+id/radio_female"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="onRadioClicked"
android:tag="sex_female"
android:text="@string/t_female" />
</RadioGroup>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<EditText
android:id="@+id/edit_add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:layout_weight="1"
android:autofillHints=""
android:ems="10"
android:hint="@string/h_address"
android:inputType="text"
android:tag="address"
android:textAlignment="viewStart" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<EditText
android:id="@+id/edit_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:layout_weight="1"
android:cursorVisible="false"
android:autofillHints=""
android:ems="10"
android:focusable="false"
android:hint="@string/h_date1"
android:inputType="date"
android:onClick="showDateDialog"
android:tag="start_date" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<EditText
android:id="@+id/edit_date2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:layout_weight="1"
android:cursorVisible="false"
android:autofillHints=""
android:ems="10"
android:focusable="false"
android:hint="@string/h_date2"
android:inputType="date"
android:onClick="showDateDialog"
android:tag="end_date" />
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/text_margin"
android:layout_marginRight="@dimen/text_margin"
android:layout_marginBottom="@dimen/option_margin"
android:layout_weight="1"
android:ems="10"
android:paddingLeft="4sp"
android:paddingRight="4sp"
android:text="@string/t_check"
android:textAppearance="@style/TextAppearance.Design.Counter"
android:textColor="?android:attr/textColorHint" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/text_margin"
android:layout_marginRight="@dimen/text_margin"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingBottom="@dimen/text_margin">
<CheckBox
android:id="@+id/check_work"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/text_margin"
android:onClick="onCheckClicked"
android:tag="work"
android:text="@string/c_work" />
<CheckBox
android:id="@+id/check_smoke"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/text_margin"
android:onClick="onCheckClicked"
android:tag="smoke"
android:text="@string/c_smoke" />
<CheckBox
android:id="@+id/check_gym"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/text_margin"
android:onClick="onCheckClicked"
android:tag="workout"
android:text="@string/c_gym" />
</LinearLayout>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Tho, in case the code above isn’t compatible with your project build settings (such as not ticking the “Use legacy android.support libraries” on newer Android Studio), you can try to follow the layout structure below. Then edit the parameters to match the parameters presented in the above code.

Below is the resulting app. You’ll notice that every EditText in the layout structure above is wrapped inside a TextInputLayout. This is to add the special effect already created by material design from Google. The effect can be seen on both screenshots below.
Empty, unfocused EditText will show the hint in the box without any title (prevent any repetition). When clicked/focused, the hint text moves smoothly into above the input box turning into a title, and turned pink (accent color). It’ll be back to normal if they’re still empty, but stay the same with only color reverted if it’s filled but isn’t focused anymore.
You can modify the color.xml inside res/values/ folder to customize the app colors.


Meanwhile below is the preview of the date picker. It’s functioning as you’d expect from any date picker from the official Android app, all done without much hassle. Truly the power of easily integrated libraries. You can tap the year up top to move between years, or click the arrows to move between months, etc.
The date picker is implemented by setting the onClick parameter of an EditText to showDateDialog , and also prevent direct editing to the text. This way, when a user clicks on the box, they’ll be presented by this picker instead of manually typing them. The resulting data is still your usual text in an EditText, thus no special case necessary for reading the data.

Now that all component examples are created, we can use those as a base for creating your form, just by modifying this same layout data. The procedure will be given on step 4 below after you’ve finished managing the Google Sheet & Script. An important thing to notice is the tag parameters given to every input components, it’ll be the one used for matching the data into Google Sheet.
3 – Google Sheet
Let’s move on to the Google Sheet part for handling the data. As already mentioned before, having the data stored in a Google Sheet makes a lot of the process easier. No need to create an account/login management (you don’t want just anyone to be able to see the data), direct & easy data handling, Google Script integration, etc.
First off, create a new Sheet, fill the first row with texts similar to the image below (ignore the data under it for now). Those are Timestamp, name, phone, address, sex, work, smoke, workout, start_date, end_date. You can also just copy that text, paste, then split to columns.

Those headers will be the ones used as a match to our tags we’ve given to every input component. In a way, it’s just a confirmation as to which data to be saved in the form.
Next, open up Tools > Script Editor > Accept any permission if asked. Here the Google Script will be paired to your Google Sheet instead of just being independent. Copy-paste the following code below. The only thing you need to change is the SHEET_NAME.
// original from: http://mashe.hawksey.info/2014/07/google-sheets-as-a-database-insert-with-apps-script-using-postget-methods-with-ajax-example/
// original gist: https://gist.github.com/willpatera/ee41ae374d3c9839c2d6
// Caution: Must be assigned to a sheet, not as an independent script.
// Enter sheet name where data is to be written below
var SHEET_NAME = "Sheet1";
var EMPTY = ""; // Data filled if there's no such input
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
// If you don't want to expose either GET or POST methods you can comment out the appropriate function
function doGet(e){
return handleResponse(e);
}
function doPost(e){
return handleResponse(e);
}
function handleResponse(e) {
// shortly after my original solution Google announced the LockService[1]
// this prevents concurrent access overwritting data
// [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
var sheet = doc.getSheetByName(SHEET_NAME);
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
// loop through the header columns
for (i in headers){
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
// check if undefined
var data = e.parameter[headers[i]];
if (data === undefined) {
row.push(EMPTY);
} else {
row.push(data);
}
}
}
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
return ContentService
.createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
.setMimeType(ContentService.MimeType.JSON);
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e}))
.setMimeType(ContentService.MimeType.JSON);
} finally { //release lock
lock.releaseLock();
}
}
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
}
Now go to Publish > Deploy as web app > Copy the URL presented > Set the bottom-most option “Who has access to the app” to “Anyone, even anonymous” > Update. That’s it. Now to give a data to your sheet, you can just do a regular HTTP Request directed towards that URL.
Now paste that web-app URL into the url variable inside our Android main program. In case you missed the URL, you can view it again by following the same process. No need to click Update, just copy the URL.
4 – Testing
4a – HTTP Request
You can test the web-app we’ve just deployed by using generic HTTP Request websites such as https://www.apirequest.io/.
- Open that link or any other tester of your choice.
- Paste the web-app URL given from the Google Script.
- Click on the “+ Params” to add a new parameter.
- Input the left side (key) to any of the headers in the Google Sheet, such as “name” (case-sensitive).
- Input the right side (value) to any value you wanted to add in that column.
- Add another parameter as necessary.
- Click send, and you’ll be given a response from our web-app.
- A successful request will give you a “success” result in the body section of the response.
- Otherwise, you might want to redeploy your web-app again.

4b – Android App
Testing the Android side means you have to be able to deploy your app. To do so, you may refer to another article online. Basically, all you have to do are either creating an AVD or Android Virtual Device (emulator) or plug-in your phone in Developer Mode to your computer.
Now, here is the step where you’ll stumble upon some errors. Try to slowly work some of those out, as there might be some adjustments needed since we’re not in the same development environment.
If done, the app will show up in your phone similar to the preview in Android Studio or our images above. Just fill in the form and send it. There’ll be a toast message giving you the HTTP Response similar to our testing step 4a above.

4c – Adding New Components
The procedures are as follow for creating every new component in the form:
- Copy-paste the components you want to add, be it directly from the XML visual view or the code view. The ID for the components aren’t important, just make sure they’re unique from each other (or just leave them blank).
- Modify the text and hint parameters (no need to edit text for EditText), you may edit them directly for easy testing (hard-coded) or use the resources (similar to the examples) for a more complete implementation.
- An important part: modify the tag parameter, make sure it’s unique from each other. This is the text for the header in Google Sheet.
- Special case: for radio buttons, set the tag to <group>_<data>. For example the sex radio group, there’re male & female. Set them to sex_male & sex_female. The result will be placed in the same column as the group, sex.
- Create a new column in the Google Sheet with header (first row) matching the tag you’ve assigned to the new component.
That’s it! You can add a lot of new components and the app wouldn’t need any further main program modification. All in all, the crucial steps are only assigning the tag for each component and modifying the visual elements such as text & hint. If those simple steps seemingly long, just try them out a couple of times and everything will feel perfectly modular.
Learning Tools
No direct example regarding this since a lot of similar projects are either superbly hard-coded or simplified just to get the “App-to-Sheet” functionalities working. Overall I took a lot of examples from Stack Overflow for more detailed problems, syntax, and/or functionalities to implement the universal data acquisition from the layout.
Learning Strategy
No major problem during the whole process of this project. The incremental testing steps help tremendously in understanding which part of the process is broken, as there’re many “parties” involved here.
Focusing on aesthetics also proved to be a very good refreshing chance to learn something outside of the technicalities. This might be something that can significantly boost time in creating another Android app, as there are already certain “feels” obtained from fine-tuning the parameters and knowing which to change.
Reflective Analysis
Usually, ease of usage is done at the expense of customizability. That’d be the case with your mainstream online forms such as Google Forms and Typeform. Now, following the same idea, we can try to balance both of them. The modularity of creating forms just in the layout side, and of course the full ability of native Android app which you can modify as much as you like. Implementing Google Sheet and Script also helps to skip technicalities of user management and authentication.
Conclusion & Future
A modular design method is not only simplistic and universal in implementation, but also gives app developers a way to expand with ease. Of course, the potential in such a method is unlimited, future development could be adding more components compatibility, a more streamlined process, etc at the expense of adding a little more complexity to your main program.
Ask away any question in the comment section, any feedback is also appreciated. As stated above, the everything is available in this GitHub repository.
You can check my other blog here about scraping contextual Reddit conversation.
Project duration + article creation: 14 hours.
How can I add validations in this form?
Unfortunately, as far as my Android dev knowledge goes, we can’t do that except if you want to break the full-modularity creation method.
But if you’re okay with partially modular method, you can try to follow instruction from this Stack Overflow:
https://stackoverflow.com/questions/2763022/android-how-can-i-validate-edittext-input/11838715#11838715
in which alongside adding the validation you wanted for specific EditText(s), you also add another boolean specifying whether every EditText is correct or not, and use that boolean to enable/disable the “send” button.
As you can see, each must be tailored for every EditText you wanted to validate. Thus no “modular” solution like we wanted here.
Validating each EditText is not what I was looking for but anyway, thanks for the stackoverflow link.
Thanks, for the detailed post this is will be very helpful for anyone creating forms.
Also, looks like a typo in line “Tho in the case above code”. Kindly check.
Thx for the feedbacks, I didn’t know it was you coz of the different username.
Do you have video tutorial ?
I am continuously getting this error : When I use your script with my google sheet {“result”:”error”,”error”:{“name”:”Exception”}}
I have followed all your instructions and started again a few times.
Getting the same error as mentioned by you.