RecyclerViews on Android

Given my background as a web developer, I have a strong bias towards the client does something and then the server does its thing and returns a full page to render.  Sure, there are plenty of exceptions especially now, but I was raised in development in the world of Java Server Pages with the server drawing out the whole page.  The biggest fundamental change as I have been developing the KitchenOS Android app is moving from that whole page write / refresh thinking into thinking along the lines that small things like a touch or a gesture can change just a small piece of content without re-writing the whole screen.  Were I actually building this piece out as a web page, I could have had these three simple screens done in a couple of days, maybe even a day.  Instead, I am trying to expand my horizons and teach myself something new while keeping my skills fresh.

Combining the new paradigm and rules within the Android platform, and you have the struggles I went through with building out a list view that would scroll within the confines of a screen.  Sure, it’s definitely “easy” stuff.  But learning how and why it works with some less than stellar documentation has made things take quite a bit longer.  It has been weeks of guessing and checking and then googling and reading the documentation more. 

But I finally did figure out how to use RecyclerViews within Android to create a scrolling list.  I even think I understand the how and why of the building blocks versus just copying and pasting code from the documentation (or Stack Overflow).

The rest of this post is my attempt to restate what I learned so I can be positive that I actually understand this to repeat the process in the future.

Problem Set: I am displaying the details of an overall recipe that I need to edit.  Part of that recipe’s details include two different lists: the ingredients and then the categories (or tags that someone has added to them).

Looking through the Android developer documentation, this is done leveraging RecyclerViews.  The RecyclerView class is a part of the Android Jet Pack Framework.  The RecyclerView provides the capabilities to load a long list of elements and scroll through them.  It will handle the majority of the memory management determining what part of the list needs to be in memory and what doesn’t.  Sure in something that I am building to only run for myself this might not matter, but I want to learn this right.

So how do I do this?  Just follow five simple steps.

Step 1: Create a new fragment

Step 2: Set up the RecyclerView within the Larger Fragment

Step 3: Set up the RecyclerViewAdapter

Step 4: Connect the RecyclerViewAdapter to the main fragment

Step 5: Load the Content into the Adapter

Five steps to get a scrollable list populated.  The third time I did it, it only took my 30 minutes.  Compare that to the first two which each took over a week.  I may have finally cracked the nut and understand what is happening and why.

Step 1: Create a New Fragment

This step was always pretty simple thanks to Android Studio.  Leveraging what Android Studio provides, I would go to the menu and go to Create a new Layout Resource File.  Android Studio ends up generating the actual resource file.  Then using the Layout Editor, I go in and create the layout for how I want each individual item within the list to look.  The only real tricky piece of this is to make sure that the restraints are set appropriately on the different pieces of the layout.

I always set up the fragment to have a Linear Layout at the root level.  I end up setting my height here to make sure the list item is limited to a certain size as I have tried using match_parent, but then it makes each individual list item the size of the container I want them to scroll within.

For the ingredient and category listings, I have set this to be 50dp.  Underneath the Linear Layout element, I create a Constraint Layout set to have layout_width and layout_height as match_constraint making sure it sits within the height limit I set on the Linear Layout.  Inside the Constraint Layout is where I end up building the overall look and feel.

Step 2: Set up the RecyclerView within the Larger Fragment

This step was always pretty simple just to hook up.  But what took me a while was to figure out how to get the layout I created in Step One to show up in the actual fragment it will be displayed in when the application is run.

The simple part was using the Layout Editor to add in a RecyclerView within the constraints of my layout.  That would immediately add in the default view that just said “Item 0, Item 1, etc.” with each on an individual line.  I didn’t like the way that looked, so I kept looking for ways to get my layout to actually show up, and I finally found it with Sample Data and the Android Studio “tools” namespace within the layout’s XML file.

Sample Data

Creating sample data was pretty simple within Android Studio.  All I needed to do was go through the menu bar and choose New > Sample Data.  From there, the sampledata folder was created at the root level of my project.  Adding new sample data, required me to create the JSON file representing the sample contents I wanted.  Simple once you know the format.  The format is a basic JSON object with a comment and a data object that represents the actual content.  The following snippet is a full sample set of data with ingredients.  This is saved within my sampledata folder  as ingredient_list.json.

{
  "comment" : "My sample list of ingredients for the layout.",
  "data" : [
    { "fullText" : "1/2 cup milk"},
    { "fullText" : "1 teaspoon cinnamon"},
    { "fullText" : "1 pound ground beef"},
    { "fullText" : "2 tablespoons brown sugar"}
  ]
}

The sample data is then applied to the project by adding the following element into the layout resource XML file.

tools:text="@sample/ingredient_list.json/data/fullText"

“List Item Layout” View

