Tutorial: Notepad App in Android

Tutorial: Notepad App in Android
Tutorial: Notepad App in Android

Why are we discussing creating a Notepad application in Android? Because With the increasing hassle all over, people often forget carrying a piece of pen and paper when they head out to the market or elsewhere. Having your own space app for noting down stuff is difficult to find in mobile phones as the usual writing spaces are present in SMS, Whatsapp, Messenger and so on.

From the consumers’ perspective, a note app is a very handy solution for noting down worthy arguments, points, to-do stuff, and so on, while from a developer’s perspective a note app gives him/her a clear picture about creating and preserving text files.

For creating and storing text files the developer needs to create objects of FileInput and FileOutput streams in the Java Code. For elementary apps that need the notes to be stored at a single target device, no server connection is required, whereas for collaborative apps such as Trello, server connection and cloud storage are critical pre-requisites.

Although, the idea might appear to be redundant, yet it is very progressive for learners. It assists you in acquiring hands-on knowledge about working with Java Input stream and Output stream and file with .txt extensions.

The untainted strategy includes the creation of an app with more features than the competitor, even if a single feature of your app stands-out among the same kind, your app will receive very high traffic in the Google Play store or App store.

Usually, the code of such apps is very redundant and bewildering, but kudos to the default Java classes that abstract most of the laborious source code into its libraries. You can include any of those by just adding the Gradle scripts in the build. Gradle file in manifests. Analogous to the usual android apps, the creation of the Notepad App also involves two aspects, one is the UI designing and the other is the backend programming.

Phase One: User-Interface Designing

The User-Interface of the Notepad app is very elementary and can be created by adding a single XML code.

The basic UI components are:

  • A linear layout that acts as the parent.
  • Edit text for writing notes.
  • Three Buttons:
  • One for creating a new document.
  • One for saving the text file.
  • One for opening any previously saved file.
Image depicts the intended app design
Image Source: Android Emulator

The complete XML code for the UI design shown in the image:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:background="#FAD8B5"
    android:orientation="vertical"
    android:weightSum="10">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="9"
    android:orientation="vertical">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:inputType="textMultiLine"
        android:gravity="top"
        android:id="@+id/text"
        android:scrollbars = "vertical"/>
</LinearLayout>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="New"
        android:background="@drawable/new_button"
        android:textColor="#FFFFFF"
        android:id="@+id/newButton"
        android:textSize="20dp"
        android:textStyle="bold"
        android:layout_weight="1"
        android:onClick="buttonAction"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Save"
        android:layout_marginLeft="6dp"
        android:id="@+id/saveButton"
        android:textColor="#FFFFFF"
        android:background="@drawable/save_button"
        android:textSize="20dp"
        android:textStyle="bold"
        android:onClick="buttonAction"
        android:layout_weight="1"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Open"
        android:id="@+id/openButton"
        android:onClick="buttonAction"
        android:textColor="#FFFFFF"
        android:layout_marginLeft="6dp"
        android:textSize="20dp"
        android:textStyle="bold"
        android:background="@drawable/open_button"
        android:layout_weight="1"/>
</LinearLayout>

</LinearLayout>

FAQs based on the above-mentioned code

1. What is the significance of the attribute android:layout_weight=”1″ in the XML code?
The Layout weight attribute is added in the XML code for specifying the size of the Child Views of any container (parent layout). For instance, refer to the image given below, for ensuring both the buttons occupy exactly half of the screen width, you need to set the Layout weight as 0.5 in the XML code of the Layout.

Apart from the parent, linear layout also allows developers to assign specific weight to each of its children individually. By default, the layout weight is equal to zero. Weight implicitly assigns an “importance” value to the child layouts in terms of size, it can occupy when placed on the screen. A large weight value allows the liberty of stretching to any void space in the parent layout. Weight should be assigned considering proportion among the view elements.

Equal distribution :
If you intend to create a linear layout with equal distribution, i.e. each component occupies a space an equal space, follow these steps:

  • Set the android:layout_height of each view to “0dp” (for a vertical layout).
  • Set the android:layout_width of each view to “0dp” (for a horizontal layout).
  • Finally, set the android:layout_weight of each view to “1”.

