Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Workshop

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Android Workshop

Workshop on March 8th, 2017

Avatar for Rúben Sousa

Rúben Sousa

March 08, 2017
Tweet

Other Decks in Programming

Transcript

  1. 2 Summary Android Framework Android Studio Making an App —

    Views and ViewGroups — Resources — Activities and Fragments — Intents — Project setup, Android Manifest and build.gradle — Creating and editing layouts — Adding icons — Installing and debugging an app — Display list of notes — Add new note — Delete notes
  2. XML attributes Views Text <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Text" /> Text

    <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Text" /> Text <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:text="Text" /> Width, height and text
  3. XML attributes Views Text Text Background, textColor and textSize <TextView

    android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/black" android:text="Text" android:textColor="@android:color/white" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Text" android:textSize="40sp"/>
  4. XML attributes Views Text <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="Text" />

    Margin and Padding 16dp Text <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingStart="16dp" android:text="Text" />
  5. ViewGroups Special views that can contain other views. A view

    inside a ViewGroup is called a child view. ViewGroup A View A View B https://developer.android.com/reference/android/view/ViewGroup.html
  6. ViewGroups A ViewGroup that displays views horizontally or vertically to

    each other View A View B LinearLayout View C LinearLayout with horizontal orientation https://developer.android.com/reference/android/widget/LinearLayout.html <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View A" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View B" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View C" /> </LinearLayout>
  7. ViewGroups A ViewGroup that displays views in relation with each

    other or to the parent View A RelativeLayout RelativeLayout https://developer.android.com/reference/android/widget/RelativeLayout.html View B <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/viewA" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:text="View A" /> <TextView android:id="@+id/viewB" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/viewA" android:layout_toRightOf="@id/viewA" android:text="View B" /> </RelativeLayout>
  8. ViewGroups A ViewGroup that displays views on top of each

    other FrameLayout FrameLayout View A View B https://developer.android.com/reference/android/widget/FrameLayout.html <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <View android:layout_width="match_parent" android:layout_height="match_parent"/> <View android:layout_width="50dp" android:layout_height="50dp" android:background="@android:color/black" /> </FrameLayout>
  9. ViewGroups A FrameLayout with rounded corners and a shadow CardView

    CardView https://developer.android.com/reference/android/support/v7/widget/CardView.html
  10. Colors Resources <resources> <color name="colorPrimary">#2196F3</color> <color name="colorPrimaryDark">#1976D2</color> <color name="colorAccent">#FF9800</color> </resources>

    Usually defined in values/colors.xml or color/colors.xml <View android:id="@+id/view" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimary"/>
  11. Drawable Resources res/drawable Vector drawable: ic_menu.xml <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp"

    android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="#FF000000" android:pathData="M3,18h18v-2L3,16v2zM3,13h18v- 2L3,11v2zM3,6v2h18L21,6L3,6z"/> </vector> PNG, JPG, GIF and WebP are also supported Useful websites for icons: https://material.io/icons/ http://romannurik.github.io/AndroidAssetStudio/ https://materialdesignicons.com/
  12. Strings Resources res/values/strings.xml strings.xml <resources> <string name="app_name">Transitions</string> </resources> <TextView android:layout_width="match_parent"

    android:layout_height="wrap_content" android:text="Transitions"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Insert long text here. Wow, can’t even format the xml properly. Wow. Is this enough to prove a point?"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/app_name"/>
  13. Themes Resources res/values/styles.xml <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item

    name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources> https://developer.android.com/training/material/images/ThemeColors.png Useful website: https://www.materialpalette.com/
  14. Activity The main window of an app https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/a ndroid/app/Activity.java God

    object with more than 7000 lines public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
  15. Bind and setup views public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView; private FloatingActionButton fab; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); } } Life cycle Activity onCreate
  16. Life cycle Activity onSaveInstanceState and onRestoreInstanceState Save and restore state

    @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("number", number); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState){ super.onRestoreInstanceState(savedInstanceState); int number = savedInstanceState.getInt("number"); }
  17. Fragment A section of the UI, managed by an Activity

    https://developer.android.com/training/basics/fragments/fragment-ui.html
  18. public class CustomFragment extends Fragment { private TextView textView; @Nullable

    @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.content_main, container, false); textView = (TextView) view.findViewById(R.id.textView); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (savedInstanceState != null) { // Restore state } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Save state } } Fragment Sample code
  19. Fragment Sample code public class MainActivity extends AppCompatActivity { @Override

    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { CustomFragment fragment = new CustomFragment(); getSupportFragmentManager() .beginTransaction() .add(R.id.container, fragment) .commit(); } } }
  20. Intents Object used to perform an action https://developer.android.com/reference/android/content/Intent.html Start SecondActivity

    from an Activity Start SecondActivity from a Fragment Open URL in browser Intent intent = new Intent(this, SecondActivity.class); startActivity(intent); Intent intent = new Intent(getActivity(), SecondActivity.class); startActivity(intent); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.pt")); startActivity(intent);
  21. https://developer.android.com/reference/android/content/Intent.html Start SecondActivity from MainActivity and return an integer named

    “number” In SecondActivity Intent intent = new Intent(this, SecondActivity.class); startActivityForResult(intent, SecondActivity.REQUEST_CODE); In MainActivity In MainActivity public void save() { Intent data = new Intent(); data.putExtra("number", DEFAULT_NUMBER); setResult(RESULT_OK, data); finish(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == SecondActivity.REQUEST_CODE && resultCode == RESULT_OK && data != null) { int number = data.getIntExtra("number", 0); } } Intents Object used to perform an action
  22. Edit the Toolbar and the FloatingActionButton in activity_main.xml 43 <android.support.v7.widget.Toolbar

    android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:title="@string/app_name" app:popupTheme="@style/AppTheme.PopupOverlay" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@drawable/ic_fab_add" /> Set the title Add the plus icon
  23. Add a RecyclerView in activity_main.xml 44 Make sure it’s a

    direct child of CoordinatorLayout <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <!-- AppBarLayout and FloatingActionButton are below --> </android.support.design.widget.CoordinatorLayout>
  24. Edit the menu_main.xml and add the delete action 45 <menu

    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_delete" android:icon="@drawable/ic_toolbar_delete" android:orderInCategory="100" android:title="@string/action_delete" app:showAsAction="ifRoom" /> </menu>
  25. Inflate the Toolbar’s menu 46 public class MainActivity extends AppCompatActivity

    { private Toolbar toolbar; private RecyclerView recyclerView; private FloatingActionButton fab; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindViews(); // Setup menu toolbar.inflateMenu(R.menu.menu_main); } private void bindViews() { toolbar = (Toolbar) findViewById(R.id.toolbar); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); fab = (FloatingActionButton) findViewById(R.id.fab); } } Now launch the app and you should have the same as screen in the left!
  26. Create a new layout called note_adapter.xml 47 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"

    android:layout_height="wrap_content" android:layout_marginEnd="@dimen/activity_horizontal_margin" android:layout_marginStart="@dimen/activity_horizontal_margin" android:layout_marginTop="@dimen/activity_vertical_margin"> The root layout can be a RelativeLayout Add the clear icon <ImageButton android:id="@+id/deleteButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:background="?attr/selectableItemBackground" android:padding="8dp" android:src="@drawable/ic_clear" />
  27. Add the TextViews 48 <TextView android:id="@+id/titleTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_toStartOf="@id/deleteButton"

    android:ellipsize="end" android:maxLines="1" android:textAppearance="@style/Base.TextAppearance.AppCompat.Body2"/> Note title Note content <TextView android:id="@+id/contentTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_below="@id/titleTextView" android:layout_marginTop="4dp" android:layout_toStartOf="@id/deleteButton" android:textAppearance="@style/Base.TextAppearance.AppCompat.Body1" />
  28. Copy activity_main.xml to activity_note.xml and modify it to create this

    screen 49 <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:navigationIcon="@drawable/ic_toolbar_back“ app:title="@string/add_note_title" app:popupTheme="@style/AppTheme.PopupOverlay" /> Set the title and the navigation icon Change the FloatingActionButton’s icon
  29. Create a new layout named activity_note_content.xml 50 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"

    android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingEnd="@dimen/activity_horizontal_margin" android:paddingStart="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> Add a vertical oriented LinearLayout as root
  30. Add the Title and Content EditTexts 51 <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content"

    android:orientation="vertical" app:counterEnabled="true" app:counterMaxLength="20"> <android.support.design.widget.TextInputEditText android:id="@+id/titleEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/add_note_field_title" android:inputType="text" android:maxLines="1"/> </android.support.design.widget.TextInputLayout> Title
  31. Add the Title and Content EditTexts 52 <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content"

    android:orientation="vertical" app:counterEnabled="true" app:counterMaxLength="100"> <android.support.design.widget.TextInputEditText android:id="@+id/contentEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/add_note_field_content" android:inputType="textAutoComplete|textMultiLine"/> </android.support.design.widget.TextInputLayout> Content
  32. Create a NoteActivity class 53 public class NoteActivity extends AppCompatActivity

    { public static final int REQUEST_ADD = 2; public static final String RESULT_NOTE = "result_note"; private Toolbar toolbar; private FloatingActionButton fab; private EditText titleEditText; private EditText contentEditText; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_note); bindViews(); // Finish on back click toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); } private void bindViews() { contentEditText = (EditText) findViewById(R.id.contentEditText); titleEditText = (EditText) findViewById(R.id.titleEditText); fab = (FloatingActionButton) findViewById(R.id.fab); toolbar = (Toolbar) findViewById(R.id.toolbar); } }
  33. Add NoteActivity to the AndroidManifest 54 <activity android:name=".MainActivity"> <intent-filter> <action

    android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".NoteActivity" android:windowSoftInputMode="adjustResize" />
  34. Launch NoteActivity from MainActivity 55 public class MainActivity extends AppCompatActivity

    { private Toolbar toolbar; private RecyclerView recyclerView; private FloatingActionButton fab; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindViews(); // Setup menu toolbar.inflateMenu(R.menu.menu_main); // Setup fab click fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { launchAddNote(); } }); } private void launchAddNote() { Intent intent = new Intent(this, NoteActivity.class); startActivityForResult(intent, NoteActivity.REQUEST_ADD); } } Build the app and launch the NoteActivity by pressing the FloatingActionButton
  35. Create a Note class 56 public class Note { private

    String title; private String content; public Note() { title = ""; content = ""; } public String getContent() { return content; } public String getTitle() { return title; } public void setContent(String content) { this.content = content; } public void setTitle(String title) { this.title = title; } }
  36. Implement Parcelable in the Note class 57 public class Note

    implements Parcelable { @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(title); dest.writeString(content); } protected Note(Parcel in) { title = in.readString(); content = in.readString(); } public static final Creator<Note> CREATOR = new Creator<Note>() { @Override public Note createFromParcel(Parcel in) { return new Note(in); } @Override public Note[] newArray(int size) { return new Note[size]; } }; } Android Studio should autogenerate the Parcelable code.
  37. Create a NoteAdapter class 58 public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.ViewHolder>

    { private ArrayList<Note> notes; public NoteAdapter() { notes = new ArrayList<>(); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.note_adapter, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.bind(notes.get(position)); } @Override public int getItemCount() { return notes.size(); } public class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View itemView) { super(itemView); } public void bind(Note note) { } } }
  38. Complete the ViewHolder 59 public class ViewHolder extends RecyclerView.ViewHolder {

    private TextView titleTextView; private TextView contentTextView; public ViewHolder(View itemView) { super(itemView); titleTextView = (TextView) itemView.findViewById(R.id.titleTextView); contentTextView = (TextView) itemView.findViewById(R.id.contentTextView); } public void bind(Note note) { titleTextView.setText(note.getTitle()); contentTextView.setText(note.getContent()); } }
  39. Setup the RecyclerView in MainActivity 60 public class MainActivity extends

    AppCompatActivity { private Toolbar toolbar; private RecyclerView recyclerView; private NoteAdapter noteAdapter; private FloatingActionButton fab; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindViews(); // Setup RecyclerView noteAdapter = new NoteAdapter(); recyclerView.setAdapter(noteAdapter); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setHasFixedSize(true); } private void bindViews() { toolbar = (Toolbar) findViewById(R.id.toolbar); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); fab = (FloatingActionButton) findViewById(R.id.fab); } }
  40. Go back to NoteActivity and return the Note 61 @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { save(); } }); } private void save() { String title = titleEditText.getText().toString().trim(); String content = contentEditText.getText().toString().trim(); // Check if we have both title and content if (!content.isEmpty() && !title.isEmpty()) { Note note = new Note(); note.setTitle(title); note.setContent(content); Intent data = new Intent(); data.putExtra(RESULT_NOTE, note); setResult(RESULT_OK, data); finish(); } }
  41. Add the Note to the adapter 62 public class NoteAdapter

    extends RecyclerView.Adapter<NoteAdapter.ViewHolder> { public void addNote(Note note) { notes.add(note); notifyItemInserted(notes.size()); } } Implement the addNote method in NoteAdapter public class MainActivity extends AppCompatActivity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == NoteActivity.REQUEST_ADD && resultCode == RESULT_OK && data != null) { noteAdapter.addNote((Note) data.getParcelableExtra(NoteActivity.RESULT_NOTE)); } } }
  42. Deleting a note 64 public class ViewHolder extends RecyclerView.ViewHolder implements

    View.OnClickListener { private TextView titleTextView; private TextView contentTextView; public ViewHolder(View itemView) { super(itemView); itemView.findViewById(R.id.deleteButton).setOnClickListener(this); titleTextView = (TextView) itemView.findViewById(R.id.titleTextView); contentTextView = (TextView) itemView.findViewById(R.id.contentTextView); } public void bind(Note note) { titleTextView.setText(note.getTitle()); contentTextView.setText(note.getContent()); } @Override public void onClick(View v) { // Delete was clicked int position = getAdapterPosition(); notes.remove(position); notifyItemRemoved(position); } }
  43. Deleting all notes 65 public class MainActivity extends AppCompatActivity implements

    Toolbar.OnMenuItemClickListener { private Toolbar toolbar; private RecyclerView recyclerView; private NoteAdapter noteAdapter; private FloatingActionButton fab; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindViews(); // Setup menu toolbar.inflateMenu(R.menu.menu_main); toolbar.setOnMenuItemClickListener(this); } @Override public boolean onMenuItemClick(MenuItem item) { if (item.getItemId() == R.id.action_delete) { noteAdapter.deleteAll(); } return true; } private void bindViews() { toolbar = (Toolbar) findViewById(R.id.toolbar); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); fab = (FloatingActionButton) findViewById(R.id.fab); } }
  44. Deleting all notes 66 public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.ViewHolder> {

    private ArrayList<Note> notes; public NoteAdapter() { notes = new ArrayList<>(); } public void addNote(Note note) { notes.add(note); notifyItemInserted(notes.size()); } public void deleteAll() { notes.clear(); notifyDataSetChanged(); } }