Giter Site home page Giter Site logo

07-composite-observer-android's Introduction

This is an assignment to the class Programmieren 3 at the University of Applied Sciences Rosenheim.

Assignment 7: Composite and Observer

First of all:

Don't panic

In this assignment, we will use the composite and a variant of the observer patterns.

Setup

  1. Install Android Studio.
  2. Create a fork of this repository (button in the right upper corner)
  3. Clone the project (get the link by clicking the green Clone or download button)
  4. Import the project to your Android Studio; it behaves almost the same as IntelliJ.
  5. Check the Android Studio guide to create a virtual device and run the empty app for the first time.

Note: It is absolutely essential that you do these steps at home -- you will have to download a ton to get started.

Composite Pattern

Although we will not directly implement the pattern, it is the cornerstone of the Android user interface library. The base class of anything that you see in the app is (at some point) android.view.View (documentation). A view can either be a "primitive" such as a Button or a TextView, or a container class that contains other views, such as a ListView or a ConstraintLayout. In user interface language, these views are also called widgets, and Android provides a large collection of standard widgets such as buttons, selectors, text inputs, etc. When you design an activity (a certain "view" of the app), you will make a hierarchical composition of such widgets.

Note: It is also possible to design your own widgets, but that is a topic for another day.

The following wireframe shows how the app should look like when we are finished.

Wireframe

As you can see, it is a rather simple layout; it is a hierarchical composition of a main app view, button, checkbox and a list view, which contains the individual dishes.

- main app view
  + button "Refresh"
  + checkbox "vegetarian only"
  + list view
    * dish 1: "Rinderroulade ..."
    * dish 2: "Hühnchenfilet ..."
    * ...

In Android, layouts are created by writing this schema in a specific XML dialect. These layout files are stored in app/res/layout, e.g. activity_main.xml. When you run the app, it will start the main activity, in our example MainActivity. Usually, the onCreate method is overwritten:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // this will inflate the layout from res/layout/activity_main.xml
    setContentView(R.layout.activity_main);

    // add your code here
}

Where the static variable R.layout.activity_main refers to the file app/layout/activity_main.xml, and the setContentView will inflate the layout based on the content of this file.

So how are the components layed out on the screen? If you look at the Android docs for design recommondations you'll notice that the ConstraintLayout is currently the preferred way to layout Android apps. So in the example above, main app view is actually a layout container, and in our case, we will use ConstraintLayout (which should be the default).

Basic Layout

Open the activity_main.xml; note that you can use the design mode (WYSIWYG) or text mode, to write the XML code manually.

Using the editor, add a Button, CheckBox and ListView to your ConstraintLayout.

Make sure that each element has an id associated with it. You will find it on the very top in the attributes bar on the left (WYSIWIG), or by setting the android:id="@+id/<some_id>" attribute; note the +, it signals that you define the id, rather than referencing it.

Now we need to define the geographical relation of the components. Change to the text mode of the editor, and add the appropriate attributes to the elements. For example:

app:layout_constraintStart_toStartOf="..."  // start is the "left" side of the component
app:layout_constraintEnd_toEndOf="..."      // end is the "right" side
app:layout_constraintTop_toTopOf="..."
app:layout_constraintTop_toBottomOf="..."

