Skip to the content.
This is the Spring 2022 final website.

AS1: Doodle

Last revised: March 29, 2022
Assigned:
  • Code and Reflection: Wed 30-Mar
  • Peer Evaluations Fri 8-Apr
Due:
  • Code and Reflection: 10:00pm, Fri 8-Apr
  • Peer Evaluations 10:00pm Sun 10-Apr

HCI Goals:

  • Use abstractions to draw on screen
  • Properly place, size and orient components
  • Animate onscreen objects
  • Develop peer reviewing techniques

Android Goals:

  • Get familiar with Android Studio
  • Reading XML
  • Develop subclasses of View
  • Load image and drawable resources

Assignment Description

For this assignment, you will be creating an activity class which will allow you to create “Doodles” consisting of images, lines, and text. There are three screens that a user can switch between based on a tab bar at the bottom. The first and second screens are to help you test your implementation against the expected output. The third screen is for you to make your own creative doodle. Here is a video of our implementation runing on the Pixel 2 emulator (use API level 29):

You’ll notice that the relative position of everything is pretty similar even when the screen is rotated. This helps to make things work better on a variety of screen sizes, including whatever device you are testing on.

However, be aware that for actual grading, we will use a Pixel 2, and we recommend that you use a Pixel 2 emulator to compare the finished doodle against our screenshot to be sure you’re implementing everything right.

This assignment should take about 10 - 12 hours to complete, and may include learning how to use Git, Android Studio, and effectively reading documentation on your own. It is imperative that you use this assignment to build good habits for this course such as starting early, reading documentation, and asking for help from the course staff, particularly if you are new to this sort of programming ecosystem.

Note that a big learning goal of this class is to help you become more resourceful and independent as programmers. Unlike other courses, you may explore on-line Android and Stack Overflow resources as not all tools will be provided each the assignment specification. However we will expect that you cite your resources in your code, particularly if found help with something like Stack Overflow, and you are NOT to use prior solutions (shared from a past student or from an on line source). Failure to cite your sources or if you do use past solutions may be considered Academic Misconduct.

We strongly recommend that you reach out to the course staff for help if you find you are taking more than 12 hours programming on this assignment. We do expect that the workload may be less on future assignments once you feel more comfortable with the programming environment and self-learning.

The assignment is broken into the following four parts:

Part 1: Learning by Doing

Tasks:

Important: Remember to add/commit/push your code frequently to Gitlab (origin/main) to keep track (and a backup) of the versions of your work. We will also require your code is pushed to your repo if you need help in office hours.

Prepping your development environment

Follow the instructions for downloading and installing the Android development environment. Please come to our first section or office hours if you are having any trouble with your machine set up.

The course staff will create a Gitlab. You will need to clone this repository to your local machine in order to edit it with Android (reach out to the course staff on our Discussion board as soon as possible if you can not find your repository).

Understanding the starter code

We have provided you with the shell of an application which has the following class structure. The structure of the code is represented by the Unified Modeling Language (UML) diagram shown below. The symbols can be read as follows: + is a public field or method, # is protected, and - is private. Any method that is in italics is an abstract method, meaning is must be overridden in the child class.

