DbTradeAlert for Android: Create the Basic Watchlist UI

First post in this series: Introduction to DbTradeAlert

Previous post: Create the Application

This post will create the infrastructure for DbTradeAlert’s main UI and provide some sample data.

Resulting Basic Watchlist UI

Resulting Basic Watchlist UI

Currently each section just shows a TextView as defined in app\src\main\res\layout\fragment_watchlist_list.xml. In DbTradeAlert each section will be a watchlist containing reports. Also an additional fragment to display each report is needed.

If you are using Git you’ll find that some files in the .idea directory change or get created automatically. Just check them in normally – preferably in a separate commit – and let .gitignore control which files to ignore.

Actually the first step is to remove the FloatingActionButton (FAB) as DbTradeAlert will not use it. The FAB is meant to prominently display an app’s main action but DbTradeAlert works in the background and doesn’t require any interaction.

1. Remove the FloatingActionButton

To remove the FloatingActionButton (FAB):

  • In WatchlistListActivity.java search for “fab” and delete the code creating and using the FAB
  • Remove the FloatingActionButton element from fragment_watchlist_list.xml
  • Test the app to make shure nothing broke and the FAB is gone
  • Optional: commit the changes

2. Add UI and Code to Show Reports

Currently each watchlist just shows a single line of text. This needs to be replaced with:

  • a layout for showing a list of reports
  • a layout for each report
  • the code required for both

Again let Android Studio do the work:

  1. In Android Studio’s Project view open the app folder’s context menu and select New | Fragment | Fragment (List)
  2. In the component configuration window
    1. Change Object Kind to “Watchlist” (the other names will change accordingly)
    2. Change Adapter class name to “WatchlistRecyclerViewAdapter”
    3. Click Finish

Unfortunately Android Studio’s generator currently generates buggy code for WatchlistRecyclerViewAdapter.java. To fix that:

  • Replace
    “import <PackageName>.ItemFragment.OnListFragmentInteractionListener;” with “import <PackageName>.WatchlistFragment.OnListFragmentInteractionListener;”
  • Replace “R.layout.fragment_item” with “R.layout.fragment_watchlist”
  • Test the app to make shure nothing broke – no visual difference because the generated components don’t get used yet
  • Optional: commit the changes

Using the generated components is only one change in WatchlistListActivity.java away:

  • In SectionsPagerAdapter.getItem() change “PlaceholderFragment.newInstance(…);” to “WatchlistFragment.newInstance(…)”

But running the app now would result in an error like

de.dbremes.dbtradealert.WatchlistListActivity@8746540 must implement OnListFragmentInteractionListener

This interface is defined at the end of WatchlistFragment.java and contains just a single method named onListFragmentInteraction(). As its comment says it’s the only means to communicate user interaction with the fragment – like tapping on a report – to the surrounding activity. Therefore each activity using that fragment needs to implement the interface.

To implement the interface in WatchlistListActivity.java:

  1. Add ” implements WatchlistFragment.OnListFragmentInteractionListener” to “public class WatchlistListActivity extends AppCompatActivity” which will result in red squiggly lines and a red bulb
  2. Click the red bulb and from its dropdown list select “Implement methods”
  3. In the “Select Methods to Implement” window click OK
  4. Leave the generated method empty for now
  5. Test the app – each of the watchlists will contain 25 “reports”
  6. Optional: commit the changes

As you probably found out the app needs 2 more adjustments:

  • The section titles got lost
  • The 2nd and 3rd watchlist have 2 respectively 3 columns

The section titles got lost because they were shown by fragment_watchlist_list.xml which DbTradeAlert no longer uses. To make them appear again add a PagerTabStrip to the ViewPager in activity_watchlist_list.xml like this:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" ... >
  <android.support.design.widget.AppBarLayout ... >
    <android.support.v7.widget.Toolbar ... >
    </android.support.v7.widget.Toolbar>
  </android.support.design.widget.AppBarLayout>

  <android.support.v4.view.ViewPager android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior">

      <android.support.v4.view.PagerTabStrip android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top" />

 </android.support.v4.view.ViewPager>

</android.support.design.widget.CoordinatorLayout>

To get rid of the unnecessary columns rename all “ColumnCount” variables in WatchlistFragment.java and the ARG_COLUMN_COUNT constant appropriately to “Position” and always use the LinearLayoutManager class

Now the generated components should work:

  • Test the app – section titles are visible again and the current section is marked; no duplication of “report” columns
  • Optional: commit the changes

