Part 1: Recipe browser (1 pt)
- Create a new Project.
- Update the activity UI XML so that instead of ConstraintLayout, it consists of one (empty) vertical LinearLayout.
- Set the ID of the Linearlayout to
list_layout
.
- Add recipes to your string resources.
- Add the string arrays defined in this file to your res/values/strings.xml .
- in MainActivity.kt, populate the ListView with buttons
- Get the string-array of recipe titles and store them in a variable
resources.getStringArray(R.array.recipe_titles)
- Iterate over the array with a for-loop
- for each item, create a button and set its' text to the corresponding recipe title
- for each button, create a click listener, make the click listener call another function
openDetailsActivity()
(you have to create this new function, you can leave it empty right now) - add the button to your "list_layout" with addView(..)
- Get the string-array of recipe titles and store them in a variable
- Create a second Activity, call it "DetailsActivity"
- It should have two TextViews: for the recipe title and for the recipe contents
- Update the openDetailsActivity()
- It should create a new intent to launch DetailsActivity
-
Intent(this, DetailsActivity::class.java)
-
- add the title of the recipe to the intent as a String Extra (use intent.putExtra( ) ). For this, you should define a new string argument to openDetailsActivity()
- Launch the second activity with your intent using startActivity()
- It should create a new intent to launch DetailsActivity
- Test your application, you should be able to navigate to the 2nd activity, but right now it does not show the recipe title.
- in onCreate() of DetailsActivity, acquire the recipe title using intent.getStringExtra( .. )
- Update the recipe title TextView so that it displays the acquired value.
Let's also add the recipe contents to the DetailsActivity. We could pass the content as an Extra from MainActivity, but instead, we are aiming to keep the data sent in Intent Extras lightweight.
- Update the openDetailsActivity() so that it accepts 2 arguments:
- 1) title: String - the recipe title
- 2) index: Int - index of clicked button within the ListView. This index corresponds to the order in which the buttons were added to the ListView.
- Easy way to get this index is to update your for-loop to use Kotlin's
for ((index, element) in array.withIndex()
- Easy way to get this index is to update your for-loop to use Kotlin's
- pass the index as another Intent extra to DetailsActivity
- Update DetailsActivity:
- Using the index extra, load the right string resource from the R.array.recipe_values string array resource. Set that value to display in the textView.
Part 2: Adding a Rating System to our Recipe browser ( 1 pt)
- Update the DetailsActivy XML UI, adding
- A RatingBar widget
- A button with the text "Save"
- Add a click handler to the "Save" button, which gets the value of the rating bar as an integer
- create an empty intent object (
val resultIntent = Intent()
)in the click handler, put the index and rating bar value into the intent Extras. - finally, the click handler should finish the activity with a result
-
setResult(resultCode, resultIntent)
andfinish()
-
- create an empty intent object (
- Note: to obtain the results of a started activity, in MainActivity, you have to use
startActivityForResult(..)
instead ofstartActivity(..)
!
- Update MainActivity to react to DetailsActivity results:
- override
onActivityResult( .. )
- obtain the score value from the attached Intent data object.
- We want to update the recipe buttons color based on the integer rating. To do this, let's first add some color resources, update colors.xml to include:
- override
<color name="color_rating_1">#850000</color> <color name="color_rating_2">#F57C00</color> <color name="color_rating_3">#FBC02D</color> <color name="color_rating_4">#AFB42B</color> <color name="color_rating_5">#388E3C</color>
- in onActivityResult, set the color based on the rating like so:
// find resource id by using its name val color_id = resources.getIdentifier("color_rating_$score", "color", packageName) val color = ContextCompat.getColor(this, color_id) // find the button in the LinearLayout val button = layout_recipelist.get(index!!) as Button button.setBackgroundColor(color)
Part 3: Fragments (3 pts)
Create a new project with empty activity. Let's recreate the above app using fragments. Instead of starting a new Activity, all our UI will be inside one MainActivity. When created the activity will first add a fragment that displays the list of recipe names. When an item inside the fragment is clicked, the activity then removes the list fragment, and adds a details fragment to its layout.
- Make the MainActivity XML consist of one LinearLayout, containing one FrameLayout with id
fragment_container
- Create a new Fragment of type Blank, name it "ListFragment" . We will add the recipe titles to it programmatically.
- To create the Fragment, right click on your project files -> new - Fragment -> (Blank) Fragment. Uncheck the boxes shown in above image.
- Its XML should be an empty, vertical LinearLayout, set the id to
list_linearlayout
- in
onCreateView()
of ListFragment.kt, add buttons showing the recipe titles to the LinearLayout list_linearlayout, similar to the way we did in part 1. Instead of immediately returning the result ofinflater.inflate( .. )
, store it in a variable called view. The result of inflation contains the View object, including thelist_linearlayout
we defined above.- Your onCreateView() should look something like this:
val view = inflater.inflate(R.layout.fragment_list, container, false) // we will add buttons to the view here // .. return view
- add buttons by looping over the recipe titles, adding them with
view.list_linearlayout.addView(button)
. Set the button titles, but don't set a click listener yet.
- add buttons by looping over the recipe titles, adding them with
- We have programatically designed the look of the ListFragment, but we have not specified for MainActivity to show it yet.
- In MainActivity.kt, create a function called "displayListFragment":
fun displayListFragment() { val listFragment = ListFragment() val fragmentManager = supportFragmentManager val transaction = fragmentManager.beginTransaction() transaction.add(R.id.fragment_container, listFragment, "listFragmentTag") .commit(); }
- this function sets ListFragment to be displayed in the
fragment_container
FrameLayout which was defined in MainActivity
- this function sets ListFragment to be displayed in the
- Now, make MainActivity display the ListFragment using the above function by calling it inside
onCreate()
of MainActivity - Test the application and make sure MainActivity is displaying ListFragment
DetailsFragment
Now we will create the details fragment. When a list button is clicked, we add it to the fragment_container
similar to above, however, we need to make sure first remove the existing list fragment before adding the details fragment.
- Create another Fragment called DetailsFragment. The UI XML should have a Layout of your choice, which contains a TextView to show the recipe title and a button to close the Details view.
- Add a 2nd function called "displayDetailsFragment". Make it similar to the displayListFragment
above, but before calling the add() transaction add code to delete the listFragment like so:
// remove details view val detailsFragment = supportFragmentManager.findFragmentByTag("detailsFragmentTag") if (detailsFragment != null) transaction.remove(detailsFragment)
- Add a click handler to the buttons created in ListFragment, so that clicking on them calls the displayDetailsFragment function of the parent MainActivity.
- To access your custom method of mainActivity
val mainActivity = activity as MainActivity mainActivity.displayDetailsFragment()
- Verify that clicking on an item in ListFragment makes the Activity remove the ListFragment with a new DetailsFragment
- Let's update the displayDetailsFragment() method to pass extra arguments to the fragment (the recipe title)
- You can do this with fragment.setArguments()
val arguments = Bundle() arguments.putString("name", title) detailsFragment.arguments = arguments
- In the Fragment onCreateView, you can access those with:
val bundle = this.getArguments() val title = bundle?.getString("name")
- Finally, programmatically add onClickBehaviour to the save button in DetailsFragment XML.
- Don't forget that to get a reference to a XML-defined button inside fragment onCreateView(..) function, you should use
view.my_button_id
not justmy_button_id
!
- Don't forget that to get a reference to a XML-defined button inside fragment onCreateView(..) function, you should use
- Finally, programmatically add onClickBehaviour to the save button in DetailsFragment XML.
- clicking this button should call displayListFragment()
- However, you should update displayListFragment() to remove the detailsFragment before adding the ListFragment! (See how we did it above)