in blog

The Dissection of an Android App: Dynamic To-Do Lists and RecyclerView.

Let’s begin the dissection of my Android app from the feature that I also implemented first: A dynamic to-do list.


This is how the List looks like in the app

The main use-case for this app, would be the use of a to-do list that contained all the required steps for their Erasmus paperwork and processes. So, at its core, this is a to-do list app. Each step would have to also contain useful (but succinct) information, and be presented in a user-friendly way, that gave clear feedback about the status of each step.

To be able to follow from here on, I believe you should have a good understanding of Java and at least some understanding of Android Activities.

The first thing that I did back then, was to create a ListView. That’s the old way of doing lists. ListViews are present on most UI frameworks, even in the old Java Swing (for desktop apps).

In an Android ListView, all you have to do is define the widget’s (ListView’s) existence in a Layout XML file, and then tell to it in the Java code, where it should take its data from.

Remember: Android seperates the UI design from the logic. You mostly define the Layout in XML files and do the normal programming in Java Classes called Activities. Sometimes, you can also define UIs from the Activity itself (and also define some logic in the XML Layout file) but the best practice is to keep those two seperated.

After that, the ListView class itself, gives you OnTouch and similar callbacks, to implement actions for when someone clicks on a List element etc.

Generally, a ListView is easy to implement, and does the trick for simple lists. But, back then, I was so full of passion for learning, that I decided to use the “better” solution, which is the newer RecyclerView.

RecyclerView is also a list widget for Android, that is built on the principle of smart recycling of Views, to aid performance in large populated lists.

What does this mean?

Everything that you see in Android’s UI, is generally part of the View Class. Every widget, button, list element, everything inherits from that base class. If you know Java and/or Object-Oriented programming, you probably understand this sentence.

Concerning List performance, that means that every element of a scrolling list, is composed of a specific View object. Each object, or to say it better, every object’s creation and destruction, has a performance overhead on the system.

If you have, let’s say 10 list elements, you should not care about List performance. This would also apply to my app, since I never have to deal with lots of data. But, I wanted to learn “the good way” of doing it.

So, if you now have 300 list elements, which could be eg. phone contact names, if you flick the list quickly to scroll to the bottom, the Android system would have to create a new View object for every Contact Name that would pass in front of you, on the screen.

If you flick hard enough, you can maybe scroll through all of that list in a matter of seconds. So, Android would have to create 200-300 View objects in three seconds, populate them, and also deal with the possible garbage collection of the View objects that are not currently visible.

This is most certainly a waste of resources, and can lead to a choppy framerate, which leads to bad user experience.

RecyclerView deals with this problem, by creating as many View objects are necessary to show a whole screen of elements (and maybe 2-3 more for when the edge list elements overlap) and when one of those goes out of View, it “recycles” it: it puts it in the place of the upcoming list element, and populates it with the respective data.

Even if you scroll your life away, you won’t ever have more active View objects, than the ones you can see in any give time.

Sounds good, doesn’t it? Well, yeah, until you find out (especially as a beginner Android programmer) that the RecyclerView class is much more Abstract, and you have to do much of the implementations already present in a ListView.

So, on one hand you get a good performance boost and you have a greater ability to customize the code to your liking, but, on the other hand, it has both a learning curve and needs much more coding to start.

RecyclerView (and ListView) are the parts that deal with the UI, with how the data is shown. Both of them need what is called an “Adapter”, which is a class that feeds them the necesary data from the defined source. There is also the Layout Manager, a component that deals with how the List elements are positioned in space, but let’s leave that for now.

The Adapter is the component responsible for deciding not only what data goes into each list element, but also is the one to recycle them, in a process called Binding. Excuse me if I’m making any mistakes here, but it’s really been some time since I did this.

In the following code, you can see the “heart” of the adapter:


public ErasmusStepsRecyclerViewAdapter(ArrayList<Step> myDataset) {
        stepsList = myDataset;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        final SimpleItemViewHolder vh;

        if (viewType == TYPE_ITEM_WITH_HEADER) {

            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.with_header_step_list_layout, parent, false);

            vh = new ItemWithHeaderViewHolder(view);

        } else {

            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.checkbox_list_for_erasmus_steps, parent, false);

            vh = new SimpleItemViewHolder(view);
        }

        return vh;
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {

        // Depending on the type of holder, we cast to the type to be able
        // to change the data in the view:

        final SimpleItemViewHolder itemViewHolder = (SimpleItemViewHolder) holder;
        final Step currentStep = stepsList.get(position);

        if (holder instanceof ItemWithHeaderViewHolder) {
            ((ItemWithHeaderViewHolder) itemViewHolder).
                    groupHeaderText.setText(currentStep.getGroup());
        }

        itemViewHolder.mTextViewTitle.setText(currentStep.getName());

        itemViewHolder.mCheckBox.setOnCheckedChangeListener(null);

        changeViewDependingOnStates(itemViewHolder, currentStep.getState());

        itemViewHolder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                if (isChecked) {
                    currentStep.setState(true);
                } else {
                    currentStep.setState(false);
                }

                changeViewDependingOnStates(itemViewHolder, isChecked);
            }
        });
    }

Somewhere else in the code, I have defined two different classes for List items, one with a header, and one without. So, when the Activity (the base of any Android running app) requests a new List element, the Adapter will return the respective one.

At onBindViewHolder, the Adapter will use the data that was given to it at the constructor, and populate each View with the required data.

Another thing that a developer must implement by himself, is onClick events. In the old ListView, these came ready and implemented into the provided Adapter. Here, we have to create our own callback function.

First, we specify a ClickListener interface in the Adapter class:

public interface ClickListener {
        void onItemClick(int position, View v);
    }

