首页 > > 详细

辅导 Project 2C-DreamCatcher Complete辅导 Java程序

Project 2C – DreamCatcher Complete

DreamCatcher App (Part 3 of 3)

In this third of three parts for this project, we will extend the app further by adding the following new features:

1. Add a New Dream menu item to the DLF app bar (Chapter 15)

2. Implement a swipe-to-delete feature to delete Dreams from DLF

3. Show message with Butt on to Add Dream when DLF is empty (Chapter 15 Challenge)

4. Share a Dream via an implicit intent (Chapter 16)

5. Take a photo via implicit intent for display in DDF (Chapter 17)

6. Display a zoomed photo in a dialog when clicked in DDF (Chapter 17 Challenge)

7. Implement a RecyclerView to hold the DreamEntry list within DDF

8. Implement a swipe-to-delete feature to delete reflecti ons from DDF

Several of these features correspond closely to BNRG Chapters 15 through 17, as noted in the list above. However, there are several places where DreamCatcher requires significant changes from the approaches described in the textbook for CriminalIntent, with the most notable divergences detailed below by feature and chapter.

Getting Started

First, please be sure to retain a backup copy of your P2B project submission.

Next, you must get some new updates from the remote Git repository that you configured when starti ng Project 2A. To do this, select Git | Update Project and choose the opti on to Merge the incoming changes. These updates provide the following changes specific to Project 2C:

build.gradle (Module :app)

A new dependency has been added to support the testi ng process

P2CTest.kt

This new instrumented test file will help ensure that your app meets all of the P2C assignment requirements

PictureUti ls.kt

This uti lity functi on is directly from BNRG Listi ng 17.14, provided here for convenience

Feature 1: Add a New Dream menu item to the DLF app bar (Chapter 15)

The goal of this feature is to allow the user to add a new Dream from a menu in the DLF.

Start Chapter 15, and follow Listi ngs 15.1 and 15.2. The string should be "New Dream" then skip Figure 15.3 because we've already added ic_add as a vector asset (for adding reflecti ons). When defining the menu, please call it "res/menu/fragment_dream_list.xml" and ensure the Item id is @+id/new_dream.

Stop at "Creati ng the menu" and skip Listi ngs 15.3 and 15.4. The approach in BNRG was replaced in late 2021, and is now deprecated and difficult to use. Instead of overriding onCreateOpti onsMenu() we'll call addMenuProvider() on the acti vity from within onCreateView(). This functi on takes a MenuProvider and a LifecycleOwner:

MenuProvider: We'll define this as an anonymous MenuProvider object (meaning object : MenuProvider). The MenuProvider itself requires the implementati on of two functi ons:

onCreateMenu() creates the menu by inflati ng it with a provided inflater and menu.

onMenuItemSelected() will react to the menu clicks, and in this case there's only one item in the menu.

LifecycleOwner: This parameter is opti onal, but by default it uses the Acti vity lifecycle. Since we're using a single-Acti vity app, the menu would be retained even when this fragment is replaced by the detail fragment. We want this menu to be removed when the fragment is replaced, so we explicitly specify viewLifecycleOwner here.

We'll get started with just the basics, and no implementati on yet:

_binding = ...

requireActivity().addMenuProvider(object : MenuProvider {

// blank for now

}, viewLifecycleOwner)

return binding.root

The "object" above will be highlighted with an error. Hover and click "Implement methods" then select both abstract functi ons to provide placeholders with the required headers. Please remove the TODO notati ons.

In onMenuItemSelected() we can just return false for now:

requireActivity().addMenuProvider(object : MenuProvider {

override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {

}

override fun onMenuItemSelected(menuItem: MenuItem): Boolean {

return false

}

}, viewLifecycleOwner)

In onCreateMenu() add something similar to Listi ng 15.3:

override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {

menuInflater.inflate(R.menu.fragment_dream_list, menu)

}

Please run the app now, and follow Figures 15.6, 15.7, and 15.8 from the texbook.

Listi ng 15.5 - database/DreamDao.kt - Please change the name of the @Insert functi on to internalInsertDream(), and also add a @Transaction functi on to add all the entries along with the dream:

@Insert

suspend fun internalInsertDream(dream: Dream)

@Transaction

suspend fun insertDreamAndEntries(dream: Dream) {

// You must implement this on your own

}

Listi ng 15.6 - DreamRepository.kt - Same as BNRG, but remember to call the @Transaction function:

suspend fun addDream(dream: Dream) {

database.dreamDao().insertDreamAndEntries(dream)

}

Listi ng 15.7 - DreamListViewModel.kt - Same as BNRG, with different names:

suspend fun addDream(dream: Dream) {

dreamRepository.addDream(dream)

}

Now we can fill in the code to create the new dream and navigate to it.

Listi ng 15.8 - DreamListFragment.kt - The code is nearly identi cal, but it belongs in the onMenuItemSelected() functi on we created above

override fun onMenuItemSelected(menuItem: MenuItem): Boolean {

return when (menuItem.itemId) {

R.id.new_dream -> {

showNewDream()

true

}

else -> false

}

}

...

private fun showNewDream() {

viewLifecycleOwner.lifecycleScope.launch {

val newDream = Dream()

vm.addDream(newDream)

findNavController().navigate(

DreamListFragmentDirections.showDreamDetail(newDream.id)

)

}

}

Important: Please skip Listi ng 15.9. Please don't disable or delete the prepopulated dream-database asset. It's sti ll useful for us throughout this project.

Run the app and you should be able to add a new Dream from the app bar menu.

