David Andrews
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # A Better Android Studio Tutorial ![reduced_android_studio_tutorial_image](https://hackmd.io/_uploads/r1MLC2pK6.jpg) *Image generated by DALLE-3* <details> <summary>Author Information</summary> <ul> <li>Created by Niki Hu, David Andrews, and Melody Lee </li> <li>Project 01 in CS2340, Spring 2024</li> <li>Dr. Pedro Guillermo Feijoo Garcia</li> </ul> </details> <!-- <center><img src="https://hackmd.io/_uploads/By4OH6YKT.png" width="50%" height="50%" alt="Android Studio 2023 Logo"></img></center> --> <!-- ## Table of Contents [TOC] --> ## Launch Briefing Welcome, Space Explorer, to the newest Android Studio lab! As a part of this series of mission, you will be preparing yourself to launch into the *space* (ha, pun intended) of app development. ### 🧑‍🚀 What should I already know? To be a successful astronaut, you will need some background knowledge! This tutorial assumes prior knowledge in object-oriented programming, namely in Java or Kotlin. However, if you have extensive experience programming, you will be able to successfully make it through this tutorial to launch with little issue! ### 🔬 What will I learn? Throughout this lab, you will be guided several stages that discuss the following: - Basic navigation through Android Studio, - Key controls and components, - Adding interactive buttons, and - Introducing an accessible second page. In this tutorial, we will be designing and launching the following app! {%youtube pdvF2PeSUDk %} When you're ready to dive in, scroll down to begin preparation for launch! ## 🛠️ Start Here: What is Android Studio? Android Studio is an integrated development environment (IDE) for developing Android Apps in the Java or Kotlin programming languages. If you've used code editors before in the past, Android Studio will feel like that while being more geared toward the Android development ecosystem. For instance, it provides convenient features such as a built-in emulator for various Android phones as well as integration with aspects unique to Android development, such as UI design and the Android Software Development Kit (SDK). In short, it allows you to easily develop and deploy Android apps. ## ⏰ Prepare for Takeoff: How do you install Android studio? **Disclaimer**: This tutorial was written when Android Studio Hedgehog was the latest version of Android Studio. Depending on your operating system (OS), the exact installation instructions may vary. ### Option 1: Download and Run The Installer Download the [latest version of Android Studio](https://developer.android.com/studio) for your OS, run the installer, and follow the installation wizard. ### Option 2: Use a Package Manager If you prefer to use a package manager (+100 bonus points), you can use `brew` for MacOS or `yay` for Arch Linux: ```console $ brew install --cask android-studio $ yay -S android-studio ``` <!-- Could we add this as a dropdown box instead? ~ Melody --> ### Oh no! I've run into an installation issue, what do I do? :( Use stackoverflow, chatgpt, and google! ## 🌟 T-8: How do you create a project? ### Objectives In this stage, you will: - Launch Android studio - Create a new project in Android studio - Set the name and characteristics of your project Open your freshly downloaded Android Studio. You can enable the new UI if it's asking you to. The "Projects" tab should be displayed by default: ![Screen Shot 2024-01-19 at 17.49.49](https://hackmd.io/_uploads/rJ-jnOuYT.png) Note that if this is your first time using Android Studio, you will not see any previous projects to choose from. Either way, you will be able to see the "New Project" button and click on it. It will lead you to this page: ![Screen Shot 2024-01-19 at 18.01.09](https://hackmd.io/_uploads/Hk7BJFdta.png) Since in this project, we are creating a Mobile App, we are going to choose a template from the "Phone and Tablet" section. See the "Basic Views Activity" option on the top right corner? Click on it, then "Next". It will take you to this page: ![Screen Shot 2024-01-19 at 18.06.30](https://hackmd.io/_uploads/SyEFlYdKT.png) Here, you are allowed to change your project's "Name" and choose the "Save Location". For "Language", change it from "Kotlin" to "Java"; for "Minimum SDK" and "Build configuration language", please just use the default ones provided by Android Studio by the time you are creating the App. After you click on "Finish", give it several seconds for it to automatically generate a working (but almost empty) App for you, like this: ![Screen Shot 2024-01-19 at 18.12.44](https://hackmd.io/_uploads/B1YefFOF6.png) Feel free to explore the folders and the files. In general, the .java files are under the "java" folder, and the resources files (which we will be editing later) are under the "res" folder. Now you have a working project! ## 🔍 T-7: How do you use Android Studio? Now that you have created your project, it is time to understand the tools you have at your disposal! These tools will be useful in helping you view the different parts of your app. Right now, your project should look something like this: ![opened_project_corrected](https://hackmd.io/_uploads/BJRyfYOKa.png) <details> <summary><b>I see a Gradle window, what do I do?</b></summary> If you see a Gradle window open on the right side of your screen, go ahead and hit the minimize button (a horizontal line) in the upper right corner. We will not be using this.<br> ![minimize gradle](https://hackmd.io/_uploads/Hko8_s6t6.png) </details><br> Let's begin by looking at what files are stored in your Rocket App. On the left side of your Android Studio window, you will see a window that lists different files and folders, circled in red below. ![File Navigator](https://hackmd.io/_uploads/rJcgAkota.jpg) Before jumping straight into the layout, observe that there are two key parts of this template: the first fragment and the second fragment. Generally, every screen you see in your will be associated with at least one **fragments**. In the case of this project, we are aiming to create two screens, each associated with the first and second fragments, respectively. To understand how to interact with these screens, we turn our attention to the layout of our rocket app. Skip the following section if you are already ### Get Your Launchpad Ready: Setting Up the Emulator Android Studio allows you to create applications that may be run on Android devices. However, as you have probably noticed, you most likely are not using Android studio on an Android phone. Instead, you will be running the application on an emulator that mimics the environment of an Android device! You will be using what is known as an **Android Virtual Device (AVD)** manager. #### Adding Your Device **Step 01:** In Android Studios, find the toolbar. Select Tools >> Device Manager. Alternatively, you may click the AVD Manager icon in the toolbar: <img src="https://hackmd.io/_uploads/H17R9iatp.png" width=30>. <details><summary><b> I don't see Device Manager or the Icon! What's wrong?</b></summary> If you are using an earlier Android Studio version, the Device Manager may be listed under a different name. Search for the words "AVD Manager" in the Tools bar. </details><br> The following window show open to the right of your screen: ![Open Device Manager](https://hackmd.io/_uploads/B1M4j96Y6.png) <!--![Open Tools Menu](https://hackmd.io/_uploads/Hy2Qqc6Yp.png) ![Tools Menu Closeup](https://hackmd.io/_uploads/BJhE5cTYT.png)--> **Step 02:** Click the '+' button near the top of the device manager to begin the process of creating a virtual device. A window labeled "Select Hardware" should open. **Step 03:** Select an arbitrary device, such as Pixel 3, and take note of the details displayed to the right of the selection panel. While the exact device you choose will not matter for this tutorial, it may be useful to know these characteristics for future reference. ![Select Hardware](https://hackmd.io/_uploads/Sy8SqqaFa.png) **Step 04:** Once you have decided on the device you would like to use, click "Next" at the bottom of the window. This will bring you to the "System Image" window. Navigate to the "Recommended" tab in the dialog window and select the most recent release. *This matters, please make sure you are choosing the most recent version.* ![Select Android Virtual Device](https://hackmd.io/_uploads/S1x8i9TK6.png) If a Download icon is indicated next to the System Image, you will need to install it first. Click the icon to begin the download. Once the download is complete, click "Next". <details><summary><b>Why is my download taking so long?</b></summary> Please note that the time for the download may take a while depending on your internet connection. Additionally, Android Studio is regarded as fairly bloated, meaning it takes up significant processing power on your system. Give it time, and if nothing loads in a long time, check your network connection. </details><br> **Step 05:** When the next dialogue box opens, simply accept the defaults and click "Finish". ![Confirm Setup](https://hackmd.io/_uploads/HkNLs96FT.png) #### Test Launch: Using Your New Emulator Once you have returned to the initial page, we will test run the emulator! **Step 01:** Click Run >> Run 'app'. Alternatively, look for the small green Play arrow near the top right side of the window. If all is successful, the icon will change to running (a curved arrow). <details><summary><b>A dialog box with "Instant Run requires... is installed" shows up! What do I do? </b></summary> If you get a pop-up dialog box telling you, "Instant Run requires that the platform corresponding to your target device (Android N...) is installed," don't worry! Simply click "Install and continue." </details><br> ![run button](https://hackmd.io/_uploads/r1zUDqRF6.png) **Step 02:** We will now need to select the virtual device that we just configured to run on! Navigate from Run >> Select Device. Under "Available Devices", select whichever virtual device you just picked. You will also be able to select the virtual device using the dropdown box to the left of the run button (see below). ![Select Device](https://hackmd.io/_uploads/SyTDjqpY6.png) **Step 03:** Give the emulator some time to start and boot -- you may see the progress in the horizontal progress bar below at the bottom of your Android Studio window. <details><summary><b>Why is my emulator taking so long to launch?</b></summary> The time it takes for the emulator to boot depends on the speed of your computer. Chances are, you will encounter the following messages while the emulator is booting: <ul> <li><i>Gradle build running</i></li> <li><i>Waiting for target device to come online</i></li> <li><i>Installing APK</i></li> <li><i>Launching activity</i></li> </ul> </details><br> Android Studio will upload the app to the emulator and then run it. You should see the app show on the device, as seen below. ![Booted App](https://hackmd.io/_uploads/S1Gz396Fp.png) <details><summary><b>I want to run my app on my own device. What do I do?</b></summary> You have the option to run the app on your own Android device! Follow these instructions to do so: <a href = "https://developer.android.com/studio/run/device">How to Run Your App On Your Own Device?</a></details> <br> <details> <summary><b>I'm having trouble with virtualization!</b></summary> The Android emulator requires virtualization functionality to be enabled on your CPU. If you encounter issues with the emulator, make sure to go into your BIOS and enable VT-x if you have an Intel CPU or enable SVM if you have an AMD CPU You can usually access the BIOS by rebooting and pressing F10, F2, F12, F1, or DEL during boot. Once you are in the BIOS, just navigate around until you find the aforementioned settings. </details> ## 📐 T-6 How do you use the Layout Editor? To find the layout editor, open the "layout," which may be reached by navigating from "app" >> "res" >> "layout". You may expand each folder by selecting the arrow to the left of each file name, which will show and contract the list of subfiles and subfolders. The layout folder will contain files, such as "fragment_first.xml" and "fragment_second.xml". <details> <summary><b>Help! I don't see "fragment_first.xml". What do I do?</b></summary> If you do not see the file, double check your version of Android Studio. The indicated version should be Android Studio 3.6 or later. <br></br> To view your Android Studio version, find the toolbar in your studio window. For Windows users, go from Help >> About to find your studio version. For Mac users, go from Android Studio >> About Android Studio. </details> <br> <details> <summary><b>What is the .xml file extension?</b></summary> XML stands for eXtensible Markup Language. A simple way of describing this file type is by calling it a way to box up data that can be read. Unlike HTML (Hypertext Markup Language), XML files are written in an "extensible" way, meaning there is no predefined way of interpreting the file, so how they are understood depends on their purpose. In our case, we use these files to describe the layout of our app. </details> <br> Your file navigator should look something like the image below. ![File navigator](https://hackmd.io/_uploads/SkSr0JoKT.png) ### Using the Layout Editor on the First Fragment To open the "first_fragment.xml" file, double click on the file name. This will prompt your view of the window to change into something similar to what is below. ![Layout editor first look](https://hackmd.io/_uploads/rkyVhqTYT.png) If you do not see this view, do not worry! In the upper right corner of the content box, there are three icons, as circled in red below. Click on each one to see how the view changes. The leftmost icon shows only the source code, the rightmost icon shows only the layout editor, and the center icon shows a split view containing both. For the time being, select either split view or show only the layout editor. ![Toggle View](https://hackmd.io/_uploads/H1JaHx2YT.png) By toggling between each of these views, you are able to select how you change your method. Android studio allows you to modify your app layout by either coding via the XML file (as mentioned previously) or the interactive visual editor, as seen below. ![Interactive Visual Editor View](https://hackmd.io/_uploads/SyBHh9atT.png) **A brief note: What is a component tree?** One of the tabs that are a part of the interactive visual editor is labeled "Component Tree". In the tutorial and the image, this component tree is rooted at a `ConstraintLayout` view. *Every* layout must have some root view that contains all other views -- kind of like how every object must have a container (e.g. a universe) to contain it. In our pre-built template, there is a `Button` element named `button_first` and a `TextView` element named `textView_first`. Right now, it looks like we still have the template's default text content included in the fragment. Let's change it! #### Changing Values of Properties in XML The text content is stored as a property of the fragment. Thus, to change the text shown on our app, we want to update the String value stored in the text. ![First Fragment Edit Text (unedited)](https://hackmd.io/_uploads/Hywwh5TFa.png) Press the source code view button (in this case, this is denoted by a series of horizontal lines, like lines of code) in the top right of the layout editor. The XML description of the fragment should now take up the screen. Take a few moments to observe the layout of the `fragment_first.xml` file. Note that there are labels corresponding to each of the elements we took note of in the Component Tree, where the root element is defined as `<androidx.constraintlayout.widget.ConstraintLayout>` and contains a `<Button>` and `<TextView>` element. While we will not concern ourselves with each individual line in this tutorial, pay close attention to the structure of the `TextView` element. ```xml <TextView android:id="@+id/textview_first" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/lorem_ipsum" ... /> ``` Observe that the `TextView` element contains an `android:text` field, although it doesn't contain the text content we saw in the fragment layout editor. Instead, it contains a reference `@string/lorem_ipsum`. This is known as a **string resource**, where the`@` symbol in front of what looks like a path denotes the location of where this resource is stored. Much like how resources are "sources of things of value", this string resource is the source of the values referred to as lorem_ipsum. So, to change the text displayed, we seek to change this value. We have two options: (1) we may hardcode the code and (2) we may use a string resource. Like any good astronaut, let's run experiments on both methods! ##### Method 1: Hardcoding the String (🚩BAD CODING PRACTICE EXAMPLE) ![Screen Shot 2024-01-20 at 11.41.01](https://hackmd.io/_uploads/SJRt550YT.png) Let us first try changing the stored string directly. Alter the `android:text` value to the following: ```xml= android:text="Hello Rocket!" ``` Switch to the split view to see both the XML file and visual interactive editor. Make sure the emulator is running. Then, click the green arrow to launch your app: your changed text should have replaced the "Lorem ipsum..." on the screen! ![Screen Shot 2024-01-20 at 11.41.51-min](https://hackmd.io/_uploads/BygXscRYp.png) Select `textView_first` in the Component Tree. You may also do this by clicking directly on the text in the visual interactive editor. On the right, look at the "Attributes" panel. If needed, expand the section titled "Common Attributes" to see any elements under it. <details><summary><b>What are attributes?</b></summary> Attributes of elements describe their characteristics. Upon closer inspection, recognize that each of the attributes listed in the "Attributes" panel correspond to a characteristic of the corresponding element in the XML file. This is a user-friendly way of editing these values! </details><br> <details><summary><b>Help! I can't see the "Attributes" panel. What do I do?</b></summary> At the top right of the window, look for the vertical "Attributes" label. Click on this label, which should expand the panel. </details><br> Look at the "Text" field under the "Common Attributes" part of the panel. Note that the text contains the "Hello Rocket!" text you hardcoded into the XML file. ![Screen Shot 2024-01-20 at 11.42.18-min](https://hackmd.io/_uploads/rkjXjc0Fa.png) **Oh no! Android Studio isn't happy 😔** Closer inspection of the "Hello Rocket!" text attribute shows the following warning: ![Screen Shot 2024-01-20 at 11.43.32](https://hackmd.io/_uploads/BJyViqCYp.png) If you head back to your `fragment_first.xml` file, note that there is a squiggly yellow underline under the line `android:text="Hello Rocket!"`. This means Android Studio is complaining that you have hardcoded your text! ![Screen Shot 2024-01-20 at 11.43.43](https://hackmd.io/_uploads/HJ24icCKT.png) To fix this, let us consider our second method for changing the values of elements. ##### Method 2: Using a String Resource (🟩 Do This for Good Coding Practices!) Instead of hardcoding the value, let us instead make use of the string resources. First, we find where the values are stored! Return to the `fragment_first.xml` file, right click on the `TextView` property `android:text`, and click "Go To" >> "Declaration or Usages". This should also open the corresponding file. Alternatively, in your file navigator, open the `strings.xml` file, located in app >> res >> values >> `strings.xml`. Find the lines: ```xml <string name="lorem_ipsum"> Lorem ipsum.... </string> ``` ![Screen Shot 2024-01-20 at 11.44.51-min](https://hackmd.io/_uploads/rJFric0FT.png) We no longer want our `TextView` element to display "Lorem ipsum..." gibberish, so let us now create a new string resource, named `hello_first_fragment`. Add the following line inside the `<resources>...</resources>` bookends (and, since we are quite happy to see our rocket again, let us use the line "Hello my dear rocket!" instead 🚀): ```xml= <string name="hello_first_fragment">Hello my dear rocket!</string> ``` It will look like this once you have added the line: ![Screen Shot 2024-01-20 at 11.45.34-min](https://hackmd.io/_uploads/BkCriqAYp.png) You have just created a string resource! Now, it is up to us to use it properly. Once again, select `textview_first` in the component tree or click it in the interactive visual editor and take a peek at the "Attributes" panel. Change the value of the text attribute to read `"@string/hello_first_fragment"`, as shown below: ![Screen Shot 2024-01-20 at 11.46.01-min](https://hackmd.io/_uploads/Sy4dicRY6.png). The line will now read as: ```xml= <textView ... android:text="@string/hello_first_fragment" .../> ``` The text you want to show will still exist, but Android Studio is no longer unhappy! You may rerun the app (or hit the "refresh" button) to see if any changes have occurred. Note that the emulator to the right now shows those updated changes. ![Screen Shot 2024-01-20 at 11.46.14-min](https://hackmd.io/_uploads/HJYOscRYp.png) As we wrap up comparing these two methods, like any good scientist, let us reflect on our experiment: <details><summary><b>What are advantages of using string resources?</b></summary> The resource file contains both values and their reference "names". As a result, you are able to change what values are stored in a single, centralized location -- which is considerably better than having to contend with having to introduce many changes across various files to alter the value of a single string or element. Furthermore, when it comes to allowing your application to be written in other languages, it makes understanding what the app is doing considerably easier! </details><br> #### 🔎 Exploring Other Attributes Let's get your gears turning and the ball rolling! Once you've launched your rocket, you will be conducting exploration on an unprecedented scale, so let us practice with some contained exploration of the "Attributes" panel. Go through the panel and change some of the text appearance properties. You have the option to set font families, text size, boldness, and even color! ![Screen Shot 2024-01-20 at 11.48.51-min](https://hackmd.io/_uploads/H1MFs5AKp.png) When launched, the app will look like this, except with your own changes to the text: ![Screen Shot 2024-01-20 at 11.49.41-min](https://hackmd.io/_uploads/H1TYs90Fp.png) ## 🎨 T-5: How do you implement resources? (String & Color) Just like how you edited the String resources, you can also edit the Color resources under res/values/colors.xml. Go to the file, you will see the two customized colors by default: ![1](https://hackmd.io/_uploads/Hys-BlnKp.png) Add another line of code to declare your new color! We can name it "screenBackground" and set the actual color to #201D1D. ![2](https://hackmd.io/_uploads/H12-HenKa.png) After the customized color is declared, we can easily find it when we want to change the colors of pages, buttons, texts... In the Attribute tab for the ConstaintLayout, set the backgroundTint to the new color that we have just declared! When you type in "@color/" and the keyword of the name of the color, you can easily find it. ![3](https://hackmd.io/_uploads/SyoWBe3Fa.png) After you change the color, the App interface would look like this! ![4](https://hackmd.io/_uploads/HkU2BgnKa.png) In order to make the ConstraintLayout to cover the entire screen, we need to manually add this line in the fragment_first.xml: ![5](https://hackmd.io/_uploads/Syu8Ue3Y6.png) This is a general way you can declare and customize a new color: you control the color itself in colors.xml and implement it elsewhere. ## 📏 T-4: How do you control Views and Constraints? We can also explore the Layouts of the widgets. Under the Layout attributes for the ConstraintLayout, we can see what happens when we change layout_width and layout_height between wrap_content and match_parent. layout_width = wrap_content and layout_height = match_parent: ![6](https://hackmd.io/_uploads/B1DILe2tT.png) layout_width = wrap_content and layout_height = wrap_content: ![7](https://hackmd.io/_uploads/SkDLIehYT.png) layout_width = match_parent and layout_height = wrap_content: ![8](https://hackmd.io/_uploads/HkPILehYa.png) In our case, to make our App aesthetically appealing, we are sticking with match_parent with both layout attributes. ![9](https://hackmd.io/_uploads/rJD8UehFa.png) ## 🖲️ T-3: How do you add buttons elements? Now for exciting changes! We turn our attention to the buttons in the template, currently identified as `button_first` and displaying the string value "Next". Let us introduce some new functionality to our first fragment! We will create a series of buttons and update their values in preparation of introducing interactive components to the app! **Step 01:** Firstly, we seek to understand the current state of the button. Initially, when you run the unedited template, this button sends the app user to the next fragment in line -- the second fragment (which we have not yet touched thus far! how exciting: new territory!). While we do want the scene to change from the first to second fragment when this button is clicked, we want to change the text attribute displayed. So, open `first_fragment.xml` and scroll to the `<Button>...</>` ![Screen Shot 2024-01-20 at 12.07.29](https://hackmd.io/_uploads/HJClSsg5T.png) Observe that the `text` attribute is currently hard coded as "Next". We want to follow best practices and change this! **Step 02:** Head to the `strings.xml` file. In the file, add the following a `random_button` string resource with the word "Random": ```xml= <string name="random_button">Random</string> ``` Your `string.xml` file will look similar to the screenshot below: ![Screen Shot 2024-01-20 at 12.10.32](https://hackmd.io/_uploads/SkeCgrixq6.png) **Step 03:** Return to the `fragment_first.xml` properties file. Now, replace the `android:text` for the button from referencing "Next" into our new `@string/random_button` string reference (make sure you include the `@` to specify the nature of the value). The button descriptors in the XML file should now look like: ```xml= <button ... android:text="@string/random_button" .../> ``` When the interactive visual editor is shown, the text attribute should also be updated. ![Screen Shot 2024-01-20 at 12.10.40](https://hackmd.io/_uploads/BJCeSox56.png) **Step 04:** Let's learn how to add a new button! At the top left of the layout editor, notice the panel named "Palette". As you sift through the different categories on this panel, take note of the different types of elements you may add to your app! ![Screen Shot 2024-01-20 at 12.14.08](https://hackmd.io/_uploads/HJx0gBieqp.png) **Step 05:** We want to add a new button. Navigate to the "Buttons" panel. Click and drag the "Button" element from the Palette onto the layout editor and please it above the TextView, near the other button. ![Screen Shot 2024-01-20 at 12.14.15](https://hackmd.io/_uploads/BykZrog96.png) In the "Attributes" panel, update the ID from "button" to "toast_button". In the corresponding **Step 06:** The button we have just added will read "Toast", to refer to a little toast that will pop up every time the user clicks the button. Navigate to the `strings.xml` file and add a "toast_button" string attribute with the value "Toast": ```xml= <string name="toast_button">Toast</string> ``` The `strings.xml` file will look like the following: ![Screen Shot 2024-01-20 at 12.19.23](https://hackmd.io/_uploads/SyCgBsxc6.png) **Step 07:** Now, we want to rearrange the positions of each of our elements. These are defined by "constraints", which constrain (or limit) where the elements are positioned on the app relative to each other. Take a closer look at the constraint properties for the `TextView` element in `fragment_first.xml`. The constraints are given as ```xml= app:layout_constraintTop_toBottomOf="@id/random_button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent" ``` (although if something different is seen, do not worry! We will adjust this accordingly). Read them carefully -- these are how constraints are defined. ### 🧠 Understanding the Constraints Widget These constraints may also be viewed in the "Attributes" panel. Scroll to the "Constraint Widget" section of this panel. The square in this widget indicates what types of constraints are applied to each of the sides (top, bottom, left, right sides) of the element. When the element in the interactive visual editor is selected, the constraints are also shown. <img src="https://hackmd.io/_uploads/rkNTaoZq6.png" width="50%"/><br> Observe that when you select the element in the interactive visual editor, the corresponding constraints are also shown. <img src="https://hackmd.io/_uploads/SynL0obq6.png" width="50%"/> **Step 08:** Now, we seek to add constraints to our newly added button. Move your cursor over the bottom of the "Toast" button. Then, click and drag it to the top of the TextView on your interactive visual editor. If the button moves, do not worry! Click and drag the constraints of the left side of the Toast button to the left side of the screen, the top of the Toast button to the top of the screen. This should fix any errors that pop up (namely a warning stating, "Not Horizontally Constrained"). ![Screen Shot 2024-01-20 at 12.24.47](https://hackmd.io/_uploads/S1ReHil96.png) Taking a peek back at your XML file should show the line `androidapp:layout_constraintBottom_toTopOf="@+id/textview_first`. **Step 09:** We revisit our "Random" button. The constraint between the button and our TextView is not indicated by the same zig-zag line seen on our other button element. Instead, the wavy like suggests that these two elements are **chained** together. When objects are chained together, two or more objects in your project are linked, rather than simply having one object refer to another (like our previous constraints). <details><summary><b>How do I delete a constraint?</b></summary> In the design layout view, move your cursor over the constraint you wish to remove. If you are on a Windows computer, hit `Ctrl`, highlight the corresponding circle on the element, and click. If you are on a Mac, use the `Command`key. Alternatively, you may alter the constraint by right clicking on it and selecting "Delete", editing the XML file, or using the 'x' on the corresponding edge of the constraints rectangle in the "Attributes" panel. </details><br> Delete the constraints from the top of the TextView to the "Random" button. Similarly, delete the constraints from the "Random" button to the TextView element. Add any necessary constraints to the TextView element to ensure it is constrained to the edges of the screen, such that it is centered on the screen. **Step 10:** Now that we have removed the old constraints on the "Random" button, we set vertical constraints to the top of the TextView (and remove any pre-existing constraints), right side of the button to the right edge of the screen (parent), and top of the button to the top of the screen (parent). ![Screen Shot 2024-01-20 at 12.24.49](https://hackmd.io/_uploads/Hk0lSig5a.png) Your final layout should look something like this: ![Screen Shot 2024-01-20 at 12.24.30](https://hackmd.io/_uploads/B1eAxBoeqT.png) **Step 11:** Before adding in any other buttons, let us make sure we rename our "Random" button accordingly. At the moment, its ID has been left as `button_first` in our tutorial. In the "Attributes" panel, alter the "id" of the element into random_button. When a window pops up, update the ID to "random_button" and click "Refactor" to confirm your choices. <details><summary><b>What does refactoring do to my code?</b></summary> Refactoring refers to the rearrangement of code for increased understandability and organization. In this case, renaming the reference for an element (the ID) is one example of refactoring. To properly implement these changes, Android Studio will update all references to your button object across the project, so that your code will not lose its functionality when you change the name of your element.</details><br> ![Screen Shot 2024-01-20 at 12.25.34](https://hackmd.io/_uploads/Bye0erjg9p.png) **Step 12:** Now, it is time to add a new button! From the "Palette" panel, click and drag a new button onto the screen. Set the ID of this button to "Launch". ![Screen Shot 2024-01-20 at 12.25.55](https://hackmd.io/_uploads/H1Rxrsxca.png) **Step 13:** As with before, we want to use best practices in setting the string values on our elements. Navigate to the `strings.xml` file and add the following line: ```xml= <string name="launch_button">Launch</string> ``` such that the file looks like the following: ![Screen Shot 2024-01-20 at 12.26.15](https://hackmd.io/_uploads/rklCeHjlqp.png) **Step 14:** Return to the "Attributes" panel of your `Launch` button. Update the "text" value to refer to the `@string/launch_button` string reference. ![Screen Shot 2024-01-20 at 12.26.30](https://hackmd.io/_uploads/r1RlBoxqT.png) **Step 15:** Constrain the new `Launch` button such that it is constrained to the TextView at the bottom, Toast button on the left, and Random button on the right. This is shown as follows: ![Screen Shot 2024-01-20 at 12.26.53](https://hackmd.io/_uploads/rkAgSje9p.png) **Step 16:** As one last step before you run your app, ensure you go through your files and apply any refactoring change. For example, make sure your `FirstFragment.java` refers to the correct button -- we have since updated `buttonFirst` to `randomButton` (or `button_first` and `random_button` in our XML files), and these changes may not be reflected in the entire project, depending on how you chose to change the ID of your elements. ![Screen Shot 2024-01-20 at 12.27.25](https://hackmd.io/_uploads/B10eBoxqp.png) **Step 17:** Run your app! If any errors occur, they should be indicated in the "Build Output". ![Screen Shot 2024-01-20 at 12.27.46](https://hackmd.io/_uploads/HkRgrox5T.png) Your final `fragment_first.xml` file should be as follows (although specific values, such as color or font, may differ): ```xml= <?xml version="1.0" encoding="utf-8"?> <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" tools:context=".FirstFragment"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/screenBackground" android:padding="16dp"> <Button android:id="@+id/random_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/random_button" app:layout_constraintBottom_toTopOf="@+id/textview_first" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textview_first" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="sans-serif-condensed-light" android:text="@string/hello_first_fragment" android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textColor="@android:color/holo_blue_dark" android:textSize="34sp" android:textStyle="bold" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.8" /> <Button android:id="@+id/toast_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/toast_button" android:textColor="@color/white" app:layout_constraintBottom_toTopOf="@+id/textview_first" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/Launch" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/launch_button" app:layout_constraintBottom_toTopOf="@+id/textview_first" app:layout_constraintEnd_toStartOf="@+id/random_button" app:layout_constraintStart_toEndOf="@+id/toast_button" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.core.widget.NestedScrollView> ``` <!-- ![Screen Shot 2024-01-20 at 12.28.29](https://hackmd.io/_uploads/SklReSox5p.png) --> ## 💡 T-2: How do you make the app interactive? Before we start making the App interactive, we need to first allow unambiguous imports in Android Studio. Click on the Android Studio button and find "Preferences". ![Screen Shot 2024-01-20 at 12.32.28](https://hackmd.io/_uploads/SJwfLjecp.png) To make everything easier, we can simply type "Add Unambiguous imports" in the search box at the upper left corner. Click on /Editor/General/Auto Import tab, and you will see the following page: ![Screen Shot 2024-01-20 at 12.32.49](https://hackmd.io/_uploads/H1wMUoxqp.png) Enable the "Add unambiguous imports on the fly" checkbox like what's shown in the screenshot above. ### Toast Let's customize the "Toast" button first! Our idea is that when we click on the "Toast" button, a string "Toasting! 🔥" will show up. So the very first thing we are going to do is to create a string in strings.xml and name it toast_text. ![Screen Shot 2024-01-20 at 12.38.05](https://hackmd.io/_uploads/rkTmIjlcT.png) Go to FristFragment.java, and add the following lines in the `onViewCreated` method: ``` binding.toastButton.setOnClickListener(v -> { Toast.makeText(getActivity(), R.string.toast_text, Toast.LENGTH_SHORT).show(); }); ``` ![Screen Shot 2024-01-20 at 12.38.47](https://hackmd.io/_uploads/rJpQUsgq6.png) After you save the files, you can rebuild the App on your virtual machine. When you clock on the "Toast" button, the ideal string would pop up. Try it on your own! ### Finishing the Launch Button Our next step is to finish the functions for the "Launch" button. For this button, our idea is to count how many rockets we have launched and show the existing number of rockets on this screen. First of all, we are going to write a countMe method in FirstFragment.java: ```java private void countMe(View view) { // Get the value of the text view String countString = binding.textviewFirst.getText().toString(); // Convert value to a number and increment it Integer count = Integer.parseInt(countString); count++; // Display the new value in the text view. binding.textviewFirst.setText(count.toString()); } ``` Like this! ![Screen Shot 2024-01-20 at 12.47.40](https://hackmd.io/_uploads/ry_4Liec6.png) Then, we need to add another line in the `onViewCreate` method: ``` binding.Launch.setOnClickListener(this::countMe); ``` This will make sure that when you click on the "Launch" button, the system would call the method countMe and record the total number of rockets launched. ![Screen Shot 2024-01-20 at 12.47.47](https://hackmd.io/_uploads/B1OVIoe96.png) Initially, we set the hello_first_fragment string as "Hello my dear rocket!". Now, since we want to represent the number of rockets launched here, we are going to set the string to "0". Remember, you don't need to create a new string - you simply need to change the value of the string. ![Screen Shot 2024-01-20 at 12.47.52](https://hackmd.io/_uploads/ryOV8ilqT.png) Rebuild the App on a virtual machine! Now you can test if the "Launch" button actually keeps track of how many rockets you have launched! ![Screen Shot 2024-01-20 at 12.48.01](https://hackmd.io/_uploads/ByO48oe96.png) ## 🧭 T-1: How do you navigate between fragments? Let us implement the last major component of our tutorial! Recall the existence of two fragments: at the moment, one of our buttons allows us to switch between the first and second fragments in our project. **Step 01:** Open `fragment_second.xml`. Note that the fragment contains both a TextView and button element. For this final step, we will be working largely with the second fragment. **Step 02:** We seek to update the value of our TextView. Navigate to `strings.xml`. Add a new string reference named `random_heading`: ```xml= <string name="random_heading">There are a random number between 0 and %d of rockets in the sky.</string> ``` The updated `strings.xml` file will appear as follows: ![Screen Shot 2024-01-20 at 12.52.43](https://hackmd.io/_uploads/SJxEWUig9T.png) **Step 03:** Return to `fragment_second.xml`. Update text attribute of `textview_second` to refer to the string reference we just created. This may be done by changing the `android:text` attribute to read: ```xml= android:text="@string/random_heading" ``` or by updating the value in the "Attributes" panel. ![Screen Shot 2024-01-20 at 12.54.15](https://hackmd.io/_uploads/SJNZUjx5T.png) **Step 04:** Now we seek to change the background of the layout. Navigate to `colors.xml` and add the following new color resource: ```xml= <color name="screenBackground">#201D1D</color> ``` Return back to the `fragment_second.xml`. Select the ConstraintLayout root and change the background to refer to our new color (in the Attributes panel, select `@color/screenBackground`). ![Screen Shot 2024-01-20 at 12.55.57](https://hackmd.io/_uploads/rkN-Lsl5p.png) **Step 05:** Update the constraints of the TextView to refer to the parent. Then, update the constraints of the button such that the `app:layout_constraintTop_toBottomOf` attribute refers to the `@+id/textview_second` TextView element. You may update the attributes of the elements to read as follows: <!-- ![Screen Shot 2024-01-20 at 12.56.49](https://hackmd.io/_uploads/HkN-8ig5T.png) --> ```xml= <Button android:id="@+id/button_second" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/previous" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textview_second" app:layout_constraintVertical_bias="0.703" /> <TextView android:id="@+id/textview_second" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="sans-serif-light" android:text="@string/random_heading" android:textColor="@android:color/holo_blue_dark" android:textSize="34sp" android:textStyle="bold" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.064" /> ``` The layout editor will now look like: ![Screen Shot 2024-01-20 at 12.56.52](https://hackmd.io/_uploads/HkVbIjx5p.png) In the "Palette" panel, click and drag a new TextView element onto the second fragment in the layout editor. Name this TextView element `textview_random`. This TextView element will display a random number generated from the first fragment. ![Screen Shot 2024-01-20 at 12.57.56](https://hackmd.io/_uploads/HJ4-8oxqa.png) **Step 06:** Constrain the top of `textview_random` to the bottom of the `textview_second` element, and the bottom of the `textview_random` to the top of the `button_second` element. To constrain the text horizontally, set the left and right constraints to refer to the edges of the parent. Once the constraints are set, use the "Attributes" panel to set both height and width to `wrap_content`. To alter the characteristics of the text itself, set the `textColor` to `@android:color/white`, `textSize` to `72sp`, and `textStyle` to `bold`. Lastly, set the `layout_constraintVertical_bias` to 0.45. Then, temporarily set the text in the TextView to the value "R". ![Screen Shot 2024-01-20 at 12.59.48](https://hackmd.io/_uploads/SkE-8jxqa.png) The final `fragment_second.xml` code for this new TextView element will be similar to the following: ```xml= <TextView android:id="@+id/textview_random" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="sans-serif-light" android:text="R" android:textColor="@android:color/white" android:textSize="96sp" android:textStyle="bold" app:layout_constraintBottom_toTopOf="@+id/button_second" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textview_second" /> ``` Now, if you attempt to run the app, the app may crash, especially if you click the "Random" button. Don't worry! This is because we will need to set up the click handler initially included in the template. **Step 07:** Before continuing, let us see where our current navigation capabilities lie. Use the `nav_graph.xml` file to view what the logic for navigating between fragments currently are. <details><summary><b>How do I understand the nav_graph.xml file? </b></summary> To view the navigation between fragments, find the nav_graph.xml file, stored in app >> res >> nav_graph.xml. Double click to open the file, which will reveal the fragments you currently have created on your app. Arrows in the layout editor indicate any existing control flows that permit for the movement between one fragment to the other. </details><br> <!-- ![Screen Shot 2024-01-20 at 13.00.07](https://hackmd.io/_uploads/By4b8ieqa.png) --> **Step 08:** To set up for controlled navigation between fragments (and the passing of values), we want to enable SafeArgs. Open Gradle Scripts >> `build.gradle (Module: app)`. The Gradle files define what values, packages, or dependencies (along with the file path for referencing those tools) your app will be working with. In the `build.gradle.kts (Project: MyRocketApp)` file, add the following under the `dependencies` entries of the `buildscript` section: ```java= val nav_version = "2.7.6" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") ``` ![Screen Shot 2024-01-20 at 13.13.44](https://hackmd.io/_uploads/ByVb8sxq6.png) **Step 09:* Then, open Gradle Scripts >> `build.gradle (Module: app)` and add the following line: ```xml= apply plugin: 'androix.navigation.safeargs' ``` Android will detect that the Gradle files have been changed. When the option to "Sync Changes" or "Sync Now" appears, click on it. It may take a few moments, but eventually, Android Studio should indicate that syncing the Gradle to the changes you made was successful. **Step 10:** You will now need to rebuild your project. Choose Build >> Make Project. ![Screen Shot 2024-01-20 at 13.12.34](https://hackmd.io/_uploads/BkEWLoxcp.png) <details><summary><b>Help! My Gradle did not sync successfully!</b></summary> Make sure you added the correct lines to the right Gradle file. If there are still issues after doing so, take a peek at Android Studio's guide on Safe Args, which is what we are adding, here: <a href="https://developer.android.com/guide/navigation/use-graph/safe-args">Safe Args Documentation</a>. </details><br> **Step 11:** In the navigation graph of your project (`nav_graph.xml`), click on the first fragment. Then, take a peek at the Attributes Panel. Note that, in the "Ations" section, the Attributes panel will show that the app will navigate to the SecondFragment. Now, click on `SecondFragment` and take a look at the Attributes panel. The "Arguments" section will currently indicate "Nothing to show". Click on the "+" button in the "Arguments" section. We will now add an argument. In the corresponding dialog, enter `myArg` for the name and set the type to Integer. Then, confirm your choices by clicking the "Add" button. Now, the second fragment will accept an argument (like an "input" of sorts when it is navigated to) from some source. **Step 12:** At the moment, the Random (formerly "Next") button was set by Android Studio to, by default, change the view from the first fragment to the second fragment. We want to update this function so that a value is passed along. To do so, open app >> java >> com.example.myfirstapp >> `FirstFragment.java`. Find the method named `onViewCreated()` and pay close attention to what part of this method tells the click listener to change the fragment shown to the user. Replace the code in that click listener with the following line: ```java int currentCount = Integer.parseInt(showCountTextView.getText().toString()); ``` **Step 13:** Then, we create an action using `currentCount` as our argument (keyword!) to `actionFirstFragmentToSecondFragment` as follows: ```java FirstFragmentDirections.ActionFirstFragmentToSecondFragment action = FirstFragmentDirections.actionFirstFragmentToSecondFragment(currentCount); ``` **Step 14:** Then, add a line to identify the navigational controller and navigate with this action we just set! ```java NavHostFragment.findNavController(FirstFragment.this).navigate(action); ``` Your complete method method should be as follows: ```java public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); view.findViewById(R.id.random_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int currentCount = Integer.parseInt(showCountTextView.getText().toString()); FirstFragmentDirections.ActionFirstFragmentToSecondFragment action = FirstFragmentDirections.actionFirstFragmentToSecondFragment(currentCount); NavHostFragment.findNavController(FirstFragment.this).navigate(action); } }); view.findViewById(R.id.toast_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast myToast = Toast.makeText(getActivity(), "Hello toast!", Toast.LENGTH_SHORT); myToast.show(); } }); view.findViewById(R.id.count_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { countMe(view); } }); } ``` Run your app to make sure things will function! Now when you press the "Random" button, the second fragment (screen) should show. What remains is configuring our second fragment to show the number of rockets you have set. **Step 15:** Open `SecondFragment.java`, which is contained in the same directory as `FirstFragment.java`. At the top of the file, add ```java import androidx.navigation.fragment.navArgs; ``` Then, in the `onViewCreated()` method, add the following below the line that begins with the keyword `super`: ```java Integer count = SecondFragmentArgs.fromBundle(getArguments()).getMyArg(); String countText = getString(R.string.random_heading, count); TextView headerView = view.getRootView().findViewById(R.id.textview_header); headerView.setText(countText); ``` This code gets the current count passed in as an argument, formats it accordingly, and then sets the `textview_header` value. **Step 16:** As one last final step, let us add code that gets the random number between 0 and the count (if you are familiar with Java, this should be comfortable to you!): ```java Random random = new java.util.Random(); Integer randomNumber = 0; if (count > 0) { randomNumber = random.nextInt(count + 1); } ``` We then convert the calculated random number into the string value for `textview_random`: ```java TextView randomView = view.getRootView().findViewById(R.id.textview_random); randomView.setText(randomNumber.toString()); ``` Your final `SecondFragment.java` file should look like: ![Screen Shot 2024-01-20 at 13.14.34](https://hackmd.io/_uploads/HJeNb8se96.png) <!-- ![Screen Shot 2024-01-20 at 13.14.45](https://hackmd.io/_uploads/S1VWIjl56.png) --> <!-- ![Screen Shot 2024-01-20 at 13.15.05](https://hackmd.io/_uploads/ryg4bLie5p.png) --> ## LIFT OFF 🚀 Now, run your app to make sure all of your components are functioning! Press the "Count" button a few times, and then give the "Random" button a try. Does it increment the counter? Does it change to the second fragment? Once you have confirmed this functionality, your app is officially ready for launch!! 🚀 ### Additional Resources Want to learn more? Check out the Developer Guides <a href="https://developer.android.com/guide">here</a> or check out any of the other templates Android Studio offers! ##### For your efforts, please enjoy the following meme: ![image](https://hackmd.io/_uploads/r1ALOmHcp.png)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully