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

Android Workshop

Android Workshop

Workshop on March 8th, 2017

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(); } }