Instead of the ..., write identifiers of other elements, e.g. parent for the enclosing container, or @id/<some_id> (note the absence of +, since we're referencing).

  • As we want to interact with both of them afterwards set ids. (Think of a naming convention you'll remember later when you need the ids.)

For example, the CheckBox (see documentation) could be defined as

<CheckBox
    android:id="@+id/checkBox1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:text="Only vegie"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/button1" />

On the "Java side" of the app, you can use the findViewById() method to get a handle on a widget, for example

CheckBox cb = (CheckBox) findViewById(R.id.checkBox1);

ListView Elements

A ListView needs a layout to be used to display the elements in the list. In our case, we want to display a simple line of text. Create a new Layout resource file in the layout folder, name it meal_entry.xml and set the Root element to TextView.

The key to populating a list view is the BaseAdapter provides the data for a list view. In our case, we will use an ArrayAdapter, that allows us to render the content of an array. According to the documentation, it uses the toString() method of each object in the array and place it into a TextView. Once you hand this adapter to the list view, it will update its display.

ListView lv = (ListView) findViewById(R.id.listView1);

lv.setAdapter(new ArrayAdapter<>(
	MainActivity.this,     // context we're in; typically the activity
	R.layout.meal_entry,   // where to find the layout for each item
	new String[] {"Hello", "world"} // your data
));

Go ahead and try displaying a few String items in your app.

Observer Pattern

The activity (app) is pretty static for now since it does not allow for any user interaction. Android (and in fact: most GUI libraries) use a variant of the observer pattern to model user input such as clicks (taps), key presses, etc.

The idea is to provide a callback to a widget that has the desired action. Depending on the version of Java you use, you can set it either using a lambda expression, or by using an instance of the View.OnClickListener instance.

Button btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
   		// do crazy stuff when you click the button :-)     
    }
});

Go ahead and add an OnClickListener to your Button that causes the list to display the string "Hans" and "Dampf".

Tips and Good Practice

As you can see, programming user interfaces can quickly become quite a mess. It is good practice to make your code modular and reusable. Here are a few tips:

  • Cache references to widgets by storing the result of findViewById in a local variable or an attribute; obtain the references after inflating the layout in the onCreate() method.
  • Use anonymous inner classes (or lambda expressions) only for very short methods; move longer logic to separate methods or even classes.
  • For heavy lifting such as file IO, sorting, network etc. use threads (more later).
  • Use an advanced toolkit such as Butterknife to reduce the amount of boilerplate code to write.

Business Logic

With all of the user interface and interactions in place (at least in principle), we're ready to add actual logic (and data) to our app.

OpenMensaAPI

The OpenMensa API is an API to retrieve the menu of a certain day of a certain canteen. In the previous assignment, we used Retrofit to interact with the API. This time, we will use it again -- but don't worry you won't have to implement any TypeAdapters or anything else, it's straight forward this time.

  • Add the method getMeals to the interface OpenMensaAPI (as shown in the following UML). Make sure to use the correct annotations to make the proper call.
  • Complete the test in the class OpenMensaAPITests to ensure that your implementation is working correctly.

API spec

Hints:

  • As shown last week, parameters of methods have to be mapped to parameters in the annotation. The inline comment in the interface shows the actual query we want to produce, which requires canteen id and date (Rosenheim is 229).
  • The Meal class is provided.
  • Check out the setup() provided in OpenMensaAPITests: it adds an interceptor to Retrofit that will print out every request (and response) on the logcat.

Putting the Pieces Together

Change your code so that once you click the button, it retrieves today's menu, and renders the dishes' names on the list view.

Hints:

  • You can get today's date from the Calendar API using the SimpleDateFormat helper class:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
String today = sdf.format(new Date());
  • You will need to map the list of Meals to a list of Strings.
  • Android forbids running network code on the UI thread; use the Call.enqueue() instead of the Call.execute() method, and pass it a Callback<> handler.
  • In the network callback, make sure to check if the response was successful, by checking response.isSuccesful() (which returns true for 2XX codes).

Your app should now be able to display what you can eat after this assignment or what you might have eaten before this assignment!

Filter the Meals

The last part is to implement the filter for vegetarian meals only. Unfortunately, the API does not expose any marker so we will use a simple workaround: a meal is vegetarian if none of the notes contains the string "fleisch".

Note: The solution is really simple, no regular expressions or alike needed.

  • Add a isVegetarian() method to the Meal class using the above workaround.
  • Change your code so that it filters the results if the checkbox is checked.

Note: Kudos if you can also trigger a refresh if the checkbox selection changes.

07-composite-observer-android's People

Contributors

prskr avatar sikoried avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.