Unequal distribution :
If you have to design a linear layout in which each view element occupies a distinct amount of space then, assign the weight to the view elements proportionally. For allowing any view element to occupy the entire remaining space, set its weight as zero.

Image depicts an Android layout with weight=0.5
Image Source: Infobrother

2. What is the significance of the attribute android:weightSum=”10″ in the XML code?
If the weightSum value is not specified explicitly in the XML code, by default, it is computed as the submission of the weight of individual layout components. In reference to the Android documentation, android: weightSum defines the maximum weight sum. weightSum is considered to be the total value of all the layout size ratios.

For convenience, weightSum accepts floating values, as in 1.2, 3.4, and so on for expressing ratios. WeightSum divides the entire parent layout into pre-defined values; the proportion allotted to each layout depends on its individual weight value, and the total WeightSum of the parent layout or container. This is a very efficient method for accurate allocation of space in various layouts, rather than assigning the height and width values after tedious mathematical calculations.

For instance, if you can a child layout to occupy 70% of the screen, then set its weight to 0.7 and the weightSum of its parent layout to 1.0.

Image depicts the layout structure with and without the weight attribute
Image Source: InfoBrother

3. What is tools:context=”.MainActivity” included in the layout file?
In the above-mentioned XML layout code, the prime need of tools: context is to specify which activity or fragment is synced with the layout file, by default during the definition of the layout. The activity class name is added in this attribute, using a dot prefix, analogous to the syntax used in the manifest file.

If you add the tools: context explicitly during definition, the Android Studio automatically creates an instance of the layout while preview, this eliminates the hassle of setting the preview settings manually, later. Themes need to defined in the Manifest file as they are associated with activities, whereas one layout file is associated with several activities.

Furthermore, the tools: context XML attribute is used for locating the accurate location invoking onClick handlers for performing operations or for rendering the action bar. Summing up, we can conclude that tools: context attribute is appended in the root element of the layout file and provides information about the activity or fragment, to which the layout file is associated. It is used for a smooth preview of the current layout and views elements.

4. Why is the need for android: scrollbars = “vertical” attribute?
To make a text View scrollable without enclosing it in the Scroll View tag, the scrollbars are added in the XML code. The Scroll View is used for vertical scrolling. Alternatively, for horizontal scrolling, you can set the scrollbars to attribute in XML to horizontal. 

You can even add custom shapes for scroll bars by creating shapes in drawable resource files and add solid for colours and stroke for borders. This is used for overcoming the limitation of the Scroll View that allows only a single child inside a layout. This also comes with an advantage of horizontal scrolling also, which is not permitted in the usual Scroll View.

Phase Two: Creating the Java Class

The Java class needs two additional classes the FileInputStream and the FileOutputStream for reading and writing from memory Input/Output. In addition to this, two alert dialogs are added to the code for taking the user’s confirmation the user clicks on the save and the new button. Just after the Button and the EditText objects are created, we add onClickListerners to the three buttons and call the respective methods.

Image depicts the OPEN File dialog box.
Image Source: Android Emulator
Image depicts the SAVE File dialog box
Image Source: Android Emulator
package com.example.notepad;
import androidx.appcompat.app.AlertDialog;
import android.app.Activity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.FileInputStream;
import java.io.FileOutputStream;


public class MainActivity extends Activity {

    Button newButton,saveButton,openButton;
    EditText text;

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