classDiagram TabActivity <|-- AbstractMainActivity AbstractMainActivity <|-- Part1ActivityA AbstractMainActivity <|-- Part1ActivityB AbstractMainActivity <|-- Part2Activity AppCompatImageView <|-- DrawView DrawView <|-- CircleView DrawView <|-- TextView DrawView <|-- LineView class DrawView { +getBrush() #initFromParentCoordsPX() #onDraw() } class AbstractMainActivity { #PHONE_DIMS +onCreate() *doodle() }

TabActivity is used to switch back and forth forth between your Part1ActivityA, the provided Part1ActivityB and your Part2Activity using the blue navigation bar at the bottom of the screen. You must not edit this part of the code but if you are curious, this tabbing functionality is controlled by the code in the AbstractMainActivity#onCreate(Bundle) method. We are using a View called BottomNavigationView that gives the functionality of a navigation bar - the switch statement in each of the onCreate methods tells the program when to show which activity.

Note: The notation such as AbstractMainActivity#onCreate(Bundle) is a common shortcut to demonstrate a specific method in a specific class (here the onCreate method which takes a Bundle as parameter in the AbstractMainActivity class).

Part1ActivityA is almost complete – all that is missing is the animated UW. However, it will only work once DrawView, CircleView, TextView, and LineView are implemented.

Part1ActivityB is complete (once the Views are implemented), it is provided to help you test your code. Do not modify this file.

Until then, your assignment will look more like this:

As you can see, the position of pictures is wrong (their size is fine because we provide that code for you), and nothing else draws at all.

You will need to design your own Part2Activity, that is the open ended, creative part of the assignment.

Implementing the View subclasses

Your next task is to implement the missing pieces of the View inheritance hierarchy.

DrawView

The DrawView parent class is responsible for maintaining a copy of the Paint object that will be used to style drawings, and for making sure that the View position and size are fixed in place properly in the parent.

The DrawView and all of its children assume they receive coordinates in density independent pixels (dp) when they are constructed. However when drawing and positioning on the screen, you must use regular (device dependent) pixels (px). We have provided a helper class to handle conversions (DimHelp). Read the code in the DrawView constructor that includes the variables parentX and parentY and note that the values are converted from dp to px before calling initFromParentCoordsPX .

Your first step is to to implement initFromParentCoordsPX(float parentX, float parentY). The goal is to position the DrawView in its parent view so that onDraw() can assume that (0,0) is the top left of the view (in px) and draw from there.

The DrawView will also ensure that if width and height are set, it disallows any drawing outside of its bounds. We have provided the solution to this for you in onMeasure(). This method is used by other parts of the Android toolkit to determine a View’s width and height. We will discuss this further in our next until on Layout.

If you ran the stub code (prior to any modifications), or review the video of it above, you will note that the initial position of the images is wrong, but the size of the images is correct. This is because we provide an implementation of initFromParentCoordsPX(parentX, parentY, width, height) and of onMeasure() which work together to correctly support width. However, since our implementation of initFromParentCoordsPX(parentX, parentY, width, height) calls initFromParentCoordsPX(parentX, parentY), the position is wrong until you implement initFromParentCoordsPX(parentX, parentY).

Once you have correctly implemented DrawView#initFromParentCoordsPX(float, float) you should see that your images are correctly placed in a heart shape in Part1ActivityA and in a grid in Part1ActivityB.

Related APIs and documentation

CircleView

We recommend you start next with CircleView as it is the simplest DrawView you will need to implement. You must not add any fields to CircleView, your entire implementation will be possible simply by using initFromParentCoordsPX(x, y, width, height) in your constructor; and getWidth() and canvas.DrawCircle() in CircleView#onDraw().

Pay careful attention to what x and y means in each instance that it is used – which coordinate system are x and y in (parent or CircleView coordinates)? What do they refer to, the bounding box or the circle’s center? If you’re stuck, try drawing a picture!

Note that the radius (r) of the circle is from its center to the middle of the stroke:

Circle drawn with a thick stroke. A line labeled 'r' is drawn from the center of the circle to the middle of the stroke

In addition, make sure to account properly for the thickness of your line, so that the circle’s radius is correct AND the bounding box doesn’t cut off the line. Line thickness is split evenly across the path you are attempting to draw. For example, if a line has a thickness of three it will increase by one on each side of the path you are drawing it on. Note that the line thickness is given in px (not dp), so no conversion is necessary.

Also be sure to use getBrush() to retrieve the Paint object used to style your circle.

LineView

A good next task to tackle is LineView. As with CircleView your solution must not add any new fields to LineView, your entire implementation will be possible simply by using initFromParentLinePX() in your constructor; and mDirection and canvas.DrawLine() in LineView#onDraw()

The most challenging aspect of LineView is to translate between the coordinates that are passed in, which specify the start and end of the line, and the position and size, which are needed for initFromParentCoordsPX(). This is done in initFromParentLinePX().

In addition, initFromParentLinePX() will need to properly set mDirection. This is because the bounding box alone is not sufficient information for onDraw() to properly draw the line. You also need to know what corner to start and end in. Otherwise, all of your lines will be drawn from top left to lower right. To understand this, let’s talk about onDraw().

onDraw() needs to know the starting, and ending, position of the line to draw it properly. It calculates this entirely from the LineView’s bounding box, and the mDirection direction. The simplest case is a line that starts in the upper left direction and ends in the lower right direction – this would simply go from 0, 0 to width, height. You’ll need to figure out what would have to happen for all the other examples.

Notes

This is visible in the following image of the horizontal line across the bottom of the screen in Part1ActivityA: :img Layout inspecter showing LineView with a horizontal line highlighted, 100%, width

This is a picture of Android Studio’s layout inspector View -> Tool Windows -> Layout Inspector, which draws bounding boxers around every view on the screen and shows coordinates in dp. The selected view’s bounding box is shown in blue, and to the right of it Android Studio shows the x, y, width and height of the line’s bounding box. This line is drawn in Part1ActivityA.java, specifically in this code on line 83 of Part1ActivityA: LineView lineView = new LineView(this, 0, bottom_offset_line, PHONE_DIMS.x, bottom_offset_line, purple);

We’ve added a printout to the Logcat View -> Tool Windows -> Logcat showing the actual position of the line, if you search for line in Logcat you’ll see something like this: 2021-04-02 18:33:03.725 3318-3318/cse340.doodle I/line: doodle line position: x 0, y 512.5714

Note the difference in y position: in the layout inspector, translationY (the LineView’s position in it’s parent View) is 1790.5 while in the printout it is 1794. This illustrates how we have adjusted the position of the boundingbox upward by 7/2 = 3.5 pixels to account for the horizontal line’s width (which is set to 7 on line 79 of Part1ActivityA when we setup the brush: purple.setStrokeWidth(7);).

TextView

Implementing TextView is more complicated than the others primarily because there are so many nitty gritty details to how graphics APIs handle text. In particular, you’ll need to calculate the width and height of your TextView from the font size and string being displayed in order to call initFromParentCoordsPX() properly in your constructor.

For this view, the following requirements should be met:

We recommend doing this by using a FontMetrics object to set the text size. You can also use paint.measureText() to get the width of your text. This only requires a few lines of code, but will require that you read the documentation for these methods and objects thoroughly. Some of the documentation you will find in the comments in our code base, but you will also have to effectively read and navigate the Android documentation as well.

Be aware that some characters (like ⎨) can go above the ascent and below the descent lines! Make sure to account for this when constructing your bounding box.

A box with three lines drawn inside. One line, near the middle of the box, is the baseline. The uppermost line within the box is `ascent` above the baseline and the lowest line is `descent` below the baseline. A line is drawn from the baseline to the top of the box. That line is labeled `top`. Another line is drawn from the baseline to the bottom of the box and is labeled `bottom`.

Additionally, be sure to think about what units we should be using where. While working, be sure to ask yourself:

Note: If you read a post on Stack Overflow make sure you understand the answers given thoroughly before making use of the solutions. In line with the course policy, we require that you provide links in your reflection to any documentation that you used to figure out how to implement this (including the Android documentation or anything on Stack Overflow).

Related APIs and documentation

Animating Text

Once you’ve implemented the three methods defined above, you also need to figure out how to animate text for Part1ActivityA. You will use animations to move the TextView variable UW from its current position to a new x position, the variable right (which we have already converted to px for you, since animation expects px). The animation must last 1000ms (milliseconds not seconds).

Related APIs and documentation:

Common Issues

Part 2: Custom Doodle

Tasks:

This is where your creativity comes into play. You are welcome to explore Android outside of what is taught in lecture. We’ve created a blank slate for you in Part2Activity.

To start, spend 5-15 minutes sketching out your idea. Here you should you think about how to incorporate lines, text, and images, and how these will be moved around the screen. Storyboarding is a good way to sketch out how this animation will progress. For more information about storyboarding watch at least the first 1:30 of this video. This storyboard does not have to be very complex - even 2-3 frames will give you an idea of how to progress. This storyboard will be turned in as part of your individual reflection.

Once you have your sketches, implement your custom doodle by implementing your MyView and Part2Activity.

You can also add use your own images instead of using those provided. If you want to position a large number of images, we recommend using a CSV similar to the data.csv (located in the assets directory) used for the heart in Part 1. Your file must also be named part2.csv and be stored in the assets directory, if used. Remember, the CSV coordinates are on a Pixel 2 and scaled to the current screen in Doodler#addAllImagesFromData(FrameLayout).

Once you are done implementing your custom doodle, take a video of the running doodle to submit through the Canvas assignment entitled Doodle Video.

Tips:

Related APIs: Android Animation / View Animation / Property Animation / Vogella Tutorials - Android Animation

Commenting your code

Most of the starter code has been fairly well commented for you. However there are some places you must add comments as you are completing your solution:

  1. If you have any significantly complicated code for your custom doodle, add comments that might help your TA effectively grade your awesome work!
  2. If you received significant help from another student on any part of your code, be sure to add a comment referencing their help. See the Collaboration Policies portion of our syllabus for more details.
  3. If you add and use an image that requires copyright, you may add the attribution in your source code where you add the image to the screen. See the Application Content portion of our syllabus for more details.

Code quality

There are also some critical code style guidelines to follow:

Part 3: Peer Review

The Custom Doodle will be peer reviewed as part of the grading process. We will use these peer reviews to ensure that you have implemented all of the parts required for Part 2.

Peer grading will take place once everyone has turned in their assignments, using the Canvas Peer Review support. Once everyone’s work has been turned in, you will receive access to three other students’ videos and will fill out the grading rubric for each. Please make sure to click the appropriate scores for each item in the rubric. Also leave a detailed comment as a critique. For some structure you can use answer finish the following two statements for each you reviewed:

Additionally, you will have a chance to nominate the most creative doodles! The winners will be shown off in class later this term.

Start by finding the Doodle Peer review assignment on Canvas

Doodle Peer Review Assignment

Then click on each of the anonymous users you are to evaluate

Doodle Finding the anonymous users to review

Make sure you click on the rubric to evaluate them! Also leave a critique!

Doodle Finding the anonymous users to review

Part 4: Reflection

Each assignment this quarter will have a reflection component as it is a vital part of your work as a Programmer, Computer Scientist, and/or Engineer. Your reflections do not have to be long (please no more than a paragraph or two in length). Details on what a well written reflection includes can be found here. You will be graded on your answers to these reflection questions based on the following

Instructions: Create a MS Word, Google or other type of document and copy the following questions (in italics below) into that document. Add your your responses below each question. You can have more than one answer per page, but if you can, please try to avoid page breaks in the middle of a question. Insert page breaks between questions as needed.

You will need to submit your write up as a PDF file. If you are new to Gradescope, see this document for instructions on how to scan and submit hand-written solutions.

Gradescope accounts will be synced with Canvas accounts during the first week or so of class. Please reach out to the staff on the discussion board if your Gradescope account does not have access to our classroom.

For this assignment, your reflection must cover the following:

Turn-in

Part 1 and Part 2 Accepted files

We will only guarantee that our Ed autograded tests will work correctly if you only modify the files listed below. Do not edit any of the other files.

─ Part1ActivityA.java
- DrawView.java
- CircleView.java
- LineView.java
- TextView.java
- Part2Activity.java
- MyView.java
- part2.csv (optional)
- additional images in res/drawable (optional)

If you use your own images (in the res/drawable directory) and assets/part2.csv file in Part 2, please make sure to add/commit/push them to your repository . If these files are not there, your custom doodle will not work for others and you will NOT get credit for the work you did.

How to turn-in Part 1 and 2

Follow these instructions to submit the coding portion of your assignment

You will turn in your Doodle Android Studio project to the Doodle Ed Lesson. Open up the lesson, and make sure the “file explorer” size of the your Ed workspace is visible - this is where you will drag and drop your code.

  1. Get a .zip file of all your code.
    • Make sure you have committed and pushed (add/commit/push) all of your code to your as1-doodle-<uwnetid> repository on Gitlab.
    • After you have pushed all your code to your remote gitlab repository you can click on the download icon (next to the clone button) and download your source code as a .zip file.
  2. Once this .zip file has been downloaded to your machine you can drag this into your workspace. Drag and drop this .zip file into this Workspace then click Upload and Extract. Note: .zip files made directly from your computer will be too big to upload directly to Ed.
  3. Move all of your app files to the root of the directory on Ed. You can do this by selecting all of the files in the subdirectory and dragging them outside of the as1-doodle-<uwnetid>/ directory. To check, make sure that app/ is at the outermost level not as1-doodle-<uwnetid>/ (you should delete the as1-doodle-<uwnetid>/ after you move everything out of it). Also make sure that the files and folders in the app directory (like src) are still under the app directory.
  4. SUPER IMPORTANT Make sure you press the Mark button in the Doodle Ed Lesson after everything successfully uploads.

See the instructions for more details.

Part 2 Video

Remember to submit a video of your custom doodle animation on Canvas at the same time you submit your code.

Reflection

The reflection will be turned in to Gradescope.

Grading (40pts)

This HW will be out of 40 points and will roughly (subject to small adjustments) be distributed as follows.