#fitcenter
Explore tagged Tumblr posts
Text
Các Đòn Đấm Trong Boxing Hiệu Quả Nhất Mà Bạn Nên Biết
Các đòn đấm trong boxing có rất nhiều loại khác nhau và mỗi loại đều có tác dụng riêng. Dưới đây là một số đòn đấm phổ biến:
壘Đố nhị: Là một trong những cú đấm cơ bản và hay dùng nhất trong boxing. Đố nhị sử dụng cánh tay thẳng với nắm đấm hướng về phía trước để tấn công mục tiêu. Cú đấm này có sức mạnh lớn và tầm với xa.
壘Đố ba: Tương tự đố nhị nhưng sử dụng cả lực xoay người khi tấn công, giúp tăng thêm sức mạnh cho cú đấm. Đố ba thường được dùng để tấn công vào ngực hoặc mặt đối thủ.
壘Đấm thẳng: Là một trong những cú đấm quan trọng nhất trong hộp. Đấm thẳng được thực hiện bằng cách duỗi thẳng hai tay về phía trước với nắm đấm hướng lên trên. Đấm thẳng có tốc độ nhanh, tầm với xa và thường được sử dụng làm đòn đánh phối hợp.
壘Đấm gối: Sử dụng lực đẩy từ đùi và chân để tạo sức mạnh cho cú đấm. Đấm gối thường tấn công vào bụng hoặc ngực đối phương, gây đau đớn và tê liệt cho đối thủ. Tuy nhiên, cú đấm này cần kỹ
https://fitcenter.vn/cac-don-dam-trong-boxing/?unique_id_xyksrqvx
0 notes
Photo
Aula aberta Les Mills - RPM. @hebraicasp #nikonD7200 #fitcenter #bike https://www.instagram.com/p/B4OZ6u5HB2i/?igshid=1k4og03hpfxvs
1 note
·
View note
Photo
As vezes vou lá na intenção de ficar forte 💪🏼 E as vezes apenas como terapia para aliviar a minha mente 🧠. #tranquilidade #equilibrio #fitnessgirl #motivation #i❤️gym #mylifestyle #fitcenter 👊🏻#gratidão💕 (em Luanda Province)
0 notes
Photo
Nada mejor que un té verde Matcha Bliss ! Ya lo probaron? 😍🌿 lo encuentran en #THEFITCENTER dentro de #paseocayala ! Suplemento 100% natural y orgánico! #fit #fitness #thefitcenter #fitcenter #cayala #te #matcha #teverde #matchatea #healthy #health #salud #organic #organico (en THE FIT CENTER)
#organic#health#matcha#thefitcenter#organico#fitcenter#teverde#matchatea#fitness#healthy#cayala#salud#fit#paseocayala#te
0 notes
Text
Badgley Mischka Women's 3/4 Sleeve Blouson Dress
Badgley Mischka Women’s 3/4 Sleeve Blouson Dress
Price: (as of – Details) ImportedZipper closureDry Clean OnlyStretch fitCenter back zipper
View On WordPress
0 notes
Text
0 notes
Photo
#morto #tiagovitti #tiago #vitti #urbanfitcenter #bauru #iphone7 #apple #insta #instagood (em Urban FitCenter)
1 note
·
View note
Text
Gym Là GÌ? Lợi Ích Của Việc Tập Gym
Văn phòng giữ sức khỏe là một phòng tập thể dục nhằm gìn giữ sức khỏe cho con người thông qua việc tập luyện thể thao. Một phòng tập thể dục được trang bị nhiều dụng cụ thể dục khác nhau để khách hàng có thể tập luyện theo sở thích của mình.
Phòng tập thể dục thường có máy chạy bộ, máy đạp xe, tủ đồ, bàn đổ mồ hôi và nhiều thiết bị khác có thể kể đến như: máy tập cơ bụng, máy tập chân, máy tập tay, máy nâng tạ, bàn thảm tập yoga, võ đường... Tại đây khách hàng có thể tập luyện bất cứ bài tập thể dục nào theo sở thích của mình như: chạy bộ, đạp xe, tập cơ, yoga, võ thuật, nh��y nhót... dưới sự hướng dẫn của huấn luyện viên nếu cần thiết.
Mỗi phòng tập có trang thiết bị hiện đại với các máy móc, dụng cụ được cập nhật liên tục theo xu hướng thời đại. Ngoài ra các phòng tập còn được thiết kế khung cảnh thoáng mát, rộng rãi với hệ thống thông gió tốt nhằm tạo sự thoải mái, dễ chịu cho người tập luyện.
Văn phòng giữ sức khỏe còn tổ chức
https://fitcenter.vn/gym-la-gi/?unique_id_f61lly8o
0 notes
Video
(vía https://www.youtube.com/watch?v=8kOuFNErzKQ)
0 notes
Text
Detailním výkladu nového usnesení Vlády jsou podle České komory fitness požadavky na provoz fitcenter, tělocvičen a posiloven takové, kterým v naší tělocvičně nemůžeme vyhovět.
Pokud bude počasí, je možné cvičit venku na hřišti při zachování těchto základních požadavků:
* počet účastníků do 10 lidí
* rozestupy 2 m
* cvičit je možné bez roušek
0 notes
Photo
https://semicolon.codes/weather-app-android-studio-java/
Weather App Android Studio Java [With Project Files]
Weather App Android Studio with Project Files
This weather app is an android studio project developed using java programming language. Android studio is android app development environment offically created by google. This software provides whole set of tool kits and environment which makes the app development process more efficient , easy and reliable. This software is used by professional developer all around the work and the good part is its free of cost. So, you get to use the software which is used by pro app developers.
Short Overview on what App does:
Clima weather app is a simple weather forecast app that uses api to get json the openweather.org website. The openweather website is a weather forecast website and allows user to integrate weather apis into your app. These api requries to send parameters to get the weather data which is usually in json format. The data can be fetched using api’s given by the openwaeather website itself by which we can send parameters to get weather data. However, to get access to api. you have to sign in and request for app_key to openweather.org website.
In my case, i used longitude and latitude and city name as parameter for api to get the result from the open weather database. The app displays current temperature and weather status in image as weather forecast for specifice city. When the app starts, user will get a toast message “”fetching location” which get your current longitude and latitude data and send those params to get weather for your current area.
Things to remeber while learning this app tutorial:
Insert uses-permission to AndroidManifest.xml file to get internet, gps address and cell tower location. These permission are mandatory to the project. Hence, internet permissions allows app to use interner, access fine location allows device to use GPS location and access coarse location allows ap to use cell network location.
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
XML layout for Main Weather Activity: This activity xml file contains a textview for location name and temperature, image for weather details , image button for changing city.
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/relativeLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/weather_background" tools:context="com.londonappbrewery.climapm.WeatherController"> <TextView android:id="@+id/locationTV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:shadowColor="@android:color/background_dark" android:shadowDx="1" android:shadowDy="1" android:shadowRadius="4" android:text="@string/default_location" android:textAppearance="?android:attr/textAppearanceLarge" android:textColor="@color/white" android:textSize="30sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.258" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/weatherSymbolIV" app:layout_constraintVertical_bias="0.572" /> <TextView android:id="@+id/tempTV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginEnd="16dp" android:layout_marginLeft="8dp" android:layout_marginRight="16dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:shadowColor="@color/black" android:text="@string/default_temp" android:textAppearance="?android:attr/textAppearanceLarge" android:textColor="@color/white" android:textSize="50sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.948" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.201" /> <ImageButton android:id="@+id/changeCityButton" android:layout_width="40dp" android:layout_height="60dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginTop="20dp" android:background="@null" android:contentDescription="@string/change_city_description" android:scaleType="fitCenter" android:src="@drawable/change_city_symbol_small" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageView android:id="@+id/weatherSymbolIV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:contentDescription="@string/weather_description" android:src="@drawable/dunno" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.473" /> <ImageButton android:id="@+id/refresh" android:layout_width="50dp" android:layout_height="50dp" android:layout_marginLeft="16dp" android:layout_marginStart="16dp" android:layout_marginTop="28dp" android:background="@android:color/transparent" android:scaleType="fitCenter" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/cloud_refresh" /> </android.support.constraint.ConstraintLayout>
XML Layout for Change City Activity: An edittext where you type city name and it will intent to main class.
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/relativeLayout2" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/city_background" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <ImageButton android:id="@+id/backButton" android:layout_width="30dp" android:layout_height="50dp" android:background="@null" android:contentDescription="@string/back_button_description" android:scaleType="fitCenter" android:src="@drawable/left" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/queryET" android:layout_width="match_parent" android:layout_height="50dp" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:background="@color/white" android:gravity="center_vertical|center_horizontal" android:hint="@string/default_query" android:imeOptions="actionGo" android:inputType="textAutoCorrect" android:textColor="@color/black" android:textSize="24sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.541" /> <TextView android:id="@+id/getWeatherTV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:shadowColor="@android:color/background_dark" android:shadowDx="1" android:shadowDy="1" android:shadowRadius="4" android:text="@string/get_weather" android:textAppearance="@style/TextAppearance.AppCompat" android:textColor="@color/white" android:textSize="36sp" app:layout_constraintBottom_toTopOf="@+id/queryET" app:layout_constraintHorizontal_bias="0.503" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.811" /> </android.support.constraint.ConstraintLayout>
Main WeatherController Activity Java Code: This activity has most of the codes and logic. The activity fetches the json data and show to the screen depending upon data.
package com.londonappbrewery.climapm; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.JsonHttpResponseHandler; import com.loopj.android.http.RequestParams; import org.json.JSONObject; import cz.msebera.android.httpclient.Header; public class WeatherController extends AppCompatActivity // Constants: final int REQUEST_CODE = 123; final String WEATHER_URL = "http://api.openweathermap.org/data/2.5/weather"; // App ID to use OpenWeather data final String APP_ID = "passte_your_app_key/id"; // Time between location updates (5000 milliseconds or 5 seconds) final long MIN_TIME = 5000; // Distance between location updates (1000m or 1km) final float MIN_DISTANCE = 1000; // TODO: Set LOCATION_PROVIDER here: final String LOCATION_PROVIDER = LocationManager.NETWORK_PROVIDER; // Member Variables: TextView mCityLabel; ImageView mWeatherImage; TextView mTemperatureLabel; // TODO: Declare a LocationManager and a LocationListener here: LocationManager mLocationManager; LocationListener mLocationListener; @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.weather_controller_layout); // Linking the elements in the layout to Java code mCityLabel = findViewById(R.id.locationTV); mWeatherImage = findViewById(R.id.weatherSymbolIV); mTemperatureLabel = findViewById(R.id.tempTV); ImageButton changeCityButton = findViewById(R.id.changeCityButton); ImageButton refreshWeather = findViewById(R.id.refresh); // TODO: Add an OnClickListener to the changeCityButton here: changeCityButton.setOnClickListener(new View.OnClickListener() @Override public void onClick(View v) Intent changeCity = new Intent(getApplicationContext(), ChangeCityController.class); startActivity(changeCity); ); refreshWeather.setOnClickListener(new View.OnClickListener() @Override public void onClick(View v) Toast.makeText(getApplicationContext(), "Refetching..",Toast.LENGTH_LONG).show(); onResume(); ); // TODO: Add onResume() here: @Override protected void onResume() super.onResume(); Log.d("Clima","onResume() is started"); Intent cityData = getIntent(); String cityName = cityData.getStringExtra("City"); if(cityName != null) getWeatherForNewCity(cityName); else getWeatherForCurrentLocation(); // TODO: Add getWeatherForNewCity(String city) here: public void getWeatherForNewCity(String cityName) RequestParams params = new RequestParams(); params.put("q",cityName); params.put("appid",APP_ID); letsDoSomeNetworking(params); // TODO: Add getWeatherForCurrentLocation() here: private void getWeatherForCurrentLocation() Log.d("Clima","getWeatherForCurrentLocation()"); mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); mLocationListener = new LocationListener() @Override public void onLocationChanged(Location location) Log.d("Clima","onLocationChanged() is started"); String Longitude = String.valueOf(location.getLongitude()); String Latitude = String.valueOf(location.getLatitude()); Log.d("Clima","The longitude is"+ Longitude); Log.d("Clima","The latitude is"+ Latitude); RequestParams params = new RequestParams(); params.put("lat",Latitude); params.put("lon",Longitude); params.put("appid",APP_ID); letsDoSomeNetworking(params); @Override public void onStatusChanged(String provider, int status, Bundle extras) @Override public void onProviderEnabled(String provider) @Override public void onProviderDisabled(String provider) Log.d("Clima","onProviderDisabled() is called"); ; if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) // TODO: Consider calling // ActivityCompat#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. ActivityCompat.requestPermissions(this, new String[]Manifest.permission.ACCESS_FINE_LOCATION, REQUEST_CODE); return; mLocationManager.requestLocationUpdates(LOCATION_PROVIDER, MIN_TIME, MIN_DISTANCE, mLocationListener); @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(requestCode == REQUEST_CODE) grantResults[0]== PackageManager.PERMISSION_GRANTED) Log.d("Clima","Permission Granted"); getWeatherForCurrentLocation(); else Log.d("Clima","Permsission Denied"); // TODO: Add letsDoSomeNetworking(RequestParams params) here public void letsDoSomeNetworking(RequestParams params) AsyncHttpClient client = new AsyncHttpClient(); client.get(WEATHER_URL, params, new JsonHttpResponseHandler() @Override public void onSuccess(int statusCode, Header[] header, JSONObject response) Log.d("Clima","Success"+response.toString()); WeatherDataModel weatherdata = WeatherDataModel.fromJSON(response); updateUI(weatherdata); Toast.makeText(WeatherController.this, "Location Fetched", Toast.LENGTH_SHORT).show(); @Override public void onFailure(int statuscode, Header[] header, Throwable e , JSONObject response) Log.d("Clima","Status code"+statuscode); Log.d("Clima","Fail"+e.toString()); Toast.makeText(WeatherController.this, "Invalid Location", Toast.LENGTH_SHORT).show(); getWeatherForCurrentLocation(); ); // TODO: Add updateUI() here: public void updateUI(WeatherDataModel data) mTemperatureLabel.setText(data.getTemperature()); mCityLabel.setText(data.getCity()); int resourceId = getResources().getIdentifier(data.getIconName(),"drawable",getPackageName()); mWeatherImage.setImageResource(resourceId); // TODO: Add onPause() here: @Override protected void onPause() super.onPause(); if(mLocationManager != null) mLocationManager.removeUpdates(mLocationListener);
WeatherData Model Class: This activity_class get weather json data from the main activity, creates an object of json data and returns it.
package com.londonappbrewery.climapm; import org.json.JSONException; import org.json.JSONObject; public class WeatherDataModel // TODO: Declare the member variables here private String mTemperature; private int condition; private String mCity; private String mIconName; // TODO: Create a WeatherDataModel from a JSON: public static WeatherDataModel fromJSON(JSONObject jsonObject) try WeatherDataModel weatherdata = new WeatherDataModel(); weatherdata.mCity = jsonObject.getString("name"); weatherdata.condition = jsonObject.getJSONArray("weather").getJSONObject(0).getInt("id"); weatherdata.mIconName = updateWeatherIcon(weatherdata.condition); double temp = jsonObject.getJSONObject("main").getDouble("temp") - 273.15; int roundValue = (int) Math.rint(temp); weatherdata.mTemperature = (""+roundValue); return weatherdata; catch(JSONException e) e.printStackTrace(); return null; // TODO: Uncomment to this to get the weather image name from the condition: private static String updateWeatherIcon(int condition) if (condition >= 0 && condition < 300) return "tstorm1"; else if (condition >= 300 && condition < 500) return "light_rain"; else if (condition >= 500 && condition < 600) return "shower3"; else if (condition >= 600 && condition <= 700) return "snow4"; else if (condition >= 701 && condition <= 771) return "fog"; else if (condition >= 772 && condition < 800) return "tstorm3"; else if (condition == 800) return "sunny"; else if (condition >= 801 && condition <= 804) return "cloudy2"; else if (condition >= 900 && condition <= 902) return "tstorm3"; else if (condition == 903) return "snow5"; else if (condition == 904) return "sunny"; else if (condition >= 905 && condition <= 1000) return "tstorm3"; return "dunno"; // TODO: Create getter methods for temperature, city, and icon name: public String getTemperature() return mTemperature +"°C"; public String getCity() return mCity; public String getIconName() return mIconName;
Change City Controller Class:
package com.londonappbrewery.climapm; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; public class ChangeCityController extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.change_city_layout); final EditText edittext = findViewById(R.id.queryET); ImageButton imageButton = findViewById(R.id.backButton); imageButton.setOnClickListener(new View.OnClickListener() @Override public void onClick(View v) finish(); ); edittext.setOnEditorActionListener(new TextView.OnEditorActionListener() @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) String city = edittext.getText().toString(); Intent goToMainActivity = new Intent(getApplicationContext(),WeatherController.class); goToMainActivity.putExtra("City",city); startActivity(goToMainActivity); return false; );
Conclusion
Basically, this is a simple weather app which you can use your self or use it as a project. So, you also can download the project source files from link down below of you want to take a look at the project.
Please follow and like us:
0 notes
Text
Android Design Support Library: Getting Started
Android Design Support Library to the rescue!
Have you been surfing through thousands of Github libraries to add a Facebook-like Sliding Navigation menu in your project? Or searching for a convenient widget that will show tabs with icons at the bottom of your app’s homepage?
Behold – the Android Design Support Library is for you!
The Android Design Support Library helps you implement those shiny, interactive design components with minimal code!
In this tutorial, you’ll create a marketplace app called iSell using the Android Design Support Library. By the end, you will learn how to:
Use common Material Design components like BottomNavigationView and FloatingActionButton
Get rid of needing different images for the same icon to support different device resolutions with VectorDrawables
Bring some wow-factors to your users with CoordinatorLayout and CollapsingToolbarLayout
This is gonna be awesome!
Getting started
To kick things off, start by downloading the materials for this tutorial (you can find a link at the top or bottom of this tutorial). Unzip iSell.zip folder into your desktop.
Now launch Android Studio 3.1.2 or greater and select Open an existing Android Studio project to import the starter project.
Choose iSell-Starter inside the iSell folder and click Open
Build and run by firing keyboard shortcut Shift + F10 (or Control + R if you are using a Mac)
And you will see a screen with the project name. Kudos – you’ve successfully kickstarted the project!
Managing Design Support Dependencies
Adding Design Support Library to your project is a piece of cake, but it requires a few different components. You should always use the latest version of each component, and make sure they’re the same version for all the components. To manage this efficiently, an external variable is defined in your Project level build.gradle file as following:
ext.supportLibraryVersion = '27.1.1'
You will use this version with all the Design Support Library components later.
Open the build.gradle file from your app module and append the following lines inside the dependencies section:
// 1: Design Support Library implementation "com.android.support:design:$supportLibraryVersion" // 2: CardView implementation "com.android.support:cardview-v7:$supportLibraryVersion"
Let’s take a moment to understand what each of dependencies provides:
Design Support Library: Adds all those “exciting” UI components we are talking about. For example: BottomNavigationView, FloatingActionButton. We’ll be adding one of each later in this tutorial.
CardView: Is a View component that shows its content in an elevated card, and highlights it to stand-out from the background. Most commonly used with list items.
Also, you’re are going to use VectorDrawables for the icons, so add this inside the defaultConfig section:
vectorDrawables.useSupportLibrary = true
Your app module’s build.gradle file finally may look like this:
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 27 defaultConfig { applicationId "com.raywenderlich.isell" minSdkVersion 16 targetSdkVersion 27 versionCode 1 versionName "1.0" vectorDrawables.useSupportLibrary = true } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } androidExtensions { experimental = true } } dependencies { // Kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion" // AppCompat implementation "com.android.support:appcompat-v7:$supportLibraryVersion" // Design Support Library implementation "com.android.support:design:$supportLibraryVersion" // CardView implementation "com.android.support:cardview-v7:$supportLibraryVersion" }
Notice that the build.gradle file has uses a plugin:
apply plugin: 'kotlin-android-extensions'
With kotlin-android-extensions, you can directly access a View‘s id without having to initialize it using findViewById(). Its just sweet syntactic sugar! If you are keen to know more about kotlin-android-extensions you can find out more here.
Before adding any Kotlin code, configure Android Studio to automatically insert import statements so that you don’t need to worry about imports for every change you make.
Go to Preferences\Editor\General\Auto Import, check Add unambiguous imports on the fly and Optimize imports on the fly checkboxes and click OK.
Again, don’t forget to click Sync Now in the top-right corner of your IDE. You’re now done with all the setup!
Showcasing Items with RecyclerView
First things first, you’ll display a list of items to sell. To show the list, replace the TextView inside activity_main.xml with a RecyclerView:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.activity.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/itemsRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutManager="android.support.v7.widget.GridLayoutManager" app:spanCount="2"/> </RelativeLayout>
Here, RecyclerView will be identified as itemsRecyclerView and the width and height properties should match its parent layout.
app:layoutManager="android.support.v7.widget.GridLayoutManager" app:spanCount="2"
You’ll notice that you set the layoutManager directly in the XML to use a GridLayoutManager. Setting the app:spanCount tells the GridLayoutManager that you want to show 2 items in a row, so you tell the layoutManager to prepare layout for the grid.
Next, you need to design the layout for the items in the list.
Highlight with CardView
For each item in being sold, you’ll want to show the image, price and title. To make a layout for that, right-click on layout folder, select New, then select Layout resource file and name it layout_list_item.xml.
Now add following code inside layout_list_item.xml:
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/default_margin" android:foreground="?android:attr/selectableItemBackground" card_view:cardBackgroundColor="@color/cardview_light_background" card_view:cardCornerRadius="@dimen/cardview_default_radius" card_view:cardElevation="@dimen/cardview_default_elevation"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/itemImage" android:layout_width="match_parent" android:layout_height="@dimen/item_image_height" android:scaleType="fitCenter" tools:src="@drawable/laptop_1"/> <TextView android:id="@+id/itemPrice" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/itemImage" android:layout_marginLeft="@dimen/default_margin" android:layout_marginRight="@dimen/default_margin" android:maxLines="1" android:textAppearance="@style/TextAppearance.AppCompat.Headline" android:textColor="@color/colorAccent" tools:text="@string/hint_price"/> <TextView android:id="@+id/itemTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/itemPrice" android:layout_marginBottom="@dimen/default_margin" android:layout_marginLeft="@dimen/default_margin" android:layout_marginRight="@dimen/default_margin" android:ellipsize="end" android:maxLines="2" android:minLines="2" android:textAppearance="@style/TextAppearance.AppCompat.Title" tools:text="@string/hint_title"/> </RelativeLayout> </android.support.v7.widget.CardView>
It will look like this in the Preview pane:
Here the CardView and its properties may seem new to you, but other components are quite familiar – you are just adding views to show the image, price and title inside a RelativeLayout sequentially.
Using CardView makes your item appear elevated with the use of drop shadows around the element.
card_view:cardBackgroundColor="@color/cardview_light_background"
The above property adds a light-themed background color for the CardView from Design Support Library.
card_view:cardCornerRadius="@dimen/cardview_default_radius"
This property makes the card’s corners to look rounded. You are using the default radius provided in Design Support Library. You can play with the values for this property, the edges will look more rounded with a larger value.
The most interesting property of CardView is:
card_view:cardElevation="@dimen/cardview_default_elevation"
This property allows a CardView to look more or less elevated. This elevation of the view determines the size of the drop shadow. The larger the value you provide, the more elevated it’ll look.
RecyclerView in Action
It’s time to bind some data to the layout. Consider the DataProvider class inside util package as a storehouse of all your Items. You need an adapter to show items in the RecyclerView added earlier. To do so, add a new class inside the adapter package named ItemsAdapter as follows:
// 1 class ItemsAdapter(private val items: List<Item>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { // 2 class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(item: Item) = with(itemView) { itemTitle.text = item.title itemImage.setImageResource(item.imageId) } } // 3 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val view = LayoutInflater.from(parent?.context) .inflate(R.layout.layout_list_item, parent, false) return ViewHolder(view) } // 4 override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { (holder as ViewHolder).bind(items[position]) } // 5 override fun getItemCount(): Int { return items.size } }
That’s a lot of code to digest at once! Let’s break it down…
ItemsAdapter is declared as a subclass of RecyclerView.Adapter which accepts a list of Item.
ViewHolder is a subclass of RecyclerView.ViewHolder. It inherits the power of being cached into memory and re-used to display an Item inside the RecyclerView. The bind(item: Item) function inside it does all the binding between the Item and the View.
onCreateViewHolder() function creates a new ViewHolder when the adapter needs a new one with the view you designed in layout_list_item.xml.
onBindViewHolder() glues each Item from the list with a ViewHolder to populate it using the bind() function.
getItemCount() function tells ItemsAdapter the number of items in the list.
Add a function that will set the data in RecyclerView according to the category you want inside MainActivity.kt:
private fun populateItemList(category: Category) { val items = when (category) { Category.LAPTOP -> DataProvider.laptopList Category.MONITOR -> DataProvider.monitorList Category.HEADPHONE -> DataProvider.headphoneList } if (items.isNotEmpty()) { itemsRecyclerView.adapter = ItemsAdapter(items) } }
This function accepts Category as input and fetches a list of items from that category through the DataProvider. Then it creates a new ItemsAdapter with those items and sets to itemsRecyclerView.
Call this function with a Category from onCreate() function inside MainActivity:
populateItemList(Category.LAPTOP)
Here you are fetching items from the LAPTOP category through the DataProvider. Feel free to play around with other categories also.
Run the app again. You’ll see the list as follows:
Listening to Click Events
You need to set a listener that’ll notify you when a user clicks on an item from the list. For that, you should declare an interface inside ItemsAdapter:
interface OnItemClickListener { fun onItemClick(item: Item, itemView: View) }
The onItemClick(item: Item, itemView: View) function will be called by the interface to let you know which specific Item object is clicked. Here itemView is the view that represents the Item in RecyclerView.
Now modify ItemsAdapter‘s constructor to match the following:
class ItemsAdapter(private val items: List<Item>, private val clickListener: OnItemClickListener)
ItemsAdapter now requires an OnItemClickListener instance when created so that you can use the instance later.
Modify onBindViewHolder() as follows:
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { (holder as ViewHolder).bind(items[position], clickListener) }
This binds the clickListener instance to every ViewHolder.
At this point you might see a compiler warning like this:
but don’t worry, all you need is to update ViewHolder class to fix that:
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(item: Item, listener: OnItemClickListener) = with(itemView) { itemTitle.text = item.title itemPrice.text = "\$" + item.price itemImage.setImageResource(item.imageId) setOnClickListener { listener.onItemClick(item, it) } } }
Notice that you are using listener.onItemClick(item, it) inside setOnClickListener for itemView. That means, onItemClick() function will be fired whenever user clicks on an itemView and passes the reference of it’s corresponding item and view through the listener interface.
You should navigate from MainActivity to DetailsActivity when a user clicks on a item to see its details. Implement the OnItemClickListener interface in MainActivity as follows:
class MainActivity : AppCompatActivity(), ItemsAdapter.OnItemClickListener { }
and override onItemClick() function inside MainActivity:
override fun onItemClick(item: Item, itemView: View) { val detailsIntent = Intent(this, DetailsActivity::class.java) detailsIntent.putExtra(getString(R.string.bundle_extra_item), item) startActivity(detailsIntent) }
When an item inside the RecyclerView is clicked on, the onItemClick(item: Item, itemView: View) function will notify MainActivity and navigate to DetailsActivity starting a new Intent. Update the creation of the ItemsAdapter
itemsRecyclerView.adapter = ItemsAdapter(items)
to include the OnItemClickListener inside populateItemList(category: Category) method.
itemsRecyclerView.adapter = ItemsAdapter(items, this)
Run again at this and click on a item from the list – you will see a white screen with a text like this:
But that’s ok for now, you will make it look good soon enough!
Before going any farther let’s import the icons you are going to use later…
Using VectorDrawables for Icons
VectorDrawables are graphic elements that consist of points, lines and color information defined in a XML. It adjusts itself to different screen densities without loss of image quality, so using one file is enough to support devices with different resolution which results a smaller APK!
Android Studio has a lot of VectorDrawables bundled with it for your convenience, but you can also use your own svg or psd icons as VectorDrawables. To import them, you would right-click on the res folder and select New > Vector Asset. This will open the Asset Studio.
The starter project for this tutorial has icons that have already been converted to vector drawables in a folder named SVG inside the project directory. Navigate to that folder and copy all the xml files into the res/drawables folder.
You can now use those vector drawables inside a BottomNavigationView.
Categorize items with BottomNavigationView
You may want to display items of different categories in different tabs. How about showing them at the bottom of MainActivity with a nice icons? Design Support Library provides a widget called BottomNavigationView that makes this task easy!
Implementing BottomNavigationView
Right-click on res > menu folder and add new Menu resource file.
Name it menu_bottom_navigation.xml and add following code:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/nav_laptops" android:icon="@drawable/ic_laptop" android:title="@string/laptops"/> <item android:id="@+id/nav_monitors" android:icon="@drawable/ic_monitor" android:title="@string/monitors"/> <item android:id="@+id/nav_headphones" android:icon="@drawable/ic_headphone" android:title="@string/headphones"/> </menu>
The code is pretty straightforward here, you are setting an id, icon and title for each item in the BottomNavigationView which will be displayed as a Tab with an icon and title.
Now add BottomNavigationView inside activity_main.xml under RecyclerView:
<android.support.design.widget.BottomNavigationView android:id="@+id/bottomNavigationView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" app:menu="@menu/menu_bottom_navigation"/>
You should also add
android:layout_above="@+id/bottomNavigationView"
to the RecyclerView so that it shows above BottomNavigationView instead of fullscreen.
Handling Item Selection on BottomNavigationView
The intention of the BottomNavigationView is to show Items of different categories when users select different tabs from the BottomNavigationView.
First, have MainActivity implement BottomNavigationView.OnNavigationItemSelectedListener. Modify the MainActivity declaration as follows:
class MainActivity : AppCompatActivity(), ItemsAdapter.OnItemClickListener, BottomNavigationView.OnNavigationItemSelectedListener { }
and add following function inside MainActivity class:
override fun onNavigationItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.nav_laptops -> populateItemList(Category.LAPTOP) R.id.nav_monitors -> populateItemList(Category.MONITOR) R.id.nav_headphones -> populateItemList(Category.HEADPHONE) else -> return false } return true }
The onNavigationItemSelected(item: MenuItem) function fires each time a user clicks a MenuItem shown as Tab in BottomNavigationView.
This function, determines which MenuItem was clicked using the item.itemId and calls populateItemList() with it’s corresponding Category to show the items of that category in the RecyclerView.
onNavigationItemSelected(item: MenuItem) will return true to notify MainActivity that a NavigationItem is selected if item.itemId matches to any item defined in the menu_bottom_navigation.xml or returns false otherwise to keep things unchanged.
Add this line inside onCreate() function in MainActivity:
bottomNavigationView.setOnNavigationItemSelectedListener(this)
Run the app again you’ll notice the items changing after each tab is selected.
Adding New Items
Adding another button inside MainActivity to add new items will eat a lot of precious real estate in your landing page, but how about overlaying a button over the list of items? Did someone say FloatingActionButton?
Using FloatingActionButton
Add a FloatingActionButton at the bottom of activity_main.xml, to look as follows:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.activity.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/itemsRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/bottomNavigationView" app:layoutManager="android.support.v7.widget.GridLayoutManager" app:spanCount="2"/> <android.support.design.widget.BottomNavigationView android:id="@+id/bottomNavigationView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" app:menu="@menu/menu_bottom_navigation"/> <android.support.design.widget.FloatingActionButton android:id="@+id/addFab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/bottomNavigationView" android:layout_alignParentRight="true" android:clickable="true" android:onClick="onClickAddFab" app:srcCompat="@drawable/ic_add" app:useCompatPadding="true"/> </RelativeLayout>
Then add following function inside MainActivity.kt:
fun onClickAddFab(view: View) { startActivity(Intent(this, AddItemActivity::class.java)) }
This function will be called when user clicks on the FloatingActionButton and navigate from MainActivity to AddItemsActivity by starting a new Intent.
Run the app again, and click on the FloatingActionButton, you should see the app transition into the AddItemsActivity:
Before moving to the next section, update imageFromCategory() inside AddItemActivity.kt so that it returns an icon that matches with provided Category:
private fun imageFromCategory(category: Category): Int { return when (category) { Category.LAPTOP -> R.drawable.ic_laptop Category.MONITOR -> R.drawable.ic_monitor else -> R.drawable.ic_headphone } }
Interacting with TextInputLayout
Add a TextInputLayout in activity_add_item.xml to wrap titleEditText and priceEditText:
<android.support.design.widget.TextInputLayout android:id="@+id/titleTextInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/categoryTitle" app:counterEnabled="true" app:counterMaxLength="30"> <EditText android:id="@+id/titleEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hint_title" android:maxLines="2" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:id="@+id/priceTextInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/titleTextInput" app:counterEnabled="true" app:counterMaxLength="30"> <EditText android:id="@+id/priceEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hint_price" android:inputType="numberDecimal" /> </android.support.design.widget.TextInputLayout> <EditText android:id="@+id/detailsEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/priceTextInput" android:hint="@string/hint_details" android:lines="2" />
TextInputLayout is basically a wrapper for EditText enhancing it to display floating hint animations, error message and character counts in a more material way.
For example, adding
app:counterEnabled="true" app:counterMaxLength="30"
in titleTextInput inside activity_add_item.xml displays 30 character limit for titleEditText input and shows character count interactively and no extra line needed!
Showing error message is easy when using TextInputLayout. You might want to check if user inputs title and price before adding an item, and show an error near the input fields if empty or invalid. Write a function in AddItemActivity.kt that does exactly that:
private fun hasValidInput(): Boolean { // 1 val title = titleEditText.text.toString() if (title.isNullOrBlank()) { titleTextInput.error = "Please enter a valid Title" return false } // 2 val price = priceEditText.text.toString().toDoubleOrNull() if (price == null || price <= 0.0) { priceTextInput.error = "Please enter a minimum Price" return false } // 3 return true }
This section checks if user leaves titleEditText blank or inserts whitespace only. Then it'll set an error to titleTextInput field and stops proceeding farther by returning false
This section tries to convert user input on priceEditText to a Double value. toDoubleOrNull() returns null if the conversion fails due to and invalid input or even whitespace. Again, user must input a price greater than 0. So you set an error on the priceTextInput field which stops proceeding farther by returning false if the price is null or no more than 0.0
It returns true if all validation filters above passed and proceeds to add an item.
Call hasValidInput() from inside onClickAddItem(view: View) function like this:
fun onClickAddItem(view: View) { if (hasValidInput()) { val selectedCategory = categorySpinner.selectedItem as Category DataProvider.addItem(Item( imageId = imageFromCategory(selectedCategory), title = titleEditText.text.toString(), details = detailsEditText.text.toString(), price = priceEditText.text.toString().toDouble(), category = selectedCategory, postedOn = System.currentTimeMillis()) ) } }
You should clear all error message whenever user starts typing on the input fields again. To do that you need to modify beforeTextChanged() function as following:
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { titleTextInput.error = null priceTextInput.error = null }
easy-peasy, huh?
Run the app and try to add an item without price - you'll be stopped with an error message!
Using Snackbar for Temporary Messages
Snackbar is the smarter version of Toasts in Android. You can provide action buttons like "OK" or "CANCEL" along with a message using a Snackbar. Unlike Toast, you need to provide a view to display the Snackbar.
It's good practice to show a confirmation message after adding an item successfully and take back to the item list after user's acknowledgement. Let's add a function for that inside AddItemActivity:
private fun showAddItemConfirmation() { Snackbar.make(addItemRootView, getString(R.string.add_item_successful), Snackbar.LENGTH_LONG) .setAction(getString(R.string.ok)) { navigateBackToItemList() } .show() }
It shows a Snackbar in addItemRootView with a success message for a longer duration defined by Snackbar.LENGTH_LONG.
You added an action button with text "OK" by appending
.setAction(getString(R.string.ok)) { navigateBackToItemList() }
which performs navigateBackToItemList() action on button click.
Add showAddItemConfirmation() at the bottom of onClickAddItem() function:
fun onClickAddItem(view: View) { if (hasValidInput()) { val selectedCategory = categorySpinner.selectedItem as Category DataProvider.addItem(Item( imageId = imageFromCategory(selectedCategory), title = titleEditText.text.toString(), details = detailsEditText.text.toString(), price = priceEditText.text.toString().toDouble(), category = selectedCategory, postedOn = System.currentTimeMillis()) ) showAddItemConfirmation() } }
Run the app again and to add a new item title, price and details, The output should be like this:
Animating Item Details
Presenting item details is an attractive way of providing the user more information that may lead to more items sold. One approach to making the detail page more attractive is to use animation. In this section, you'll leverage what Design Support Library offers to make the app more lucrative...
Using CoordinatorLayout and CollapsingToolbarLayout
Combining CoordinatorLayout with CollapsingToolbarLayout is a killer-combo that can make your app lot more fascinating to the users. Before seeing them in action, replace everything inside activity_details.xml with the following:
<?xml version="1.0" encoding="utf-8"?> <!-- 1 --> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.activity.DetailsActivity"> <!-- 2 --> <android.support.design.widget.AppBarLayout android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="@dimen/app_bar_height" android:theme="@style/AppTheme.AppBarOverlay"> <!-- 3 --> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsingToolbarLayout" android:layout_width="match_parent" android:layout_height="match_parent" app:contentScrim="@color/colorPrimary" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <ImageView android:id="@+id/itemImageView" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"/> <!-- 4 --> <android.support.v7.widget.Toolbar android:id="@+id/toolBar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" app:popupTheme="@style/AppTheme.PopupOverlay"/> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <!-- 5 --> <include layout="@layout/content_details"/> <!-- 6 --> <android.support.design.widget.FloatingActionButton android:id="@+id/shareFab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/fab_margin" android:onClick="onClickShareFab" app:layout_anchor="@+id/appBar" app:layout_anchorGravity="bottom|end" app:srcCompat="@android:drawable/ic_menu_share"/> </android.support.design.widget.CoordinatorLayout>
Switch to the layout-blueprint for a better overview, then you'll go over each item in the layout, one by one:
CoordinatorLayout is the root layout and the container for it's child views. By specifying a behavior to a direct child of CoordinatorLayout, you’ll be able to intercept touch events, window insets, measurement, layout, and nested scrolling. Don't panic - you'll learn to implement them in the next section!
AppBarLayout allows your Toolbar and other views (such as the ImageView) to react to scroll events in a sibling view.
android:theme="@style/AppTheme.AppBarOverlay"
You used the above property to overwrite relevant attributes for light overlay style.
CollapsingToolbarLayout is a wrapper for Toolbar which allows the Toolbar to expand or collapse as the user scrolls through a view.
app:contentScrim="@color/colorPrimary"
Using the above property gradually changes CollapsingToolbarLayout's color to the provided color when it's collapsing.
app:layout_scrollFlags="scroll|exitUntilCollapsed"
The above property means the view should scroll off until it reaches to its minimum height (?attr/actionBarSize in this case)
Toolbar is actually a more flexible and customizable ActionBar that holds your navigation button, activity title etc. Here, using
android:layout_height="?attr/actionBarSize"
assures the Toolbar has exactly same height of a regular ActionBar, and
app:layout_collapseMode="pin"
pins it on top when CollapsingToolbarLayout is fully collapsed. Finally
app:popupTheme="@style/AppTheme.PopupOverlay"
styles it to be elevated as like a Popup when the Toolbar is visible.
You are including a layout from content_details.xml that shows the price, title and details of the item.
The FloatingActionButton allows you to share your item via a share-intent.
android:onClick="onClickShareFab"
Setting the above property will fire the onClickShareFab(view: View) function inside DetailsActivity when user clicks on it.
app:layout_anchor="@+id/appBar" app:layout_anchorGravity="bottom|end"
These last two properties keep the button it attached to the bottom-end of the AppBarLayout. The CoordinatorLayout automatically manages the visibility of FloatingActionButton when AppBarLayout is collapsed as you set the appBar as layout_anchor.
Put everything inside content_details.xml within a NestedScrollView, so the layout will look like this:
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".ui.activity.DetailsActivity" tools:showIn="@layout/activity_details"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="@dimen/default_padding"> <TextView android:id="@+id/priceTextView" style="@style/TextAppearance.AppCompat.Display1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/colorAccent" tools:text="@string/hint_price"/> <TextView android:id="@+id/titleTextView" style="@style/TextAppearance.AppCompat.Title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/default_margin" android:layout_marginTop="@dimen/default_margin" android:transitionName="@string/transition_title" tools:targetApi="lollipop" tools:text="@string/hint_title"/> <TextView android:id="@+id/detailsTextView" style="@style/TextAppearance.AppCompat.Body1" android:layout_width="match_parent" android:layout_height="wrap_content" tools:text="@string/hint_details"/> </LinearLayout> </android.support.v4.widget.NestedScrollView>
app:layout_behavior="@string/appbar_scrolling_view_behavior"
With the above property, you share scroll events on the NestedScrollView with AppBarLayout so that it can expand or collapse accordingly.
Finally, set the Toolbar inside onCreate() function in DetailsActivity.kt:
setSupportActionBar(toolBar) supportActionBar?.setDisplayHomeAsUpEnabled(true)
and modify populateDetails(item: Item?) function like this:
private fun populateDetails(item: Item?) { supportActionBar?.title = item?.category?.name itemImageView.setImageResource(item?.imageId!!) priceTextView.text = getString(R.string.currency_symbol) + item?.price.toString() titleTextView.text = item?.title detailsTextView.text = item?.details }
This sets the category name in the Toolbar title and the Item's image to the ImageView.
Run the app again, and navigate to the DetailsActivity of any item - you should see something amazing:
Adding Parallax Scrolling Effect
As you already seen, Design Support Library does all the heavy-lifting when it comes to animation, providing your users a rich user experience. Why not add one more effect! add following to the ImageView inside activity_details.xml:
app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="0.7"
This adds a nice parallax-scrolling effect to the ImageView when AppBarLayout open or collapse. The layout_collapseParallaxMultiplier impacts on speed and visible area during scroll. Default value for this is 0.5, you can play around this value and check which suits you best.
Build and Run and see the changes in all its glory!
This completes your quest into making an awesome marketplace app! Don't stop here; keep working on it - and maybe you can make the next eBay!
Where To Go From Here?
You can download the final project using the link at the top or the bottom of this tutorial.
To take your Material Design skills to the next level, here are some additional things to explore:
Learn to implement Activity Transitions With Shared Elements, The Palette API and more from raywenderlich.com
Learn to use AnimatedVectorDrawables
Use Downloadable Fonts to improve overall look and feel.
I hope you enjoyed materializing your app with Android Design Support Library. If you have any questions, comments, or awesome modifications to this project app please join the forum discussion and comment below!
The post Android Design Support Library: Getting Started appeared first on Ray Wenderlich.
Android Design Support Library: Getting Started published first on https://medium.com/@koresol
0 notes
Photo
Todos unos guerreros fitcenter team a mejorar, de pie y con toda la actitud, renovación total! (en Fit Center MX)
0 notes
Video
Todavía me falta trabajar en el snatched #crossfit #mondaymotivation #lunes @fitcenter_club (at FitCenter)
0 notes
Photo
Ricas empanadas bolivianas muy sabrosas amiga gracias (en E-FitCenter)
0 notes