With the infrastructure in place let’s adjust the sample data for watchlists:

  • In WatchlistListActivity.java change SectionsPagerAdapter.getCount() to return 2
  • In WatchlistListActivity.java change SectionsPagerAdapter.getPageTitle() to “CH Stocks” and “D Stocks”
  • Test the app – section titles are updated and only 2 left
  • Optional: commit the changes

And add some sample data for quotes as well:

  • In WatchlistRecyclerViewAdapter.java replace “DummyItem” with “String”
  • In WatchlistRecyclerViewAdapter.java replace “setText(mValues.get(position).id)” with “setText(Integer.toString(position))”
  • In WatchlistListActivity.java replace “DummyContent.DummyItem” with “String”
  • In WatchlistFragment.java replace DummyContent.ITEMS with sample data (see code)
  • Test the app – each watchlist now displays its stock symbols
  • Optional: commit the changes
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_quote_list, container, false);

        // Set the adapter
        if (view instanceof RecyclerView) {
            Context context = view.getContext();
            RecyclerView recyclerView = (RecyclerView) view;
            recyclerView.setLayoutManager(new LinearLayoutManager(context));
            List<String> symbols;
            if (mPosition == 1) {
                symbols = Arrays.asList("NESN.VX","NOVN.VX");
            }
            else {
                symbols = Arrays.asList("BAYN.DE","SIE.DE");
            }
            recyclerView.setAdapter(new QuoteRecyclerViewAdapter(symbols, mListener));
        }
        return view;
    }

3. Some houskeeping

First delete unused code:

  • Delete the DummyContent package and its import statements
  • In WatchlistListActivity.java delete onCreateView()
  • Delete fragment_watchlist_list.xml
  • In WatchlistListActivity.java delete the PlaceholderFragment class and update the comment in SectionsPagerAdapter.getItem()

After that refactor WatchlistListActivity.java. First rename SectionsPagerAdapter to reflect the business logic:

  • In SectionsPagerAdapter’s context menu select Refactor | Rename
  • Change the name to “WatchlistListPagerAdapter” and hit Enter
  • In the “Rename Variables” window click OK to change mSectionsPagerAdapter’s type
  • Update the class’ comments accordingly
  • Rename mSectionsPagerAdapter to “mWatchlistListPagerAdapter” the same way
  • Test the app to make shure nothing broke
  • Optional: commit the changes

Finally move WatchlistListPagerAdapter into its own file:

  1. In WatchlistFragment’s context menu select Refactor | Move
  2. In the “Select Refactoring” window just click Refactor
  3. Test the app to make shure nothing broke
  4. Optional: commit the changes

4. Dissecting the App Once Again

Most of the code required to show watchlists and reports was generated by Android Studio. Let’s look at how that code works.

Everything starts with WatchlistListActivity.java, DbTradeAlert’s main and only activity. WatchlistListActivity.onCreate() connects the layout’s ViewPager to a WatchlistListPagerAdapter instance. By using the adapter pattern an activity doesn’t need to know about the data or how that data is connected to the UI. A PagerAdapter provides the number of pages – watchlists in this case – available and their titles. It also creates the fragments – WatchlistFragments in this case – representing the individual pages. But each page could be a different type and a PagerAdapter is somewhat special in that it provides the actual data.

The WatchlistFragment class represents a watchlist with its reports. It tasks a RecyclerView instance with managing each reports’ view. RecyclerViews are advanced ListViews in that they only create the views actually visible to the user. And when a view becomes invisible they don’t delete it but keep it around until they need a new one. Then instead of creating the new view from scratch they just fill the old one with new data. Both mechanisms save time and memory.

The WatchlistFragment class uses the adapter pattern too. WatchlistFragment.onCreateView() connects the view to a WatchlistRecyclerViewAdapter instance which it connects to the data. WatchlistRecyclerViewAdapter’s job is to provide a quote’s view with data. But instead of accessing each TextView directly it uses a ViewHolder instance. The reason for this is the time it takes to find a view with findViewById() which quickly adds up when a user scrolls through a list. Storing those references in a ViewHolder keeps the scrolling experience smooth.

Another concept used by both WatchlistListActivity and WatchlistFragment is that of a saved instance state. This instance state is acquired automatically when Android decides to destroy the current instance. Destruction may happen for example to free system resources. But when the user presses the back button he expects to find the app to be in the same state he left it. Android will recreate that state – like contents of TextViews – automatically from saved instance state. The onCreate() handlers only receive that saved instance state as a parameter because you may want to use additional state information.

Next post: Add a Database

Additional Ressources

Advertisements
This entry was posted in Uncategorized and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s