Feature 2: Implement a swipe-to-delete feature to delete Dreams from DLF

The goal of this feature is for the user to swipe any Dream to the left to delete the dream and its entries from the system.

First we must add a way to delete a dream (and its entries) from the database, which requires updati ng several files.

database/DreamDao.kt:

@Delete

suspend fun internalDeleteDream(dream: Dream)

@Transaction

suspend fun deleteDreamAndEntries(dream: Dream) {

// You must implement this on your own

}

DreamRepository.kt:

suspend fun deleteDream(dream: Dream) {

// You must implement this on your own

}

DreamListViewModel.kt:

fun deleteDream(dream: Dream) {

// You must implement this on your own

}

DreamListAdapter.kt - In order for the swipe handler to know which dream to delete, the DreamHolder class must be adjusted to expose the bound dream object as a public read-only property, set by its bind() functi on. This is the Kotlin equivalent to exposing a public gett er with a private sett er in Java:

class DreamHolder(private val binding: ListItemDreamBinding) :

RecyclerView.ViewHolder(binding.root) {

lateinit var boundDream: Dream

private set

...

fun bind(...) {

boundDream = dream

...

}

DreamListFragment.kt - Consult the API for ItemTouchHelper along with the autocomplete of the IDE to create a private functi on that returns a new ItemTouchHelper object:

private fun getItemTouchHelper(): ItemTouchHelper {

return ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {

override fun onMove(...): Boolean = true

override fun onSwiped(...) {

// You must implement this on your own

}

}

}

Hint: In the onSwiped() functi on above, the viewHolder must be cast to a DreamHolder type, using the as operator of Kotlin. To actually delete the dream and its entries, call vm.deleteDream() on the boundDream property of the DreamHolder.

Feature 3: Show message with Button to Add Dream when DLF is empty (Chapter 15 Challenge)

The goal of this feature is to display a brief message to the user if there are no dreams displayed in the DLF. There should also be a Butt on displayed to allow the user to add a new Dream (in additi on to the menu item in the app bar implemented above).

layout/fragment_dream_list.xml - Currently the RecyclerView is the top-level view of the layout. We need to place this RecyclerView within a ConstraintLayout component, so that we can add two new views that will overlap the RecyclerView. Because the RecyclerView height and width are both match_parent it doesn't require any constraints. However, the new view components will require you to specify constraints. You can review Chapter 11 for details about working with the ConstraintLayout.

Please use the following naming conventi ons for the two new view components:

TextView:

android:id="@+id/no_dream_text"

android:text="@string/no_dreams"

Butt on:

android:id="@+id/no_dream_add_button"

android:text="@string/new_dream"

app:icon="@drawable/ic_add"

DreamListFragment.kt - Set an onClickListener for the new butt on to call the showNewDream() functi on implemented earlier.

Hints: Before constructi ng the adapter, but within the collect{} block for dreams, set the visibility of the new views to either View.VISIBLE or View.GONE, based on whether the collected dreams list is empty or not. Also, don't forget to set a listener for the new butt on, so that it behaves exactly the same as the New Dream app bar menu from Feature 1.

Feature 4: Share a Dream via an implicit intent (Chapter 16)

The goal of this feature is for the user to use an app bar menu from the detail screen to share dream details with others via SMS, email, or copy-and-paste.

Chapter 16: Read – but please don't implement – BNRG up through the "Using a Format String" secti on.

Follow the concepts of Listi ngs 16.6 and 16.7, but shared dream will have the following format:

[Dream Title]

[Date String]

Reflections:

* [Reflection 1]

* [Reflection 2]

* [Reflection 3]

This dream has been [Deferred|Fulfilled].

Example:

Ride in a hot air balloon

Last updated 2023-09-10 at 11:12:13 AM

Reflections:

* One

* Two

This dream has been Deferred.

Formatti ng Notes:

The Date String should be the same format as in the last_updated_text of DDF

Only show as many Reflecti on lines as there are Reflecti on entries

If there are no reflecti ons, the "Reflecti ons:" header should be omitt ed

The joinToString() functi on is very useful for handling the reflecti ons

The last line should be omitt ed if the dream is neither fulfilled nor deferred

DreamDetailFragment.kt - BNRG uses a butt on placed in the layout for to trigger the implicit intent. Instead, we'll add a new menu to the DDF app bar using the same approach that we used to add a menu to the app bar of the DLF in Feature 1 above.

Use the Resource Manager to add a new Vector Asset from clip art called ic_share, and create another Menu resource called menu/fragment_dream_detail.xml. Please use the following naming conventi ons in the att ributes:

Item:

android:id="@+id/share_dream_menu"

android:icon="@drawable/ic_share"

android:title="@string/share_dream"

app:showAsAction="ifRoom|withText"

Listi ng 16.8 and 16.9 - DreamDetailFragment.kt - Follow the BNRG concepts, but place the listener code from Listi ng 16.8 into a helper functi on private fun shareDream(dream: Dream) instead. We'll call this functi on from the onMenuItemSelected functi on, aft er extracti ng the current dream value from the StateFlow held in the view model:

R.id.share_dream_menu -> {

vm.dream.value?.let { shareDream(it) }

return true

}

Aft er this, please read — but don't implement anything — starti ng from the "Asking Android for a contact" secti on through the end of this chapter.



联系我们
  • QQ:99515681
  • 邮箱:99515681@qq.com
  • 工作时间:8:00-21:00
  • 微信:codinghelp
热点标签

联系我们 - QQ: 99515681 微信:codinghelp
程序辅导网!