BracketCove / SpaceNotes

SpacesNotes, by Ryan M. Kay, with inspiration from Martin Fowler, Robert C. Martin, and Darel Bitsy for architecture.
199 stars 114 forks source link

Question About MVVM Blah Blah Pattern #5

Open ShinJaehun opened 9 months ago

ShinJaehun commented 9 months ago

Hello. I am a Korean fan who is studying hard through your Android Kotlin tutorial. First of all, I want to say thank you so much. Your lessons are very unique compared to other Android tutorials. So I also became very interested in the MVVM blah blah pattern you suggested. I'm trying to create a note app using your pattern, but I'm running into a problem. It wasn't much different from SpaceNote, so I thought it would be easy to implement, but I got stuck in this process and couldn't get any help. So I decided to leave a direct message like this. I'm really sorry for bothering you.

Activity: https://github.com/ShinJaehun/WinterNotes/blob/master/app/src/main/java/com/shinjaehun/winternotes/note/notedetail/NoteDetailActivity.kt

ViewModel: https://github.com/ShinJaehun/WinterNotes/blob/master/app/src/main/java/com/shinjaehun/winternotes/note/notedetail/NoteDetailViewModel.kt

viewmodel handle event and viewmodel observe also work properly. Please pay attention to the NoteDetailEvent.OnDoneClick() process when the save button is pressed in line 74 of Activity.

The text of the EditText can be easily passed, but the problem is passing the colorCode of the view or the file path of the image. At least I figured out how to get the colorCode from the View and passed it like that, but I don't know how to get the file path from ImageView. The file path is temporarily placed in a global variable and the value is changed each time an event occurs. It works fine, but I'm worried that if I do this, something is going against the pattern you suggested. (Of course, I know that I should never access it directly like viewmodel.changedNoteImage!! ;-) )

And I have one more question. How about passing the note object directly to Note Detail Event.OnNodeClick? I think it looks much better with the reduced arguments of ViewModel updateNote(), but I don't know how to write updateNote(). Unlike Note and RoomNote in the SpaceNote example, I am using noteId with @PrimaryKey(autoGenerate=true) instead of creationDate.

BracketCove commented 9 months ago

안녕하세요,

I am always happy to help people who ask good questions instead of asking me to do their work for them :).

  1. How to get file path from Android: This is actually quite annoying to do on Android, for good reason. It used to be that we could just actually get the real path of an image, but this was not great for security. As Android has gotten older and older, they make it harder and harder to directly access things outside of your own application's specific storage space.

Unfortunately I do not have a solution that I can guarantee will work for you on the latest Android versions. You will have to find a project which supports up to the Android OS verison you want to support, and look at how they do it. I think the general process is:

In any case, if it seems really hard to simply get the file URI, it isn't because you are bad at programming. Android really makes this hard to do, and they change the rules for how to do it every couple of OS version changes. Much of the advice explains old ways which crash under certain circumstances or just don't work at all. Welcome to being an Android developer ;)

  1. How about passing the note object directly to Note Detail Event.OnNodeClick? Let me answer this in general, which will hopefully be more useful to you. Any time you feel like passing a class instead of bunch of arguments looks better than passing just the arguments, try it out. Same thing if the opposite seems to be better, try it out. Early on in my career, I wanted to find the "correct way" and apply it in all situations. But this didn't end up being how things really were. In fact, the correct way depends entirely on the situation. Hopefully that makes sense!

Now, I think you are specifically about some difficulty with "mapping" from a Note to a RoomNote. I agree that it is good to keep these things separate as a general rule. You will need to write mapping functions which convert Note -> RoomNote when writing data, and RoomNote -> Note when reading data to the database. I like to write that code using Kotlin extension generally. You can see an example of that here: https://github.com/BracketCove/SpaceNotes/blob/master/data/src/main/java/com/wiseassblog/data/DataExt.kt

Anyways, I have not touched Room in quite a long time, so I don't recall how @PrimaryKey works in terms of instantiating a model which uses it. Good luck in the search.

Last point I want to make. SpaceNotes was something I wrote many years ago now, and we tend to evolve how we write apps every year or two. It is a snapshot of my ideas about software back at during that time; many of which have change. I just bring that up to encourage you not to follow everything I did closely. Take what is useful and ignore the rest.

I would like to spend more time and give you more detailed answers, but I am quite busy. Hopefully at least something here was useful. 화이팅.

ShinJaehun commented 9 months ago

Thank you for your kind explanation. I didn't expect you to give me this kind of detailed information. Because of my poor English, I hesitated to leave a question. Finally I feel relieved after reading your careful answer. In addition to what I asked, you also answered exactly what I was worried about. You understood everything thoroughly and gave me a lot of advice! Thank you very much!

I teach students in elementary school. I have experience developing an Android app that practices arithmetic rules for children who are not good at math.

https://github.com/ShinJaehun/SukSuk

Software development is really fun and valuable because the results I create can help others. However, since this is not my major, it is difficult to get help from those around me when I run into difficulties. (I should add that I live in a small rural elementary school far from Seoul.) So, each and every piece of advice is precious and grateful to me.

To be honest, I don't understand 100% of what you said, but I will try as you said. You are right. Rather than sitting there worrying about whether the path is right, I should try it and if it is the wrong path, I will be able to realize through experience why it is wrong. Thank you again. It's an awesome Sunday!

P.S. Your Korean sense is also great!

BracketCove commented 9 months ago

I am glad my response was useful.

I actually had a Korean roommate for nearly two years, so that is where my Korean sense came from. I can't speak it well except for swear words ㅋㅋㅋ.

ShinJaehun commented 9 months ago

ㅋㅋㅋ

ShinJaehun commented 9 months ago

Ah~ I'm sorry, I forgot the most important part! Note Detailed Activity's selectedImagePath is the object to be observed as LiveData of NoteDetailedViewModel, as shown in lines 42, 81, 373 and 409, but is used as a global variable.

Activity: https://github.com/ShinJaehun/WinterNotes/blob/1492427f1ad2261466b1547158cd6e2e117a401b/app/src/main/java/com/shinjaehun/winternotes/note/notedetail/NoteDetailActivity.kt

ViewModel: https://github.com/ShinJaehun/WinterNotes/blob/1492427f1ad2261466b1547158cd6e2e117a401b/app/src/main/java/com/shinjaehun/winternotes/note/notedetail/NoteDetailViewModel.kt

In the pattern you suggested, is it okay to treat the object of observation as a global variable like this? (As you advised, I succeeded in copying the image file to internal storage when loading it).

ShinJaehun commented 9 months ago

Hello. I am writing again because there are some ambiguous parts in my question I wrote before. Among the objects observed as an observer, I saved the file path in the global variable selectedImagePath and used it to save the note object. That's because I didn't know how to get the file path directly from Image View. This works properly, but no matter how hard I looked at the official Android development documentation, other MVVM tutorials, and the architecture you suggested, I couldn't find any example of temporarily storing an object to a variable in activity like this.

I was worried that I shouldn't do this and kept looking for a way. Today I found a very simple solution.

https://stackoverflow.com/questions/31651209/how-to-get-the-image-name-of-the-imageview-in-android

This is what I've done.

Activity: https://github.com/ShinJaehun/WinterNotes/blob/8f84fd0294558f5e9d09475c831563e6577c8a2c/app/src/main/java/com/shinjaehun/winternotes/note/notedetail/NoteDetailActivity.kt

Line 299: I save the file path using the image view's tag and pass the event with it to the viewModel. Line 68: When saving an object, the tag is loaded again from the image view. Of course, as you indicated, I need to copy the image to local storage to extract the file path.

Much better! Thank you.