24

Introduction to Android App Development With Kotlin: Input Validation and Naviga...

 5 years ago
source link: https://www.tuicool.com/articles/hit/aqmUfyV
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Most apps must implement some form of navigation. Whenever we want to do something in an app, it’s quite normal that we must navigate to another screen. Also, when an app takes in user input, it should be validated so that we don’t end up with blank records in a database. Those are the two things that we’ll be discussing in today’s episode of our journey towards effective Android app development.

Lesson 5

Input Validation

Before we accept user input, we must set the rules. For example:

  • The input may be either optional or required,
  • It may not consist of just empty spaces,
  • It must be of a certain length, minimum 1 character and maximum of say 20,
  • It may be either numeric or alphabetical,
  • It must be an email,
  • And so forth.

Because we are only accepting movie titles in our app for the moment, we’ll set the rules as follows:

  • A title is required
  • Minimum length of the input is 1 character
  • Maximum length of the input is 30 characters
  • Title consisting only out of whitespaces will not be accepted

If either one of the above rules is not satisfied, we’ll prompt the user with an appropriate message.

In the NewMovieFragment , inside our button listener, let’s assign user input to a read-only variable.

val input = editText.text.toString()

Let’s check if it’s empty, and if it is, we’ll let the user know that that’s not good enough. The code below will make sure that the first two rules are satisfied.

if (input.isEmpty()) {
    Toast.makeText(activity, "Title required", Toast.LENGTH_SHORT).show()
    return@setOnClickListener
}

Next, let’s make sure that the input is not longer than 30 characters.

if (input.length > 30) {
    Toast.makeText(activity, "Title too long", Toast.LENGTH_SHORT).show()
    return@setOnClickListener
}

We’re almost there. We stopped users from trying to submit empty titles as well as titles that are too long. However, there’s still a weakness in our validation method. If the user types in a few empty spaces, they bypass our rigorous set of rules, which is not desirable. This is where the trim() method comes to our rescue. We'll call it on the string fetched from editText . Here’s the official description of the trim() method.

“Returns a string having leading and trailing whitespace removed.”

val input = editText.text.toString().trim()

If the user tries to input “ “ (4 whitespaces), they will now be greeted by a “Title required” message.

This is what our complete listener looks like:

button.setOnClickListener {
    val input = editText.text.toString().trim()

    if (input.isEmpty()) {
        Toast.makeText(activity, "Title required", Toast.LENGTH_SHORT).show()
        return@setOnClickListener
    }

    if (input.length > 30) {
        Toast.makeText(activity, "Title too long", Toast.LENGTH_SHORT).show()
        return@setOnClickListener
    }


    Toast.makeText(activity, "$input entered", Toast.LENGTH_SHORT).show()
}

Now that we’ve successfully validated the user input, we should store it in a database. We won’t do that now, but we’ll come back to do it once we cover some basics about Room, which is coming up in one of the next few lessons.

Navigation

For now, let’s pretend that we have successfully stored the user input. What should happen next? In my opinion, the app should take the user to a screen listing all of their movie title entries. That means that we need a fragment that will list all of the items in the database. Again, there is no database yet, so for now, we’ll just create the fragment.

Create MovieListFragment in the same fashion as previously and let’s clean it up by removing arguments but keeping the listener. You should be left with the same as below:

class MovieListFragment : Fragment() {

    private var listener: OnFragmentInteractionListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_movie_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        btnAdd.setOnClickListener {
            listener?.goToNewMovieFragment()
        }
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is OnFragmentInteractionListener) {
            listener = context
        } else {
            throw RuntimeException(context.toString() + " must implement OnFragmentInteractionListener")
        }
    }

    override fun onDetach() {
        super.onDetach()
        listener = null
    }


    interface OnFragmentInteractionListener {
        fun goToNewMovieFragment()
    }

    companion object {

        @JvmStatic
        fun newInstance() = MovieListFragment()
    }
}

Notice that addBtn has a click listener attached to it, and when called, it triggers the OnFragmentInteractionListener method, goToNewMovieFragment() . Naturally, our MainActivity must implement this method. We’ll do that in a second.

Let’s adjust the layout file to make it clear what fragment we’re looking at when the app is running. This new fragment will use RelativeLayout instead of LinearLayout . The reason for it is that RelativeLayout gives us access to attributes such as layout_alignParentBottom , which allows us to put a button on the bottom of the screen, regardless of its height.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
                android:padding="12dp"
                tools:context=".MovieListFragment">

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:textSize="30sp"
              android:text="Movie List"
    />


    <Button android:id="@+id/btnAdd"
            android:text="Add new movie"
            android:layout_alignParentBottom="true"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

</RelativeLayout>

Let’s head to the NewMovieFragment to update the OnFragmentInteractionListener method, so its name is more appropriate, i.e. goToMovieListFragment() with no arguments.

interface OnFragmentInteractionListener {
    fun goToMovieListFragment()
}

Now, open the MainActivity as we have some refactoring to do and navigation code to write!

MainActivity must now implement two interfaces

class MainActivity : AppCompatActivity(), NewMovieFragment.OnFragmentInteractionListener,
    MovieListFragment.OnFragmentInteractionListener {

We must refactor the code in the onCreate method. Move it to its own method called goToNewMovieFragment() .

override fun goToNewMovieFragment() {
        val manager = supportFragmentManager
        val transaction = manager.beginTransaction()
        transaction.replace(R.id.flContent, NewMovieFragment.newInstance())
        transaction.commit()
    }

We will also require a goToMovieListFragment() method, which will be called upon successful storage of user input.

override fun goToMovieListFragment() {
    val manager = supportFragmentManager
    val transaction = manager.beginTransaction()
    transaction.replace(R.id.flContent, MovieListFragment.newInstance())
    transaction.commit()
}

Let’s call this method in the onCreate , as presumably, our users would like to see the list of their movies in the app upon startup.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)


    if (savedInstanceState == null) {
        goToMovieListFragment()
    }
}

Run the app and freely navigate between the screens! Make sure to test our validation code.

Conclusion

We’ve covered a lot of ground today. You now understand how to implement navigation between multiple screens and how to do basic input validation.

The complete working code can be found in my GitHub account here .


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK