├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── thirtysparks │ │ └── tutorial │ │ └── designlib │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── thirtysparks │ │ └── tutorial │ │ └── designlib │ │ ├── AnotherFabActivity.java │ │ ├── AppBarActivity.java │ │ ├── Contact.java │ │ ├── ContactsAdapter.java │ │ ├── DividerItemDecoration.java │ │ ├── FloatingActionButtonBehavior.java │ │ ├── MainActivity.java │ │ ├── ScrollingFabBehavior.java │ │ ├── TabActivity.java │ │ ├── TabFragment.java │ │ ├── TabPagerAdapter.java │ │ └── ViewPagerIndicatorActivity.java │ └── res │ ├── drawable-hdpi │ └── ic_add_circle_outline_white.png │ ├── drawable-mdpi │ └── ic_add_circle_outline_white.png │ ├── drawable-nodpi │ └── dog.jpg │ ├── drawable-xhdpi │ └── ic_add_circle_outline_white.png │ ├── drawable-xxhdpi │ └── ic_add_circle_outline_white.png │ ├── drawable │ ├── ic_launcher.png │ └── tab_background.xml │ ├── layout │ ├── activity_another_fab.xml │ ├── activity_appbar.xml │ ├── activity_main.xml │ ├── activity_tab.xml │ ├── activity_view_pager_indicator.xml │ ├── drawer_header.xml │ ├── fragment_tab.xml │ └── item_contact.xml │ ├── menu │ ├── drawer.xml │ ├── menu_another_fab.xml │ ├── menu_appbar.xml │ └── menu_main.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 │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Goofyz Leung 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Material Design Pattern Tutorial 2 | 3 | A sample app for the tutorial of Android Material Design Pattern. You can refer to the 4 | [detail tutorial](http://blog.30sparks.com/material-design-patterns-tutorial/?utm_source=github&utm_medium=web&utm_campaign=material), but please note that 5 | all tutorial are written in Chinese. 6 | 7 | ## Part 1: Navigation Drawer 8 | 9 | Adding `DrawerLayout` and `NavigationView` to create the Navigation Drawer. 10 | 11 | [Tutorial Part 1](http://blog.30sparks.com/material-design-1-navigation-drawer/?utm_source=github&utm_medium=web&utm_campaign=material) 12 | 13 | ## Part 2: Floating Action Button 14 | 15 | Add `FloatingActionButton` to `MainActivity`. 16 | 17 | [Tutorial Part 2](http://blog.30sparks.com/material-design-2-floating-action-button/?utm_source=github&utm_medium=web&utm_campaign=material) 18 | 19 | ## Part 3: `Snackbar` and `CoordinatorLayout` 20 | 21 | Use `Snackbar` to alert user and `CoordinatorLayout` to play nice with `FloatingActionButton`. 22 | 23 | [Tutorial Part 3](http://blog.30sparks.com/material-design-3-snackbar-coordinatorlayout/?utm_source=github&utm_medium=web&utm_campaign=material) 24 | 25 | ## Part 4: `RecyclerView` 26 | 27 | Use `RecyclerView` to replace `ListView`. 28 | 29 | [Tutorial Part 4](http://blog.30sparks.com/material-design-4-recyclerview/?utm_source=github&utm_medium=web&utm_campaign=material) 30 | 31 | ## Part 5: `AppBarLayout` 32 | 33 | Animate the `Toolbar` with `AppBarLayout`. 34 | 35 | [Tutorial Part 5](http://blog.30sparks.com/material-design-5-appbarlayout/?utm_source=github&utm_medium=web&utm_campaign=material) 36 | 37 | ## Part 6: `TextInputLayout` 38 | 39 | Enhance the `EditText` with `TextInputLayout`. 40 | 41 | [Tutorial Part 6](http://blog.30sparks.com/material-design-6-textinputlayout/?utm_source=github&utm_medium=web&utm_campaign=material) 42 | 43 | ## Part 7: `TabLayout` 44 | 45 | `TabLayout` for, well, tab. 46 | 47 | [Tutorial Part 7](http://blog.30sparks.com/material-design-7-tablayout/?utm_source=github&utm_medium=web&utm_campaign=material) 48 | 49 | ## Part 8: Coming soon... -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.thirtysparks.tutorial.designlib" 9 | minSdkVersion 15 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:23.1.1' 25 | compile 'com.android.support:recyclerview-v7:23.1.1' 26 | compile 'com.android.support:design:23.1.1' 27 | compile 'com.github.clans:fab:1.6.0' 28 | compile 'jp.wasabeef:recyclerview-animators:1.2.0@aar' 29 | compile 'com.viewpagerindicator:library:2.4.1@aar' 30 | } 31 | -------------------------------------------------------------------------------- /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 D:\Software\android-sdk\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/com/thirtysparks/tutorial/designlib/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 26 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/AnotherFabActivity.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.support.design.widget.Snackbar; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.support.v7.widget.Toolbar; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.view.View; 10 | import android.widget.TextView; 11 | 12 | import com.github.clans.fab.FloatingActionButton; 13 | 14 | public class AnotherFabActivity extends AppCompatActivity { 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_another_fab); 20 | 21 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 22 | setSupportActionBar(toolbar); 23 | 24 | final TextView contentView = (TextView)findViewById(R.id.content_view); 25 | FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab); 26 | fab.setOnClickListener(new View.OnClickListener() { 27 | @Override 28 | public void onClick(View v) { 29 | Snackbar.make(contentView, R.string.another_fab_snackbar, Snackbar.LENGTH_SHORT).show(); 30 | } 31 | }); 32 | } 33 | 34 | @Override 35 | public boolean onCreateOptionsMenu(Menu menu) { 36 | // Inflate the menu; this adds items to the action bar if it is present. 37 | getMenuInflater().inflate(R.menu.menu_another_fab, menu); 38 | return true; 39 | } 40 | 41 | @Override 42 | public boolean onOptionsItemSelected(MenuItem item) { 43 | // Handle action bar item clicks here. The action bar will 44 | // automatically handle clicks on the Home/Up button, so long 45 | // as you specify a parent activity in AndroidManifest.xml. 46 | int id = item.getItemId(); 47 | 48 | //noinspection SimplifiableIfStatement 49 | if (id == R.id.action_settings) { 50 | return true; 51 | } 52 | 53 | return super.onOptionsItemSelected(item); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/AppBarActivity.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.app.AlertDialog; 4 | import android.os.Bundle; 5 | import android.support.design.widget.FloatingActionButton; 6 | import android.support.design.widget.Snackbar; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.GridLayoutManager; 9 | import android.support.v7.widget.LinearLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.support.v7.widget.Toolbar; 12 | import android.view.Menu; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | 16 | import java.util.List; 17 | 18 | 19 | public class AppBarActivity extends AppCompatActivity { 20 | private List mContacts; 21 | private ContactsAdapter adapter; 22 | private RecyclerView rvContacts; 23 | 24 | @Override 25 | public void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_appbar); 28 | 29 | setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); 30 | 31 | mContacts = Contact.generateSampleList(); 32 | 33 | RecyclerView.LayoutManager layoutManager; 34 | //layoutManager = new GridLayoutManager(this, 3); 35 | layoutManager = new LinearLayoutManager(this); 36 | 37 | rvContacts = (RecyclerView) findViewById(R.id.recyclerView); 38 | adapter = new ContactsAdapter(mContacts); 39 | rvContacts.setAdapter(adapter); 40 | rvContacts.setLayoutManager(layoutManager); 41 | 42 | RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST); 43 | rvContacts.addItemDecoration(itemDecoration); 44 | 45 | initFab(); 46 | } 47 | @Override 48 | public boolean onCreateOptionsMenu(Menu menu) { 49 | // Inflate the menu; this adds items to the action bar if it is present. 50 | getMenuInflater().inflate(R.menu.menu_appbar, menu); 51 | return true; 52 | } 53 | 54 | @Override 55 | public boolean onOptionsItemSelected(MenuItem item) { 56 | int id = item.getItemId(); 57 | 58 | switch (id){ 59 | case R.id.action_add: 60 | addContacts(); 61 | return true; 62 | case R.id.action_remove: 63 | removeContacts(); 64 | return true; 65 | } 66 | 67 | return super.onOptionsItemSelected(item); 68 | } 69 | 70 | private void initFab(){ 71 | FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab); 72 | fab.setOnClickListener(new View.OnClickListener() { 73 | @Override 74 | public void onClick(View v) { 75 | Snackbar.make(rvContacts, "I am snackbar", Snackbar.LENGTH_SHORT) 76 | .setAction("Undo", new View.OnClickListener() { 77 | @Override 78 | public void onClick(View v) { 79 | new AlertDialog.Builder(AppBarActivity.this) 80 | .setMessage("Undo pressed") 81 | .setNeutralButton("OK", null) 82 | .show(); 83 | } 84 | }) 85 | .show(); 86 | } 87 | }); 88 | } 89 | 90 | private void addContacts(){ 91 | Contact contact = new Contact(); 92 | contact.setName("Frodo Baggins"); 93 | 94 | mContacts.add(1, contact); 95 | adapter.notifyItemInserted(1); 96 | } 97 | 98 | private void removeContacts(){ 99 | mContacts.remove(mContacts.size() - 1); 100 | adapter.notifyItemRemoved(mContacts.size()); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/Contact.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Contact { 7 | private String name; 8 | 9 | public Contact() { 10 | } 11 | 12 | public String getName() { 13 | return name; 14 | } 15 | 16 | public void setName(String name) { 17 | this.name = name; 18 | } 19 | 20 | public static List generateSampleList(){ 21 | List list = new ArrayList<>(); 22 | for(int i=0; i < 30; i++){ 23 | Contact contact = new Contact(); 24 | contact.setName("Name - " + i); 25 | list.add(contact); 26 | } 27 | return list; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/ContactsAdapter.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.Snackbar; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | import java.util.List; 12 | 13 | public class ContactsAdapter extends RecyclerView.Adapter { 14 | public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ 15 | public TextView nameTextView; 16 | public MyViewHolderClick mListener; 17 | public ViewHolder(View itemView, MyViewHolderClick listener){ 18 | super(itemView); 19 | mListener = listener; 20 | 21 | nameTextView = (TextView) itemView.findViewById(R.id.tv_name); 22 | itemView.setOnClickListener(this); 23 | } 24 | 25 | @Override 26 | public void onClick(View v) { 27 | mListener.clickOnView(v, getLayoutPosition()); 28 | } 29 | 30 | public interface MyViewHolderClick { 31 | void clickOnView(View v, int position); 32 | } 33 | } 34 | 35 | private List mContacts; 36 | 37 | public ContactsAdapter(List contacts){ 38 | mContacts = contacts; 39 | } 40 | 41 | @Override 42 | public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { 43 | Context context = viewGroup.getContext(); 44 | View contactView = LayoutInflater.from(context).inflate(R.layout.item_contact, viewGroup, false); 45 | 46 | ViewHolder viewHolder = new ViewHolder(contactView, new ViewHolder.MyViewHolderClick() { 47 | @Override 48 | public void clickOnView(View v, int position) { 49 | Contact contact = mContacts.get(position); 50 | Snackbar.make(v, contact.getName(), Snackbar.LENGTH_LONG).show(); 51 | } 52 | }); 53 | 54 | return viewHolder; 55 | } 56 | 57 | @Override 58 | public void onBindViewHolder(ViewHolder viewHolder, int position) { 59 | Contact contact = mContacts.get(position); 60 | TextView nameTextView = viewHolder.nameTextView; 61 | nameTextView.setText(contact.getName()); 62 | } 63 | 64 | @Override 65 | public int getItemCount() { 66 | return mContacts.size(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/DividerItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.View; 11 | 12 | /** 13 | * Created by Ryan on 23/11/2015. 14 | */ 15 | /* 16 | * Copyright (C) 2014 The Android Open Source Project 17 | * 18 | * Licensed under the Apache License, Version 2.0 (the "License"); 19 | * you may not use this file except in compliance with the License. 20 | * You may obtain a copy of the License at 21 | * 22 | * http://www.apache.org/licenses/LICENSE-2.0 23 | * 24 | * Unless required by applicable law or agreed to in writing, software 25 | * distributed under the License is distributed on an "AS IS" BASIS, 26 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 27 | * See the License for the specific language governing permissions and 28 | * limitations under the License. 29 | */ 30 | public class DividerItemDecoration extends RecyclerView.ItemDecoration { 31 | 32 | private static final int[] ATTRS = new int[]{ 33 | android.R.attr.listDivider 34 | }; 35 | 36 | public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; 37 | 38 | public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; 39 | 40 | private Drawable mDivider; 41 | 42 | private int mOrientation; 43 | 44 | public DividerItemDecoration(Context context, int orientation) { 45 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 46 | mDivider = a.getDrawable(0); 47 | a.recycle(); 48 | setOrientation(orientation); 49 | } 50 | 51 | public void setOrientation(int orientation) { 52 | if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { 53 | throw new IllegalArgumentException("invalid orientation"); 54 | } 55 | mOrientation = orientation; 56 | } 57 | 58 | @Override 59 | public void onDraw(Canvas c, RecyclerView parent) { 60 | if (mOrientation == VERTICAL_LIST) { 61 | drawVertical(c, parent); 62 | } else { 63 | drawHorizontal(c, parent); 64 | } 65 | } 66 | 67 | public void drawVertical(Canvas c, RecyclerView parent) { 68 | final int left = parent.getPaddingLeft(); 69 | final int right = parent.getWidth() - parent.getPaddingRight(); 70 | 71 | final int childCount = parent.getChildCount(); 72 | for (int i = 0; i < childCount; i++) { 73 | final View child = parent.getChildAt(i); 74 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 75 | .getLayoutParams(); 76 | final int top = child.getBottom() + params.bottomMargin; 77 | final int bottom = top + mDivider.getIntrinsicHeight(); 78 | mDivider.setBounds(left, top, right, bottom); 79 | mDivider.draw(c); 80 | } 81 | } 82 | 83 | public void drawHorizontal(Canvas c, RecyclerView parent) { 84 | final int top = parent.getPaddingTop(); 85 | final int bottom = parent.getHeight() - parent.getPaddingBottom(); 86 | 87 | final int childCount = parent.getChildCount(); 88 | for (int i = 0; i < childCount; i++) { 89 | final View child = parent.getChildAt(i); 90 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 91 | .getLayoutParams(); 92 | final int left = child.getRight() + params.rightMargin; 93 | final int right = left + mDivider.getIntrinsicHeight(); 94 | mDivider.setBounds(left, top, right, bottom); 95 | mDivider.draw(c); 96 | } 97 | } 98 | 99 | @Override 100 | public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { 101 | if (mOrientation == VERTICAL_LIST) { 102 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 103 | } else { 104 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); 105 | } 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/FloatingActionButtonBehavior.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.CoordinatorLayout; 5 | import android.support.design.widget.Snackbar; 6 | import android.util.AttributeSet; 7 | import android.view.View; 8 | import com.github.clans.fab.FloatingActionButton; 9 | 10 | public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior { 11 | public FloatingActionButtonBehavior(Context context, AttributeSet attributeSet){ 12 | } 13 | 14 | @Override 15 | public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) { 16 | return dependency instanceof Snackbar.SnackbarLayout; 17 | } 18 | 19 | @Override 20 | public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) { 21 | float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight()); 22 | child.setTranslationY(translationY); 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.Intent; 5 | import android.support.design.widget.FloatingActionButton; 6 | import android.support.design.widget.NavigationView; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.design.widget.TextInputLayout; 9 | import android.support.v4.widget.DrawerLayout; 10 | import android.os.Bundle; 11 | import android.support.v7.app.ActionBarDrawerToggle; 12 | import android.support.v7.app.AppCompatActivity; 13 | import android.support.v7.widget.Toolbar; 14 | import android.view.Menu; 15 | import android.view.MenuItem; 16 | import android.view.View; 17 | import android.widget.Button; 18 | import android.widget.EditText; 19 | import android.widget.TextView; 20 | 21 | 22 | public class MainActivity extends AppCompatActivity { 23 | private static final String NAV_ITEM_ID = "nav_index"; 24 | 25 | DrawerLayout drawerLayout; 26 | TextView contentView; 27 | 28 | private int navItemId; 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_main); 34 | 35 | initDrawerLayout(); 36 | initNavView(savedInstanceState); 37 | initFab(); 38 | initEditText(); 39 | } 40 | 41 | @Override 42 | public boolean onCreateOptionsMenu(Menu menu) { 43 | // Inflate the menu; this adds items to the action bar if it is present. 44 | getMenuInflater().inflate(R.menu.menu_main, menu); 45 | return true; 46 | } 47 | 48 | @Override 49 | public boolean onOptionsItemSelected(MenuItem item) { 50 | // Handle action bar item clicks here. The action bar will 51 | // automatically handle clicks on the Home/Up button, so long 52 | // as you specify a parent activity in AndroidManifest.xml. 53 | int id = item.getItemId(); 54 | 55 | //noinspection SimplifiableIfStatement 56 | if (id == R.id.action_settings) { 57 | return true; 58 | } 59 | 60 | return super.onOptionsItemSelected(item); 61 | } 62 | 63 | private void initDrawerLayout(){ 64 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 65 | setSupportActionBar(toolbar); 66 | 67 | contentView = (TextView) findViewById(R.id.content_view); 68 | drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); 69 | 70 | ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.openDrawer, R.string.closeDrawer){ 71 | @Override 72 | public void onDrawerClosed(View drawerView) { 73 | super.onDrawerClosed(drawerView); 74 | } 75 | 76 | @Override 77 | public void onDrawerOpened(View drawerView) { 78 | super.onDrawerOpened(drawerView); 79 | } 80 | }; 81 | 82 | drawerLayout.setDrawerListener(actionBarDrawerToggle); 83 | actionBarDrawerToggle.syncState(); 84 | } 85 | 86 | private void initNavView(Bundle savedInstanceState){ 87 | NavigationView view = (NavigationView) findViewById(R.id.navigation_view); 88 | view.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { 89 | @Override 90 | public boolean onNavigationItemSelected(MenuItem menuItem) { 91 | Snackbar.make(contentView, menuItem.getTitle() + " pressed", Snackbar.LENGTH_LONG).show(); 92 | navigateTo(menuItem); 93 | 94 | drawerLayout.closeDrawers(); 95 | return true; 96 | } 97 | }); 98 | 99 | if(null != savedInstanceState){ 100 | navItemId = savedInstanceState.getInt(NAV_ITEM_ID, R.id.navigation_item_1); 101 | } 102 | else{ 103 | navItemId = R.id.navigation_item_1; 104 | } 105 | 106 | navigateTo(view.getMenu().findItem(navItemId)); 107 | } 108 | 109 | private void initFab(){ 110 | FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab); 111 | fab.setOnClickListener(new View.OnClickListener() { 112 | @Override 113 | public void onClick(View v) { 114 | Snackbar.make(contentView, "I am snackbar", Snackbar.LENGTH_SHORT) 115 | .setAction("Undo", new View.OnClickListener() { 116 | @Override 117 | public void onClick(View v) { 118 | new AlertDialog.Builder(MainActivity.this) 119 | .setMessage("Undo pressed") 120 | .setNeutralButton("OK", null) 121 | .show(); 122 | } 123 | }) 124 | .show(); 125 | } 126 | }); 127 | } 128 | 129 | private void navigateTo(MenuItem menuItem){ 130 | switch(menuItem.getItemId()){ 131 | case R.id.navigation_item_another_fab: 132 | startActivity(new Intent(this, AnotherFabActivity.class)); 133 | break; 134 | case R.id.navigation_item_appbar: 135 | startActivity(new Intent(this, AppBarActivity.class)); 136 | break; 137 | case R.id.navigation_item_tab: 138 | startActivity(new Intent(this, TabActivity.class)); 139 | break; 140 | case R.id.navigation_item_vpi: 141 | startActivity(new Intent(this, ViewPagerIndicatorActivity.class)); 142 | break; 143 | default: 144 | contentView.setText(menuItem.getTitle()); 145 | 146 | navItemId = menuItem.getItemId(); 147 | menuItem.setChecked(true); 148 | } 149 | } 150 | 151 | @Override 152 | protected void onSaveInstanceState(Bundle outState) { 153 | super.onSaveInstanceState(outState); 154 | outState.putInt(NAV_ITEM_ID, navItemId); 155 | } 156 | 157 | private void initEditText(){ 158 | final TextInputLayout nameLayout = (TextInputLayout)findViewById(R.id.til_et_name); 159 | final TextInputLayout messageLayout = (TextInputLayout)findViewById(R.id.til_et_message); 160 | final EditText nameEditText = (EditText)findViewById(R.id.et_name); 161 | final EditText messageEditText = (EditText)findViewById(R.id.et_message); 162 | final Button button = (Button) findViewById(R.id.btn_submit); 163 | 164 | button.setOnClickListener(new View.OnClickListener() { 165 | @Override 166 | public void onClick(View view) { 167 | if(nameEditText.length() == 0){ 168 | nameLayout.setError("Error in name input"); 169 | } 170 | else{ 171 | nameLayout.setError(null); 172 | } 173 | } 174 | }); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/ScrollingFabBehavior.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.AppBarLayout; 5 | import android.support.design.widget.CoordinatorLayout; 6 | import android.support.design.widget.FloatingActionButton; 7 | import android.util.AttributeSet; 8 | import android.util.TypedValue; 9 | import android.view.View; 10 | 11 | /** 12 | * Created by Ryan on 17/12/2015. 13 | */ 14 | public class ScrollingFabBehavior extends CoordinatorLayout.Behavior { 15 | private int toolbarHeight; 16 | 17 | public ScrollingFabBehavior(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | this.toolbarHeight = getToolbarHeight(context); 20 | } 21 | 22 | private int getToolbarHeight(Context context){ 23 | int height = 0; 24 | TypedValue tv = new TypedValue(); 25 | if(context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)){ 26 | height = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics()); 27 | } 28 | return height; 29 | } 30 | 31 | @Override 32 | public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton fab, View dependency) { 33 | return dependency instanceof AppBarLayout; 34 | } 35 | 36 | @Override 37 | public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton fab, View dependency) { 38 | CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams(); 39 | int fabBottomMargin = lp.bottomMargin; 40 | int distanceToScroll = fab.getHeight() + fabBottomMargin; 41 | float ratio = (float)dependency.getY()/(float)toolbarHeight; 42 | fab.setTranslationY(-distanceToScroll * ratio); 43 | 44 | return true; 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/TabActivity.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.TabLayout; 5 | import android.support.v4.view.ViewPager; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.Toolbar; 8 | 9 | public class TabActivity extends AppCompatActivity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_tab); 15 | 16 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 17 | setSupportActionBar(toolbar); 18 | 19 | ViewPager viewPager = (ViewPager)findViewById(R.id.viewpager); 20 | viewPager.setAdapter(new TabPagerAdapter(getSupportFragmentManager(), this)); 21 | 22 | TabLayout tabLayout = (TabLayout)findViewById(R.id.tab_layout); 23 | tabLayout.setupWithViewPager(viewPager); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/TabFragment.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | public class TabFragment extends Fragment { 12 | private static final String ARG_PARAM1 = "param1"; 13 | private static final String ARG_PARAM2 = "param2"; 14 | 15 | private String mParam1; 16 | private String mParam2; 17 | 18 | public static TabFragment newInstance(String param1, String param2) { 19 | TabFragment fragment = new TabFragment(); 20 | Bundle args = new Bundle(); 21 | args.putString(ARG_PARAM1, param1); 22 | args.putString(ARG_PARAM2, param2); 23 | fragment.setArguments(args); 24 | return fragment; 25 | } 26 | 27 | public TabFragment() { 28 | } 29 | 30 | @Override 31 | public void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | if (getArguments() != null) { 34 | mParam1 = getArguments().getString(ARG_PARAM1); 35 | mParam2 = getArguments().getString(ARG_PARAM2); 36 | } 37 | } 38 | 39 | @Override 40 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 41 | Bundle savedInstanceState) { 42 | return inflater.inflate(R.layout.fragment_tab, container, false); 43 | } 44 | 45 | @Override 46 | public void onViewCreated(View view, Bundle savedInstanceState) { 47 | super.onViewCreated(view, savedInstanceState); 48 | TextView param1TextView = (TextView) view.findViewById(R.id.tv_param1); 49 | TextView param2TextView = (TextView) view.findViewById(R.id.tv_param2); 50 | 51 | param1TextView.setText(mParam1); 52 | param2TextView.setText(mParam2); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/TabPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v4.app.FragmentManager; 7 | import android.support.v4.app.FragmentPagerAdapter; 8 | import android.text.SpannableString; 9 | import android.text.Spanned; 10 | import android.text.style.ImageSpan; 11 | 12 | 13 | public class TabPagerAdapter extends FragmentPagerAdapter { 14 | private static final int PAGE_COUNT = 5; 15 | private Context context; 16 | 17 | public TabPagerAdapter(FragmentManager fm, Context context) { 18 | super(fm); 19 | this.context = context; 20 | } 21 | 22 | @Override 23 | public Fragment getItem(int position) { 24 | return TabFragment.newInstance("Fragment tab", "No. "+ position); 25 | } 26 | 27 | @Override 28 | public int getCount() { 29 | return PAGE_COUNT; 30 | } 31 | 32 | @Override 33 | public CharSequence getPageTitle(int position) { 34 | Drawable icon = context.getResources().getDrawable(R.drawable.ic_add_circle_outline_white); 35 | // should use : icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); 36 | // however, my icon is too large, so I used a fix size, please use an icon with proper size :) 37 | icon.setBounds(0, 0, 50, 50); 38 | ImageSpan iconSpan = new ImageSpan(icon, ImageSpan.ALIGN_BOTTOM); 39 | 40 | SpannableString sb = new SpannableString(" " + "Tab " + position); 41 | sb.setSpan(iconSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 42 | 43 | return sb; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/thirtysparks/tutorial/designlib/ViewPagerIndicatorActivity.java: -------------------------------------------------------------------------------- 1 | package com.thirtysparks.tutorial.designlib; 2 | 3 | import android.support.design.widget.TabLayout; 4 | import android.support.v4.view.ViewPager; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.support.v7.widget.Toolbar; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import com.viewpagerindicator.TitlePageIndicator; 11 | 12 | public class ViewPagerIndicatorActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_view_pager_indicator); 18 | 19 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 20 | setSupportActionBar(toolbar); 21 | 22 | ViewPager viewPager = (ViewPager)findViewById(R.id.viewpager); 23 | viewPager.setAdapter(new TabPagerAdapter(getSupportFragmentManager(), this)); 24 | 25 | TitlePageIndicator tabLayout = (TitlePageIndicator)findViewById(R.id.pagerIndicator); 26 | tabLayout.setViewPager(viewPager); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_add_circle_outline_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goofyz/android-material-design-tutorial/19970b64cc9a20043812155e37693526e4d236f9/app/src/main/res/drawable-hdpi/ic_add_circle_outline_white.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_add_circle_outline_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goofyz/android-material-design-tutorial/19970b64cc9a20043812155e37693526e4d236f9/app/src/main/res/drawable-mdpi/ic_add_circle_outline_white.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goofyz/android-material-design-tutorial/19970b64cc9a20043812155e37693526e4d236f9/app/src/main/res/drawable-nodpi/dog.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_add_circle_outline_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goofyz/android-material-design-tutorial/19970b64cc9a20043812155e37693526e4d236f9/app/src/main/res/drawable-xhdpi/ic_add_circle_outline_white.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_add_circle_outline_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goofyz/android-material-design-tutorial/19970b64cc9a20043812155e37693526e4d236f9/app/src/main/res/drawable-xxhdpi/ic_add_circle_outline_white.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goofyz/android-material-design-tutorial/19970b64cc9a20043812155e37693526e4d236f9/app/src/main/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/tab_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_another_fab.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 18 | 19 | 28 | 29 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_appbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 24 | 32 | 38 | 39 | 40 | 41 | 48 | 49 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 14 | 15 | 23 | 24 | 30 | 31 | 37 | 38 | 49 | 50 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 |