And also a method that modified a Class-local ClickListener variable:


private static ClickListener clickListener;

...

public void setOnItemClickListener(ClickListener clickListener) {
        ErasmusStepsRecyclerViewAdapter.clickListener = clickListener;
    }

This allows us to set a custom Click Listener for the RecyclerView Adapter, from the point of creation in the calling Activity. All of this, happens in the Activity called StepsListActivity that deals with everything that has to do with the Steps List.

// List View attachment with a process:
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.processRecycleView);

recyclerViewAdapter = new ErasmusStepsRecyclerViewAdapter(mErasmusProcess.getSteps());
RecyclerView.LayoutManager recyclerViewLayoutManager = new LinearLayoutManager(this);

recyclerView.setAdapter(recyclerViewAdapter);
recyclerView.setLayoutManager(recyclerViewLayoutManager);
recyclerView.addItemDecoration(new SimpleDividerItemDecoration(this));
recyclerView.setItemAnimator(new DefaultItemAnimator());

recyclerViewAdapter.setOnItemClickListener(new ErasmusStepsRecyclerViewAdapter.ClickListener() {
      @Override
      public void onItemClick(int position, View v) {

      intent = new Intent(getApplicationContext(), StepInfoScreen.class);
      intent.putExtra("step", recyclerViewAdapter.getItem(position));
      intent.putExtra("stepPosition", position);
      startActivityForResult(intent, ACTIVITY_INFO_REQUEST_CODE);
      }
});

As you can see, first of all we initialize a RecyclerView variable, and give to it the reference of the XML-declared RecyclerView, the one that will reside in the UI. This is totally Android API. Android knows that you want a RecylerView in that Activity, because you write that in the related XML Layout file. But, even if you write XML, Android on the back-end, creates Java classes.

So, using findViewById we are requesting a pointer to that certain Object defined in the XML file.

After that, we create a recyclerViewAdapter, using our own custom implementation. As you see, I’m passing a List (the mErasmusProcess.getSteps() returns a List of Step objects) as a parameter to the Adapter.

The LinearLayoutManager is ready-made from Google, so no need to worry about that.

Then, we set the Adapter and the Layout Manager to the recyclerView, aaaaand… we set the OnClickListener that we created. You can check the code for yourself, but how this works is:

  • We call the setOnItemClickListener method that we defined in the Adapter class.
  • We give a new ClickListener object as a parameter
  • But this new object is only defined as an Interface. That means, we also have to implement its required onItemClick method
  • So, we do that: When an item is clicked, we want the adapter to get the position of the clicked item and call another Activity that’s out of our scope for now…

The good thing with setting the OnClick in the Calling Activity, is that we can more easily reuse our Adapter code. We could set any onClick action directly in the Adapter code, but then, the code would be only good for this specific use.

The Holder parameter inside the onBindViewHolder method of our Adapter, refers to the View elements of our List. Every element of the List uses a ViewHolder to be shown to the user.

To define the Layout used in each for these List Elements, I have two different XML layout files. They are two, because I use two types: One with a header and one without.

These XML layouts reside in the /res/layout/ folder, and are checkbox_list_for_erasmus_steps.xml and with_header_steps_list_layout.xml. We’ll take a look at the first, since the with_header one just adds a small UI element and includes the other.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:orientation="horizontal" android:padding="@dimen/medium_padding_for_list_elements" android:layout_gravity="center" android:background="?android:attr/selectableItemBackground" android:focusable="true">

    <CheckBox android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/steps_list_checkbox" android:paddingRight="@dimen/medium_padding_for_list_elements" android:layout_gravity="center"/>

    <TextView android:id="@+id/stepTitle" android:textSize="@dimen/secondary_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="false" android:focusable="false" android:text="@string/step_title" android:textColor="@color/black" android:layout_gravity="center_vertical"/>

</LinearLayout>

This is sooo simple. Just a horizontal Linear Layout that includes a checkbox and a text. As you see, for each XML Tag that defines an Android UI Element (e.g. “CheckBox” or “TextView”), we have Attributes that define more specific characteristics.

The most important for now, is the ID Attribute, that is used in the Java Classes to Bind a specific layout element to our code. This is what happen in every findViewById in the code.

@Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {

        // Depending on the type of holder, we cast to the type to be able
        // to change the data in the view:

        final SimpleItemViewHolder itemViewHolder = (SimpleItemViewHolder) holder;
        final Step currentStep = stepsList.get(position);

        if (holder instanceof ItemWithHeaderViewHolder) {
            ((ItemWithHeaderViewHolder) itemViewHolder).
                    groupHeaderText.setText(currentStep.getGroup());
        }

        itemViewHolder.mTextViewTitle.setText(currentStep.getName());

        itemViewHolder.mCheckBox.setOnCheckedChangeListener(null);

        changeViewDependingOnStates(itemViewHolder, currentStep.getState());

        itemViewHolder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                if (isChecked) {
                    currentStep.setState(true);
                } else {
                    currentStep.setState(false);
                }

                changeViewDependingOnStates(itemViewHolder, isChecked);
            }
        });
    }

You saw this code before. It’s from inside our custom Adapter. What happens here is that we use the currentStep data (we haven’t seen that yet, but it’s just a simple Class used as a data structure), to populate the TextView with the Name of the Step, and set the state of the CheckBox depending on the status of the specific Step.

And all of these together, create a working List using a RecyclerView.

Thanks for reading up to here! You can find the full code here: https://github.com/polaralex/Android-ToDo.

In the next post, we will take a look at how to retain data between Android screen orientation changes. Because, yeah, even something as common as this, can be a real pain to solve in Android.

Comment: