├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── me │ │ └── srodrigo │ │ └── androidhintspinnersample │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── me │ │ └── srodrigo │ │ └── androidhintspinnersample │ │ ├── MainActivity.java │ │ ├── User.java │ │ └── Util.java │ └── res │ ├── drawable-hdpi │ └── ic_action_face_unlock.png │ ├── drawable-mdpi │ └── ic_action_face_unlock.png │ ├── drawable-xhdpi │ └── ic_action_face_unlock.png │ ├── drawable-xxhdpi │ └── ic_action_face_unlock.png │ ├── layout │ ├── activity_main.xml │ └── row_user_spinner.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── art └── Android-Hint-Spinner.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── me │ │ └── srodrigo │ │ └── androidhintspinner │ │ ├── HintAdapterTest.java │ │ ├── HintSpinnerTest.java │ │ └── User.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── me │ │ └── srodrigo │ │ └── androidhintspinner │ │ ├── HintAdapter.java │ │ └── HintSpinner.java │ └── res │ └── values │ └── strings.xml ├── maven_push.gradle └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Android ### 4 | # Built application files 5 | *.apk 6 | *.ap_ 7 | 8 | # Files for the Dalvik VM 9 | *.dex 10 | 11 | # Java class files 12 | *.class 13 | 14 | # Generated files 15 | bin/ 16 | gen/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | /*/build/ 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | 26 | # Proguard folder generated by Eclipse 27 | proguard/ 28 | 29 | # Log Files 30 | *.log 31 | 32 | 33 | ### Intellij ### 34 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 35 | 36 | *.iml 37 | 38 | ## Directory-based project format: 39 | .idea/ 40 | # if you remove the above rule, at least ignore the following: 41 | 42 | # User-specific stuff: 43 | # .idea/workspace.xml 44 | # .idea/tasks.xml 45 | # .idea/dictionaries 46 | 47 | # Sensitive or high-churn files: 48 | # .idea/dataSources.ids 49 | # .idea/dataSources.xml 50 | # .idea/sqlDataSources.xml 51 | # .idea/dynamic.xml 52 | # .idea/uiDesigner.xml 53 | 54 | # Gradle: 55 | # .idea/gradle.xml 56 | # .idea/libraries 57 | 58 | # Mongo Explorer plugin: 59 | # .idea/mongoSettings.xml 60 | 61 | ## File-based project format: 62 | *.ipr 63 | *.iws 64 | 65 | ## Plugin-specific files: 66 | 67 | # IntelliJ 68 | out/ 69 | 70 | # mpeltonen/sbt-idea plugin 71 | .idea_modules/ 72 | 73 | # JIRA plugin 74 | atlassian-ide-plugin.xml 75 | 76 | # Crashlytics plugin (for Android Studio and IntelliJ) 77 | com_crashlytics_export_strings.xml 78 | crashlytics.properties 79 | crashlytics-build.properties 80 | 81 | 82 | ### Eclipse ### 83 | *.pydevproject 84 | .metadata 85 | .gradle 86 | bin/ 87 | tmp/ 88 | *.tmp 89 | *.bak 90 | *.swp 91 | *~.nib 92 | local.properties 93 | .settings/ 94 | .loadpath 95 | 96 | # Eclipse Core 97 | .project 98 | 99 | # External tool builders 100 | .externalToolBuilders/ 101 | 102 | # Locally stored "Eclipse launch configurations" 103 | *.launch 104 | 105 | # CDT-specific 106 | .cproject 107 | 108 | # JDT-specific (Eclipse Java Development Tools) 109 | .classpath 110 | 111 | # PDT-specific 112 | .buildpath 113 | 114 | # sbteclipse plugin 115 | .target 116 | 117 | # TeXlipse plugin 118 | .texlipse 119 | 120 | 121 | ### Gradle ### 122 | .gradle 123 | build/ 124 | 125 | # Ignore Gradle GUI config 126 | gradle-app.setting 127 | 128 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 129 | !gradle-wrapper.jar 130 | 131 | 132 | ### OSX ### 133 | .DS_Store 134 | .AppleDouble 135 | .LSOverride 136 | 137 | # Icon must end with two \r 138 | Icon 139 | 140 | 141 | # Thumbnails 142 | ._* 143 | 144 | # Files that might appear on external disk 145 | .Spotlight-V100 146 | .Trashes 147 | 148 | # Directories potentially created on remote AFP share 149 | .AppleDB 150 | .AppleDesktop 151 | Network Trash Folder 152 | Temporary Items 153 | .apdisk 154 | 155 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sergio Rodrigo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Hint Spinner 2 | 3 | ## Sample App 4 | ![Maven Central](https://img.shields.io/maven-central/v/me.srodrigo/androidhintspinner.svg) 5 | 6 | A hint spinner that allows showing a hint, similar to the HTML Select <option value="">Select your option</option> 7 | 8 | You can use either a default or a custom layout. 9 | 10 | ![](art/Android-Hint-Spinner.gif) 11 | 12 | ## Including the library 13 | Add this dependency to your build.gradle file: 14 | ```java 15 | dependencies { 16 | compile 'me.srodrigo:androidhintspinner:1.0.0' 17 | } 18 | ``` 19 | 20 | ## Usage 21 | ### Hint functionality 22 | The HintSpinner class works as a container, binding a plain Android Spinner and a HintAdapter, which manages the hint. A callback is needed at the moment to skip the hint selected event, as otherwise it would have to be handled by the user (this might be optional in future versions). 23 | 24 | All you need to do is to instantiate HintSpinner and call the init() method. This will automatically select the hint for you and bind the spinner to the adapter. You can select the hint again manually when you want, just calling the selectHint() method. 25 | 26 | ### Default layout 27 | The HintAdapter class uses the default spinner layout by default and no extra configuration is required. You just need to instanciate the HintSpinner with the default layout constructor: 28 | 29 | ```java 30 | HintSpinner hintSpinner = new HintSpinner<>( 31 | yourAndroidSpinner, 32 | // Default layout - You don't need to pass in any layout id, just your hint text and 33 | // your list data 34 | new HintAdapter<>(this, R.string.your_hint_text, yourStringsList), 35 | new HintSpinner.Callback() { 36 | @Override 37 | public void onItemSelected(int position, String itemAtPosition) { 38 | // Here you handle the on item selected event (this skips the hint selected event) 39 | } 40 | }); 41 | hintSpinner.init(); 42 | ``` 43 | 44 | ### Custom layout 45 | You can use whatever layout you want by overriding the HintAdapter#getCustomView() method. You can use any entity to store the data you want to show on your layout. 46 | ```java 47 | HintAdapter hintAdapter = new HintAdapter( 48 | this, 49 | R.layout.your_custom_layout, 50 | R.string.your_hint_text, 51 | usersList) { 52 | 53 | @Override 54 | protected View getCustomView(int position, View convertView, ViewGroup parent) { 55 | final User user = getItem(position); 56 | final String name = user.getName(); 57 | final String lastName = user.getLastName(); 58 | 59 | // Here you inflate the layout and set the value of your widgets 60 | View view = inflateLayout(parent, false); 61 | view.findViewById(R.id.user_image_view).setBackgroundResource( 62 | R.drawable.ic_action_face_unlock); 63 | ((TextView) view.findViewById(R.id.user_name_text_view)).setText(name); 64 | ((TextView) view.findViewById(R.id.user_last_name_text_view)).setText(lastName); 65 | 66 | return view; 67 | } 68 | } 69 | 70 | HintSpinner hintSpinner = new HintSpinner<>( 71 | yourAndroidSpinner, 72 | hintAdapter, 73 | new HintSpinner.Callback() { 74 | @Override 75 | public void onItemSelected(int position, String itemAtPosition) { 76 | // Here you handle the on item selected event (this skips the hint selected event) 77 | } 78 | }); 79 | hintSpinner.init(); 80 | ``` 81 | 82 | ## ChangeLog 83 | ** 1.0 ** 84 | Initial version. 85 | 86 | ## Developed by 87 | Sergio Rodrigo Royo 88 | 89 | Email: sergio.rodrigo.royo@gmail.com 90 | 91 | Twitter: @srodrigoDev 92 | 93 | ## License 94 | The MIT License (MIT) 95 | 96 | Copyright (c) 2015 Sergio Rodrigo 97 | 98 | Permission is hereby granted, free of charge, to any person obtaining a copy of 99 | this software and associated documentation files (the "Software"), to deal in 100 | the Software without restriction, including without limitation the rights to 101 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 102 | the Software, and to permit persons to whom the Software is furnished to do so, 103 | subject to the following conditions: 104 | 105 | The above copyright notice and this permission notice shall be included in all 106 | copies or substantial portions of the Software. 107 | 108 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 109 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 110 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 111 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 112 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 113 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 114 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion parent.ext.androidCompileSdkVersion 5 | buildToolsVersion parent.ext.androidBuildToolsVersion 6 | 7 | defaultConfig { 8 | minSdkVersion parent.ext.androidMinSdkVersion 9 | targetSdkVersion parent.ext.androidTargetSdkVersion 10 | versionCode parent.ext.androidVersionCode 11 | versionName parent.ext.androidVersionName 12 | } 13 | compileOptions { 14 | sourceCompatibility JavaVersion.VERSION_1_7 15 | targetCompatibility JavaVersion.VERSION_1_7 16 | } 17 | 18 | defaultConfig { 19 | applicationId "me.srodrigo.androidhintspinnersample" 20 | minSdkVersion parent.ext.androidMinSdkVersion 21 | targetSdkVersion parent.ext.androidTargetSdkVersion 22 | versionCode parent.ext.androidVersionCode 23 | versionName parent.ext.androidVersionName 24 | } 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | } 32 | 33 | dependencies { 34 | compile fileTree(dir: 'libs', include: ['*.jar']) 35 | compile parent.ext.libAppcompat 36 | compile project(':library') 37 | } 38 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/srodrigo/software/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/me/srodrigo/androidhintspinnersample/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Sergio Rodrigo 3 | * 4 | * This software may be modified and distributed under the terms 5 | * of the MIT license. See the LICENSE file for details. 6 | */ 7 | package me.srodrigo.androidhintspinnersample; 8 | 9 | import android.app.Application; 10 | import android.test.ApplicationTestCase; 11 | 12 | /** 13 | * Testing Fundamentals 14 | */ 15 | public class ApplicationTest extends ApplicationTestCase { 16 | public ApplicationTest() { 17 | super(Application.class); 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/me/srodrigo/androidhintspinnersample/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Sergio Rodrigo 3 | * 4 | * This software may be modified and distributed under the terms 5 | * of the MIT license. See the LICENSE file for details. 6 | */ 7 | package me.srodrigo.androidhintspinnersample; 8 | 9 | import android.os.Bundle; 10 | import android.support.v7.app.ActionBarActivity; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.Button; 14 | import android.widget.Spinner; 15 | import android.widget.TextView; 16 | import android.widget.Toast; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import me.srodrigo.androidhintspinner.HintAdapter; 22 | import me.srodrigo.androidhintspinner.HintSpinner; 23 | 24 | 25 | public class MainActivity extends ActionBarActivity { 26 | 27 | private static final String VALUE_STRING = "Value %d"; 28 | 29 | private HintSpinner defaultHintSpinner; 30 | private List defaults; 31 | 32 | private HintSpinner userHintSpinner; 33 | private List users; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_main); 39 | 40 | // Hint Spinner using default provided layout 41 | initDefaultHintSpinner(); 42 | 43 | // Hint Spinner using custom layout 44 | initUserHintSpinner(); 45 | } 46 | 47 | private void initDefaultHintSpinner() { 48 | defaults = new ArrayList<>(); 49 | defaults.add(String.format(VALUE_STRING, 1)); 50 | defaults.add(String.format(VALUE_STRING, 2)); 51 | defaults.add(String.format(VALUE_STRING, 3)); 52 | 53 | Spinner defaultSpinner = (Spinner) findViewById(R.id.default_spinner); 54 | 55 | defaultHintSpinner = new HintSpinner<>( 56 | defaultSpinner, 57 | // Default layout - You don't need to pass in any layout id, just your hint text and 58 | // your list data 59 | new HintAdapter<>(this, R.string.default_spinner_hint, defaults), 60 | new HintSpinner.Callback() { 61 | @Override 62 | public void onItemSelected(int position, String itemAtPosition) { 63 | // Here you handle the on item selected event (this skips the hint selected 64 | // event) 65 | showSelectedItem(itemAtPosition); 66 | } 67 | }); 68 | 69 | Button addMoreButton = (Button) findViewById(R.id.add_new_value_button); 70 | addMoreButton.setOnClickListener(new View.OnClickListener() { 71 | @Override 72 | public void onClick(View view) { 73 | String randomValue = String.format(VALUE_STRING, Util.generateRandomPositive()); 74 | defaults.add(randomValue); 75 | defaultHintSpinner.selectHint(); 76 | } 77 | }); 78 | defaultHintSpinner.init(); 79 | } 80 | 81 | private void initUserHintSpinner() { 82 | users = new ArrayList<>(); 83 | users.add(new User("Albert", "Einstein")); 84 | users.add(new User("Charles", "Darwin")); 85 | users.add(new User("Isaac", "Newton")); 86 | 87 | Spinner userSpinner = (Spinner) findViewById(R.id.user_spinner); 88 | 89 | userHintSpinner = new HintSpinner<>( 90 | userSpinner, 91 | // Custom layout - You can pass in a layout id and override the getCustomView method 92 | // to set the value of your widgets 93 | new HintAdapter( 94 | this, 95 | R.layout.row_user_spinner, 96 | R.string.user_spinner_hint, 97 | users) { 98 | 99 | @Override 100 | protected View getCustomView(int position, View convertView, ViewGroup parent) { 101 | final User user = getItem(position); 102 | final String name = user.getName(); 103 | final String lastName = user.getLastName(); 104 | 105 | // You need to inflate the layout. It will use the layout id form the 106 | // constructor 107 | View view = inflateLayout(parent, false); 108 | view.findViewById(R.id.user_image_view).setBackgroundResource( 109 | R.drawable.ic_action_face_unlock); 110 | ((TextView) view.findViewById(R.id.user_name_text_view)).setText(name); 111 | ((TextView) view.findViewById(R.id.user_last_name_text_view)).setText(lastName); 112 | 113 | return view; 114 | } 115 | }, 116 | new HintSpinner.Callback() { 117 | @Override 118 | public void onItemSelected(int position, User itemAtPosition) { 119 | // Here you handle the on item selected event (this skips the hint selected 120 | // event) 121 | Toast.makeText(MainActivity.this, "Selected " + itemAtPosition, Toast.LENGTH_SHORT).show(); 122 | } 123 | }); 124 | userHintSpinner.init(); 125 | 126 | Button addMoreButton = (Button) findViewById(R.id.add_new_user_button); 127 | addMoreButton.setOnClickListener(new View.OnClickListener() { 128 | @Override 129 | public void onClick(View view) { 130 | users.add(User.generateRandom()); 131 | userHintSpinner.selectHint(); 132 | } 133 | }); 134 | } 135 | 136 | private void showSelectedItem(String itemAtPosition) { 137 | Toast.makeText(this, "Selected " + itemAtPosition, Toast.LENGTH_SHORT).show(); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/src/main/java/me/srodrigo/androidhintspinnersample/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Sergio Rodrigo 3 | * 4 | * This software may be modified and distributed under the terms 5 | * of the MIT license. See the LICENSE file for details. 6 | */ 7 | package me.srodrigo.androidhintspinnersample; 8 | 9 | /** 10 | * Model class. 11 | */ 12 | public class User { 13 | private String name; 14 | private String lastName; 15 | 16 | public static User generateRandom() { 17 | int num = Util.generateRandomPositive(); 18 | return new User("Random user", String.valueOf(num)); 19 | } 20 | 21 | public User(String name, String lastName) { 22 | this.name = name; 23 | this.lastName = lastName; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getLastName() { 35 | return lastName; 36 | } 37 | 38 | public void setLastName(String lastName) { 39 | this.lastName = lastName; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "User{" + 45 | "name='" + name + '\'' + 46 | ", lastName='" + lastName + '\'' + 47 | '}'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/me/srodrigo/androidhintspinnersample/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Sergio Rodrigo 3 | * 4 | * This software may be modified and distributed under the terms 5 | * of the MIT license. See the LICENSE file for details. 6 | */ 7 | package me.srodrigo.androidhintspinnersample; 8 | 9 | import java.util.Random; 10 | 11 | public class Util { 12 | 13 | private static Random random = new Random(); 14 | 15 | public static int generateRandomPositive() { 16 | return Math.abs(random.nextInt()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_face_unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srodrigo/Android-Hint-Spinner/5ca10eae96d01bfd14f27467774d664c60be5cb9/app/src/main/res/drawable-hdpi/ic_action_face_unlock.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_face_unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srodrigo/Android-Hint-Spinner/5ca10eae96d01bfd14f27467774d664c60be5cb9/app/src/main/res/drawable-mdpi/ic_action_face_unlock.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_face_unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srodrigo/Android-Hint-Spinner/5ca10eae96d01bfd14f27467774d664c60be5cb9/app/src/main/res/drawable-xhdpi/ic_action_face_unlock.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_face_unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srodrigo/Android-Hint-Spinner/5ca10eae96d01bfd14f27467774d664c60be5cb9/app/src/main/res/drawable-xxhdpi/ic_action_face_unlock.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 16 | 17 | 22 | 23 | 28 | 29 |