I don’t know if this is the official name, but it is my name for this idea.  Basically, making sure that the fragment I created in Step 1 is visible to me when I end up viewing the overall “major” fragment. I did a bunch of different Google searches that took me to the Android Documentation for the tools XML namespace in the layout resource XML file. This namespace allows me to show the look & feel of another fragment into my fragment.  In this instance, I can see my design for the list view within the ingredients and categories section when I am looking at the overall recipe layout.  It’s really simple to do, too. 

Add the following line into the appropriate layout XML file within the RecyclerView tag:

tools:listitem="@layout/list_item_category"

This snippet tells my RecyclerView to pull the layout I have called list_item_category into the view.  Now I see the sample data I set up and the list’s layout itself when I view the recipe_detail_fragment within using the Android Studio Layout Editor.  Sure, this has not impact from a pure functionality standpoint, but it matters from an aesthetic standpoint when developing, which is what I was going for.

Step 3: Set Up the RecyclerView Adapter

According to the Android documentation, the RecyclerView Adapter “creates views for items, and replaces the content of some of the views with new data items when the original item is no longer visible.”

OK, so what does that mean?

Within the RecyclerView context, Adapters store the actual contents of the list I am scrolling through and then provide the system with the element when they need to display it.  Simple, right?  In theory, yes, but when I read through everything you need to do it got muddled in my head.  But it really is pretty simple, you need to override three different functions:

  • onCreateViewHolder

  • onBindViewHolder

  • getItemCount

The getItemCount is pretty self-explanatory, that’s the count of items in the list / array you are wanting to display to users on the screen.  So return the length of the list.

The other two are a bit more complicated.

The onCreateViewHolder function creates the abstract view object that will be populated by each list item.  But what is View Holder?  Simple, it is the overall view that I created in Step 2 for the list item.  To handle this, Android Studio will create an inner class.  I called it IngredientViewHolder because I am working on the ingredient list.  This class takes the View that I created and allows me to create and fill the contents of its a sub-views.  In my ingredient list item view, it is currently just a single TextView displaying the ingredient name.  Within the IngredientViewHolder I created an instance variable ingredientNameTextView which houses my TextView.  The class is then returned to the caller, so now I have a ViewHolder returned to the application.

The onBindViewHolder function takes the current place the system is at drawing the screen and says, “ok, how do I actually put content in here?”.  Enter the View Holder.  In my case the View Holder was the IngredientViewHolder inner class.  The View Holder itself is passed into the function, and then I set its sub view content (my simple TextView) to the value of the current position in my ingredient list.

val item = ingredients[position]
holder.ingredientNameTextView.text = item.fullText

with(holder.lineItemView) {
    tag = item
}

Step 4: Connect the Adpater to the Main Fragment

Connecting a RecyclerView Adapter into the main fragment takes two steps:

  1. Setting up an empty adapter when the View is created

  2. Filling the adapter with the list of items when the content is obtained from the ViewModel

Both are straight forward.  But it took me a while to understand the difference between the two.  Within the onCreateView function of my RecipeDetailFragment (the fragment I use to display the full contents of a recipe), I needed to add the creation of the RecyclerView.

ingredientRecyclerView = myView.findViewById<RecyclerView>(R.id.recyclerViewIngredients).apply {
            setHasFixedSize(true)
            layoutManager = LinearLayoutManager(context)
            adapter = IngredientListRecyclerViewAdapter(emptyList())
}

This is pretty simple code. I end up using the larger view (myView which needs a better name I know) to get the sub-view, my RecyclerView.  From there, I leverage Kotlin to apply my layout manager (Linear in my case) and my adapter which is what I created in Step 3.  Notice here I end up filling this with an emptyList() because I don’t yet have the content I need to display.  I don’t load the content into the RecylerView until the system is ready to do so.

Step 5: Load the Content into the Adapter

The final step is to actually place the content I pulled from my backend service into the Adapter and display it on the screen.  For all other pieces of data, I have been leveraging the Live Data and View Model architecture provided as part of the Android Jetpack framework. 

In my architecture, I created a ViewModel associated with my Recipe Detail fragment containing the actual recipe I want to display.  The model for the Recipe includes all of the different attributes I identified such as title, ingredient list, category list, details, etc.  But how do I load that content into the Adapter?

With the LiveData architecture, this is very straightforward.  In the onActivityCreated function, I have the following code that loads the content into the two RecyclerView Adpaters I created.

recipeListViewModel.recipeList.observe(this, Observer { list ->
            var selectedRecipe = list?.filter{ recipe -> recipe.uuid == uuid }?.single()

            // Other code creating the fragment
            ingredientRecyclerView.adapter = IngredientListRecyclerViewAdapter(selectedRecipe!!.ingredients)
})

After that, the content is written to the screen from my list. 

Now on to manipulating it and performing actions against it like editing and enhancement and probably trying to figure out how to create an overlay within an Android app to perform some content editing.

KitchenOS: A Look Back and a Look Forward

It's been over a year since I really came up with the idea for KitchenOS, and not much has been actually developed. But I think as the year has continued on, I have ended up actually building out in my head what I want to do and learned quite a bit along the way specifically around Amazon Web Services (AWS), Kotlin, and Android programming. Since it's the start of the year, I figured now would be the perfect time to look at what I have accomplished and what I want to accomplish on this project in the future.

A Look Back

I admit it, I spend way too much time thinking about cooking and what I am going to make for dinner, and that drove the whole KitchenOS idea. KitchenOS, for lack of a better name right now, is an "operating system" that I would be able to use to power my cooking experience both inside and outside of the kitchen.

My overall cooking experience happens in four different places: (1) my kitchen, (2) browsing recipes on my Surface Pros, (3) at the grocery store, and (4) reading cookbooks on the couch. And while the actual process of cooking is the most important, it also misses the rest of what I do which includes meal planning, determine what every one in the family likes to eat (and that isn't easy with a toddler hanging around), and tryign to come up with new recipes that will excite me to cook and have the family eat. This is where KitchenOS came in, it gave me a project and I have a direction of what I want to do.

So what did I do last year? I thought a lot. I read a lot of different stuff on both Android and just general programming paradigms to keep my skills up to date. And then I actually spent some time building out some of the features of KitchenOS up to what I would consider prototype level.

Recipe Parser

Knowing that parsing recipes from different websites was going to be a main function of what I needed, I decided to start working on this piece first. It also had the major benefit to it that I was going to write it in Java, and I already am very familiar with Java given that it is the programming language that I use the most professionally, when I do still code. The learning points here for me were more around how to leverage "new ways" (ok, I know most of them aren't new to every one, but when I was coding every day Java 8 wasn't even around) to code in Java and then to build out an application leveraging solely AWS technologies, focusing as much as possible to make sure I am not making a monthly payment for a server which brought me to AWS Lambda and DynamoDB. Given that I can use the free tier right now, who knows if I will ever end up paying for this unless other people start to use the application.

Going back to the days of me programming new features for IlliniBoard all the time and I wanted to scrape Illinois box scores web scraping and parsing has been one of those things I always wanted to do. I just never really spent the time to actually do it, mostly because I don't think I actually understood the necessary way to do it and that I could make web calls and then just parse text. Don't ask me why I was a moron then, I just was. So now that I finally understand conceptually what it is doing, I started the process of building out my scraper.

The architecture for it is pretty simple: an AWS Lambda function that I call passing in a URL. From there, the Lambda function leverages the JSoup HTML parser to parse the data through a collection of services. Right now I only parse from one site, Food Network so this will have to change because my code is written pretty tied to the Food Network as this snippet (which is really the whole Lambda function outside of error handling) shows:

Recipe recipe = new Recipe.Builder()
        .ingredients(FoodNetworkRecipeScraperUtil.getIngredients(recipePage))
        .directions(FoodNetworkRecipeScraperUtil.getDirections(recipePage))
        .title(recipePage.title())
        .categories(FoodNetworkRecipeScraperUtil.getSiteCategories(recipePage))
        .needsEnhancement()
        .sourceUrl(in.url())
        .build();

recipe.save();

I have followed the Builder pattern pretty heavily in things I do on the side because while it adds a lot of boiler plate code, I think it is easier to read and use. And now if I want to add in some sort of compiler level restrictions I can do so within the Builder when I am making an object. Yes, I know I can do it with annotations through Lombok but I don't really like the idea of annotations generating a lot of the code I would be using. It feels like I am losing some of the control there, but I really haven't dug into it too much.

I stopped working with the Recipe Parser once I got to the point where I needed to manually enhance the recipe itself to clean thing up. That is when I started to dig into the next topic, building out something that I would use in a more traditional fashion to clean up the different things that I parsed.

So ending 2018, my recipe parser will perform the following tasks:

  • Take a URL from FoodNetwork.com and parse it
  • Add that data into the DynamoDB with some basic fields: categories, directions, ingredients, sourceUrl, title, uuid, and whether or not the recipe has been enhanced

This is what I like to call a "working prototype". I can also invoke the lambda function by leveraging the command line through Gradle which is how I was performing all of my "unit testing".

Recipe Editor

This was my first foray into any sort of Android development, and I can say it has kicked my ass. I am a web developer (from the late aughts) by trade, and with that comes some inherent thoughts in terms of how things are done, i.e. the user clicks a button to make a call to a server, the server performs the function and then redraws the page for the user. I know there is much more you can do on the front-end with technologies like React now, but I never got into them as the commerce world is very risk averse and trails the bleeding edge by quite a bit. To get into the world of native app development, I needed to have a problem to solve, and I finally did (albeit one that I could easily have written a web page for), enhancing the recipes that I have parsed from the web.

So I did something that I don't really normally do, I went right to the official Android developer documentation and read. It was really interesting and I probably spent a good month just reading and making "My First Android App" both for Android Things and just a traditional one that I ran through the emulator. It was an eye opening experience. The most interesting thing was getting into the Android Architcture Components and the different concepts there like LiveData, ViewModels, and Navigation Components.

Putting all of this together was definitely not easy, especially while at the same time I am attempting to teach myself Kotlin. Yep, I am a masochist.

So at times I struggled with Kotlin. At times I struggled with the documentation from Google. At times I struggled with the boiler plate code added by Android Studio not aligning to the documentation provided by Google. ALl of this caused for some trying times as I was continuing to push through. But over the Christmas week and last week I think I finally hit a break through in understanding the most basic elements of what I was doing. I finally have a working Android app that does the following:

  • Loads up a Main Activity with an Image
  • Click on the Image to call my RESTful service to get recipes
  • Display a list of Recipes on the screen
  • Click on a recipe to select it and go to a detail screen

Sure, there's a ton more. It is not even prototype level, but it is to a point where I now have samples in my own code that will let me move forward. I fought with this for probably two months, but I got it to a point last week where I said to myself "I finally get this, it makes sense." And I consider that a win heading forward.

A Look Ahead

So what's next? A lot.

Is there a plan? Not really, just a lot of ideas on what I want to build. That being:

  • A recipe browser that has all of the recipes I like from the Internet, things I have made, and the cookbooks I own
  • A meal planner that will allow me to plan and vary my family's meals throughout the week
  • Something that will help me plan out grocery shopping a bit better, be it a list of some sort of something else (this is really vague in my mind right now)

So I am goign to move forward. This week I hope to end up with a working prototype of the Android app that I have. Of course, all of this will be without an actual Chrome or Android Tablet to run it. Then I will probably want to move back to more of the web scraping and parsing, and then also do picture parsing from my cookbooks thanks to the services provided by the different cloud hosts.

I also plan to try to do more of this as I think it helps me actually think and plan things out in my head. I don't want this to be all KitchenOS, but when it comes to things that I think about which aren't my family or work (you know the stuff I won't blog much about), this is about it.

KitchenOS Development Blog: Choosing Dynamo DB

Not having to wake up in the middle of the night to feed a bottle to a baby has given me more time to continue to work on my side projects.  I also realized that now paying full price for a nanny here in New York is getting expensive, so I should be focusing on ways to cut expenses.  One of those ways for me is to actually plan out my breakfasts and lunches and bring them to work instead of spending about $30 a day on food.  Combine these things, and it makes me want to get KitchenOS working even more.

Last night I started to look at what I had and had not done.  Simple answer: I haven't done much.  I have a quick recipe parser done for the Food Network sites.  But of course when I went to use it, they changed their site and I had to update it.  

After that, I needed to persist it somewhere to access it from everywhere else.  Since I don't know the full structure of every recipe I will see, I decided to make sure that I can change the structure if needed and decided to use a document based database, specifically Amazon's DynamoDB versus leveraging a SQL-based database.  The only hiccup I sort of had was understanding the new API for DynamoDB, but it was relatively straightforward even though building the value map seems a little bit verbose.

As an example, here is creating a simple String attribute

itemValues.put("title", AttributeValue.builder().s(title).build());

I know there are some benefits to this, but this seems excessive.  I would have liked to be able to just put a String in here.  But then when I went to needing to add in the ingredient list, it made more sense when I could combine the different builders and get the AttributeValue object which would have been much more difficult without this structure in place.

itemValues.put("ingredients", AttributeValue.builder().l(
                ingredients.stream()
                        .map(ingredient -> AttributeValue.builder().s(ingredient.fullText()).build())
                        .collect(Collectors.toList())
        ).build());
Now, all I had to do to push this content to the database was building a simple request and executing it.

The one thing I am still looking at with this is if I have the code structured in the way I want to have it.  Right now, I have a save() method on the Recipe class which allows me to simply call recipe.save() to save this current recipe instead of invoking a whole other data access layer.  But what if I need that in the future?  Though last night I decided that if I needed that in the future I can change.  That is the joy of not having to plan everything out.

Open Question from Last Night

  • How will I structure my shared code across my different modules?  Right now, that is future John's problem.  I can pull things out of the recipe scraper when I need them in a different application.