about
For those reading this and not having read part 1, this is just a compilation of my personal experiences programming and publishing my first app. While eventually I would like to get good enough at writing to write tutorials, for now I am just practicing using my recent experiences. Thank you all for your time.
Coding
My app OWL Flashcards level 1 en - cn uses a total of 6 screens, in this part I will be going over some of the screens and explaining how I built it and some improvements I made along the way.
Title Screen
The title screen is composed of text views, image views, and a button for going to the next screen. They are all inside a linear layout(vertical or horizontal depending on the orientation) and that linear layout is wrapped in a scroll view inside of a constraint view.
I initially set up the scroll view to try and work around creating multiple screen layouts for different orientations and dimensions... it did not work. I eventually went and created a separate layout for every needed dimension, but as I was writing this, noticed I forgot to clean up the old scroll views... oops.
The title screen is not the parent activity to any other screens, because I only wanted it to appear briefly while the user enters the app, this screen was designed to introduce basic info about the app when ever it was first started and after that it was not necessary. The only significant programming I did on the title screen was that the very first time the app was started a pop up custom alert and toast message would be displayed.
The custom alert dialog was for entering a users name and saving it into the default shared preferences, so that for every following time onCreate() is called for the activity a welcome back, 'display name' custom toast was shown.
The alert init code:
AlertDialog.Builder alert = new AlertDialog.Builder(this);
final EditText edittext = new EditText(this);
alert.setMessage(R.string.set_display_name_alert_message);
alert.setTitle(R.string.welcome_to_owl_flash_cards_alert_title);
edittext.setText(mPreferences.getString(SettingsActivity.KEY_PREF_EDIT_USER_NAME, ""));
alert.setView(edittext);
It creates an edit text and if a user name was already in the default shared preferences, it displays that username so that it can be overwritten. After that it sets only the positive alert button.
alert.setPositiveButton(R.string.alert_dialog_ok_button, new DialogInterface.OnClickListener()
inside the positive alert it saves the new user name and sets the get_user_name preferences to false so that the alert doesn't pop up every time.
This allowed me all the data I would need to populate my greeting message for returning users.
Toasts are interesting, cause googles guidelines usually say to remove toasts before publishing, so I tried to find an alternative like snackbar for my popup message, but it's color library had trouble with cardviews color library, and I decided I wanted cardviews color library, so I couldn't use snack bar.
The solution was a simple hack where you generate the toast message, but change its layout. To do this you create an xml layout, inflate it with the layout and a view group.
View layout = layoutInflater.inflate(R.layout.welcome_custom_toast_message, (ViewGroup) findViewById(R.id.custom_toast_welcome_message));
then you can create a new toast, set the gravity(optional), the duration, and pass in your layout.
toast.layout(layout)
and show it. it is a remarkably easy and simple fix, but I still have to look into why toast messages are generally frowned upon in published apps.
Main Screen/Menu Screen
The menu screen consists of 10 buttons, (9 of which lead to the same fragment activity but with different intent data for populating it, and the other 1 leads to its own activity which is essentially a long screen of text.) It also has an options menu to go to the settings activity or the search activity. The main menu is also used as the parent activity for nearly all screens in my app.
The 9 major buttons were different categories of flash cards for easier reading, the way I handled this was to pass in a public static final int into the intent extras so that when the new activity received the intent it populated itself using the correct data. I use to have more then one activity handling each button, but after taking my certification test through google, I realized I could simplify the app greatly by using Android ViewModel correctly.
The Menu screen has two interesting code segments.
First I wanted my buttons to be perfect squares, so that when they scrolled up and down the screen they were nice and uniform. To do this I had to create a ViewTreeObserver object based off of one of my buttons, then I set a GlobalLayoutListener for it. Inside the GlobalLayoutListener I was able to use the buttons getMeasuredWidth() to get the final width and pass it into my other buttons as the height, so they would become square.
ex:
ViewTreeObserver buttonSizeObserver = mAnimalButton.getViewTreeObserver();
buttonSizeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mAnimalButton.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int width = mAnimalButton.getMeasuredWidth();
mAnimalButton.setHeight(width);
The whole reasoning for it, is when you are in oncreate the button widths are sometimes not set, so if you call the getMeasuredWidth() without the observer the width could be null, causing your buttons to dissapear.
The second interesting thing I had to do in this activity, was to init my database. I discovered through testing that the first time the app was installed if you went straight to the flashcard page they would be blank and you would have to either press up or back to go back to the menu and then you could try again. I assumed no user would want to have to go back just to use the app the first time and so the solution was to init the database early so that any prepopulated data was ready for use when the user entered the next screen.
Flashcard Screen
So this screen consisted of 2 text view elements, 1 image view, and 1 image button element. The text views got populated with either the current English or Chinese word and its pronunciation spelling. The image view was one of two images based on what language the text view were in, and the image button was a play button to play the sound of the word from the text view.
To make each card feel like a flash card I had to set an onClickListener() to my primary Viewgroup, this allowed me to change the text, and image views/buttons based on a user click/tap. I used a boolean for this to decide which language to use.
To make it so there was more then 1 card at a time I used a viewPager which was populated in the activity class. The view pager initiated my fragments by calling a static function named newInstance for the fragment, which then in turn called each ones own default constructor and returned the fragment. The static function also took in data passed in from the pager and placed it into a bundle so that the fragments onCreateActivity could parse it.
The Fragments bundle extras, caused me confusion at first because I created a database with multiple tables and tried passing in a unique table each time, this method forced me to have multiple fragments and pageadapters. when I simplified my database to 1 table but with a row for categories I was able to reduce a lot of code redundancy for creating the fragments.
I discovered while building this page that ViewObservers are fun and have multiple options for storing and recalling data in a database. While tutorials might be lacking a bit on them, a few android doc codes have enough details to get many people started.
Search Page
Android has a search widget and documentation to try and use it, But I wanted to try my hand at a custom view. So I simply created a layout for a button and edit text field, and attempted to put it into a custom view, got annoyed and put it directly in the search page instead. I've learned since then how to fix my problem I had with the custom view, but that is for a later blog post.
The search page used an interesting technique with LiveObservers interacting with the viewmodel and database.
It consisted of initializing a live observer, but also setting a second MutableLiveObserver with the search term. It did this so that the viewmodel class could keep the first LiveObserver as private, and not have to the user interacting with it directly, basically simplifying its job so that it could handle data changes more efficiently.
ex: search Activity
mViewModel = ViewModelProviders.of(this).get(CardViewModel.class);
final Observer> cardObserver = new Observer>() {
@Override
public void onChanged(@Nullable List cards) {}
mViewModel.setSearchTerm(mLastSearchTerm);
View Model Class
public LiveData> mSearchedCard =
Transformations.switchMap(mSearchTerm, searchTerm -> {
return mRepository.findCard(searchTerm);
});
private MutableLiveData mSearchTerm = new MutableLiveData<>();
public void setSearchTerm(String term){
mSearchTerm.setValue(term);
}
The reason for the LiveObserver for the search activity was so that the user could search more then once, and that I wouldn't have to use loaders and async tasks to retrieve new results or to store the previous results.
The concludes the major interesting points for coding my First Published App, Later I might add in a part 3 about the publishing side through google play.
Top comments (0)