        newButton=(Button)findViewById(R.id.newButton);
        saveButton=(Button)findViewById(R.id.saveButton);
        openButton=(Button)findViewById(R.id.openButton);
        text=(EditText)findViewById(R.id.text);
    }

    public void buttonAction(View v) {
        final EditText fileName=new EditText(this);
        AlertDialog.Builder ad=new AlertDialog.Builder(this);
        ad.setView(fileName);

        if (v.getId() == R.id.saveButton) {
            ad.setMessage("Save File");

            ad.setPositiveButton("Save",new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    try {
                        FileOutputStream fout=openFileOutput(fileName.getText().toString()+".txt",MODE_WORLD_READABLE);
                        fout.write(text.getText().toString().getBytes());
                    } catch (Exception e) {
                        Toast.makeText(getApplicationContext(), "Error Occured: "+e,Toast.LENGTH_LONG).show();
                    }
                }
            });

            ad.setNegativeButton("Cancel",new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });

            ad.show();

        }

        if(v.getId()==R.id.openButton) {
            ad.setMessage("Open File");

            ad.setPositiveButton("Open",new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {

                    int c;
                    text.setText("");

                    try {
                        FileInputStream fin = openFileInput(fileName.getText().toString()+".txt");

                        while ((c = fin.read()) != -1)
                        {
                            text.setText((text.getText().toString() + Character.toString((char) c)));
                        }
                    }catch (Exception e) {
                        Toast.makeText(getApplicationContext(), "Error Occured: "+e,Toast.LENGTH_LONG).show();
                    }
                }
            });

            ad.setNegativeButton("Cancel",new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });

            ad.show();
        }

        if(v.getId()==R.id.newButton) {
            text.setText("");
        }
    }
}

FAQs for implementing the Java Class

1. Why do we import the FileOutputStream class and create its object?
FileOutputStream is an output stream used for writing data to a File or a FileDescriptor, in the case of our Notepad app we employ it for creating text files with .txt extension. The FileOutputStream can also be used for manipulating unstructured data consisting of raw bytes as in images, audio, or video files.

FileOutputStream class is a subset of the superclass: OutputStream, it becomes very convenient to convert bytes of data into the text as PDF, document or excel file.

The popular method of the Java FileOutputStream:

  • close(): It is used for closing the file output stream, it destroys the object and de-allocates all the resources instantly.
  • finalise(): This method exits the file connection and ensures that the close() method has been invoked.
  • getFD(): This method is used for returning the FileDescriptor object that represents the connection to the specific file in the file system being used by this FileOutputStream object.
  • write(byte[] b): This method writes b.length bytes from the specified byte array to this file output stream.
  • write(byte[] b, int off, int len): Writes len bytes from the specified byte array starting at offset off to this file output stream.
  • write(int b): Writes the specified byte to this file output stream and implements the write method of OutputStream.

2. Why do we import the FileInputStream class and create its object?
Java FileInputStream class is inherited for obtaining input bytes from a file. It is used for reading byte-oriented data, that is, streams of raw bytes, for instance, image data, audio, video, etc. In our notepad app, we use it for reading character-stream data. Alternatively, for reading a stream of characters, you may also use a FileReader class. But, the methods in this class are quite sparse, therefore the prior one is used.

3. What is the difference between a toast and a dialog interface?
Toast Notification is a short message that shows up for some time, at the bottom of the screen and after a certain time lapse, disappears on its own. The duration of the toast can be set as Long or short, depending upon the importance of the message. Usually, a toast contains feedback or an acknowledgement of the user’s last action. In registration forms, toasts are used for prompting the user while entering data. The user cannot restrict the toast and influence it’s working. (Alprazolam)

Whereas, an alert dialogue box appears at the centre of the screen as a window and obscures all the pre-existing UI components. There are a set of options coded into buttons, it is mandatory for the user to explicitly pick any option else all the operations are withheld. Alert boxes are used for recording the user’s confirmation for any irreversible, two-fold, or expensive operations.

Image showing the difference between AlertDialog and Toast
Image Source: spaceOtechnologies

4. Why do we have to code the setPositiveButton and setNegativeButton distinctly?
An alert dialogue consists of three components-the title, the content, and the action buttons. An alert dialogue box can have a single button,two-button, or three buttons. The three action buttons are – positive, neutral, and negative action buttons. The colour of the action buttons can be changed by changing the accent colour of the app.

An alert dialogue restricts all the operations of the activity until the user confirms by pressing the positive or the negative button. In our code, we have used the positive and negative buttons as we want to give only two choices to the user-either to save the file or cancel the action. When the user clicks on the positive button, the editText is saved as a text file, whereas the negative button is used for discarding the operation.

Try implementing the code yourself and extend the functionalities by adding adapters or fragments. The more you practise the more you grow. However, if you stumble across anything you can refer to my GitHub repository

Liked reading this article? Want to create more dynamic applications on your own? Take a look.

By Vanshika Singolia