Software Design Principles
SummaryAndroid Database:Andorid components[EndToEnd] Android ActivityAndroid Services TemplatesAndroid Content ProvidersAndroid Broadcast ReceiverAndroid UI DesignsLayoutsControlsNavigationsDialog and NavigationEvents and GuesturesAndroid All Layout Discussion[EndToEnd] Building Custom Views[EndToEnd] Android Animation:[EndToEnd] Recyler ViewAndroid Notification End-to-End[EndToEnd] Toast and Android Dialog.[EndToEnd] View pagerAndroid fragmentsExecuting Async Jobs in android.WebSocket Support to AndroidSupport personalize Experice.Supporting mutiple languagesSupporting seperate layout for differnt devices.Supporting different button width for diffent devices.Supporting multiple imagesAndroid NetworkingConnecting websocketSimple Network Calls to parse JSON.Having HTTP server inside android AppAndroid Thread and processApplication SandBoxinterprocess communication:Sharing private data, resource and processes by multiple app.Android Threads.[EndToEnd] Improving Android performanceReducing frequenct GC Events:Avoid Memoty leaks in your applicationOptimizing UI Rendering performanceAndroid DesignMVP Patterns in detailsDesign communication between mutiple compoenens in android:Monitization your app.Google AddMob.Defining a modelJava Libs for android:JacksonAndroid Pro Dev Trciks.Connecting device using wifiAndroid Design issuesSave and Restore statesHandling Configuration Changes with FragmentsADB Tricks
Summary
Android is a bigger topics - so I would like to give obly the sumary at the beigging which contains the minimum words to expain the internal - if you want to know more, fell free to explore the details sections.
Android Database:
In android, SQLITE is used for the offline relation database storage. You can define any relatinal database table and can run any CRUD operation to do that. Android provides some wrapper class to make it easy. To have a database you need to do: (1) Define StudentdataBaseContacts which conbatisl the table name and columon name and table spaciifc state,mey like CREATE or DROP. (2) You need to extend SQLITE open helper to override onCreate and onUpdate to exetecte the table creattiom, importing Index or inietring predefine collumms (3) In your database manger or activity, you need to hace the instace of StudentOpenHelper class. You can use getReadbleDarabse of getWritableDatabase to get the database and can do insert, .query, update, delete method to do CRUD operation.
For inserting, you set all the claues in ContactValues and pass it to insert(). For Quesry, you can give projectiom, selection, selection args , groupby, orderby and limkit . The delete and update supports multi-delete or multi-update based on selecetuon. As you have the db isntae, youcan execute any other RAW SQL command. The query returns Cursor objects which can be iterateted though to move over the results. For ListView/Spinner/Reclerview can also be integerated with CursorAdapetr to populate the live data from database.
Database is expensive operation - DONOT do it MIAN thread. Use Loader and Lodernmaner for query and Askyc Task for insert/update and delete.
Yes. It is possible to investiagte the database using sqlite3 - which is already in your emulator . Run the commands as (1) $adb shell, (2) run-as in.co.dipankat.xxx (3) cd database/ (4) ls to see the database (5) sqlite3 students.db. (6) .table (7) select * from students.
As part of ARCH comments android introducet ORM called Room, where you need to annotate the class to have a ORM. It;s simple and given in my test example.
Andorid components
This tutorials is mainly design for providing templates for android devlopment and helping out my copy-paste efficiency.
In this section, we wil mainly discussed abount the android compoents as shown in the table:
Table: Android Compoenent Summary
ActivityAn example of sample android Action, We will also discuss actibity and application life cycle callback.
Android ServiceA simple of Android Service example and how to start, stop and communicate with android service.
Android Content ProvidersAn example of Android Content Providers
Android Broadcast ReceiverAn example of Android Broadcast Receiver
FragemntAn sample example of creating reusable compoents
[EndToEnd] Android Activity
Below I am providing a simple templates for having a easy Activity which just have a Click buttons Clicking of that button shows a Toast. extremely Simple.
Step 1: First, let;s define the menufest with single activity.
(expand code)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="in.co.dipankar.app"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Step 2: Let's define it's layout Here is the Layout
(expand code)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/MyButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Button" /> </RelativeLayout>
Step 3: Let's define the MainActibity which Extend the Activity/
(expand code)
import android.os.Bundle; import android.app.Activity; import android.content.Intent; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { Button button; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.MyButton); button.setOnClickListener(new OnClickListener() { public void onClick(View arg0) { // DO Something. } }); } }
Step 4: Let's undersand Activity Life Cycle Callback
In this section , we will discuss all the override functions of Activity and theoir purpose.
(expand code)
import android.os.Bundle; import android.app.Activity; import android.util.Log; public class MainActivity extends Activity { String msg = "Android : "; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(msg, "The onCreate() event"); // You might call this and call the code should be in onNewIntent // onNewIntent(getIntent()); } /** Called when the activity is about to become visible. */ @Override protected void onStart() { super.onStart(); Log.d(msg, "The onStart() event"); } /** Called when the activity has become visible. */ @Override protected void onResume() { super.onResume(); Log.d(msg, "The onResume() event"); } /** Called when another activity is taking focus. */ @Override protected void onPause() { super.onPause(); Log.d(msg, "The onPause() event"); } /** Called when the activity is no longer visible. */ @Override protected void onStop() { super.onStop(); Log.d(msg, "The onStop() event"); } /** Called just before the activity is destroyed. */ @Override public void onDestroy() { super.onDestroy(); Log.d(msg, "The onDestroy() event"); } }
Step 5: Save and Restore states of Activity:
(expand code)
// Save /STore State - https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en // These variable are destroyed along with Activity private int someVarA; private String someVarB; @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("someVarA", someVarA); outState.putString("someVarB", someVarB); Log.d(msg, "The onSaveInstanceState() event"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); someVarA = savedInstanceState.getInt("someVarA"); someVarB = savedInstanceState.getString("someVarB"); Log.d(msg, "The onRestoreInstanceState() event"); }
Step 6: Try to Entry the activity when it is still opend:
(expand code)
// onNewIntent() is meant as entry point for singleTop activities which already run somewhere else in the stack and therefore can't call onCreate() @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); // getIntent() should always return the most recent setIntent(intent); }
Step 7: Handling run time Permission :
(expand code)
// Run time Permission private void askPermission(){ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { if (!checkIfAlreadyhavePermission()) { requestForSpecificPermission(); } else{ proceedAfterPermission(); } } else{ proceedAfterPermission(); } } private boolean checkIfAlreadyhavePermission() { int result = ContextCompat.checkSelfPermission(this, Manifest.permission.GET_ACCOUNTS); if (result == PackageManager.PERMISSION_GRANTED) { return true; } else { return false; } } private void requestForSpecificPermission() { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS, Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 101); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case 101: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { //granted } else { //not granted } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_PERMISSION_SETTING) { if (ActivityCompat.checkSelfPermission(MultiplePermissionsActivity.this, permissionsRequired[0]) == PackageManager.PERMISSION_GRANTED) { //Got Permission proceedAfterPermission(); } } } private void proceedAfterPermission() { txtPermissions.setText("We've got all permissions"); Toast.makeText(getBaseContext(), "We got All Permissions", Toast.LENGTH_LONG).show(); } @Override protected void onPostResume() { super.onPostResume(); if (sentToSettings) { if (ActivityCompat.checkSelfPermission(MultiplePermissionsActivity.this, permissionsRequired[0]) == PackageManager.PERMISSION_GRANTED) { //Got Permission proceedAfterPermission(); } } } } // You need to add permission String in the menifest. <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Step 8: Open Another activity from mainActivity
(expand code)
Step 8: Activity BackStack managenet:
(expand code)
Step 9: Android activity Themes
(expand code)
Step 10: Full Screen Activity:
(expand code)
Step 11: Activity with Navigation Drawer:
(expand code)
Step 12: Activity with Action Bar:
(expand code)
Step 13: Activity attach and Detach:
(expand code)
Step 14: Getting result from another activity:
(expand code)
Android Services Templates
A Service is an Android application component without a UI that runs on the main thread (of the hosting process). It also has to be declared in the AndroidManifest.xml
  1. A Started Service can be started by calling the startService(Intent)method in your Activity or Service. This Intent has to be an explicit Intent, which means that you either make a reference to the Service’s class
2.
A Service can be started multiple times and each time onStartCommand() is called, we can pass extra while stated which is received by this callback. Even if you start a Service multiple times, it will only call onCreate() only once. To stop a service by calling stopSelf() from it. If you stop from outside, you must call as stopService.When the Service does stop then it will call onDestroy().
Service might be crashed or killed. In that case Service might be restarted base don the flag as it returned by onStartCommand.
  1. Service.START_STICKY - Service is restarted if it gets terminated. Intent data passed to the onStartCommand method is null. Used for services which manages their own state and do not depend on the Intent data.
  2. Service.START_NOT_STICKY - Service is not restarted. this is used for services which are periodically triggered anyway. The service is only restarted if the runtime has pending startService() calls since the service termination.
  3. Service.START_REDELIVER_INTENT - Similar to Service.START_STICKY but the original Intent is re-delivered to the onStartCommand method.
We can have comminucation between service and Activity. The service receives the intent data from the starting Android component and performs its work. So the communication from Activity to service is statght forword. However, We need to use Receive for communicating service to activity. In this case your activity can register a broadcast receiver for an event and the service sends outs corresponding events. Android provides the LocalBroadcastManager class in the support library v4. This is a helper class to register for and send broadcasts of Intents to local objects within your process.
A sample service is giving as below:
  1. First Create a Service Calss.
(expand code)
import android.app.Service; import android.os.IBinder; import android.content.Intent; import android.os.Bundle; import android.widget.Toast; public class HelloService extends Service { /** indicates how to behave if the service is killed */ int mStartMode; /** interface for clients that bind */ IBinder mBinder; /** indicates whether onRebind should be used */ boolean mAllowRebind; /** Called when the service is being created. */ @Override public void onCreate() { } /** The service is starting, due to a call to startService() */ @Override public int onStartCommand(Intent intent, int flags, int startId) { //process intent Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show(); return START_STICKY; } /** A client is binding to the service with bindService() */ @Override public IBinder onBind(Intent intent) { return mBinder; } /** Called when all clients have unbound with unbindService() */ @Override public boolean onUnbind(Intent intent) { return mAllowRebind; } /** Called when a client is binding to the service with bindService()*/ @Override public void onRebind(Intent intent) { } /** Called when The service is no longer used and is being destroyed */ @Override public void onDestroy() { super.onDestroy(); Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show(); } }
Step 2: Activity can start or stop Service as below:
(expand code)
import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.os.Bundle; import android.app.Activity; import android.util.Log; import android.view.View; public class MainActivity extends Activity { String msg = "Android : "; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(msg, "The onCreate() event"); } public void startService(View view) { startService(new Intent(getBaseContext(), MyService.class)); } // Method to stop the service public void stopService(View view) { stopService(new Intent(getBaseContext(), MyService.class)); } }
Step 3: Dont forget to put thease into menufest.
(expand code)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.tutorialspoint7.myapplication"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".MyService" /> </application> </manifest>
We have another kind of service call intent servivce: The Service can be used in tasks with no UI, but shouldn't be too long. If you need to perform long tasks, you must use threads within Service. The IntentService can be used in long tasks usually with no communication to Main Thread. If communication is required, can use Main Thread handler or broadcast intents. Another case of use is when callbacks are needed.
Below example shows an Intent Service and a comminucation from service to Activity.
Step1: Let;s have the service here.
(expand code)
import android.app.IntentService; import android.content.Intent; public class CashbackIntentService extends IntentService{ final static String CASHBACK_INFO = "cashback_info"; public CashbackIntentService() { super("Cashback IntentService"); } @Override protected void onHandleIntent(Intent intent) { String cb_category = intent.getStringExtra("cashback_cat"); String cbinfo = getCashbackInfo(cb_category); sendCashbackInfoToClient(cbinfo); } private String getCashbackInfo(String cbcat){ String cashback; if("electronics".equals(cbcat)){ cashback = "Upto 20% cashback on electronics"; }else if("fashion".equals(cbcat)){ cashback = "Upto 60% cashbak on all fashion items"; }else{ cashback = "All other categories except fashion and electronics, flat 30% cashback"; } return cashback; } // This runs on a worker thread. private void sendCashbackInfoToClient(String msg){ Intent intent = new Intent(); intent.setAction(CASHBACK_INFO); intent.putExtra("cashback",msg); sendBroadcast(intent); // LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } }
Step2: Let;s have the activity and Have a Broadcast Receiver.
(expand code)
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.EditText; import android.widget.TextView; public class IntentServiceActivity extends AppCompatActivity { private CashbackReciver cashbackReciver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_intentservice); registerCashbackReceiver(); startCashbackService(); } @Override protected void onStop() { super.onStop(); unregisterReceiver(cashbackReciver); } public void startCashbackService(View view){ Intent cbIntent = new Intent(); cbIntent.setClass(this, CashbackIntentService.class); cbIntent.putExtra("cashback_cat", "SomeText"); startService(cbIntent); } private void registerCashbackReceiver(){ cashbackReciver = new CashbackReciver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(CashbackIntentService.CASHBACK_INFO); registerReceiver(cashbackReciver, intentFilter); } private class CashbackReciver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String cbinfo = intent.getStringExtra("cashback"); } } }
Android Content Providers
Android Broadcast Receiver
Android UI Designs
In this section, I am mainly concentret on android UI Design. In andorid, we can build UI either using XML files or Programitically. We need Layouts and Controls to have a Layout. Layouts are the holders of the Controls and Controls are the actual Objects like button or TextView etc.
The summary of the section is as below:
Table: Summary of Andorid UI compoents
LayoutsA way to align UI contraols
ControlsThe indivsial UI componenet
Events and GuesturesHandlaing user inputs
NavigationA way to perform android Navigations
Dialog and NotificationA way to show alert and Notification.
Layouts
Table: List of All Layoit supported by Android
LinearLayoutAndroid LinearLayout is a view group that aligns all children in either vertically or horizontally. Some important properties is: android:baselineAligned, android:divider="000", android:gravity="center", android:orientation="vertical", android:weightSum=3
Relative LayoutRelativeLayout is a view group that displays child views in relative positions.
Table LayoutTableLayout is a view that groups views into rows and columns.
AbsoluteLayoutenables you to specify the exact location of its children.
Frame LayoutThe FrameLayout is a placeholder on screen that you can use to display a single view.
List ViewListView is a view group that displays a list of scrollable items.
Grid ViewGridView is a ViewGroup that displays items in a two-dimensional, scrollable grid.
Recyler ViewSmart Views
CustomExtendViewsThis is an extension on aelement View
CustomCopositeViewThis loads and layout and Reperesnt a simpel view.
CustomDrawViewHere we build a view by drawing in canvus
Controls
Table: List of all controls/ Widgetsin Android
TextViewThis control is used to display text to the user.
EditTextEditText is a predefined subclass of TextView that includes rich editing capabilities.
AutoCompleteTextViewThe AutoCompleteTextView is a view that is similar to EditText, except that it shows a list of completion suggestions automatically while the user is typing.
ButtonA push-button that can be pressed, or clicked, by the user to perform an action.
ImageButtonAn ImageButton is an AbsoluteLayout which enables you to specify the exact location of its children. This shows a button with an image (instead of text) that can be pressed or clicked by the user.
CheckBoxAn on/off switch that can be toggled by the user. You should use check box when presenting users with a group of selectable options that are not mutually exclusive.
ToggleButtonAn on/off button with a light indicator.
RadioButtonThe RadioButton has two states: either checked or unchecked.
RadioGroupA RadioGroup is used to group together one or more RadioButtons.
ProgressBarThe ProgressBar view provides visual feedback about some ongoing tasks, such as when you are performing a task in the background.
SpinnerA drop-down list that allows users to select one value from a set.
TimePickerThe TimePicker view enables users to select a time of the day, in either 24-hour mode or AM/PM mode.
DatePickerThe DatePicker view enables users to select a date of the day.
Navigations
Table: List of Navigation Patterns in Andrroid
ViewPager #
Navigation #
Menu #
Dialog and Navigation
Table: Dialog and Notifications
AlertDiloagsA Dialog is small window that prompts the user to a decision or enter additional information. You might show some info and Ok button or Ask some confirmation ( OK/Cancel) or Misght show a list or might ask for selecet item from a list.
DialogFragmentDialog hosted inside a fragemnet
ProgressDialogProgress bars are used to show progress of a task
DatePickerDialogUsed for showing the date Picker dialog and selecet dates.
NotificationA notification is a message you can display to the user outside of your application's normal UI.
Events and Guestures
Table: List of all Evenet Guesture
onClickHandling click events
onLongClickHanldong the log tap events
addTextChangedListenerCalled when you chnages the text in EditText
onFocusChangeThis is called when the widget looses its focus ie. user goes away from the view item.
onKeyThis is called when the user is focused on the item and presses or releases a hardware key on the device.
onTouchThis is called when the user presses the key, releases the key, or any movement gesture on the screen.
onMenuItemClickThis is called when the user selects a menu item. You will use onMenuItemClick() event handler to handle such event.
onCreateContextMenuThis is called when the context menu is being built(as the result of a sustained "long click)
GestureDetector.SimpleOnGestureListenerDetect Android provides special types of touch screen events such as pinch , double tap, scrolls , long presses and flinch. These are all known as gestures.
ScaleGestureDetectorDelect the scale events.
Android All Layout Discussion
This section, We will give some sample templte codes for all mostly used layouts:
  1. Android LinearLayout is a view group that aligns all children in either vertically or horizontally. Some of mostly used attributes are :android:orientation = horizontal| vertical, android:gravity = top, bottom, left, right, center, center_vertical, center_horizontal ( how the elment inside are placed), android:layout_gravity( how itself is placed over parenet), android:baselineAligned = true | false ( aligning all its children's baselines.), android:weightSum and android:weight.
A sample tempalte is as below:
(expand code)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/btnStartService" android:layout_width="270dp" android:layout_height="wrap_content" android:text="start_service"/> <Button android:id="@+id/btnPauseService" android:layout_width="270dp" android:layout_height="wrap_content" android:text="pause_service"/> <Button android:id="@+id/btnStopService" android:layout_width="270dp" android:layout_height="wrap_content" android:text="stop_service"/> </LinearLayout>
  1. RelativeLayout: Android RelativeLayout enables you to specify how child views are positioned relative to each other. Some Most common used attribute is: Center spacific: android:layout_centerHorizontal, android:layout_centerInParent, android:layout_centerVertical, (w.r.t parent:) android:layout_alignParentTop,android:layout_alignParentBottom, android:layout_alignParentStart, android:layout_alignParentLeft, android:layout_alignParentRight,android:layout_alignParentStart, android:layout_alignParentEnd,( with resperct to other): android:layout_alignLeft, android:layout_above, android:layout_below, android:layout_toLeftOf, android:layout_toRightOf, android:layout_toStartOf, android:layout_toEndOf.
If you dont assign anything- it will just placed at top left corner. A sample example is as below:
(expand code)
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ef9b00" android:orientation="vertical"> <EditText android:id="@+id/name" android:layout_width="200dp" android:layout_height="wrap_content" android:background="#f8eff8f3" android:hint="Name" android:padding="16dp" /> <EditText android:id="@+id/email" android:layout_width="180dp" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_toRightOf="@+id/name" android:background="#f8eff8f3" android:hint="Address" android:padding="16dp" /> <EditText android:id="@+id/subject" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/name" android:layout_marginTop="16dp" android:background="#f8eff8f3" android:hint="Subject" android:padding="16dp" /> <EditText android:id="@+id/message" android:layout_width="match_parent" android:layout_height="200dp" android:layout_below="@+id/subject" android:layout_marginTop="16dp" android:background="#f8eff8f3" android:gravity="top" android:hint="Message" android:padding="16dp" /> <Button android:id="@+id/submit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/message" android:layout_centerHorizontal="true" android:layout_marginTop="16dp" android:padding="16dp" android:text="Submit" /> </RelativeLayout>
  1. Android Constraint Layout: ConstraintLayout allows you to create large and complex layouts with a flat view hierarchy (no nested view groups). It's similar to RelativeLayout but have more flexiable and some advance feature.This design is closely related to iOS’s Constraint Layout. Constraint Layout provide better support for Drag and Drop views.
The Common constratins is defines as : layout_constraintTop_toTopOf , layout_constraintTop_toBottomOf ,layout_constraintBottom_toTopOf , layout_constraintBottom_toBottomOf etc. Mainly all 16 combination likes:
layout_constraint[X]_to[Y]Of: where X and Y =>[Left, right, Top, Buttom, Start, End ]: that is total 6*6 = 36 combination.  layout_constraintRight_toTopOf means Align this layout so that the right of this view to the top of another view.
A quick example is as below :
(expand code)
// Add this deps. dependencies { compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha3' }
[EndToEnd] Building Custom Views
Table: Type of Views
Simple ViewThis is Just a indipent view like Button
ContainerThis a ViewGroup which think as a container ex: Liner layout.
Compound ControlThis is View where multiple view put together Like Inc/Dec View or a Spinner View.
Why custom component:
Table 2: Techniques for Building CustomViews:
Shortcut View # Extend Nearest veiw to add some feature like VersionView
Composite View # Build a collection of View( Like XML) to tread as a single View.
Custom View # Extend Views and Apply drawing some Newly looks.
Custom ViewGroup # Build new continer of Container for child views ( like Building a Photo Priral and Dialogal or Percemtagelayout)
Step 1: Exending a Simple View to create VersionView. Importnat things to note:
(expand code)
public class VersionView extends TextView { public VersionView(Context context) { super(context); setVersion(); } public VersionView(Context context, AttributeSet attrs) { super(context, attrs); setVersion(); } public VersionView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setVersion(); } private void setVersion() { try { PackageInfo packageInfo = getContext().getPackageManager().getPackageInfo( getContext().getPackageName(), 0); setText(packageInfo.versionName); } catch (PackageManager.NameNotFoundException e) { } } } public class TestActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new VersionView(this)); } } // in xml: <com.sqisland.android.versionview.VersionView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#0f0" android:textSize="40sp" tools:context="com.sqisland.android.versionview.MainActivity"/>
Step 2: Builds a Compount Control like a Length Picker: Importnat Steps to builds such view is
(expand code)
// XML: <merge xmlns:android="http://schemas.android.com/apk/res/android"> <Button android:id="@+id/minus_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/minus" /> <TextView android:id="@+id/text" android:layout_width="96dp" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:textAppearance="@android:style/TextAppearance.Large" /> <Button android:id="@+id/plus_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/plus" /> </merge> public class LengthPicker extends LinearLayout { public interface OnChangeListener { public void onChange(int length); } private static final String KEY_SUPER_STATE = "superState"; private static final String KEY_NUM_INCHES = "numInches"; private View mPlusButton; private TextView mTextView; private View mMinusButton; private int mNumInches = 0; private OnChangeListener mListener = null; public LengthPicker(Context context) { super(context); init(); } public LengthPicker(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { LayoutInflater inflater = LayoutInflater.from(getContext()); inflater.inflate(R.layout.length_picker, this); mPlusButton = findViewById(R.id.plus_button); mTextView = (TextView) findViewById(R.id.text); mMinusButton = findViewById(R.id.minus_button); updateControls(); View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.plus_button: mNumInches++; if (mListener != null) { mListener.onChange(mNumInches); } updateControls(); break; case R.id.minus_button: if (mNumInches > 0) { mNumInches--; if (mListener != null) { mListener.onChange(mNumInches); } updateControls(); } break; } } }; mPlusButton.setOnClickListener(listener); mMinusButton.setOnClickListener(listener); setOrientation(LinearLayout.HORIZONTAL); } @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(KEY_SUPER_STATE, super.onSaveInstanceState()); bundle.putInt(KEY_NUM_INCHES, mNumInches); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; mNumInches = bundle.getInt(KEY_NUM_INCHES); super.onRestoreInstanceState(bundle.getParcelable(KEY_SUPER_STATE)); } else { super.onRestoreInstanceState(state); } updateControls(); } private void updateControls() { int feet = mNumInches / 12; int inches = mNumInches % 12; String text = String.format("%d' %d\"", feet, inches); if (feet == 0) { text = String.format("%d\"", inches); } else { if (inches == 0) { text = String.format("%d'", feet); } } mTextView.setText(text); mMinusButton.setEnabled(mNumInches > 0); } public int getNumInches() { return mNumInches; } public void setOnChangeListener(OnChangeListener listener) { mListener = listener; } } public class MainActivity extends Activity { private LengthPicker mWidth; private LengthPicker mHeight; private TextView mArea; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWidth = (LengthPicker) findViewById(R.id.width); mHeight = (LengthPicker) findViewById(R.id.height); mArea = (TextView) findViewById(R.id.area); LengthPicker.OnChangeListener listener = new LengthPicker.OnChangeListener() { @Override public void onChange(int length) { updateArea(); } }; mWidth.setOnChangeListener(listener); mHeight.setOnChangeListener(listener); } private void updateArea() { int area = mWidth.getNumInches() * mHeight.getNumInches(); mArea.setText(area + " sq in"); } @Override protected void onResume() { super.onResume(); updateArea(); } } // Activity: <com.sqisland.android.lengthpicker.LengthPicker android:id="@+id/width" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <com.sqisland.android.lengthpicker.LengthPicker android:id="@+id/height" android:layout_width="wrap_content" android:layout_height="wrap_content" />
Step 3: Communication with Activity: Callback and Public APIs
(expand code)
Step 4: Save and Restore State ( Rotate the Screen)
(expand code)
... @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(KEY_SUPER_STATE, super.onSaveInstanceState()); bundle.putInt(KEY_NUM_INCHES, mNumInches); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; mNumInches = bundle.getInt(KEY_NUM_INCHES); super.onRestoreInstanceState(bundle.getParcelable(KEY_SUPER_STATE)); } else { super.onRestoreInstanceState(state); } updateControls(); } ...
Step 5: 3 Steps for Influate Views:
How Android Draws Views. It;s starts From the root node and do three pass
Table: How It daws something
MeasureIt mesures how much space it requres byt it or by it's child.You need to override whne you make squere View
LayoutSays how to layout;s it's childYou need to override on spacisl ordenttaion on view.
DrawActual Drawing the viewYou need this when you want to draw soemtjing.
Messure --> Layout --> Draw.
Step 5: SquereView: Custom Layout exploring mesusre pahse.
(expand code)
@Override public void onMeasure(int widthSpec, int heightSpec) { super.onMeasure(widthSpec, heightSpec); int size = Math.min( getMeasuredWidth(), getMeasuredHeight()); setMeasuredDimension(size, size); } <com.sqisland.android.square_view.SquareView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:layout_margin="48dp" android:background="#c9f" />
Step 7: Photo Spriral View: Exploring onLayout:
(expand code)
<?xml version="1.0" encoding="utf-8"?> <com.sqisland.android.photo_spiral.PhotoSpiral xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/petronas_twin_tower" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/himeji" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ulm" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/chiang_mai" /> </com.sqisland.android.photo_spiral.PhotoSpiral> public class PhotoSpiral extends ViewGroup { public PhotoSpiral(Context context) { super(context); } public PhotoSpiral(Context context, AttributeSet attrs) { super(context, attrs); } public PhotoSpiral(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthSpec, int heightSpec) { measureChildren(widthSpec, heightSpec); View first = getChildAt(0); int size = first.getMeasuredWidth() + first.getMeasuredHeight(); int width = ViewGroup.resolveSize(size, widthSpec); int height = ViewGroup.resolveSize(size, heightSpec); setMeasuredDimension(width, height); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // Assume all children are of the same dimensions // Order: landscape, portrait, landscape, portrait View first = getChildAt(0); final int childWidth = first.getMeasuredWidth(); final int childHeight = first.getMeasuredHeight(); for (int i = 0; i < getChildCount(); ++i) { View child = getChildAt(i); int x = 0; int y = 0; switch (i) { case 1: x = childWidth; y = 0; break; case 2: x = childHeight; y = childWidth; break; case 3: x = 0; y = childHeight; break; } child.layout(x, y, x + child.getMeasuredWidth(), y + child.getMeasuredHeight()); } } }
Step 8: Pizza View: Hvaing a onDraw.
(expand code)
public class Pizza extends View { private Paint paint; private int numWedges = 5; public Pizza(Context context) { super(context); init(context, null); } public Pizza(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public Pizza(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { int strokeWidth = 4; int color = Color.YELLOW; if (attrs != null) { TypedArray array = context.obtainStyledAttributes( attrs, R.styleable.Pizza); strokeWidth = array.getDimensionPixelSize( R.styleable.Pizza_stroke_width, strokeWidth); color = array.getColor(R.styleable.Pizza_color, color); numWedges = array.getInt(R.styleable. Pizza_num_wedges, numWedges); } paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(strokeWidth); paint.setColor(color); } @Override protected void onDraw(Canvas canvas) { final int width = getWidth() - getPaddingLeft() - getPaddingRight(); final int height = getHeight() - getPaddingTop() - getPaddingBottom(); final int cx = width / 2 + getPaddingLeft(); final int cy = height / 2 + getPaddingTop(); final float diameter = Math.min(width, height) - paint.getStrokeWidth(); final float radius = diameter / 2; canvas.drawCircle(cx, cy, radius, paint); drawPizzaCuts(canvas, cx, cy, radius); } private void drawPizzaCuts(Canvas canvas, float cx, float cy, float radius) { final float degrees = 360f / numWedges; canvas.save(); for (int i = 0; i < numWedges; ++i) { canvas.rotate(degrees, cx, cy); canvas.drawLine(cx, cy, cx, cy - radius, paint); } canvas.restore(); } } <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal"> <com.sqisland.android.pizza.Pizza android:layout_width="120dp" android:layout_height="120dp" android:layout_margin="8dp" app:color="#f00" app:num_wedges="4"/> <com.sqisland.android.pizza.Pizza android:layout_width="160dp" android:layout_height="160dp" android:layout_margin="8dp" app:stroke_width="16dp" app:num_wedges="6"/> </LinearLayout>
Step 9: Suport custom attributes:
(expand code)
//values/attrs.xml <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="Pizza"> <attr name="num_wedges" format="integer"/> <attr name="stroke_width" format="dimension"/> <attr name="color" format="color"/> </declare-styleable> </resources> private void init(Context context, AttributeSet attrs) { int strokeWidth = 4; int color = Color.YELLOW; if (attrs != null) { TypedArray array = context.obtainStyledAttributes( attrs, R.styleable.Pizza); strokeWidth = array.getDimensionPixelSize( R.styleable.Pizza_stroke_width, strokeWidth); color = array.getColor(R.styleable.Pizza_color, color); numWedges = array.getInt(R.styleable. Pizza_num_wedges, numWedges); }
Step 11: Creating Bar Graph: Dispatch Draw :
(expand code)
// XML: <com.sqisland.android.sideways_layout.SidewaysLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="8dp" android:textColor="@color/yellow" android:background="@color/dark_green" android:text="@string/good_night_moon" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8dp" android:paddingRight="20dp" android:textColor="@android:color/white" android:background="@color/deep_orange" android:textStyle="bold" android:text="@string/green_eggs_and_ham" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8dp" android:paddingRight="28dp" android:textColor="@android:color/black" android:background="@color/purple" android:text="@string/matilda" /> </com.sqisland.android.sideways_layout.SidewaysLayout> public class SidewaysLayout extends LinearLayout { public SidewaysLayout(Context context) { super(context); } public SidewaysLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthSpec, int heightSpec) { super.onMeasure(widthSpec, heightSpec); setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); } // Will call once it dispaths to draw by it's child. @Override protected void dispatchDraw(Canvas canvas) { canvas.save(); canvas.translate(0, getHeight()); canvas.rotate(-90); // >>> Ask others to draw/ super.dispatchDraw(canvas); canvas.restore(); } }
Step 10: When to use custom view and Fragments:
Use of Fragments
Use of Custom View:
Table: Example of Uses of Fragment and CustomView.
FragmentCustomView
Take photo(onActivityResult)Show photo in 4:3 (onMeasure)
Menu button(onCreateOptionsMenu)Fan out the photos(onLayout)
MavigationHistogram (onDraw)
[EndToEnd] Android Animation:
Animation are sapcially useful when screen changes states like when content loads or new activity chnagesd. Animation can alos add a polished look to your app which give a higher quality feel. Howver, adding too much animation can slow down your app performace.
There are three types of animation supprted by android.
Table: Android Animations:
View AnimationMostly dealsing with chaning the view apperneces but no propert chnages. They are used to do simple animations like changing size, position, rotation, control transparency - here Their state changes but their property does not change. So Effect will lost once the animation complete.
Tween AnimationType of View animation: Perform one or more transformation to a single view, The possible transformation are rotation, scaling, translation, and fading. Two ways to implement View ( Tween ) animation (1) XML file in res/anim directory of the project. (2) Java code
Frame Animation/ Drawable Animations An XML file specifying various list of drawables is made which are run one by one just like a roll of a film. Lessed used in the app.
Property Animations They are used to alter property of objects (Views or non view objects). We specify certain properties(like translateX, TextScaleX) of the objects to change. Applicable for non-view objects as well as views.Animated object retains it’s new properties i.e. properties of the objects are changed. it can be achived by Value Animator, Object Animator and Animator Set. Object Animator is the subclass of Value Animator.Two Ways to execute Property Animation (1) XML Resource file – inside Animator Directory and (2) Java file.
Value AnimatorAnimates a int, float or object value over time. it Calculates animation values and contains the timing details of each animation. it also carries the information about whether an animation repeats, listeners that receive update events, and the ability to set custom types to evaluate.
Object AnimatorSubclass of Value Animator.
Evaluators #
Reveal Effect
Activity transitions #
Fragment transitions
Shared Element transitions #
Table: Comparims of View Animation and Property Animation
View AnimationProperty Animation
It only animates the View objects. Do not animate non-view objectsIt animates the non-view objects too.
Helps to animate few aspects of view such as scaling, rotation etc but not the background color.It overcomes such Limitation of View Animation.
The actual property of the View is not changed. When the animation finishes the original position is not changed only the view is redrawn.The actual properties of the objects are modified.
Step 1: Define View/Tween Animation in XML files: Like all XML file, there must be a single root element.The elements performing animations are: , , and . Animations elements may be grouped using the element. In addition to other properties, each animation’s starting time and duration are specified allowing for either sequential or simultaneous animation.
(expand code)
// anim/hide_ani.xml <alpha android:fromAlpha = “1.0” android:toAlpha = “0.0” android:duration = “300” />
Step 2: Load Animation and Perform on Java Code:
(expand code)
import android.view.animation.Animation; import android.view.animation.AnimationUtils; public class MainActivity extends ActionBarActivity{ private Animation mAlphaAnim; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mImageView = (ImageView) findViewById(R.id.volleyball); // 1. load animation. mAlphaAnim = AnimationUtils.loadAnimation(this, R.anim.alpha_anim); // 2. Start the animation for imageview. mImageView.startAnimation(mAlphaAnim); } }
Step 3: Adding Animation Listener: Animation listener receives notifications from an animation. such as the end or the repetition of the animation. the Public Methods are onAnimationEnd( Animation anim ), Notifies the end of the Animation, onAnimationRepeat( Animation anim )- Notifies the repetition of the animation and onAnimationStart( Animation anim ) which Notifies the start of the animation.
(expand code)
mAlphaAnim.setAnimationListener( new Animation.AnimationListener(){ @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationEnd(Animation animation) {} @Override public void onAnimationRepeat(Animation animation) {} });
Step 4: Tween animation using Java Code: We can have tween animation using java code. We have API like AlphaAnimation(float fromAlpha, float toAlpha).
(expand code)
public class MainActivity extends ActionBarActivity{ private Animation mAlphaAnim; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mImageView = (ImageView) findViewById(R.id.volleyball); // 1. Create Animation object. mAlphaAnim = new AlphaAnimation(1.0f, 0.0f); // 2. Set properies mAlphaAnim.setDuration(2000); mAlphaAnim.setAnimationListener(...); // 3. Apply Start animation on View. mImageView.startAnimation(mAlphaAnim); // Some Extra ops as mRotateAnim.setRepeatMode(Animation.REVERSE); mRotateAnim.setRepeatCount(1); } }
Step 5: Using Animation Set and Interpolator : An interpolator defines the rate of change of an animation.This allows the basic animation effects (alpha, scale, translate, rotate) to be accelerated, decelerated, repeated, etc. Represents an AnimationSet. it;s A container that holds other animation elements ( , , ,
) or other elements.
(expand code)
// define interpolator_anim.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="true" android:interpolator="@android:anim/bounce_interpolator" <<< Define interpolar android:fillAfter="true"> <<< Keep final pos. <scale android:fromXScale="0.0" android:toXScale="2.0" android:fromYScale="0.0" android:toYScale="2.0" android:pivotX="50%" android:pivotY="50%" android:fillAfter="false" android:duration="1000" /> <<< Will happens for 1s <set android:startOffset="1000" > <<< Start after 1s and They will do parallel. <scale android:fromXScale="1.0" android:toXScale="0.6" android:fromYScale="1.0" android:toYScale="0.6" android:pivotX="50%" android:pivotY="50%" android:duration="1000" /> <rotate android:fromDegrees="0" android:toDegrees="360" android:pivotX="50%" android:pivotY="50%" android:duration="1000" /> </set> </set> // Use it from main activity. Animation tvAnim = AnimationUtils.loadAnimation(this, R.anim.interpolator_anim); mImageView.startAnimation(tvAnim);
Step 6: Demerits of Tween animation : We move a image view and try to click after move but it will not work.
(expand code)
public class MainActivity extends ActionBarActivity { ImageView mImageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mImageView = (ImageView) findViewById(R.id.dummyImage); mImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "Image Clicked", Toast.LENGTH_SHORT).show(); } }); translateAnimation(); // after this move the click will not work. } public void translateAnimation() { TranslateAnimation mTranslateAnim = new TranslateAnimation( Animation.ABSOLUTE, 0.0f, Animation.ABSOLUTE, 0.0f, Animation.ABSOLUTE, 0.0f, Animation.ABSOLUTE, 200.0f ); mTranslateAnim.setDuration(1000); mTranslateAnim.setFillAfter(true); // stay at the final posssition. mImageView.startAnimation(mTranslateAnim); } }
Step 5: Frame Animation Example: Let's have a simple XML based frame aniation.
(expand code)
// define frame.xml inside drawable folder, You also need to place all frame1, frame2 icon inside drabale, <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/frame1" android:duration="200" /> <item android:drawable="@drawable/frame2" android:duration="200" /> <item android:drawable="@drawable/frame3" android:duration="200" /> <item android:drawable="@drawable/frame4" android:duration="200" /> </animation-list> // Play it from activity. public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void startFrameAnimation(View view) { ImageView dummyFrame = (ImageView) findViewById(R.id.imageFrame); dummyFrame.setBackgroundResource(R.drawable.frame); AnimationDrawable frameAnimation = (AnimationDrawable) dummyFrame.getBackground(); if (frameAnimation.isRunning()) { frameAnimation.stop(); } else { frameAnimation.stop(); frameAnimation.start(); } } }
Step 7: Object Animator using programitically:
(expand code)
import android.animation.AnimatorSet; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; public class MainActivity extends AppCompatActivity { private ImageView imageView; private RelativeLayout relativeLayout; int ctr = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = (ImageView) findViewById(R.id.imageview); imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "Clicked", Toast.LENGTH_SHORT).show(); } }); relativeLayout = (RelativeLayout) findViewById(R.id.rlayout); relativeLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 1. define Object Animator Object. ObjectAnimator anim; if(ctr %2 == 0){ anim = ObjectAnimator.ofObject(relativeLayout, "backgroundColor", new ArgbEvaluator(),Color.GREEN, Color.RED); } else{ anim = ObjectAnimator.ofObject(relativeLayout, "backgroundColor", new ArgbEvaluator(),Color.RED, Color.GREEN); } // 2. Set properties. anim.setDuration(2000); // 3. Start the animation. anim.start(); ctr++; } }); } // Another Examples. public void rotateAnimation(View view) { ObjectAnimator anim = ObjectAnimator.ofFloat(imageView, "rotation", 0.0f, 360.0f); anim.setDuration(500); anim.start(); // ObjectAnimator anim = ObjectAnimator.ofFloat(imageView, "scaleX", 1.0f, 2.0f); // ObjectAnimator anim = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 80f); // ObjectAnimator anim = ObjectAnimator.ofFloat(imageView, "alpha", 1.0f, 0.0f); } public void setAnimation(View view) { // Parent AnimatorSet rootSet = new AnimatorSet(); // Child 1 ObjectAnimator rotate = ObjectAnimator.ofFloat(imageView, "rotation", 0.0f, 360.0f); rotate.setDuration(500); // Child 2 AnimatorSet childSet = new AnimatorSet(); ObjectAnimator scaleX = ObjectAnimator.ofFloat(imageView, "scaleX", 1.0f, 2.0f); scaleX.setDuration(300); ObjectAnimator scaleY = ObjectAnimator.ofFloat(imageView, "scaleY", 1.0f, 2.0f); scaleY.setDuration(300); // define rule and merge. rootSet.play(rotate).before(childSet); childSet.play(scaleX).with(scaleY); // starts. rootSet.start(); } }
Step 8: Example of Property Animation using objectAnimator :
(expand code)
// define file animator/alpha.xml <?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="alpha" android:duration="500" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"/> // Access from MainActivity. public class MainActivity extends AppCompatActivity { private ImageView mImage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mImage = (ImageView) findViewById(R.id.image); } public void alphaAnimation(View view) { // 1. Load Animation. Animator anim = AnimatorInflater.loadAnimator(this, R.animator.alpha); // 2. Now Add Image as target anim.setTarget(mImage); // 3. Start the animation. anim.start(); } }
Step 9: Example to define objectAnimator Set:
(expand code)
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="sequentially" > <objectAnimator android:propertyName="rotation" android:duration="500" android:valueFrom="0" android:valueTo="360" android:valueType="floatType"/> <set android:ordering="together" > <objectAnimator android:propertyName="scaleX" android:duration="500" android:valueFrom="1" android:valueTo="1.5" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="500" android:valueFrom="1" android:valueTo="1.5" android:valueType="floatType"/> </set> </set>
Step 10: Example of Evaluators: Helps to calculate values for a given property over time and Helps to animate ARGB colors and Rect Objects with the help of ofObject method. IntEvaluator (Calculate values for int properties), FloatEvaluator(Calculate values for float properties), ArgbEvaluator(Calculate values for color properties that are represented as hexidecimal values), TypeEvaluator(An interface that helps us to create own custom evaluator),
(expand code)
Step 11: Animation on Activity Transitions: You need to call overridePendingTransition(OpenTraistion, Close Trasition). The way to remeber is overridePendingTransition(open, close);
(expand code)
// Put the code in FirstActivity -> SecondActivity. public void slideTransition(View view) { startActivity(new Intent(MainActivity.this, SecondActivity.class)); // activity_open_translate apply for second actibity to open, // activity_close_scale apply for outgoing activity which is first activity and which is clsing. overridePendingTransition(R.anim.activity_open_translate,R.anim.activity_close_scale); } // Back Trasition: public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.second_activity); } @Override protected void onPause(){ super.onPause(); //closing transition animations // activity_open_scale: Apply to FirstActivity that is opening // activity_close_translate apply to now activity which is close. overridePendingTransition(R.anim.activity_open_scale,R.anim.activity_close_translate); } // Do the same for onDestroty and onbackPrssed. }
Step 12: Animation on Fragemnets Transitions: it can be acive by FragmentTransaction.setCustomAnimations (int enter,int exit, int popEnter, int popExit) API. you need to set specific animation resources to run for the fragments that are entering and exiting in this transaction. The popEnter and popExit animations will be played for enter/exit operations specifically when popping the back stack.Here is an example.
(expand code)
enter_from_left.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> <translate android:fromXDelta="-100%" android:toXDelta="0%" android:fromYDelta="0%" android:toYDelta="0%" android:duration="700"/> </set> enter_from_right.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> <translate android:fromXDelta="100%" android:toXDelta="0%" android:fromYDelta="0%" android:toYDelta="0%" android:duration="700" /> </set> exit_to_left.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> <translate android:fromXDelta="0%" android:toXDelta="-100%" android:fromYDelta="0%" android:toYDelta="0%" android:duration="700"/> </set> exit_to_right.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> <translate android:fromXDelta="0%" android:toXDelta="100%" android:fromYDelta="0%" android:toYDelta="0%" android:duration="700" /> </set> FragmentTransaction transaction = supportFragmentManager.beginTransaction(); transaction.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_left, R.anim.slide_out_right, R.anim.slide_in_right) transaction.replace(R.id.content_frame, fragment); transaction.addToBackStack(null); transaction.commit();
Step 13: Animation on Recyler View Transitions:
(expand code)
//1. define a static utils. import android.animation.AnimatorSet; import android.animation.ObjectAnimator; public class AnimationUtils { public static void animate(RecyclerView.ViewHolder holder ,boolean goesDown){ slidingWithBounce(holder, goesDown); } private static void slidingWithBounce(RecyclerView.ViewHolder holder, boolean goesDown){ AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator animatorTranslateY = ObjectAnimator.ofFloat(holder.itemView, "translationY", goesDown? 200 : -200, 0); ObjectAnimator animatorTranslateX = ObjectAnimator.ofFloat(holder.itemView,"translationX",-30,30,-20,20,-5,5,0); animatorTranslateX.setDuration(1000); animatorTranslateY.setDuration(1000); animatorSet.playTogether ( animatorTranslateX, animatorTranslateY); animatorSet.start(); } } // 2. integrate to adapter which is resposible for cerating view and apply the trasition while creating the view. public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> { ... @Override public void onBindViewHolder(final CustomAdapter.MyViewHolder holder, final int position) { // Where we build the view and this time you just apply the animation. holder.title.setText(data.get(position).title); holder.imageView.setImageResource(data.get(position).ItemId); if (position > previousPosition){ AnimationUtils.animate(holder,true); } else { AnimationUtils.animate(holder, false); } previousPosition = position; ... } ... }
Step 14: Shared Elemnet trasitions:
(expand code)
Step 15: Circular reveal animation support:
(expand code)
Step 16: View pager animation support:
(expand code)
Step 2A: Some example of other animation:
(expand code)
//Alpha. <?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:fromAlpha="1.0" android:toAlpha="0.0" /> // Rotation. <?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:toDegrees="-180" android:duration="1000" android:pivotX="50%" android:pivotY="50%" android:repeatMode="reverse" android:repeatCount="1" /> // Scale <?xml version="1.0" encoding="utf-8"?> <scale xmlns:android="http://schemas.android.com/apk/res/android" android:fromXScale="1.0" android:toXScale="1.5" android:fromYScale="1.0" android:toYScale="1.5" android:pivotX="50%" android:pivotY="50%" android:duration="1000" /> // Translate <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:toXDelta="150" android:fromYDelta="0" android:toYDelta="0" android:duration="1000" />
Step 12: Some Example of mostky useful animation for Displaying some View.
(expand code)
// 1. Animaton on Jumping Logon. objectAnimator = ObjectAnimator.ofPropertyValuesHolder( imageView, PropertyValuesHolder.ofFloat("scaleX", 1.8f), PropertyValuesHolder.ofFloat("scaleY", 1.8f)); objectAnimator.setDuration(1000); objectAnimator.setInterpolator(new BounceInterpolator()); objectAnimator.setRepeatCount(ObjectAnimator.INFINITE); objectAnimator.setRepeatMode(ObjectAnimator.REVERSE); objectAnimator.start(); // 2. Increae the width of Textbox. objectAnimator = ObjectAnimator.ofPropertyValuesHolder(editText, PropertyValuesHolder.ofFloat("scaleX", 2.0f)); objectAnimator.setDuration(1000); objectAnimator.setInterpolator(timeInterpolator); objectAnimator.start(); // 3. Diff Type of Interpolator: timeInterpolator = new BounceInterpolator(); timeInterpolator = new LinearInterpolator(); timeInterpolator = new AccelerateInterpolator(); timeInterpolator = new AccelerateDecelerateInterpolator(); timeInterpolator = new AnticipateInterpolator();
Step 12: Some Common Anition for Activity Trsaition.
(expand code)
// Exampel 1: Fade in and Fade Out: fade_in.xml <?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500" /> <?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:fromAlpha="1.0" android:toAlpha="0.0" android:fillAfter="true" android:duration="500" /> Add the below line after startActivity(i), onDestroty onBack. overridePendingTransition(R.anim.fade_in, R.anim.fade_out); Example 2: Slide in slide out: slide_in_right.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="100%p" android:toXDelta="0" android:duration="@android:integer/config_mediumAnimTime"/> </set> slide_out_left.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="@android:integer/config_mediumAnimTime"/> </set> slide_in_left.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="-100%p" android:toXDelta="0" android:duration="@android:integer/config_mediumAnimTime"/> </set> slide_out_right.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="0" android:toXDelta="100%p" android:duration="@android:integer/config_mediumAnimTime"/> </set> public abstract class BaseActivity extends AppCompatActivity { @Override public void startActivity(Intent intent) { super.startActivity(intent); overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_right); } @Override public void startActivity(Intent intent, Bundle options) { super.startActivity(intent, options); overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_right); } @Override public void finish() { super.finish(); overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right); } }
[EndToEnd] Recyler View
Step 0: Adding the dependecy in the graddle
(expand code)
implementation 'com.android.support:recyclerview-v7:26.1.0'
Step 1: Define your DataItems
(expand code)
public class Movie { private String title, genre, year; public Movie(String title, String genre, String year) { this.title = title; this.genre = genre; this.year = year; } ... }
Step 2: Adding Recyler view in your layout.
(expand code)
<android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"/>
Step 3: Define Item Layout.
(expand code)
movie_list_row.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content"/> <TextView android:id="@+id/genre" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/title" /> <TextView android:id="@+id/year" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:textColor="@color/year" /> </RelativeLayout>
Step 4: Define Adapter and View Holder.
(expand code)
public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MyViewHolder> { private List<Movie> moviesList; public class MyViewHolder extends RecyclerView.ViewHolder { public TextView title, year, genre; public MyViewHolder(View view) { super(view); title = (TextView) view.findViewById(R.id.title); genre = (TextView) view.findViewById(R.id.genre); year = (TextView) view.findViewById(R.id.year); } } public MoviesAdapter(List<Movie> moviesList) { this.moviesList = moviesList; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.movie_list_row, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { Movie movie = moviesList.get(position); holder.title.setText(movie.getTitle()); holder.genre.setText(movie.getGenre()); holder.year.setText(movie.getYear()); } @Override public int getItemCount() { return moviesList.size(); } }
Step 5: Integrating Adapter and View Together:
(expand code)
// config view recyclerView = (RecyclerView) findViewById(R.id.recycler_view); RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext()); recyclerView.setLayoutManager(mLayoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); //have adaper mAdapter = new MoviesAdapter(movieList); // bind togethr recyclerView.setAdapter(mAdapter);
Step 5A: Adding RecyclerView Divider / Separator
(expand code)
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
Step 5B: Custom RecyclerView Divider / Separator
(expand code)
public class MyDividerItemDecoration extends RecyclerView.ItemDecoration { ... } recyclerView.addItemDecoration(new MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16));
Step 6: Add / Remove / ReplaceAll item Dynamatically.
Step 7: Having Hoz/ Vertical/Grid and Stack View using Layout manager.
(expand code)
//Horizantal RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false); recyclerView.setLayoutManager(mLayoutManager); //Vertocical RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.VERTICAL, false);
Step 8: Adding item click listeren
(expand code)
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener { private GestureDetector gestureDetector; private ClickListener clickListener; public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) { this.clickListener = clickListener; gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public void onLongPress(MotionEvent e) { View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null) { clickListener.onLongClick(child, recyclerView.getChildPosition(child)); } } }); } @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { View child = rv.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) { clickListener.onClick(child, rv.getChildPosition(child)); } return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) {} @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {} public interface ClickListener { void onClick(View view, int position); void onLongClick(View view, int position); } } recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() { @Override public void onClick(View view, int position) { Movie movie = movieList.get(position); } @Override public void onLongClick(View view, int position) { } }));
Step 9: Adding button listner inside the views.
(expand code)
@Override public void onBindViewHolder(final MyViewHolder holder, int position) { Album album = albumList.get(position); holder.title.setText(album.getName()); ... holder.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Click in holder, } }); holder.button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Click in some buttons, } }); }
Step 10: Adding animation while adding item in the RV: In this tutorial we’ll learn an easy way to add an initial content animation for a RecyclerView. There are some ways of doing this, e.g.:
  1. Implementing a custom ItemAnimator
  2. Add animation handling to onBindViewHolder() in the Adapter
  3. and third option, LayoutAnimation : which Defining an animation for each item and then Defining a LayoutAnimation using the item animation and Applying the LayoutAnimation programmatically and in XML
Ref: https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213
(expand code)
// Step1: define item_animation_fall_down.xml in res/anim/ which mainly do 3 thinks in parrral. <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@integer/anim_duration_medium"> <translate android:fromYDelta="-20%" android:toYDelta="0" android:interpolator="@android:anim/decelerate_interpolator" /> <alpha android:fromAlpha="0" android:toAlpha="1" android:interpolator="@android:anim/decelerate_interpolator" /> <scale android:fromXScale="105%" android:fromYScale="105%" android:toXScale="100%" android:toYScale="100%" android:pivotX="50%" android:pivotY="50%" android:interpolator="@android:anim/decelerate_interpolator" /> </set> // Step2: Defining the LayoutAnimation: With the item animation done it’s time to define // the layout animation which will apply the item animation to each child in the layout. // Create a new file called layout_animation_fall_down.xml in res/anim/ <?xml version="1.0" encoding="utf-8"?> <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" android:animation="@anim/item_animation_fall_down" android:delay="15%" android:animationOrder="normal" /> // Step 3: Applying the LayoutAnimation - A LayoutAnimation can be applied both programmatically and in XML. <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" android:layoutAnimation="@anim/layout_animation_fall_down" /> OR: LayoutAnimationController animation = AnimationUtils.loadLayoutAnimation(ctx, R.anim.layout_animation_fall_down); recyclerview.setLayoutAnimation(animation); // Step 4: If you are changing data set or just want to re-run the animation you can do it like this: private void runLayoutAnimation(final RecyclerView recyclerView) { final Context context = recyclerView.getContext(); final LayoutAnimationController controller = AnimationUtils.loadLayoutAnimation(context, R.anim.layout_animation_fall_down); recyclerView.setLayoutAnimation(controller); recyclerView.getAdapter().notifyDataSetChanged(); recyclerView.scheduleLayoutAnimation(); } // Left to right <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@integer/anim_duration_long"> <translate android:interpolator="@android:anim/decelerate_interpolator" android:fromXDelta="100%p" android:toXDelta="0" /> <alpha android:fromAlpha="0.5" android:toAlpha="1" android:interpolator="@android:anim/accelerate_decelerate_interpolator" /> </set> // Slide from Button <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@integer/anim_duration_long"> <translate android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:fromYDelta="50%p" android:toYDelta="0" /> <alpha android:fromAlpha="0" android:toAlpha="1" android:interpolator="@android:anim/accelerate_decelerate_interpolator" /> </set> Grid Animation See here : https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-2-grids-688829b1d29b
Step 11: Adding filters on RecyclerView :
(expand code)
public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.MyViewHolder> implements Filterable { .... @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence charSequence) { String charString = charSequence.toString(); if (charString.isEmpty()) { contactListFiltered = contactList; } else { List<Contact> filteredList = new ArrayList<>(); for (Contact row : contactList) { if (row.getName().toLowerCase().contains(charString.toLowerCase()) || row.getPhone().contains(charSequence)) { filteredList.add(row); } } contactListFiltered = filteredList; } FilterResults filterResults = new FilterResults(); filterResults.values = contactListFiltered; return filterResults; } @Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { contactListFiltered = (ArrayList<Contact>) filterResults.values; notifyDataSetChanged(); } }; } } // listening to search query text change searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { // filter recycler view when query submitted mAdapter.getFilter().filter(query); return false; } @Override public boolean onQueryTextChange(String query) { // filter recycler view when text is changed mAdapter.getFilter().filter(query); return false; } });
Step 12: Supporting Sweep left or right Guesture.
(expand code)
https://www.androidhive.info/2017/09/android-recyclerview-swipe-delete-undo-using-itemtouchhelper/
Step 13: Suppoting multi selecet like Gamil
(expand code)
// https://www.androidhive.info/2017/02/android-creating-gmail-like-inbox-using-recyclerview/
Step 14: RV with GridLayout with CardView : GridLayoutManager is used to display the RecyclerView in Grid manner instead of list, GridSpacingItemDecoration is used to give equal margins around RecyclerView grid.
(expand code)
// define deps compile 'com.android.support:cardview-v7:23.3.+' // define item layout. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:card_view="http://schemas.android.com/apk/res-auto"> <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" android:id="@+id/card_view" android:layout_gravity="center" android:layout_width="250dp" android:layout_height="250dp" card_view:cardCornerRadius="4dp"> <TextView android:text="Hello Card" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.v7.widget.CardView> </LinearLayout> // Define Decorator. public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { private int spanCount; private int spacing; private boolean includeEdge; public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) { this.spanCount = spanCount; this.spacing = spacing; this.includeEdge = includeEdge; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int position = parent.getChildAdapterPosition(view); // item position int column = position % spanCount; // item column if (includeEdge) { outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) if (position < spanCount) { // top edge outRect.top = spacing; } outRect.bottom = spacing; // item bottom } else { outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) if (position >= spanCount) { outRect.top = spacing; // item top } } } } /** * Converting dp to pixel */ private int dpToPx(int dp) { Resources r = getResources(); return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics())); } } // Activity: RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(this, 2); recyclerView.setLayoutManager(mLayoutManager); recyclerView.addItemDecoration(new GridSpacingItemDecoration(2, dpToPx(10), true)); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(adapter);
Step 15: RV With mutiple View Type: is it possible to create RecyclerView with multiple view types? Yes, it's possible. Just implement getItemViewType(), and take care of the viewType parameter in onCreateViewHolder().
(expand code)
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { class ViewHolder0 extends RecyclerView.ViewHolder { ... public ViewHolder0(View itemView){ ... } } class ViewHolder2 extends RecyclerView.ViewHolder { ... public ViewHolder2(View itemView){ ... } @Override public int getItemViewType(int position) { // Just as an example, return 0 or 2 depending on position // Note that unlike in ListView adapters, types don't have to be contiguous return position % 2 * 2; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case 0: return new ViewHolder0(...); case 2: return new ViewHolder2(...); ... } } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { switch (holder.getItemViewType()) { case 0: ViewHolder0 viewHolder0 = (ViewHolder0)holder; ... break; case 2: ViewHolder2 viewHolder2 = (ViewHolder2)holder; ... break; } } }
Step 16: efficent updates on rv:
(expand code)
// Insert single item: Add "Pig" at index 2. String item = "Pig"; int insertIndex = 2; data.add(insertIndex, item); adapter.notifyItemInserted(insertIndex); // Insert multiple items: Insert three more animals at index 2. ArrayList<String> items = new ArrayList<>(); items.add("Pig"); items.add("Chicken"); items.add("Dog"); int insertIndex = 2; data.addAll(insertIndex, items); adapter.notifyItemRangeInserted(insertIndex, items.size()); //Remove single item: Remove "Pig" from the list. int removeIndex = 2; data.remove(removeIndex); adapter.notifyItemRemoved(removeIndex); // Remove multiple items: Remove "Camel" and "Sheep" from the list. int startIndex = 2; // inclusive int endIndex = 4; // exclusive int count = endIndex - startIndex; // 2 items will be removed data.subList(startIndex, endIndex).clear(); adapter.notifyItemRangeRemoved(startIndex, count); // Remove all items: Clear the whole list. data.clear(); adapter.notifyDataSetChanged(); // Replace old list with new list: Clear the old list then add a new one. data.clear(); ArrayList<String> newList = new ArrayList<>(); newList.add("Lion"); newList.add("Wolf"); newList.add("Bear"); data.addAll(newList); adapter.notifyDataSetChanged(); // Update single item: Change the "Sheep" item so that it says "I like sheep." String newValue = "I like sheep."; int updateIndex = 3; data.set(updateIndex, newValue); adapter.notifyItemChanged(updateIndex); // Move single item: Move "Sheep" from position 3 to position 1. int fromPosition = 3; int toPosition = 1; // update data array String item = data.get(fromPosition); data.remove(fromPosition); data.add(toPosition, item); adapter.notifyItemMoved(fromPosition, toPosition);
Step 17: Adding Custom animation to RV items when loads: In this case We use ObjectAnimator and animation set to perform some animation.
(expand code)
public class AnimationUtil { public static void animate(RecyclerView.ViewHolder holder ,boolean goesDown){ AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator animatorTranslateY = ObjectAnimator.ofFloat(holder.itemView, "translationY", goesDown==true ? 200 : -200, 0); animatorTranslateY.setDuration(1000); ObjectAnimator animatorTranslateX = ObjectAnimator.ofFloat(holder.itemView,"translationX",-50,50,-30,30,-20,20,-5,5,0); animatorTranslateX.setDuration(1000); animatorSet.playTogether(animatorTranslateX,animatorTranslateY); //animatorSet.playTogether(animatorTranslateY); animatorSet.start(); } } public class MyCustomAdapter extends RecyclerView.Adapter<MyCustomAdapter.MyViewHolder> { .... @Override public void onBindViewHolder(MyViewHolder myViewHolder, final int position) { myViewHolder.textview.setText(data.get(position).title); myViewHolder.imageView.setImageResource(data.get(position).imageId); if(position > previousPosition){ // We are scrolling DOWN AnimationUtil.animate(myViewHolder, true); }else{ // We are scrolling UP AnimationUtil.animate(myViewHolder, false); } previousPosition = position; final int currentPosition = position; final Information infoData = data.get(position); myViewHolder.imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context, "OnClick Called at position " + position, Toast.LENGTH_SHORT).show(); addItem(currentPosition, infoData); } }); myViewHolder.imageView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { Toast.makeText(context, "OnLongClick Called at position " + position, Toast.LENGTH_SHORT).show(); removeItem(infoData); return true; } }); }
Android Notification End-to-End
Steo1: Define Notifcation Layouts:
(expand code)
// Notificationlayout.xml <?xml version="1.0" encoding="UTF-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/msglbl" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="test" /> <TextView android:id="@+id/message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/msglbl" /> <Button android:id="@+id/btn1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="play" android:layout_margin="10dp"/> </RelativeLayout>
Step 2: Define Your Notification calls.
(expand code)
public class MyNotification extends Notification { private Context ctx; private NotificationManager mNotificationManager; public MyNotification(Context ctx){ super(); this.ctx=ctx; String ns = Context.NOTIFICATION_SERVICE; mNotificationManager = (NotificationManager) ctx.getSystemService(ns); CharSequence tickerText = "Shortcuts"; long when = System.currentTimeMillis(); Notification.Builder builder = new Notification.Builder(ctx); Notification notification=builder.getNotification(); notification.when=when; notification.tickerText=tickerText; notification.icon=R.drawable.ic_launcher; RemoteViews contentView=new RemoteViews(ctx.getPackageName(), R.layout.messageview); //set the button listeners setListeners(contentView); notification.contentView = contentView; notification.flags |= Notification.FLAG_ONGOING_EVENT; CharSequence contentTitle = "From Shortcuts"; mNotificationManager.notify(548853, notification); } public void setListeners(RemoteViews view){ //radio listener Intent radio=new Intent(ctx,HelperActivity.class); radio.putExtra("DO", "radio"); PendingIntent pRadio = PendingIntent.getActivity(ctx, 0, radio, 0); view.setOnClickPendingIntent(R.id.radio, pRadio); //volume listener Intent volume=new Intent(ctx, HelperActivity.class); volume.putExtra("DO", "volume"); PendingIntent pVolume = PendingIntent.getActivity(ctx, 1, volume, 0); view.setOnClickPendingIntent(R.id.volume, pVolume); //reboot listener Intent reboot=new Intent(ctx, HelperActivity.class); reboot.putExtra("DO", "reboot"); PendingIntent pReboot = PendingIntent.getActivity(ctx, 5, reboot, 0); view.setOnClickPendingIntent(R.id.reboot, pReboot); //top listener Intent top=new Intent(ctx, HelperActivity.class); top.putExtra("DO", "top"); PendingIntent pTop = PendingIntent.getActivity(ctx, 3, top, 0); view.setOnClickPendingIntent(R.id.top, pTop);*/ //app listener Intent app=new Intent(ctx, com.example.demo.HelperActivity.class); app.putExtra("DO", "app"); PendingIntent pApp = PendingIntent.getActivity(ctx, 4, app, 0); view.setOnClickPendingIntent(R.id.btn1, pApp); } }
Step3: Invoke it from the activity:
(expand code)
public void showNotification(View view){ new MyNotification(this); finish(); }
[EndToEnd] Toast and Android Dialog.
Step 1: Building a simple Toast:
(expand code)
TODO.
Step 2: Having a Simple Dialog with OK.
(expand code)
new AlertDialog.Builder(YourActivity.this) .setTitle("Your Alert") .setMessage("Your Message") .setCancelable(false) .setPositiveButton("ok", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // Whatever... } }).show();
Step 3: Having a Simple Dialog with YES/NO.
(expand code)
new AlertDialog.Builder(this) .setTitle("AlertDialog"); .setMessage("Would you like to continue learning how to use Android alerts?"); .setPositiveButton("Continue", null), .setNegativeButton("Cancel", null); .show();
Step 4: Having a Simple Dialog with YES/NO/Cancel.
(expand code)
// Three button. new AlertDialog.Builder(this) .setTitle("Notice") .setMessage("Launching this missile will destroy the entire universe. Is this what you intended to do?") .setPositiveButton("Launch missile", null) .setNeutralButton("Remind me later", null) .setNegativeButton("Cancel", null) .show(); // Hanlde buuton clicks builder.setPositiveButton("Launch missile", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // do something like... launchMissile(); } });
Step 5: Using Dialog builder
(expand code)
// Having a builder and create dialogs. AlertDialog.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder = new AlertDialog.Builder(context, android.R.style.Theme_Material_Dialog_Alert); } else { builder = new AlertDialog.Builder(context); } ... // create and show the alert dialog AlertDialog dialog = builder.create(); dialog.show();
Step 6: Traditional single-choice list: Hvaing a list of item in Dilogas and choose one of them
(expand code)
AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Choose an animal"); String[] animals = {"horse", "cow", "camel", "sheep", "goat"}; builder.setItems(animals, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: // horse case 1: // cow case 2: // camel case 3: // sheep case 4: // goat } } }); AlertDialog dialog = builder.create(); dialog.show();
Step 7: Persistent single-choice list (radio buttons): Dillog with radio button list:
(expand code)
AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("Choose an animal"); String[] animals = {"horse", "cow", "camel", "sheep", "goat"}; int checkedItem = 1; // cow builder.setSingleChoiceItems(animals, checkedItem, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // user checked an item } }); // add OK and Cancel buttons builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // user clicked OK } }); builder.setNegativeButton("Cancel", null); AlertDialog dialog = builder.create(); dialog.show();
Step 8: Persistent multiple-choice list (checkboxes)
(expand code)
AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("Choose some animals"); String[] animals = {"horse", "cow", "camel", "sheep", "goat"}; boolean[] checkedItems = {true, false, false, true, false}; builder.setMultiChoiceItems(animals, checkedItems, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { // user checked or unchecked a box } }); builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // user clicked OK } }); builder.setNegativeButton("Cancel", null); AlertDialog dialog = builder.create(); dialog.show()
Step 9: Having a custom view to a dialog.
(expand code)
// have your own custom_layout.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:paddingLeft="20dp" android:paddingRight="20dp" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/editText" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> // Add this code in your main activity. AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Name"); // set the custom layout final View customLayout = getLayoutInflater().inflate(R.layout.custom_layout, null); builder.setView(customLayout); // add a button builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // send data from the AlertDialog to the Activity EditText editText = customLayout.findViewById(R.id.editText); sendDialogDataToActivity(editText.getText().toString()); } }); // create and show the alert dialog AlertDialog dialog = builder.create(); dialog.show();
Step 10: Uisng Dialog Fragemnets: DialogFragment is a specialized Fragment used when you want to display an overlay modal window within an activity that floats on top of the rest of the content.
(expand code)
<!-- fragment_edit_name.xml --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/edit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="vertical" > <TextView android:id="@+id/lbl_your_name" android:text="Your name" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <EditText android:id="@+id/txt_your_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="text" android:imeOptions="actionDone" /> </LinearLayout> public class EditNameDialogFragment extends DialogFragment { private EditText mEditText; public EditNameDialogFragment() { // Empty constructor is required for DialogFragment } public static EditNameDialogFragment newInstance(String title) { EditNameDialogFragment frag = new EditNameDialogFragment(); Bundle args = new Bundle(); args.putString("title", title); frag.setArguments(args); return frag; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_edit_name, container); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Get field from view mEditText = (EditText) view.findViewById(R.id.txt_your_name); // Fetch arguments from bundle and set title String title = getArguments().getString("title", "Enter Name"); getDialog().setTitle(title); // Show soft keyboard automatically and request focus to field mEditText.requestFocus(); getDialog().getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); } } // Invoke public class DialogDemoActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); showEditDialog(); } private void showEditDialog() { FragmentManager fm = getSupportFragmentManager(); EditNameDialogFragment editNameDialogFragment = EditNameDialogFragment.newInstance("Some Title"); editNameDialogFragment.show(fm, "fragment_edit_name"); } }
Step 11: Uisng Dialog Fragemnets without custom View.: We can create traditinal dialog using DialogFragemnt as below:
(expand code)
class MyAlertDialogFragment extends DialogFragment { public MyAlertDialogFragment() { // Empty constructor required for DialogFragment } public static MyAlertDialogFragment newInstance(String title) { MyAlertDialogFragment frag = new MyAlertDialogFragment(); Bundle args = new Bundle(); args.putString("title", title); frag.setArguments(args); return frag; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { String title = getArguments().getString("title"); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); alertDialogBuilder.setTitle(title); alertDialogBuilder.setMessage("Are you sure?"); alertDialogBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // on success } }); alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (dialog != null && dialog.isShowing()) { dialog.dismiss(); } } }); return alertDialogBuilder.create(); } }
Step 12: Passing data to activity Using Custom Callback mechisam. . You can also do that using customCreated Listner. In short: you Define an interface with methods that can be invoked to pass data result to the activity, etup a view event which invokes the custom listener passing data through the method abd Implement the interface in the Activity defining behavior for when the event is triggere
(expand code)
public class EditNameDialogFragment extends DialogFragment implements OnEditorActionListener { private EditText mEditText; // 1. Defines the listener interface with a method passing back data result. public interface EditNameDialogListener { void onFinishEditDialog(String inputText); } // ... @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { // ... // 2. Setup a callback when the "Done" button is pressed on keyboard mEditText.setOnEditorActionListener(this); } // Step 3: Fires @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (EditorInfo.IME_ACTION_DONE == actionId) { // Return input text back to activity through the implemented listener EditNameDialogListener listener = (EditNameDialogListener) getActivity(); // Calls Here ---> listener.onFinishEditDialog(mEditText.getText().toString()); // Close the dialog and return back to the parent activity dismiss(); return true; } return false; } } And your activity needs to implement public class DialogDemoActivity extends AppCompatActivity implements EditNameDialogListener { // This method is invoked in the activity when the listener is triggered Access the data result passed to the activity here @Override public void onFinishEditDialog(String inputText) { Toast.makeText(this, "Hi, " + inputText, Toast.LENGTH_SHORT).show(); } }
[EndToEnd] View pager
In this section, I will be discussion on View Pager. it allows the user to swipe left or right to see an entirely new screen.
Step 1. Adding View pager to your XML. You first need to define the view pager elemnet in the XMP as below:
(expand code)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v4.view.ViewPager android:id="@+id/vpPager" android:layout_width="match_parent" android:layout_height="wrap_content"> </android.support.v4.view.ViewPager> </LinearLayout>
Step 2: Define Pages as Fragment: Now you can define the pages as fragemmet. Now Let's define some Fragemnets like FirstFragment, FirstFragment etc which will actually conatins of view for each page.
(expand code)
public class FirstFragment extends Fragment { // Store instance variables private String title; private int page; // newInstance constructor for creating fragment with arguments public static FirstFragment newInstance(int page, String title) { FirstFragment fragmentFirst = new FirstFragment(); Bundle args = new Bundle(); args.putInt("someInt", page); args.putString("someTitle", title); fragmentFirst.setArguments(args); return fragmentFirst; } // Store instance variables based on arguments passed @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); page = getArguments().getInt("someInt", 0); title = getArguments().getString("someTitle"); } // Inflate the view for the fragment based on layout XML @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_first, container, false); TextView tvLabel = (TextView) view.findViewById(R.id.tvLabel); tvLabel.setText(page + " -- " + title); return view; } }
Step 3: Define Adapter: Now We need to provides a Adapter extending FragmentPagerAdapter, which is essentially holds all thrree fragments - will create and send it back as requested.
(expand code)
public static class MyPagerAdapter extends FragmentPagerAdapter { private static int NUM_ITEMS = 3; public MyPagerAdapter(FragmentManager fragmentManager) { super(fragmentManager); } // Returns total number of pages @Override public int getCount() { return NUM_ITEMS; } // Returns the fragment to display for that page @Override public Fragment getItem(int position) { switch (position) { case 0: // Fragment # 0 - This will show FirstFragment return FirstFragment.newInstance(0, "Page # 1"); case 1: // Fragment # 0 - This will show FirstFragment different title return FirstFragment.newInstance(1, "Page # 2"); case 2: // Fragment # 1 - This will show SecondFragment return SecondFragment.newInstance(2, "Page # 3"); default: return null; } } // Returns the page title for the top indicator @Override public CharSequence getPageTitle(int position) { return "Page " + position; } }
Step 4: Bind all together in Activity: Now Let's define the activity whihc will bind everything - it will get the view pager view and initilize the adapter and then bind adapter with ViewPager.
(expand code)
public class MainActivity extends AppCompatActivity { FragmentPagerAdapter adapterViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); // 1. get View pager ViewPager vpPager = (ViewPager) findViewById(R.id.vpPager); // 2. Build adapter adapterViewPager = new MyPagerAdapter(getSupportFragmentManager()); // 3. binds vpPager.setAdapter(adapterViewPager); } ... }
Step 5: Get and Set Current Item: You can programitically get or set a perticulr item as below:
(expand code)
// Set and get pages vpPager.getCurrentItem(); // --> 2 vpPager.setCurrentItem(2)
Step 6: Do Something when user chnage any page: You can add listner on when user chnage on some page.
(expand code)
// Attach the page change listener inside the activity vpPager.addOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int position) { Toast.makeText(HomeActivity.this, "Selected page position: " + position, Toast.LENGTH_SHORT).show(); // This method will be invoked when a new page becomes selected. } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // This method will be invoked when the current page is scrolled } @Override public void onPageScrollStateChanged(int state) { // Called when the scroll state changes: // SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING } });
Step 7: Define OffScreen Page Limit: user also can set on how many page fragmnet can be live in memeory.
(expand code)
// Set Offscreen Page Limit: provided by ViewPager to set how many page instances you want the system to keep in memory on either side of your current page. vpPager.setOffscreenPageLimit(3);
Step 8: Define gaps between the pages:
(expand code)
// Defien gaps between Pagers // http://blog.neteril.org/blog/2013/10/14/android-tip-viewpager-with-protruding-children/ vpPager.setClipToPadding(false); vpPager.setPageMargin(12);
Step 9: Using Custom animation. We can customize how the pages animate as they are being swiped between using the PageTransformer.
(expand code)
// compile 'com.ToxicBakery.viewpager.transforms:view-pager-transforms:1.2.32@aar' pager = (ViewPager) findViewById(R.id.container); pager.setAdapter(mAdapter); pager.setPageTransformer(true, new RotateUpTransformer()); // https://github.com/ToxicBakery/ViewPagerTransforms/tree/master/library/src/main/java/com/ToxicBakery/viewpager/transforms // Build your own. vpPager.setPageTransformer(false, new ViewPager.PageTransformer() { @Override public void transformPage(View page, float position) { int pageWidth = view.getWidth(); int pageHeight = view.getHeight(); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if(position <= 1){ // Page to the left, page centered, page to the right // modify page view animations here for pages in view } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } } });
Step 10: Adapter without Fragemnets : It is also possible to have a View Pager without any Fragemnt. If you provides a list of view, we can have a view pager with fragemnts.
(expand code)
public class mAdapter extends PagerAdapter { List<View> mlist; public void addView(View view, int index) { mList.add(index, view); notifyDataSetChanged(); } public void removeView(int index) { mList.remove(index); notifyDataSetChanged(); } @Override public int getItemPosition(Object object) { if (mList.contains((View) object) { return mList.indexOf((View) object); } else { return POSITION_NONE; } } }
Step 11: ViewPager with Same View but having differnt content: You might needs a view pager where all the pages are the same view - however, the content is different. It can be done as below:
PagerAdapter is the base class providing the adapter to populate pages inside of a ViewPager. You will most likely want to use a more specific implementation of this, such as FragmentPagerAdapter or FragmentStatePagerAdapter. It's inportnt to undertand PagerAdapter LifeCycle when you are are swaping the view.
Table: PagerAdapter LifeCycle
instantiateItem(ViewGroup, int pos)this function is called when a page needs to be added as it is asked for. so you need to create the page for posistion "pos" and add the view to "ViewGroup" and return the view.
destroyItem(ViewGroup container, int pos, Object view)this function is called when a page is removed from the view. The Object is mainly view and you need to remove the view from the container.
getCount()return the number of pages.
isViewFromObject(View, Object)we need to check of the object is a view.
getItemPositionCalled when the host view is attempting to determine if an item’s position has changed. Returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present in the adapter.
It might be hard to have add or remove in case of fragemnt. so you need to user FragmentStatePagerAdapter which will be taking care of instantiateItem and destroyItem.( see https://billynyh.github.io/blog/2014/03/02/fragment-state-pager-adapter/)
(expand code)
class CustomPagerAdapter extends PagerAdapter { Context mContext; LayoutInflater mLayoutInflater; ArrayList<Page> pages = new ArrayList<>(); public CustomPagerAdapter(Context context) { mContext = context; mLayoutInflater = LayoutInflater.from(mContext); } // Returns the number of pages to be displayed in the ViewPager. @Override public int getCount() { return pages.size(); } // Returns true if a particular object (page) is from a particular page @Override public boolean isViewFromObject(View view, Object object) { return view == object; } // This method should create the page for the given position passed to it as an argument. @Override public Object instantiateItem(ViewGroup container, int position) { // Inflate the layout for the page View itemView = mLayoutInflater.inflate(R.layout.pager_item, container, false); // Find and populate data into the page (i.e set the image) ImageView imageView = (ImageView) itemView.findViewById(R.id.imageView); // ... // Add the page to the container container.addView(itemView); // Return the page return itemView; } // Removes the page from the container for the given position. @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } } OR class CustomPagerAdapter extends PagerAdapter { Context mContext; LayoutInflater mLayoutInflater; int[] mResources = { R.drawable.first, R.drawable.second, R.drawable.third, R.drawable.fourth, R.drawable.fifth, R.drawable.sixth }; public CustomPagerAdapter(Context context) { mContext = context; mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public int getCount() { return mResources.length; } @Override public boolean isViewFromObject(View view, Object object) { return view == ((LinearLayout) object); } @Override public Object instantiateItem(ViewGroup container, int position) { View itemView = mLayoutInflater.inflate(R.layout.pager_item, container, false); ImageView imageView = (ImageView) itemView.findViewById(R.id.imageView); imageView.setImageResource(mResources[position]); container.addView(itemView); return itemView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((LinearLayout) object); } } // res/layout/pager_item.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/imageView" /> </LinearLayout>
Step 12: View Pager with Tab or Navigation Indigator:
(expand code)
<android.support.v4.view.ViewPager android:id="@+id/vpPager" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v4.view.PagerTabStrip android:id="@+id/pager_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" android:paddingBottom="4dp" android:paddingTop="4dp" /> </android.support.v4.view.ViewPager> OR <com.viewpagerindicator.TitlePageIndicator android:id="@+id/titles" android:layout_height="wrap_content" android:layout_width="fill_parent" />
Step 13: FragmentPagerAdapter vs FragmentStatePagerAdapter
Table: FragmentPagerAdapter vs FragmentStatePagerAdapter
FragmentPagerAdapterFragmentStatePagerAdapter
This version of the pager is best for use when there are a handful of typically more static fragments to be paged through, such as a set of tabsThis version of the pager is more useful when there are a large number of pages, working more like a list view.
The fragment of each page the user visits will be kept in memory, though its view hierarchy may be destroyed when not visible.When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment.
This can result in using a significant amount of memory since fragment instances can hold on to an arbitrary amount of state - but page switch faster#This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages. For smaller set of pagesFor larger sets of pages, consider FragmentStatePagerAdapter.
Application like having multiple tab like facebook.If you were to do an application like a book reader, you will not want to load all the fragments into memory at once.
Step 14: Vertical Swipe View Pager:
(expand code)
public class CustomViewPager extends ViewPager { public static final int HORIZONTAL = 0; public static final int VERTICAL = 1; private int mSwipeOrientation; private ScrollerCustomDuration mScroller = null; public CustomViewPager(Context context) { super(context); mSwipeOrientation = HORIZONTAL; } public CustomViewPager(Context context, AttributeSet attrs) { super(context, attrs); setSwipeOrientation(context, attrs); } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(mSwipeOrientation == VERTICAL ? swapXY(event) : event); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (mSwipeOrientation == VERTICAL) { boolean intercepted = super.onInterceptHoverEvent(swapXY(event)); swapXY(event); return intercepted; } return super.onInterceptTouchEvent(event); } public void setSwipeOrientation(int swipeOrientation) { if (swipeOrientation == HORIZONTAL || swipeOrientation == VERTICAL) mSwipeOrientation = swipeOrientation; else throw new IllegalStateException("Swipe Orientation can be either CustomViewPager.HORIZONTAL" + " or CustomViewPager.VERTICAL"); initSwipeMethods(); } private void setSwipeOrientation(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomViewPager); mSwipeOrientation = typedArray.getInteger(R.styleable.CustomViewPager_swipe_orientation, 0); typedArray.recycle(); initSwipeMethods(); } private void initSwipeMethods() { if (mSwipeOrientation == VERTICAL) { // The majority of the work is done over here setPageTransformer(true, new VerticalPageTransformer()); // The easiest way to get rid of the overscroll drawing that happens on the left and right setOverScrollMode(OVER_SCROLL_NEVER); } } /** * Set the factor by which the duration will change */ public void setScrollDurationFactor(double scrollFactor) { mScroller.setScrollDurationFactor(scrollFactor); } private MotionEvent swapXY(MotionEvent event) { float width = getWidth(); float height = getHeight(); float newX = (event.getY() / height) * width; float newY = (event.getX() / width) * height; event.setLocation(newX, newY); return event; } private class VerticalPageTransformer implements ViewPager.PageTransformer { @Override public void transformPage(View page, float position) { if (position < -1) { // This page is way off-screen to the left page.setAlpha(0); } else if (position <= 1) { page.setAlpha(1); // Counteract the default slide transition page.setTranslationX(page.getWidth() * -position); // set Y position to swipe in from top float yPosition = position * page.getHeight(); page.setTranslationY(yPosition); } else { // This page is way off screen to the right page.setAlpha(0); } } } } How to user: <com.myapp.CustomViewPager xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" app:swipe_orientation="vertical" />
Step 15: ViewPager animation fade in/out instead of slide:
(expand code)
public class FadePageTransformer implements ViewPager.PageTransformer { public void transformPage(View view, float position) { view.setTranslationX(view.getWidth() * -position); if(position <= -1.0F || position >= 1.0F) { view.setAlpha(0.0F); } else if( position == 0.0F ) { view.setAlpha(1.0F); } else { // position is between -1.0F & 0.0F OR 0.0F & 1.0F view.setAlpha(1.0F - Math.abs(position)); } } } // Activity: mViewPager.setPageTransformer(false, new FadePageTransformer());
Example: Listen text xhnage of Edit Text
(expand code)
EditText value = (EditText) view.findViewById(R.id.value); value.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { HashMap<String, String> edit = new HashMap<>(); edit.put("string", s.toString()); openEntry.edit_list.add(edit); } });
Example: Have a Simple Guesture detcetor
(expand code)
GestureDetector myG; myG = new GestureDetector(this,new Gesture()); class Gesture extends GestureDetector.SimpleOnGestureListener{ public boolean onSingleTapUp(MotionEvent ev) { } public void onLongPress(MotionEvent ev) { } public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { } public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { } } // Here is your activity. import android.app.Activity; import android.graphics.Matrix; import android.os.Bundle; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.widget.ImageView; public class MainActivity extends Activity { private ImageView iv; private Matrix matrix = new Matrix(); private float scale = 1f; private ScaleGestureDetector SGD; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv=(ImageView)findViewById(R.id.imageView); SGD = new ScaleGestureDetector(this,new ScaleListener()); } public boolean onTouchEvent(MotionEvent ev) { SGD.onTouchEvent(ev); return true; } private class ScaleListener extends ScaleGestureDetector. SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { scale *= detector.getScaleFactor(); scale = Math.max(0.1f, Math.min(scale, 5.0f)); matrix.setScale(scale, scale); iv.setImageMatrix(matrix); return true; } } }
Example: Click and Long Click
(expand code)
import android.app.ProgressDialog; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends ActionBarActivity { private ProgressDialog progress; Button b1,b2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progress = new ProgressDialog(this); b1=(Button)findViewById(R.id.button); b2=(Button)findViewById(R.id.button2); b1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TextView txtView = (TextView) findViewById(R.id.textView); txtView.setTextSize(25); } }); b2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TextView txtView = (TextView) findViewById(R.id.textView); txtView.setTextSize(55); } }); } }
Example: Some Touch Evnets
(expand code)
import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity { float xAxis = 0f; float yAxis = 0f; float lastXAxis = 0f; float lastYAxis = 0f; EditText ed1, ed2, ed3, ed4; TextView tv1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ed1 = (EditText) findViewById(R.id.editText); ed2 = (EditText) findViewById(R.id.editText2); ed3 = (EditText) findViewById(R.id.editText3); ed4 = (EditText) findViewById(R.id.editText4); tv1=(TextView)findViewById(R.id.textView2); tv1.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { final int actionPeformed = event.getAction(); switch(actionPeformed){ case MotionEvent.ACTION_DOWN:{ final float x = event.getX(); final float y = event.getY(); lastXAxis = x; lastYAxis = y; ed1.setText(Float.toString(lastXAxis)); ed2.setText(Float.toString(lastYAxis)); break; } case MotionEvent.ACTION_MOVE:{ final float x = event.getX(); final float y = event.getY(); final float dx = x - lastXAxis; final float dy = y - lastYAxis; xAxis += dx; yAxis += dy; ed3.setText(Float.toString(xAxis)); ed4.setText(Float.toString(yAxis)); break; } } return true; } }); } }
Example: Here is the simple Notification example
(expand code)
public class MainActivity extends Activity { Button b1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); b1 = (Button)findViewById(R.id.button); b1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addNotification(); } }); } private void addNotification() { NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.abc) .setContentTitle("Notifications Example") .setContentText("This is a test notification"); Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(contentIntent); // Add as notification NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(0, builder.build()); } }
Example: Here I will sow you of all Kind of alrt dialog
(expand code)
// Simple AlertDiloags AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); alertDialogBuilder.setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) alertDialogBuilder.setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) AlertDialog alertDialog = alertDialogBuilder.create(); alertDialog.show(); // Using Dialog Fragments public class DialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { toast.makeText(this,"enter a text here",Toast.LENTH_SHORT).show(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); }); // Create the AlertDialog object and return it return builder.create(); } } } // Show List in the Dilaog public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(Pick a Color) .setItems(R.array.colors_array, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // The 'which' argument contains the index position // of the selected item } }); return builder.create(); } // Single-choice list dialog public Dialog onCreateDialog(Bundle savedInstanceState) { mSelectedItems = new ArrayList(); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle("This is list choice dialog box"); .setMultiChoiceItems(R.array.toppings, null, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { if (isChecked) { // If the user checked the item, add it to the selected items mSelectedItems.add(which); } else if (mSelectedItems.contains(which)) { // Else, if the item is already in the array, remove it mSelectedItems.remove(Integer.valueOf(which)); } } }) // Set the action buttons .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { // User clicked OK, so save the mSelectedItems results somewhere // or return them to the component that opened the dialog ... } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { ... } }); return builder.create(); }
Example: Let's have an example of all dialog.
(expand code)
import android.app.ProgressDialog; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends ActionBarActivity { Button b1; private ProgressDialog progress; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); b1 = (Button) findViewById(R.id.button2); } public void testProgressDilog(View view){ progress=new ProgressDialog(this); progress.setMessage("Downloading Music"); progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progress.setIndeterminate(true); progress.setProgress(0); progress.show(); final int totalProgressTime = 100; final Thread t = new Thread() { @Override public void run() { int jumpTime = 0; while(jumpTime < totalProgressTime) { try { sleep(200); jumpTime += 5; progress.setProgress(jumpTime); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; t.start(); } // test Date DatePicker calendar = Calendar.getInstance(); year = calendar.get(Calendar.YEAR); month = calendar.get(Calendar.MONTH); day = calendar.get(Calendar.DAY_OF_MONTH); showDate(year, month+1, day); private DatePickerDialog.OnDateSetListener myDateListener = new DatePickerDialog.OnDateSetListener() { @Override public void onDateSet(DatePicker arg0, int arg1, int arg2, int arg3) { // TODO Auto-generated method stub // arg1 = year // arg2 = month // arg3 = day showDate(arg1, arg2+1, arg3); } };
Example: Sample Linier layouts
(expand code)
import android.os.Bundle; import android.app.Activity; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/btnStartService" android:layout_width="270dp" android:layout_height="wrap_content" android:text="start_service"/> <Button android:id="@+id/btnPauseService" android:layout_width="270dp" android:layout_height="wrap_content" android:text="pause_service"/> <Button android:id="@+id/btnStopService" android:layout_width="270dp" android:layout_height="wrap_content" android:text="stop_service"/> </LinearLayout>
Example: Let's see an example of all Mostly usefule control :
(expand code)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="I am a TextView" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="I am a Button" /> <EditText android:id="@+id/edittext" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignLeft="@+id/button" android:layout_below="@+id/textView1" android:layout_marginTop="61dp" android:ems="10" android:text="@string/enter_text" android:inputType="text" /> <AutoCompleteTextView android:id="@+id/autoCompleteTextView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/textView2" android:layout_below="@+id/textView2" android:layout_marginTop="54dp" android:ems="10" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageButton" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:src="@drawable/abc"/> <CheckBox android:id="@+id/checkBox1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Do you like Tutorials Point" android:layout_above="@+id/button" android:layout_centerHorizontal="true" /> <ToggleButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="On" android:id="@+id/toggleButton" android:checked="true" android:layout_below="@+id/imageButton" android:layout_toEndOf="@+id/button2/> <RadioGroup android:layout_width="fill_parent" android:layout_height="90dp" android:layout_below="@+id/imageView" android:layout_marginTop="58dp" android:weightSum="1" android:id="@+id/radioGroup" android:layout_alignLeft="@+id/textView2" android:layout_alignStart="@+id/textView2" android:layout_alignRight="@+id/textView3" android:layout_alignEnd="@+id/textView3"> <RadioButton android:layout_width="wrap_content" android:layout_height="55dp" android:text="Male" android:id="@+id/radioButton" android:layout_gravity="center_horizontal" android:checked="false" android:textSize="25dp" /> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Female" android:id="@+id/radioButton2" android:layout_gravity="center_horizontal" android:checked="false" android:textSize="25dp" android:layout_weight="0.13" /> </RadioGroup> <Spinner android:id="@+id/spinner" android:layout_width="fill_parent" android:layout_height="wrap_content" android:prompt="@string/spinner_title"/> <TimePicker android:id="@+id/timePicker1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; public class MainActivity extends Activity { AutoCompleteTextView autocomplete; String[] arr = { "Paries,France", "PA,United States","Parana,Brazil", "Padua,Italy", "Pasadena,CA,United States"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Autocomplte autocomplete = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView1); ArrayAdapter<String> adapter = new ArrayAdapter<String> (this,android.R.layout.select_dialog_item, arr); autocomplete.setThreshold(2); autocomplete.setAdapter(adapter); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { StringBuffer result = new StringBuffer(); result.append("Thanks : ").append(ch1.isChecked()); result.append("\nThanks: ").append(ch2.isChecked()); Toast.makeText(MainActivity.this, result.toString(), Toast.LENGTH_LONG).show(); } }); } void testSpinner(){ Spinner spinner = (Spinner) findViewById(R.id.spinner); spinner.setOnItemSelectedListener(new onItemSelected(AdapterView<?> parent, View view, int position, long id) { String item = parent.getItemAtPosition(position).toString(); Toast.makeText(parent.getContext(), "Selected: " + item, Toast.LENGTH_LONG).show(); }); // Spinner Drop down elements List<String> categories = new ArrayList<String>(); categories.add("Automobile"); categories.add("Business Services"); categories.add("Computers"); categories.add("Education"); categories.add("Personal"); categories.add("Travel"); ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, categories); dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(dataAdapter); } // Timer import android.widget.TimePicker; private TimePicker timePicker1; timePicker1 = (TimePicker) findViewById(R.id.timePicker1); int hour = timePicker1.getCurrentHour(); int min = timePicker1.getCurrentMinute(); }
Android fragments
A fragment is a reusable class implementing a portion of an activity.
Table: Callbacks
onAttach()is called when a fragment is connected to an activity.
onCreate()is called to do initial creation of the fragment.
onCreateView()is called by Android once the Fragment should inflate a view.
onViewCreated()is called after onCreateView() and ensures that the fragment's root view is non-null. Any view setup should happen here. E.g., view lookups, attaching listeners.
onActivityCreated()is called when host activity has completed its onCreate() method.
onStart()is called once the fragment is ready to be displayed on screen.
onResume()Allocate “expensive” resources such as registering for location, sensor updates, etc.
onPause()Release “expensive” resources. Commit any changes.
onDestroyView()is called when fragment's view is being destroyed, but the fragment is still kept around.
onDestroy()is called when fragment is no longer in use.
onDetach()is called when fragment is no longer connected to the activity.
  1. Activities can initialize fragments with data during construction: pass data though the constracter of fragmnets.
  2. Activities can pass data to fragments using methods on the fragment instance: Pass data using member function of fragments,
  3. Fragments can communicate up to their parent activity using an interface and listeners: Fragemnt defines the callback inetrface and Parenet implementing it.
  4. Fragments should pass data to other fragments only routed through their parent activity
  5. Fragments can pass data to and from dialog fragments though the interface like sendBackResult.
  6. Fragments can contain nested child fragments as outlined here
First, let;s define a xml file which will be render as a part of fragemnts
(expand code)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout>
Second, Let's define the Fragment class which influate the view in xmlns
(expand code)
import android.support.v4.app.Fragment; public class FooFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_foo, parent, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { // EditText etFoo = (EditText) view.findViewById(R.id.etFoo); // Add listners } }
Thirds, Now we can add the fragemnts inside activity layout as
(expand code)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <fragment android:name="com.example.android.FooFragment" android:id="@+id/fooFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
OR
(expand code)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/your_placeholder" <<< Placeholder android:layout_width="match_parent" android:layout_height="match_parent"> </FrameLayout> </LinearLayout>
Forth, This can now refer by Activity( if it is statically linked) or can be loaded dynamically using FragmentManager. We can lookup fragments either using ID or TAG or Pagers
(expand code)
public class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { // Look up by ID. DemoFragment fragmentDemo = (DemoFragment) getSupportFragmentManager().findFragmentById(R.id.fragmentDemo); //ADD THE TAG getSupportFragmentManager().beginTransaction(). replace(R.id.flContainer, new DemoFragment(), "SOMETAG"). commit(); // Now later we can lookup the fragment by tag DemoFragment fragmentDemo = (DemoFragment) getSupportFragmentManager().findFragmentByTag("SOMETAG"); // If you have view pager do this like. adapterViewPager.getRegisteredFragment(0); // Add Fragemnts Dynamically: It;s a three step procress. FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.your_placeholder, new FooFragment()); ft.commit(); } } }
Fifth, Let;s pass some data to the fragment while creating, generally It can be done using static method. This is because a Fragment must have only a constructor with no arguments.
(expand code)
public class DemoFragment extends Fragment { // Creates a new fragment given an int and title // DemoFragment.newInstance(5, "Hello"); public static DemoFragment newInstance(int someInt, String someTitle) { DemoFragment fragmentDemo = new DemoFragment(); Bundle args = new Bundle(); args.putInt("someInt", someInt); args.putString("someTitle", someTitle); fragmentDemo.setArguments(args); return fragmentDemo; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); int SomeInt = getArguments().getInt("someInt", 0); String someTitle = getArguments().getString("someTitle", ""); } } // Calls from Activity. public class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); DemoFragment fragmentDemo = DemoFragment.newInstance(5, "my title"); ft.replace(R.id.your_placeholder, fragmentDemo); ft.commit(); } }
Let's define a fragemnt and pass the data on runtime.
(expand code)
// Have Fragment public class DemoFragment extends Fragment { public void doSomething(String param) { // handle this data. } } // In Activity. public class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); DemoFragment fragmentDemo = (DemoFragment) getSupportFragmentManager().findFragmentById(R.id.fragmentDemo); fragmentDemo.doSomething("some param"); } }
Now, Let;s try to pass some data from fragemnt to Activity.
(expand code)
import android.support.v4.app.Fragment; public class MyListFragment extends Fragment { private OnItemSelectedListener listener; public interface OnItemSelectedListener { public void onItemSelected(String link); } // Store the listener (activity) that will have events fired once the fragment is attached @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnItemSelectedListener) { listener = (OnItemSelectedListener) context; } else { throw new ClassCastException(context.toString() + " must implement MyListFragment.OnItemSelectedListener"); } } public void onSomeClick(View v) { listener.onItemSelected("some link"); } } // Activity implements the Callback. import android.support.v7.app.AppCompatActivity; public class RssfeedActivity extends AppCompatActivity implements MyListFragment.OnItemSelectedListener { private DetailFragment fragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_rssfeed); fragment = (DetailFragment) getSupportFragmentManager() .findFragmentById(R.id.detailFragment); } @Override public void onItemSelected(String link) { if (fragment != null && fragment.isInLayout()) { // Handle the result. } } }
The FragmentManager is responsible for all runtime management of fragments including adding, removing, hiding, showing, or otherwise navigating between fragments. It suppprts below method to do some work:
  1. addOnBackStackChangedListener - Add a new listener for changes to the fragment back stack.
  2. beginTransaction() - Creates a new transaction to change fragments at runtime.
  3. findFragmentById(int id) - Finds a fragment by id usually inflated from activity XML layout.
  4. findFragmentByTag(String tag): Finds a fragment by tag usually for a runtime added fragment.
  5. popBackStack() - Remove a fragment from the backstack.
  6. executePendingTransactions() Forces committed transactions to be applied.
Here is an example of Fragment Trasation:
(expand code)
Using fragmen, it is possible to customize the backStack operation. A record of all Fragment transactions is kept for each Activity by the FragmentManager. When used properly, this allows the user to hit the device’s back button to remove previously added Fragments.
(expand code)
//add to backStack FragmentTransaction fts = getSupportFragmentManager().beginTransaction(); fts.replace(R.id.flContainer, new FirstFragment()); fts.addToBackStack("optional_tag"); fts.commit(); // Just remove the lastly added. FragmentManager fragmentManager = getSupportFragmentManager(); if (fragmentManager.getBackStackEntryCount() > 0) { fragmentManager.popBackStack(); } //Just go back to specif TAG FragmentManager fragmentManager = getSupportFragmentManager(); if (fragmentManager.getBackStackEntryCount() > 0) { fragmentManager.popBackStack("optional_tag"); }
We can easily show/hide the fragemnt then calling replaces - This can be good because this will release memory and make the UI snappier. For example :
(expand code)
private FragmentA fragmentA; private FragmentB fragmentB; private FragmentC fragmentC; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { fragmentA = FragmentA.newInstance("foo"); fragmentB = FragmentB.newInstance("bar"); fragmentC = FragmentC.newInstance("baz"); } } // NOT so good protected void displayFragmentA() { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); // removes the existing fragment calling onDestroy ft.replace(R.id.flContainer, fragmentA); ft.commit(); } // Better protected void displayFragmentA() { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); if (fragmentA.isAdded()) { // if the fragment is already in container ft.show(fragmentA); } else { // fragment needs to be added to frame container ft.add(R.id.flContainer, fragmentA, "A"); } // Hide fragment B if (fragmentB.isAdded()) { ft.hide(fragmentB); } // Hide fragment C if (fragmentC.isAdded()) { ft.hide(fragmentC); } // Commit changes ft.commit(); }
Executing Async Jobs in android.
WebSocket Support to Android
Step1: Let's consider the below example for build the server.
(expand code)
const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', function connection(ws) { ws.on('message', function incoming(message) { console.log('received: %s', message); }); ws.send('something'); }); // Store this as server.js file. // $node server.js
Step2: Let's test the code from sample HTML
(expand code)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> WebSocket</title> <script src="/socket.io/socket.io.js" ></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script> <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> <script> var socket = io.connect('http://0.0.0.0:8088'); socket.on('message', function (data) { console.log(data); socket.emit('my other event', { my: 'data' }); }); function send(){ socket.emit('message', {tag: 'info',data:"hello"}); } </script> </head> <body> <button type="button" onclick="send();">Click Me!</button> </body> </html>
Step3: Now let's write android plagins to send and recv data from our server.
(expand code)
Support personalize Experice.
In this perticulr section, i will be discussion with how to personalized your app experince for the user. The term "Personlization" means that your app should resposnce perfactly for the multiple devices, user can choose their languages and user can have their own theme etc.
Table: Summary of having a better Personalized experince
Supporting mutiple languagesExample to support mutiple languages
Supporting seperate layout for differnt devicesHaving different layout for diffent devices.
Supporting diffent dimens for diffent devicesHaving multiple dimes for diff devices.
Supporting multiple imagesSupporting multiple images for diffent size device
Supporting mutiple languages
Let's consider supporting hindi and english langues for a app.
Step1: Let;s have this layout. Note that we NOT hardcoding the string.
(expand code)
Step2: Define the defalut string in res/values/strings.xml.
(expand code)
Step3: Define the hindi language string in res/values/strings-hi.xml. You can support mutiple languages using the file name as res/values/strings-.xml
(expand code)
Step 4: Now we we just have the fun. You can go the device setting -> personal -> languages -> choose language and You will see the language of the app has chnages. We can do it programitically as below:
(expand code)
import android.app.Activity; import android.content.res.Configuration; import android.os.Bundle; public class Main extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String languageToLoad = "fa"; // your language "hi" for hindi and "en" for english Locale locale = new Locale(languageToLoad); Locale.setDefault(locale); Configuration config = new Configuration(); config.locale = locale; getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); this.setContentView(R.layout.main); } private void change(){ Resources res = context.getResources(); // Change locale settings in the app. DisplayMetrics dm = res.getDisplayMetrics(); android.content.res.Configuration conf = res.getConfiguration(); conf.setLocale(new Locale(language_code.toLowerCase())); // API 17+ only. // Use conf.locale = new Locale(...) if targeting lower versions res.updateConfiguration(conf, dm); } }
Step 5: You can persisit the language choice for the app as below:
(expand code)
public class BaseActivity extends AppCompatActivity { private Locale mCurrentLocale; @Override protected void onStart() { super.onStart(); mCurrentLocale = getResources().getConfiguration().locale; } @Override protected void onRestart() { super.onRestart(); Locale locale = getLocale(this); if (!locale.equals(mCurrentLocale)) { mCurrentLocale = locale; recreate(); } } public static Locale getLocale(Context context){ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); String lang = sharedPreferences.getString("language", "en"); switch (lang) { case "English": lang = "en"; break; case "Spanish": lang = "es"; break; } return new Locale(lang); } } public class App extends Application { @Override public void onCreate() { super.onCreate(); setLocale(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); setLocale(); } private void setLocale() { final Resources resources = getResources(); final Configuration configuration = resources.getConfiguration(); final Locale locale = getLocale(this); if (!configuration.locale.equals(locale)) { configuration.setLocale(locale); resources.updateConfiguration(configuration, null); } } }
Supporting seperate layout for differnt devices.
Sometime it's required to have multiple layout of the UI for phone or tablet. We can easilty do that by spacifying mutple layout file. We can alos spacify multiple dimention files to spacidify differnet weidth of the buttons for phone and tablet.
Step1: We can have two diffetnt layoyt for phone and 7 each tablet in res/layout/main_activity.xml and res/layout-sw600dp/main_activity.xml(600dp or bigger).
(expand code)
Table: Topical screen size:
320dpa typical phone screen (240x320 ldpi, 320x480 mdpi, 480x800 hdpi, etc). 4 inch phone
480dpa large phone screen ~5" (480x800 mdpi). 5 inch phone
600dpa small 7” tablet (600x1024 mdpi).
720dpa 10” Larget tablet (720x1280 mdpi, 800x1280 mdpi, etc).
840dplarget tablet
960dpVery large tablet
1280dplarge
1440dplarge
1600dplarge
1920dpExtra large
Table: Which file to store what?
res/layout/main_activity.xmlFor handsets (smaller than 600dp available width)
res/layout-w600dp/main_activity.xmlFor 7” tablets or any screen with 600dp
res/layout/main_activity.xmlFor handsets
res/layout-land/main_activity.xmlFor handsets in landscape
res/layout-sw600dp/main_activity.xmlFor 7” tablets
res/layout-sw600dp-land/main_activity.xmlFor 7” tablets in landscape
res/layout/main_activity.xmlFor handsets (smaller than 640dp x 480dp)
res/layout-large/main_activity.xmlFor small tablets (640dp x 480dp and bigger)
res/layout-xlarge/main_activity.xmlFor large tablets (960dp x 720dp and bigger)
You could also use layout alias. It can be defined all files inside layout, however, we can distrigushed the by having differnet values file.
(expand code)
res/layout/main.xml # single-pane layout res/layout/main_twopanes.xml # two-pane layout And add these two files: res/values-large/layout.xml: <resources> <item name="main" type="layout">@layout/main_twopanes</item> </resources> res/values-sw600dp/layout.xml: <resources> <item name="main" type="layout">@layout/main_twopanes</item> </resources>
Supporting different button width for diffent devices.
You must understand the diff betwen dip and sp. Yhen you specify spacing between two views, use dp and When specifying text size, always use sp:
(expand code)
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/clickme" android:layout_marginTop="20dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" />
For example, You have a button view and You would like to have multiple dimention for this button for multiple devices. How to do that? Let's creates values folders and a dimens files in each folder. Based on your requirement you can give padding or any dimen values. As you given, you should give same name for dimen in all the folders. Android will automatically pick the dimen value based on your screen size.
(expand code)
<textView android:padding="@dimen/padding_1 /> values/dimens.xml <dimen name="padding_1">5dp</dimen> values-sw360dp/dimens.xml <dimen name="padding_1">5dp</dimen> values-sw480dp/dimens.xml <dimen name="padding_1">8dp</dimen> values-sw600dp/dimens.xml <dimen name="padding_1">10dp</dimen> values-sw720dp/dminen.xml <dimen name="padding_1">13dp</dimen>
Supporting multiple images
When you want to use image, you must provides the below variabtion:
(expand code)
res/ drawable-xxxhdpi/ awesome-image.png drawable-xxhdpi/ awesome-image.png drawable-xhdpi/ awesome-image.png drawable-hdpi/ awesome-image.png drawable-mdpi/ awesome-image.png
Table: This table is showing image size of each
36x36(0.75x) for low-density (ldpi)
48x48(1.0x baseline) for medium-density (mdpi)
72x72(1.5x) for high-density (hdpi)
96x96(2.0x) for extra-high-density (xhdpi)
144x144(3.0x) for extra-extra-high-density (xxhdpi)
192x192(4.0x) for extra-extra-extra-high-density (xxxhdpi)
Android Networking
Table: Summary of Android Networking supports
Accessing HTTPusing okhttp3
Accessing WebSocketusing okHTTP3 agains
HTTP sreverWe will show an example how to create HTTP server inside andorid app and from outside app.
Connecting websocket
WebSocket providing full-duplex communication channels over a single TCP connection. It is supported in HTML 5. Since the version 3.5 of the OkHttp library, you can also use WebSockets connection in your Android applications. Let's try to use it.
(expand code)
Step1: Add Dependency compile 'com.squareup.okhttp3:okhttp:3.6.0'
Step2:
package in.co.dipankar.quickandorid.utils;
(expand code)
package in.co.dipankar.quickandorid.utils; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.WebSocket; import okhttp3.WebSocketListener; import okio.ByteString; public class WSUtils { private String mUrl; private OkHttpClient mClient; public interface Callback{ public void onConnected(); public void onDisconnected(); public void onMessage(String message); public void onError(); } private WebSocket mWebSocket; private Callback mCallback; public WSUtils(String url, Callback callback){ mUrl = url; mCallback = callback; init(); } private void init(){ mClient = new OkHttpClient(); Request request = new Request.Builder().url(mUrl).build(); WebSocket ws = mClient.newWebSocket(request, new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) { super.onOpen(webSocket, response); mCallback.onConnected(); mWebSocket = webSocket; } @Override public void onMessage(WebSocket webSocket, String text) { super.onMessage(webSocket, text); mCallback.onMessage(text); } @Override public void onMessage(WebSocket webSocket, ByteString bytes) { super.onMessage(webSocket, bytes); mCallback.onMessage(bytes.toString()); } @Override public void onClosing(WebSocket webSocket, int code, String reason) { super.onClosing(webSocket, code, reason); mWebSocket.close(1, null); mWebSocket = null; } @Override public void onClosed(WebSocket webSocket, int code, String reason) { super.onClosed(webSocket, code, reason); mCallback.onDisconnected(); } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { super.onFailure(webSocket, t, response); mCallback.onError(); } }); } public boolean sendMessage(String message){ if(mWebSocket != null){ mWebSocket.send(message); return true; } else { mCallback.onError(); return false; } } public boolean connect(){ return true; } public boolean disconnect(){ if(mWebSocket != null){ mClient.dispatcher().executorService().shutdown(); return true; } else{ return false; } } }
Step 3: Here is how it can be used form your activity:
(expand code)
private void testWebSocket() { WSUtils ws = new WSUtils("ws://echo.websocket.org", new WSUtils.Callback(){ @Override public void onConnected() { DLog.e("WS onConnected"); } @Override public void onDisconnected() { DLog.e("WS onDisconnected"); } @Override public void onMessage(String message) { DLog.e("WS onMessage"); } @Override public void onError() { DLog.e("WS onError"); } }); ws.sendMessage("Hello"); ws.disconnect(); }
Simple Network Calls to parse JSON.
Similar to web socket, we can have our simple network Module to send web request as given as example.
(expand code)
compile 'com.squareup.okhttp3:okhttp:3.2.0'
Step2: Now you have some simple Utils class like this:
(expand code)
package in.co.dipankar.quickandorid.utils; import org.json.JSONObject; import java.io.IOException; import java.util.Map; import okhttp3.Call; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; public class HTTPUtils { public interface Callback{ public void onBeforeSend(); public void onSuccess(JSONObject obj); public void onError(String msg); } private OkHttpClient mClient; public HTTPUtils(){ mClient = new OkHttpClient(); } public boolean get(String url, final Callback callback){ Request request = new Request.Builder() .url(url) .get() .build(); callback.onBeforeSend(); mClient.newCall(request).enqueue( new okhttp3.Callback() { @Override public void onFailure(Call call, IOException e) { if(callback != null) { callback.onError("Internal error:" + e.getMessage()); } } @Override public void onResponse(Call call, Response response) throws IOException { if (callback != null) { try { String jsonData = response.body().string(); JSONObject Jobject = new JSONObject(jsonData); callback.onSuccess(Jobject); } catch (Exception e) { e.printStackTrace(); callback.onError( "Internal error happened while parsing the json object"); } } } }); return true; } public boolean post(String url, Map<String, String> data, final Callback callback){ JSONObject json = new JSONObject(); if(data!= null) { try { for (Map.Entry<String, String> entry : data.entrySet()) { json.put(entry.getKey(), entry.getValue()); } } catch (Exception e) { e.printStackTrace(); return false; } } MediaType JSON = MediaType.parse("application/json; charset=utf-8"); RequestBody body = RequestBody.create(JSON, json.toString()); callback.onBeforeSend(); Request request = new Request.Builder().url(url).post(body).build(); mClient.newCall(request).enqueue(new okhttp3.Callback() { @Override public void onFailure(Call call, IOException e) { if (callback != null) { callback.onError(e.toString()); } } @Override public void onResponse(Call call, Response response) throws IOException { if (callback != null) { try { String jsonData = response.body().string(); JSONObject Jobject = new JSONObject(jsonData); callback.onSuccess(Jobject); } catch (Exception e) { e.printStackTrace(); callback.onError("Internal error happened while parsing the json object"); } } } }); return true; } }
Step3: He is how you can call thease utils from your android Activity or from whatever you want:
(expand code)
private void testHTTPUtils() { HTTPUtils httpUtils = new HTTPUtils(); httpUtils.get("http://simplestore.dipankar.co.in/api/test", new HTTPUtils.Callback() { @Override public void onBeforeSend() { DLog.e("HTTPUtils onBeforeSend"); } @Override public void onSuccess(JSONObject obj) { try { DLog.e("HTTPUtils onSuccess"+obj.getString("status")); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(String msg) { DLog.e("HTTPUtils onError"); } }); Map<String, String> data = new HashMap<>(); data.put("name", "dipankar"); httpUtils.post("http://simplestore.dipankar.co.in/api/test", data, new HTTPUtils.Callback() { @Override public void onBeforeSend() { DLog.e("HTTPUtils onBeforeSend"); } @Override public void onSuccess(JSONObject obj) { try { DLog.e("HTTPUtils onSuccess"+obj.getString("status")); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(String msg) { DLog.e("HTTPUtils onError"); } }); }
Synchronous Get
(expand code)
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("http://www.ssaurel.com/tmp/toto.txt") .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) { // manage error Log.e("Unexpected code " + response); return; } Headers responseHeaders = response.headers(); // show response headers for (int i = 0; i < responseHeaders.size(); i++) { Log.i(responseHeaders.name(i) + ": " + responseHeaders.value(i)); } // show body content Log.i(response.body().string());
Aync Get Request
(expand code)
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("http://www.ssaurel.com/tmp/toto.txt") .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { // manage failure ! } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { // manage error Log.e("Unexpected code " + response); return; } Headers responseHeaders = response.headers(); // show response headers for (int i = 0; i < responseHeaders.size(); i++) { Log.i(responseHeaders.name(i) + ": " + responseHeaders.value(i)); } // show body content Log.i(response.body().string()); } });
Accessing headers
(expand code)
private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { Request request = new Request.Builder() .url("https://api.github.com/repos/square/okhttp/issues") .header("User-Agent", "OkHttp Headers.java") .addHeader("Accept", "application/json; q=0.5") .addHeader("Accept", "application/vnd.github.v3+json") .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println("Server: " + response.header("Server")); System.out.println("Date: " + response.header("Date")); System.out.println("Vary: " + response.headers("Vary")); } }
POST jSON Request
(expand code)
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); OkHttpClient client = new OkHttpClient(); String url = "http://www.ssaurel.com/tmp/jsonService"; String json = "{'mode' : 'test'}"; // Json Content ... RequestBody body = RequestBody.create(JSON, json); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { // manage failure } @Override public void onResponse(Call call, Response response) throws IOException { // Manage response Log.i(response.body().string()); } });
Posting a file
(expand code)
ublic static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8"); private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { File file = new File("README.md"); Request request = new Request.Builder() .url("https://api.github.com/markdown/raw") .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file)) .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); } }
Post Request with form params
(expand code)
private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { RequestBody formBody = new FormBody.Builder() .add("search", "Jurassic Park") .build(); Request request = new Request.Builder() .url("https://en.wikipedia.org/w/index.php") .post(formBody) .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); } }
OKHTTP Cacheing
(expand code)
int cacheSize = 10 * 1024 * 1024; // 10MB OkHttpClient.Builder builder = new OkHttpClient.Builder() .cache(new Cache(context.getCacheDir(), cacheSize) .... new Request.Builder().cacheControl(CacheControl.FORCE_CACHE) ...;
Handing Timeouts for all calls
(expand code)
private final OkHttpClient client; public ConfigureTimeouts() throws Exception { client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build(); } public void run() throws Exception { Request request = new Request.Builder() .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay. .build(); try (Response response = client.newCall(request).execute()) { System.out.println("Response completed: " + response); } }
Handing Timeouts for each call
(expand code)
private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { Request request = new Request.Builder() .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay. .build(); // Copy to customize OkHttp for this request. OkHttpClient client1 = client.newBuilder() .readTimeout(500, TimeUnit.MILLISECONDS) .build(); try (Response response = client1.newCall(request).execute()) { System.out.println("Response 1 succeeded: " + response); } catch (IOException e) { System.out.println("Response 1 failed: " + e); } // Copy to customize OkHttp for this request. OkHttpClient client2 = client.newBuilder() .readTimeout(3000, TimeUnit.MILLISECONDS) .build(); try (Response response = client2.newCall(request).execute()) { System.out.println("Response 2 succeeded: " + response); } catch (IOException e) { System.out.println("Response 2 failed: " + e); } }
Having HTTP server inside android App
Step1: Add Dependency
(expand code)
compile 'org.nanohttpd:nanohttpd:2.2.0'
Write your own Server Utils
(expand code)
package in.co.dipankar.quickandorid.utils; import org.json.JSONObject; import java.io.IOException; import java.util.Map; import fi.iki.elonen.NanoHTTPD; public class HttpdUtils extends NanoHTTPD { public interface Result{ public String getStatus(); public String getData(); } public interface Callback{ public Result Handle(String key); public void onSuccess(String msg); public void onError(String msg); } private Callback mCallback; public HttpdUtils(Callback callback) { super(8081); mCallback = callback; this.start(); } @Override public Response serve(IHTTPSession session) { Method method = session.getMethod(); String uri = session.getUri(); System.out.println(method + " '" + uri + "' "); Result result = mCallback.Handle(uri); return NanoHTTPD.newFixedLengthResponse(result.getData()); } @Override public void start(){ try { super.start(); mCallback.onSuccess("started"); } catch (IOException e) { e.printStackTrace(); mCallback.onError("Not able start"); } } @Override public void stop(){ super.stop(); mCallback.onSuccess("stoped"); } }
Now when you start your app the server will start
Is my server is runing :
(expand code)
$ adb shell $ generic_x86:/ # lsof -l | grep 8081 kandroide 24907 10088 54u IPv6 0t0 1569923 TCP []:8081->[]:0 (LISTEN)
Now connect it from your host and Hit the BOX
(expand code)
$ adb forward tcp:8081 tcp:8081 $ curl http://localhost:8081/gsf
With this infra, we can build our own App Automation and App helper to execute any code for debugging, Let's consider the below Remote Debugger which allows us to set and get the Pref from your laptop.
(expand code)
package in.co.dipankar.quickandorid.utils; import android.content.Context; import android.content.SharedPreferences; import java.util.Arrays; import java.util.List; import java.util.Map; import static android.content.Context.MODE_PRIVATE; public class RemoteDebug { private HttpdUtils mHttpdUtils; private Context mContext; public RemoteDebug(Context context){ mContext = context; mHttpdUtils = new HttpdUtils(8081, new HttpdUtils.Callback() { @Override public String Handle(String method, String key, Map<String, String> param) { String data = param.get("data"); switch (key){ case "/setPref": return setPerf(data); case "/getPref": return getPerf(data); default: return "Invalid command Sent!\n"; } } @Override public void onSuccess(String msg) {} @Override public void onError(String msg) {} }); } private String setPerf(String data){ if(data == null || data.length() < 1){ return "Please pass some args\n"; } List<String> tokens = Arrays.asList(data.split(" ")); if(tokens.size() < 3){ return "Less args sent"; } SharedPreferences.Editor editor = mContext.getSharedPreferences("test", MODE_PRIVATE).edit(); switch(tokens.get(0)){ case "S": editor.putString(tokens.get(1), tokens.get(2)); editor.apply(); return "Set Successfully"; case "I": editor.putInt(tokens.get(1), Integer.parseInt(tokens.get(2))); editor.apply(); return "Set Successfully"; } return "Not able to set the shared pref"; } private String getPerf(String data){ if(data == null || data.length() < 1){ return "Please send some args\n"; } List<String> tokens = Arrays.asList(data.split(" ")); if(tokens.size() < 2){ return "Less args sent"; } SharedPreferences prefs = mContext.getSharedPreferences("test", MODE_PRIVATE); switch(tokens.get(0)){ case "S": return prefs.getString(tokens.get(1),"null"); case "I": int x = prefs.getInt(tokens.get(1),-1); return x+""; } return "Not able to get the shared pref"; } }
From Activity, You needs to initilized this:
(expand code)
private void testRemoteDebuging() { RemoteDebug remoteDebug = new RemoteDebug(this); }
Ans how it can be used:
(expand code)
dip-pro:books dip$ curl http://localhost:8081/getPref?data=I%20me%2010 10 dip-pro:books dip$ curl http://localhost:8081/setPref?data=I%20me%20100 Set Successfully dip-pro:books dip$ curl http://localhost:8081/getPref?data=I%20me%2010 100 dip-pro:books dip$ curl http://localhost:8081/getPref?data=I%20me%2010 100 dip-pro:books dip$ curl http://localhost:8081/setPref?data=I%20me%20100 Set Successfully dip-pro:books dip$
Android Thread and process
Application SandBox
Android application are mainly sandboxed. They are executed in it's own process and have their secure storage area. Every application has it's own Application to UserID mappaing , assigned by the android system while installation.
interprocess communication:
Table: Way to to interprocess communication.
Unix IPCNot suited from Android.
Network based IPCAvilable under android but not efficent.
IntentGeneric, extensible message. They contains “addressable” content (Action, category) and Android delivers appropriately. They can have Additional payload. Android Efficiently “routed” between apps which make sApps can become like plug-ins they can be used to return data possible.
MesengerAnother way to do IPC. They use Cross process Handler reference. It Sends Message to specific process and Other process must support this.
ContentResolverMainly Storage centric IPC, Used to interface with ContentProvider. it Supports standard SQL type operations like Insert, Delete, Query and also Support custom method calls
IBinder and Binder ObjectThis is Base IPC mechanism in Android and Other IPC mechanism layered on top. It Leverages Android specific kernel feature like Shared memory between processes It Uses a special serialized data format which Serialized data via Parcel, Supports any object implementing Parcelable - Non-permanent serial form of data. It Can define custom interfaces or methods. It is Used by internal framework Services. This is the bast to acive Cross-process method calls. It required defing AIDL.
Sharing private data, resource and processes by multiple app.
What if I need something different? Is there a way to get re-use and access existing data? Is there a way to get better efficiency? Yes it is possible.
Table: Effective way of sharing across apps.
The Almost-Unique User IDWe know the Unique User ID is assigned app at install time. However, we can override it in menufest by pspacifying android:sharedUserId="com.example.sharedId">. Shared ID is a string and Not the real user ID! but anndoid system lookup for this string to assign unique user id. We also needs to sign the app using same certificate to avoid security concern. advantage of Shared User ID : access other app data like Files, Resources and SharedPreferences, Howvery they are not RAM efficient as both process needs to be loaded. Another concern is access other app data is hard.
Sharing ProcesAllow sharing process by having . Startwith‘:’forprivateprocessinapp and Star twith a lower case character for global(sharable). It must share user ID.
Table: advantage of Process sharing:
Reduced IPC: IBinderIBinder is efficient, right? Yes, but is still cross-process Memory still has to be marshalled. Process context switch. But IBinder is smart, process aware, As they know two app running on the same process they calls target interface directly which reduce context switch and memoery marshalling.
Reduced IPC: ContentProviderIt Meant for exposing storage schema globally and it is Process aware which helps Reduced memory usage and Reduced context switching.
Android Threads.
In this section we will discuss concurrenact vis android thread.
  1. Android == Concurrency -
  2. unresponsive UI thread and ANR demo: The UI unresposive happens when app fail to respond to user input after timeout ( 5 sec) or BroadcastReceiver fails to complete after timeout( 10 sec) or app in a Critical error state. We will give a demo of unresponsive.
  3. Main thraed( UI thread)- UI thread is central to app. All the compoents like- Activity, Service and BroadcastReceive runs on UI thread.
Table: Top ways to having backgroud operations:
Option1 :Thread/Looper/Handler comibinationThread class, defined by Java, Executes a specific Runnable and it is the Base class for other threading options. It is Primitive, not hooked into framework. It is a Single invocation ( that means will do one task and then terminates), can be long running. Looper bound to a specific Thread instance. Looper waits for Message objects and Looper hands Message objects to Handler. We can create hanlder by Extend Handler or provide Handler.Callback. It Used for well-defined interfaces like handling Message contents or Can execute Runnables in Handler. This is also Not directly tied to framework states.
Option 2: HandlerThreadSimplified Thread/Looper/Handler construct by andorid. It Automatically creates Thread and Looper . Can hook when Looper initialized - Extend the class, provide onLooperPrepared() It is also Single invocation ( Can be used once), can be long running.
Option 3: AsyncTaskAutomatically handles coordination with UI thread and perfact for Short lived operations. It has Restrictions on creation, execution. It's Operations changed with Android versions( very bad). Originally serially executed on background thread, then Donut used parallel execution and breaks a lot of apps. then Honeycomb reverted to serial execution but support parranl ops whihc Can be overridden using executeOnExecutorwith THREAD_POOL_EXECUTOR it is Single invocation only. But is Easy to leak resource and should be taken care properly.
Option 4: Thread PoolsExtensible framework for offloading like Longer running tasks which supports Parallel/serial operations. It is Efficient thread usage via re-use It alos support different policies for cancel, reject. We can also se pool size.
Option 5: Native C++ ThreadsC/C++ accessible via Java which Requires the Android NDK and Requires JNI. We can have Two options available (1) Threads in Java, access native methods or (2) Native pthreads behind the scenes. It is Very flexible, super high performance and No built-in framework coordination.
Table: Android Ways to post a Job to UI Threass
Activity.runOnUiThread(Runnable)Post a job from avcativity.
View.post(Runnable)Post a runnable when you have soem async job in View.
View.postDelayed(Runnable, long)Post a Job with a delay.
Handler + myRunnableWith
Table: Working on a sparate threads
Java Thread(Runnable)Java Thread
Android AsyncTaskIt performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.
Android Hanlder + LopperAndroid Hanlder
Android HandlerThreadHanlder and Looper Combo.
Android IntentServiceAndorid IntentSrevices
Android LoaderAndorid Apis
Android JobSchedulerAndorid Jobs
Android GcmNetworkManagerAndorid APIS
Android AlarmManagerAndroid Alerm to setup Jobs
Java Executor/Thread PoolJava Spacific
Java FutureTaskJava APIS
Android CountDownTimerAndroid API
Java TimerJava APIs
Java ScheduledThreadPoolExecutorJava APIs
Using Java Threads: Post a Job in backgroud thread and when it is done post the result in UI- Unfortunately, these classes and methods also tend to make your code more complicated and more difficult to read.
(expand code)
public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap b = loadImageFromNetwork(); mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(b); } }); } }).start(); }
Using Async Task: Perhaps the best known of the asynchronous API, AsyncTask is easy to implement and returns results on the main thread
onPreExecute(), onPostExecute() and onProgressUpdate() are all invoked on the UI thread
You can call publishProgress() at anytime in doInBackground() to execute onProgressUpdate() on the UI thread
(expand code)
public void onClick(View v) { new DownloadImageTask().execute("http://example.com/image.png"); } private class DownloadImageTask extends AsyncTask { protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); } }
Anotehr example;
(expand code)
new AsyncTask<URL, Integer, Long>() { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }.execute(url1, url2, url3);
There are some issues with AsyncTask, for example, they are not aware of the activity or fragment lifecycle and so it is the programmer's responsibility to handle the AsyncTasks behaviour when the activity is destroyed. This means that they are not the best option for long running operations and also, if the app is in the background and the app is terminated by Android, your background processing is also terminated.
IntentService: This is the defacto choice for long running processing on Android, a good example would be to upload or download large files. The upload and download may continue even if the user exits the app and you certainly do not want to block the user from being able to use the app while these tasks are going on.
(expand code)
public class RSSPullService extends IntentService { @Override protected void onHandleIntent(Intent workIntent) { // Gets data from the incoming Intent String dataString = workIntent.getDataString(); ... // Do work here, based on the contents of dataString ... } } // AndroidManifest.xml <application android:icon="@drawable/icon" android:label="@string/app_name"> ... <!-- Because android:exported is set to "false", the service is only available to this app. --> <service android:name=".RSSPullService" android:exported="false"/> ... <application/>
Loader: Loaders are a complex topic, any single implementation of Loaders probably deserved a post all for itself. For now it would be worth pointing out that Loaders were introduced in Android Honeycomb and are part of the compatibility library.
JobScheduler: JobScheduler is the Android framework API for scheduling tasks or work. It first became available in Android. JobScheduler is implemented in the platform, which allows it to collect information about jobs that need to run across all apps. This information is used to schedule jobs to run at, or around, the same time. Batching job execution in this fashion allows the device to enter and stay in sleep states longer, preserving battery life.
(expand code)
// Step1: public class JobSchedulerService extends JobService { @Override public boolean onStartJob(JobParameters params) { return false; } @Override public boolean onStopJob(JobParameters params) { return false; } } [more] https://code.tutsplus.com/tutorials/using-the-jobscheduler-api-on-android-lollipop--cms-23562
AlarmManager: The AlarmManager API is another option that the framework provides for scheduling tasks. This API is useful in cases in which an app needs to post a notification or set off an alarm at a very specific time.
Firebase JobDispatcher: Firebase JobDispatcher is an open-source library that provides an API similar to JobScheduler in the Android platform. Firebase JobDispatcher serves as a JobScheduler-compatibility layer for apps targeting versions of Android lower than 5.0.
GcmNetworkManager: It allow to do Schedule tasks and chedule a one-off task when network is connected.
(expand code)
mGcmNetworkManager = GcmNetworkManager.getInstance(this); //Schedule a one-off task when network is connected OneoffTask task = new OneoffTask.Builder() .setService(MyTaskService.class) .setTag(TASK_TAG_WIFI) .setExecutionWindow(0L, 3600L) .setRequiredNetwork(Task.NETWORK_STATE_UNMETERED) .build(); mGcmNetworkManager.schedule(task); /// Schedule a periodic task PeriodicTask task = new PeriodicTask.Builder() .setService(MyTaskService.class) .setTag(TASK_TAG_PERIODIC) .setPeriod(30L) .build(); mGcmNetworkManager.schedule(task); // Schedule a persistent task adb shell dumpsys activity service GcmService --endpoints YOUR_ENDPOINT
CountDownTimer:
(expand code)
new CountDownTimer() { @Override public void onFinish() { } @Override public void onTick(long millisUntilFinished) { } }.start();
Looper, Handler, and HandlerThread: Java threads are one-time use only and die after executing its run method.To ensure, thread reusability, the thread should kept alive, in a loop via it’s run() method.The task is executed serially by that thread and is maintained in a queue (MessageQueue).Then, thread must be terminated when done. The above model is implemented in the Android via Looper, Handler, and HandlerThread
An sample example of looper thread is as below:
(expand code)
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here // this will run in non-ui/background thread } }; Looper.loop(); } }
FutureTask: FutureTask performs asynchronous processing, however, if the result is not ready yet or processing has not complete, calling get() will be block the thread.
(expand code)
RequestFuture<JSONObject> future = RequestFuture.newFuture(); JsonObjectRequest request = new JsonObjectRequest(URL, null, future, future); requestQueue.add(request); try { JSONObject response = future.get(); // this will block (forever) } catch (InterruptedException e) { // exception handling } catch (ExecutionException e) { // exception handling }
Java Timer : An example of using Java Timer to do something after 5 seconds. These can be used to schedule some processing on a background thread. There are other ways to handle the same in Android, you could use a Handler with
(expand code)
Timer timer = new Timer(); timer.schedule(new TimerTask(){ public void run() { // time ran out. timer.cancel(); } }, 5000);
ScheduledThreadPoolExecutor: A ThreadPoolExecutor that can additionally schedule commands to run after a given delay, or to execute periodically. This class is preferable to Timer when multiple worker threads are needed, or when the additional flexibility or capabilities of ThreadPoolExecutor (which this class extends) are required. Delayed tasks execute no sooner than they are enabled, but without any real-time guarantees about when, after they are enabled, they will commence. Tasks scheduled for exactly the same execution time are enabled in first-in-first-out (FIFO) order of submission.
The ScheduledThreadPoolExecutor suffers from a lot of the same problems on Android as Timer and Java Threads. If you need to update the UI you’ll need to use a Handler to post messages to the UI thread or pass in a Listener. A ScheduledThreadPoolExecutor is part of Java, it’s not aware of the Activity or Fragment lifecycle and as such any Listeners will have to be cleaned up or replaced manually in order to prevent memory leaks.
(expand code)
public class CustomScheduledExecutor extends ScheduledThreadPoolExecutor { static class CustomTask<V> implements RunnableScheduledFuture<V> { ... } protected <V> RunnableScheduledFuture<V> decorateTask( Runnable r, RunnableScheduledFuture<V> task) { return new CustomTask<V>(r, task); } protected <V> RunnableScheduledFuture<V> decorateTask( Callable<V> c, RunnableScheduledFuture<V> task) { return new CustomTask<V>(c, task); } // ... add constructors, etc. } // Code from Java API
Example1: In service, We have created thread(s) for doing some background task. Now, thread need to post certain task on main thread's(UI) message queue, for example a Runnable. Here is way to access from a worker thread in a service
(expand code)
Case 1. If your background thread has a reference to a Context object, Get a handler that can be used to post to the main thread Handler mainHandler = new Handler(context.getMainLooper()); Runnable myRunnable = new Runnable() { @Override public void run() {....} // This is your code }; mainHandler.post(myRunnable); Case 2. If your background thread does not have (or need) a Context object, Get a handler that can be used to post to the main thread Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = new Runnable() { @Override public void run() {....} // This is your code }; mainHandler.post(myRunnable); Case 3. Simple: new Handler(Looper.getMainLooper()).post(mYourUiThreadRunnable);
Example: Example of an Generic Thread Utils class:
(expand code)
import android.os.Handler; import android.os.Looper; public interface ThreadUtil { boolean isUiThread(); void runOnUiThread(Runnable runnable); void postToUiThread(Runnable runnable); void postToUiThread(Runnable runnable, long delayMs); public void postToCurrentThread(Runnable runnable) ; public void postToCurrentThread(Runnable runnable, long delayMs); public void sleep(long timeMillis); } public class MyThreadUtil implements ThreadUtil { private static final MyThreadUtil INSTANCE = new MyThreadUtil(); private static final Thread MAIN_THREAD = Looper.getMainLooper().getThread(); private Handler mUiThreadHandler; private MyThreadUtil() { mUiThreadHandler = new Handler(Looper.getMainLooper()); } @Override public boolean isUiThread() { return MAIN_THREAD == Thread.currentThread(); } @Override public void runOnUiThread(Runnable runnable) { if (isUiThread()) { runnable.run(); } else { mUiThreadHandler.post(runnable); } } @Override public void postToUiThread(Runnable runnable) { mUiThreadHandler.post(runnable); } @Override public void postToUiThread(Runnable runnable, long delayMs) { mUiThreadHandler.postDelayed(runnable, delayMs); } public static MyThreadUtil get() { return INSTANCE; } public void postToCurrentHandlerThread(Runnable runnable) { Handler handler = new Handler(); handler.post(runnable); } @Override public void postToCurrentHandlerThread(Runnable runnable, long delayMs) { Handler handler = new Handler(); handler.postDelayed(runnable, delayMs); } @Override public void sleep(long timeMillis) throws InterruptedException { Thread.sleep(timeMillis); } }
[EndToEnd] Improving Android performance
Developing amazing Android applications is not a simple task, which involves mustering a complex set of skills. One of the most important and challenging ones is to analyze our code for performance weaknesses, and eliminate them in order to deliver our users with a superior experience. Here, we are going to learn together how to analyze, detect, and optimize common performance issues in our applications related to the following subjects:
Reducing frequenct GC Events:
What's garbage collection? Garbage collection is a form of automatic memory management where The garbage collector or GC attempts to reclaim garbage or memory occupied by objects that are no longer used by our application. GC to be able to reclaim the object's memory, if it is not strong referenced in our code. If the GC fails to reclaim the object's memory, a memory leak will occur.
As we allocate more objects in our application, a periodical garbage collection will occur. The more objects we allocate in our app, the more GC events will be invoked. Doing a GC event, all threads are suspended, thus Frequent GC events can cause you UI hiccups, AKA our application's UI is not responsive. So our targetis to allocate as less objects as we can and avoid frequent GC events in order to provide our users with a smooth experience.
Example 1: Loop Optimization: First example shows the good and Bad Code for a simple Loop: The main problem for this loop is we have high number of objects with short life time. We can improve this by having Minimize number of objects and reduce short leave object. The app become faster as less object creation happens as object creating is expensive.
(expand code)
ITERATIONS = 4000 protected void runLoop() { new Thread() { @Override public void run() { long sum = 0; for (int i = 0; i < ITERATIONS; i++) { sum += poorPerformanceLoop(); } } }.start(); } //bad loop: Memoty = 20*500 Byte public long poorPerformanceLoop() { long timeStart = System.currentTimeMillis(); ArrayList<Double> doubleNumbers = new ArrayList<>(); for (int j = 0; j < 20; j++) { // Memory : 20String * 24 byte + 1 Double *16 Byte => 500 Byte String[] stringsNums = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"}; --->(!) Double dObj = new Double(stringsNums[j]); doubleNumbers.add(dObj); } return System.currentTimeMillis() - timeStart; } >>> Calculation: Total Short memory users: 4000*20*500 = > 40Byte. // good Loop: // Object are caches : 16 bytes x 20 = 320 bytes final static double[] NUMS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; final static int LENGTH = NUMS.length; final static ArrayList<Double> arrayList = new ArrayList<>(LENGTH); public long betterPerformanceLoop() { arrayList.clear(); timeStart = System.currentTimeMillis(); for (short j = 0; j < LENGTH; j++) arrayList.add(NUMS[j]); // No new objct is created here - but only refers return System.currentTimeMillis() - timeStart; } >>> Calculation; Total Meory footprint : 320B*4000 = 1.28 MB.
Example 2: String optimization using String builder calss:
(expand code)
// before -> create string in everytime. for (int i = 0; i < 500; i++) { mHandler.post(() -> { String ramUsage = String.format("%.2f", ((float) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1000000))+"MB\nfree:"+String.format("%.2f MB",(float) Runtime.getRuntime().freeMemory() / 1000000); mTvRamUsage.setText(ramUsage); }); } // After : Prefer to use StringBuilder for dynamic strings and extract constants where possible private static final String STR = "Used:0.00MB\nfree:0.00MB"; private final StringBuilder mStrBuilder = new StringBuilder(STR); ... for (int i = 0; i < 500; i++) { mHandler.post(() -> { extractValues(); formated=Float.toString(mUsedRamShow).substring(0,4); mStrBuilder.delete(5,9); mStrBuilder.insert(5,formated); formated=Float.toString(mFreeRamShow).substring(0,4); mStrBuilder.delete(17,21); mStrBuilder.insert(17,formated); mTvRamUsage.setText(mStrBuilder.toString()); });
Example 3: Lading Image : For simplicity, let's assume that our image's format is ARGB8888, which implies that our pixel size in memory is four bytes. If the width of our image is 1,280 pixels and the height of our image is 850 pixels, then total consution is : 1,280*850*$ = 4.4 MB. If we have 100 image, then it takes 400MB Memory only for images :(. How to optimize it? Our first optimization is to scale down the image to fit the container size. In addition, we will cache the image in memory in order to make sure our image is allocated only once. We can use Glide libs for that.
(expand code)
Avoid Memoty leaks in your application
What is a memory leak? A memory leak is a side effect in which our application persistently retains an object's memory. It occurs when our code doesn't release allocated memory, even after it is not needed anymore. But why GC doesn’t solve the problem? GC can identify an object is unused only if it's not hard referenced in our code. So handleing Application or Activity life cycle is extermply immprtant.
For example: If we call the finish() method from the activity class, the Android runtime will try to destroy the activity then the GC will try to mark it for removal and reclaim its memory. but if someother compoenet ( say service holds the ref) Holds the ref it is not possible to claim - hence meoty leaks.
:
Example 1: Memoy leaks with static ref:
(expand code)
// BAD CODE public class StaticRefLeakingActivity extends AppCompatActivity { private static View mLeakingView; <<< NOT RIGHT AS STATIC private byte[] mBytes = new byte[1000000]; // huge byte array which is leacks. @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity); mLeakingView = findViewById(R.id.mainView); mLeakingView.setOnClickListener(view -> finish()); } } //GOOD CODE Just remove static from View.
Example 2: Memoy leaks with static instance or SingleTon Class. The member ourInstance is static and MyStringHelper’s life cycle > activity life cycle
(expand code)
//BAD CODE //Singleton calss - Never destroyed and holds the ref of activity. public class StaticRefLeakingActivity extends AppCompatActivity { private byte[] mBytes = new byte[1000000]; // huge byte array which is leacks. @Override protected void onCreate(@Nullable Bundle savedInstanceState) { // PASS ACTIVITY REF TO SINGLETON> MyStringHelper.getInstance().setActivity(this); } } public class MyStringHelper { private static MyStringHelper ourInstance = new MyStringHelper(); private Context mActivityContext; public static MyStringHelper getInstance() { return ourInstance; } private MyStringHelper() { } public void setActivity(Context context){ mActivityContext=context; } } // GOOD CODE : pass the application Context: MyStringHelper.getInstance().setAppContext(getApplicationContext()); //ALTERNATIVE SOLUTION: IF you still need to use static , please set to null at destrction. public class StaticRefLeakingActivity1 extends AppCompatActivity { private static final String TAG = StaticRefLeakingActivity1.class.getSimpleName(); private static View mLeakingView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { ... mLeakingView = findViewById(R.id.mainView); MyStringHelper.getInstance().setActivityContext(this); mLeakingView.setOnClickListener(view -> finish()); } @Override protected void onDestroy() { //clearing all references before super.onDestroy() is called mLeakingView=null; MyStringHelper.getInstance().setActivityContext(null); super.onDestroy(); } }
Example 3: Memoy leaks due to Anonymous Inner Classes( like Async task or handler): An inner class declared without a class name is known as an anonymous inner class. It is declared and instantiated in the same time. it's by defulat Hold a reference to the outer class.
(expand code)
// BAD CODE public class InnerClassLeakingActivity extends AppCompatActivity { //Defined as an anonymous class: In java anonymous classes hold a reference to the outer class: our activity. private final Handler mHandler = new Handler(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { ... mHandler.postDelayed(new MyRunnable(), 20000); } private class MyRunnable implements Runnable { @Override public void run() { // InnerClassLeakingActivity.this CAUSE THIS ISSUE Log.d(TAG, String.format("I am running in activity:%s",InnerClassLeakingActivity.this.getComponentName())); } } } // GOOD CODE public class InnerClassLeakingActivity extends AppCompatActivity { private final Handler mHandler = new Handler(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { ... // pass the activity as week ref. mHandler.postDelayed(new MyRunnable(this),20000); } // create static class private static class MyRunnable implements Runnable { WeakReference<InnerClassLeakingActivity> mWeakActivity; public MyRunnable(InnerClassLeakingActivity activity) { this.mWeakActivity = new WeakReference<>(activity); } @Override public void run() { final InnerClassLeakingActivity activity=mWeakActivity.get(); if (activity!=null){ Log.d(TAG, String.format("I am running in activity:%s",activity.getComponentName())); // do the job here. } } } }
Example 4: Non-static Inner Classes: it is defined within another class and Exists within an instance of the outer class and it also Hold a reference to the outer class.
(expand code)
// BAD CODE public class AnonClassLeakingActivity extends AppCompatActivity { ... //Defined as an anonymous class: In java anonymous classes hold a reference to the outer class: our activity. private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { Log.d(TAG,"handleMessage executed!!"); } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { ... mLeakyHandler.sendEmptyMessageDelayed(MESSAGE_ID,20000); } } // GOOD CODE public class AnonClassLeakingActivity extends AppCompatActivity { private static final String TAG = AnonClassLeakingActivity.class.getSimpleName(); public static final int MESSAGE_ID = 5; ... private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { Log.d(TAG,"handleMessage executed!!"); } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { ... mLeakyHandler.sendEmptyMessageDelayed(MESSAGE_ID,20000); } @Override protected void onDestroy() { // MUST REMOVE THIS MESSAGE mLeakyHandler.removeMessages(MESSAGE_ID); super.onDestroy(); } }
Table: Rule of thumb
Static RefMake sure to clear all references to our activity before on Destroy Called.
AviodAvoid static references to Activities, Views and Other Android components
Application ContextPrefer using application context when possible
StrictModeRun Application StrictMode. Detects accidental mistakes and Brings them to your attention like Notify in the logcat
Avoid Non-static Inner ClassMake MyRunnable to be a static inner class and Use the WeakReference class. Weekref Will be held until the GC reclaims its referent’s memory and it Will return null if the object was already reclaimed
LeakCanaryIt;s a developer tool whihc Specify desired objects to watch. it can Monitor objects and Notify via notification if one of the objects was leaked.
Optimizing UI Rendering performance
(expand code)
Android Design
In this section, I will discusstion the common design to be followed by :
Table: Android Design Patterns
Spalsh Screen DesignThis will tell how to design First time user Experice
Loading screen DesignHow to design loading screen when the app is laucnhed for second time.
Muti-Page App with ActivitysHow Design multi-page aplication with mutiple activity.
Multi-Page with Multiple FragemntsIn this section, we will discuss how to design a app with same activity with mutiple screen
MVPThis section discuss how to decouple UI login with Contrler logic using MVC Patterns
MVVCDesign discussion on MV-VP patatrens
MVP Patterns in details
The Model View Presenter (MVP) design pattern dictates that you separate your presentation (the View) from the business logic and the data sources (the Presenter and the Model). The design mainly consist of 3 things.
  1. Model: it is an interface responsible for managing data.
  2. Presenter: the presenter is the middle-man between model and view. The presenter is responsible for querying the model and updating the view, reacting to user interactions updating the model.
  3. View: it is only responsible for presenting data in a way decided by the presenter. The view can be implemented by Activities, Fragments, any Android widget.
The correct design of the MVP should be based on below priciples:
  1. Make View dumb and passive - that means delegates all evnets to presenter so - if you have a username/password form and a “submit” button, you don’t write the validation logic inside the view but inside the presenter.
  2. Make presenter framework-independent: make sure that presenter doesn’t depend on Android classes but only have Java deps. It gives two advantage : unit test - flexyable.
  3. Write a contract to describe the interaction between View and Presenter: describes the communication between view and presenter. It should bundle ViewContracts and Presenter contracts like this.
(expand code)
public interface SearchRepositoriesContract { interface View { void addResults(List<Repository> repos); void clearResults(); void showContentLoading(); void hideContentLoading(); void showListLoading(); void hideListLoading(); void showContentError(); void hideContentError(); void showListError(); void showEmptyResultsView(); void hideEmptyResultsView(); } interface Presenter extends BasePresenter<View> { void load(); void loadMore(); void queryChanged(String query); void repositoryClick(Repository repo); } }
  1. The presenter must depend on the View interface and not directly on the Activity or the real View..
  2. Define a naming convention to separate responsibilities: All Presenter methods should be an actions like Load() , where as all view methods will be userEvents. like queryChanged(), onButtonClick or UI functions like Show/Hide/Render().
  3. Do not create Activity-lifecycle-style callbacks in the Presenter interface: presenter shouldn’t have methods like onCreate(...), onStart(), onResume() as The presenter shouldn’t have a so complex lifecycle.
  4. Presenter has a 1-to-1 relation with the view - The presenter doesn’t make sense without a view. It comes with the view and goes when the view is destroyed as It should manages one and only view. How to pass View to presentre, either pass it by constracter( which is not so good)- better we can have attach(View view) and detach() ( which makes View nullbale and we need to have darty null check everwhere). We can solve the problem using BasePresenter as below:
(expand code)
public interface BasePresenter<V> { void attach(V view); void detach(); @Nubbale V getView(); }
  1. Statleless presentre: (a) Do not save the state inside the presenter - as You can’t serialize data into a Bundle because presenter would be coupled with an Android class. Presentre should be stateless. (b)Do not retain the presenter - kill it when app destry and recrate it. (c) Provide a cache for the Model to restore the View state.
In this section, I will discussss to alternative Design for MVP Patterns
Step1: Define contracts.
(expand code)
public interface Contracts{ public interface IMainView { void showProgress(); void hideProgress(); void setItems(List<String> items); void showMessage(String message); } public interface IMainPresenter { void onAttach(); void onItemClicked(int position); void onDetach(); } //model public interface IItemProvider { interface OnFinishedListener { void onFinished(List<String> items); } void loadItems(OnFinishedListener listener); } }
Step2: Define Concreate implements: Note that:
(expand code)
// implement presentation public class MainPresenter implements IMainPresenter, IItemProvider.OnFinishedListener { private IMainView mainView; <<<< Not that it's an interface. private IItemProvider itemProvider; public MainPresenter(IMainView mainView, IItemProvider itemProvider) { this.mainView = mainView; this.itemProvider = itemProvider; } @Override public void onAttach() { if (mainView != null) { mainView.showProgress(); } itemProvider.loadItems(this); } @Override public void onItemClicked(int position) { if (mainView != null) { mainView.showMessage(String.format("Position %d clicked", position + 1)); } } @Override public void onDetach() { mainView = null; } @Override public void onFinished(List<String> items) { if (mainView != null) { mainView.setItems(items); mainView.hideProgress(); } } public IMainView getMainView() { return mainView; } } // implement model public class ItemProvider implements IItemProvider{ @Override public void loadItems(final IItemProvider.OnFinishedListener listener) { new Handler().postDelayed(new Runnable() { @Override public void run() { listener.onFinished(createArrayList()); } }, 2000); } private List<String> createArrayList() { return Arrays.asList( "Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Item 9", "Item 10" ); } } // implement view. public class MainActivity extends Activity implements IMainView, AdapterView.OnItemClickListener { private ListView listView; private ProgressBar progressBar; private IMainPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView) findViewById(R.id.list); listView.setOnItemClickListener(this); progressBar = (ProgressBar) findViewById(R.id.progress); // Presneter created and passes. presenter = new MainPresenter(this, new ItemProvider() { }); } @Override protected void onResume() { super.onResume(); presenter.onAttach(); } @Override protected void onDestroy() { presenter.onDetach(); super.onDestroy(); } @Override public void showProgress() { progressBar.setVisibility(View.VISIBLE); listView.setVisibility(View.INVISIBLE); } @Override public void hideProgress() { progressBar.setVisibility(View.INVISIBLE); listView.setVisibility(View.VISIBLE); } @Override public void setItems(List<String> items) { listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items)); } @Override public void showMessage(String message) { Toast.makeText(this, message, Toast.LENGTH_LONG).show(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { presenter.onItemClicked(position); } }
Example 2: This is an alternative way to do that:
Step 1: Define common Base MVP infra like base presenter and base activity.
(expand code)
public interface MvpView { void showLoading(String msg); void hideLoading(); void showError(String errorMsg); } public interface Presenter<V extends MvpView> { void attachView(V mvpView); void detachView(); } public class BasePresenter<V extends MvpView> implements Presenter<V> { private V mvpView; @Override public void attachView(V mvpView) { this.mvpView = mvpView; } @Override public void detachView() { this.mvpView = null; } public boolean isViewAttached() { return mvpView != null; } public V getMvpView() { return mvpView; } public void checkViewAttached() { if (!isViewAttached()) { throw new MvpViewNotAttachedException(); } } public static class MvpViewNotAttachedException extends RuntimeException { public MvpViewNotAttachedException() { super("You must call attachView(MvpView)"); } } }
Step2: Now Define your customView and Presenter which deps on the infra.
(expand code)
public interface ILoginPresenter { public void login(); } public class LoginPresenter extends BasePresenter<ILoginView> implements ILoginPresenter { private IUserModel userModel; public LoginPresenter(IUserModel userModel) { this.userModel = userModel; } @Override public void login() { checkViewAttached(); getMvpView().showLoading("登录中..."); <<< SEE How to invoke the common infra. userModel.login(getMvpView().getUsername(), getMvpView().getPassword(), new Callback() { @Override public void onSuccess() { if (isViewAttached()) { <<<< Common Infra getMvpView().hideLoading(); getMvpView().showResult("Success"); } } @Override public void onFailure(String errorMsg) { if (isViewAttached()) { getMvpView().hideLoading(); getMvpView().showResult(errorMsg); } } }); } }
Step 3: Define Concreate Views
public interface ILoginView extends MvpView { String getUsername(); String getPassword(); void showResult(String result); } public class LoginActivity extends AppCompatActivity implements ILoginView, View.OnClickListener { private EditText username; private EditText password; private ProgressDialog progressDialog; private LoginPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); username = (EditText) findViewById(R.id.username); password = (EditText) findViewById(R.id.password); findViewById(R.id.login).setOnClickListener(this); progressDialog = new ProgressDialog(this); presenter = new LoginPresenter(new UserModel()); presenter.attachView(this); <<<< Attch iT. } @Override protected void onDestroy() { presenter.detachView(); >>>> Call the base MVP Infra super.onDestroy(); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.login: presenter.login(); <<<< Just deligate here. break; } } @Override public String getUsername() { return username.getText().toString().trim(); } @Override public String getPassword() { return password.getText().toString().trim(); } @Override public void showResult(String result) { Toast.makeText(LoginActivity.this, result, Toast.LENGTH_SHORT).show(); } @Override public void showLoading(String msg) { progressDialog.setMessage(msg); if (!progressDialog.isShowing()) { progressDialog.show(); } } @Override public void hideLoading() { if (progressDialog.isShowing()) { progressDialog.dismiss(); } } @Override public void showError(String errorMsg) { Toast.makeText(LoginActivity.this, errorMsg, Toast.LENGTH_SHORT).show(); } }
Step 4: An beter way to attch and detach:
(expand code)
public interface BasePresenter { void onViewAttached(MVPView view); void onViewDetached(); } // In case of Activity. public class BaseActivity extends AppCompatActivity { @Override protected void onStart() { super.onStart(); presenter.onViewAttached(this); } @Override protected void onStop() { super.onStop(); presenter.onViewDetached(); } }
Example 4: An alternative design for MVP.
We saw that View Despends on presenter anyway.
Step1: Have a BaseViewPresenter
(expand code)
public abstract class BaseViewPresenter<V> { private V mView; public final void attachView(V view) { if (view == null) { return; } mView = view; onViewAttached(); } protected void onViewAttached() {} public final void detachView() { onViewDetached(); mView = null; } protected void onViewDetached() {} public @Nullable V getView() { return mView; } }
Step2: Have a LoginViewPresenter - which says what needs on View Side.
(expand code)
public class LoginViewPresenter< V extends BaseViewPresenter.ViewContract> extends BaseViewPresenter<V> { public interface ViewContract { void onAuthSuccess(); void onAuthFailure(); } public LoginViewPresenter(....) { .... } public void doLogin(credentials) { LoginUtil.doLogin(credentials, new Callback<Result>() { @Override public void onSuccess(Result result) { UiHandler.post( new Runnable() { @Override public void run() { onAuthSuccess(); } }); } @Override public void onFailure() { UiHandler.post( new Runnable() { @Override public void run() { onAuthFailure(); } }); } }); } private void onAuthSuccess() { if (getView() == null) { return; } getView().onAuthSuccess(); } private void onAuthSuccess() { if (getView() == null) { return; } getView().onAuthSuccess(); } }
Step3: Define the View which will do all the stuff required by presenter.
(expand code)
public class LoginView extends BaseView implements LoginViewPresenter.ViewContract { ... public LoginView(Context context) { this(context, null, 0); } public LoginView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public LoginView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setContentView(R.layout.my_view); mLoginViewPresenter = .... .... mLoginViewPresenter.doLogin(....) } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mLoginViewPresenter.attachView(this); } @Override protected void onDetachedFromWindow() { mLoginViewPresenter.detachView(); super.onDetachedFromWindow(); } @Override public void onAuthSuccess() { // Do ops. } @Override public void onAuthFailure() { // DO Ops. } }
Design communication between mutiple compoenens in android:
Table: Communication summary table
Activity/Fragemnets to CustomView #
Custom view to Activity/Fragemnets #
CustomView to CustomView under same holders #
Activity to Fragemnt #
Fragemnt to Activity #
Fragemnt to Fragemnt #
Activity to Service #
Service to Activity #
Monitization your app.
Google AddMob.
Step1: configure graddle
To use addmob just add the below configuraration in the graddle files. Then go to AddMob(https://apps.admob.com/v2/home?pli=1) and create new banner add id .
Now we are ready to build two type of adds.
  1. Banner add
  2. Full screen add.
(expand code)
implementation 'com.google.android.gms:play-services-ads:15.0.0'
Step2: To have a banner add , please add this view in your layout and do the initilization as shown in code.
(expand code)
// add layout <com.google.android.gms.ads.AdView xmlns:ads="http://schemas.android.com/apk/res-auto" android:id="@+id/adView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" ads:adSize="BANNER" << You may use BANNER | LARGE_BANNER | FULL_BANNER ads:adUnitId="ca-app-pub-3940256099942544/6300978111"> <<< test uit id </com.google.android.gms.ads.AdView> //initilize. public class MainActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MobileAds.initialize(this, "ca-app-pub-3940256099942544~3347511713"); << Test Id #MobileAds.initialize(this, "YOUR_ADMOB_APP_ID"); mAdView = findViewById(R.id.adView); AdRequest adRequest = new AdRequest.Builder().build(); mAdView.loadAd(adRequest); mAdView.setAdListener(new AdListener() { ... @Override public void onAdOpened() { } @Override public void onAdClosed() { } } } void createProgramitically() { AdView adView = new AdView(this); adView.setAdSize(AdSize.BANNER); adView.setAdUnitId("ca-app-pub-3940256099942544/6300978111"); <<< test uit Id. // TODO: Add adView to your view hierarchy. }
Step 3: To have a interstitial ads , No need to add anythig in your view, but do the initilization as shown in code. they should call show() method to show the add and then you can make some action when the ad is close.
(expand code)
import com.google.android.gms.ads.InterstitialAd; import com.google.android.gms.ads.AdRequest; public class MainActivity extends Activity { private InterstitialAd mInterstitialAd; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MobileAds.initialize(this,"ca-app-pub-3940256099942544~3347511713"); // test id mInterstitialAd = new InterstitialAd(this); mInterstitialAd.setAdUnitId("ca-app-pub-3940256099942544/1033173712"); //test id mInterstitialAd.loadAd(new AdRequest.Builder().build()); mMyButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mInterstitialAd.isLoaded()) { mInterstitialAd.show(); } else { Log.d("TAG", "The interstitial wasn't loaded yet."); } } }); mInterstitialAd.setAdListener(new AdListener() { @Override public void onAdLoaded() {} @Override public void onAdFailedToLoad(int errorCode) {} @Override public void onAdOpened() {} @Override public void onAdLeftApplication() {} @Override public void onAdClosed() { // Prepare next add mInterstitialAd.loadAd(new AdRequest.Builder().build()); MoveToSecondScreen(); } }); } }
Defining a model
Step 1: Define your models. Note that your model has no setting - which makes them immutable.
public class User implements Serializable {
private static final long serialVersionUID = 0;
@JsonProperty("user_id")
public final String userId;
@JsonProperty("display_name")
public final String displayName;
public User(UserBuilder builder) {
this.userId = builder.getUserId();
this.displayName = builder.getDisplayName();
}
/** For JSON deserialization. */
public User() {
userId = null;
displayName = null;
}
public String getProfilePicUri() {
return profilePicUri;
}
@Override
public String toString() {
return toStringHelper().toString();
}
protected MoreObjects.ToStringHelper toStringHelper() {
return MoreObjects.toStringHelper(User.class)
.add("userId", userId)
.add("displayName", displayName)
}
}
#Step2: Now provides a builder class for your model which can set the values ( in any order in a chain ) and then call build method to get the immutable object.
public class UserBuilder {
private String mUserId;
private String mDisplayName;
public UserBuilder() {}
public String getUserId() {
return mUserId;
}
public UserBuilder setUserId(String userId) {
mUserId = userId;
return this;
}
public String getDisplayName() {
return mDisplayName;
}
public UserBuilder setDisplayName(String displayName) {
mDisplayName = displayName;
return this;
}
public User build() {
return new User(this);
}
}
Step3: Here is how you can use to create the objects using the UserBuilder
(expand code)
private static User getUser() { UserBuilder builder = new UserBuilder(); builder.setUserId("123"); builder.setDisplayName("Dipankar"); return builder.build(); }
Step 4: Provides a way to serilize and desrize with json
Sometime, you get data from network as json string and you might constract the java object by manuualy parsing it.You can use jackson lib to have this.
(expand code)
//First create a mapper : import com.fasterxml.jackson.databind.ObjectMapper;// in play 2.3 ObjectMapper mapper = new ObjectMapper(); // As Array: MyClass[] myObjects = mapper.readValue(json, MyClass[].class); // As List: List<MyClass> myObjects = mapper.readValue(jsonInput, new TypeReference<List<MyClass>>(){}); // Another way to specify the List type: List<MyClass> myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class));
Java Libs for android:
Jackson
(expand code)
compile 'com.fasterxml.jackson.core:jackson-databind:2.8.5' compile 'com.fasterxml.jackson.core:jackson-core:2.8.5' compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.5' ObjectMapper mapper = new ObjectMapper(); Person person = new Person(); mapper.writeValue(new File("/a/path/to/person.json"), person); // write to file String jsonStr = mapper.writeValueAsString(person); // write to string Person person1; //read from an url person1 = mapper.readValue(new URL("https://api.myjson.com/bins/hoh4j"), Person.class); //read from a string String personJsonStr = "{\"firstname\":\"John\",\"lastname\":\"Doe\"}"; person2 = mapper.readValue(personJsonStr, Person.class); //read from a file person3 = mapper.readValue(new File("/a/path/to/person.json"), Person.class);
Sample model class with Jackson annotations for the above json string.
(expand code)
import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "firstname", "lastname" }) public class Person { @JsonProperty("firstname") private String firstname; @JsonProperty("lastname") private String lastname; @JsonIgnore private Map<String, Object> additionalProperties = new HashMap<String, Object>(); @JsonProperty("firstname") public String getFirstname() { return firstname; } @JsonProperty("firstname") public void setFirstname(String firstname) { this.firstname = firstname; } @JsonProperty("lastname") public String getLastname() { return lastname; } @JsonProperty("lastname") public void setLastname(String lastname) { this.lastname = lastname; } @JsonAnyGetter public Map<String, Object> getAdditionalProperties() { return this.additionalProperties; } @JsonAnySetter public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } }
(expand code)
private void jsonToObjectUrl() { final ObjectMapper mapper = new ObjectMapper(); final Handler handler = new Handler(); new Thread(new Runnable() { @Override public void run() { try { Person person = mapper.readValue(new URL("https://api.myjson.com/bins/hoh4j"), Person.class); // read from url handler.post(new Runnable() { @Override public void run() { tvDisplay.setText("json string -> object\n" + person.getFirstname() + " " + person.getLastname()); } }); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } private void objectToJson() { ObjectMapper mapper = new ObjectMapper(); try { //mapper.writeValue(new File(getFilesDir(), "person.json"), person); // write to file String jsonStr = mapper.writeValueAsString(person); // write to string tvDisplay.setText("object -> json string\n" + jsonStr); } catch (JsonProcessingException e) { e.printStackTrace(); tvDisplay.setText(e.getMessage()); } catch (IOException e) { e.printStackTrace(); tvDisplay.setText(e.getMessage()); } }
Android Pro Dev Trciks.
Connecting device using wifi
(expand code)
// Set the target device to listen for a TCP/IP connection on port 5555. adb tcpip 5555 //Connect the device through usb and get the ip address of the device adb shell ip -f inet addr show wlan0 adb shell ip route | awk '{print $9}' //Connect the device, 192.168.1.213 is the ip address obtained from the previous step. adb connect 192.168.1.213:5555 //Disconnect the device. adb disconnect 192.168.1.213 //See connected devices. adb devices
Android Design issues
Save and Restore states
Let's Understand what happens while Activity's State is being Saved/Restored - When Activity's onSaveInstanceState is called, Activity will automatically collect View's State from every single View in the View hierachy( Please note that only View that is implemented View State Saving/Restoring internally that could be collected the data from).when on Resume/Recreating happens onRestoreInstanceState is called. and Activity will send those collected data back to the View in the View hierachy that provides the same android:id as it is collected from one by one. This is the reason why text typed inside EditText still persisted even though Activity is already destroyed and we didn't do anything special. There is no magic. Those View State are automatically collected and restored back. And this is also the reason why those View without android:id defined isn't able to restore its View's state. Although those View's state are automatically saved but the Activity's member variables are not. They will be destroyed along with Activity. You have to manually save and restore them through onSaveInstanceState and onRestoreInstanceState method. In case that Fragment is destroyed by the system. Everything will just happen exactly the same as Activity. It means that every single member variables are also destroyed. You have to manually save and restore those variables through onSaveInstanceState and onActivityCreated method respectively. Please note that there is no onRestoreInstanceState method inside Fragment.For Fragment, there is some special case that is different from Activity: Once Fragment is returned from backstack, its View would be destroyed and recreated. In this case, Fragment is not destroyed. Only View inside Fragment does. As a result, there is no any Instance State saving happens.But View State Saving/Restoring are internally called inside Fragment in this case. As a result, every single View that is implemented a View State Saving/Restoring internally, for example EditText or TextView with android:freezeText="true", will be automatically saved and restored the state. Causes it to display just perfectly the same as previous. Please note that only View is destroyed (and recreated) in this case. Fragment is still there, just like those member variables inside. So you don't have to do anything with them. No any additional code is required. So we have two rule:
  1. Every single View used in your application must be internally implemented State Saving/Restoring. Basically every single standard View such as EditText, TextView, Checkbox and etc. are all already internally implemented those things. But if we talk about 3rd Party Custom View distributed all over the internet. I must say that many of them aren't implemented this part of code yet which may cause a big problem in real use.And if you create your own Custom View or Custom Viewgroup, don't forget to implement those two methods as well. It is really important that every single type of View used in the application is implemented this part. And also don't forget to assign android:id attribute to every single View placed in the layout that you need to enable View State Saving and Restoring or it will not be able to restore the state at all.
  2. Clearly seperate Fragment State from View State : Don't save View's State inside Fragment's onSaveInstanceState and vice versa.
Table: Save and Restore State of View
protected Parcelable onSaveInstanceState()This function is called whne you rotate the screen or make puase the screen. That means you need to save the state as the View will be recreated.
protected void onRestoreInstanceState(Parcelable state)This fucntion is accled as when the view is created afte the rotation. It is not called for resume like Pause ->Resume. but it will be called if you do ->Pause -> Rotate ->Resume as the rotation happend and view is recrated.
AutomaticIf you have the ID of each it will automatoically happens for Default View But you need to implemts for cutom View. View without android:id defined isn't able to restore its View's state.
Table: Save and Restore State of Activity( Exactly same as View)
protected void onSaveInstanceState(Bundle outState)This function is called whne you rotate the screen or make puase the screen. That means you need to save the state as the View will be recreated.
protected void onRestoreInstanceState(Bundle savedInstanceState) {This fucntion is accled as when the view is created afte the rotation. It is not called for resume like Pause ->Resume. but it will be called if you do ->Pause -> Rotate ->Resume as the rotation happend and view is recrated.
Table: Save and Restore State of Fragmnet
public void onSaveInstanceState(Bundle outState)Must put all fragemnet states as it moving out .
public void onActivityCreated(@Nullable Bundle savedInstanceState)Called when activity recreated on Rotation.
Table: Save and Restore State for Fragment
public static MainFragment newInstance(int someInt, String someTitle)You will create the Fragment and set the args.
public void onCreate(Bundle savedInstanceState)Extrcat the arguments and set to local varibale.
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)Just create new View of Influte here.
public void onViewCreated(View view, Bundle savedInstanceState)Acess the Variable and Memober.
public void onSaveInstanceState(Bundle outState)Called when onpause and Rotate done.
public void onActivityCreated(@Nullable Bundle savedInstanceState)Called when this is come back .
public void onDestroyView()Call when app is detroyed.
public void onAttach(Activity activity)When your fragment is visible.
public void onDetach()Remove listner.
Table : LifeCycle Aware class ( where you add listner and relase listner)
ITEM # ATTCHED # DEATCH # DESTROY
View # Override void onAttachedToWindow() # void onDetachedFromWindow() #
Activity # onPause() # onResume() # onDestroy()
Fragemnets # public void onAttach(Activity activity) # public void onDetach() # onDestroy
RecyclerView.Adapter # public void onViewAttachedToWindow(LifecycleAwareViewHolder holder) # public void onViewDetachedFromWindow(LifecycleAwareViewHolder holder) # public void onViewRecycled()(LifecycleAwareViewHolder holder)
Example: How to define LifecycleAwareViewHolder:
public class LifecycleAwareViewHolder extends RecyclerView.ViewHolder { public LifecycleAwareViewHolder(View itemView) { super(itemView); } public void onViewAttachedToWindow() {} public void onViewDetachedFromWindow() {} public void onRecycled() {} } public class MyAdapter extends RecyclerView.Adapter<LifecycleAwareViewHolder> { @Override public void onViewAttachedToWindow(LifecycleAwareViewHolder holder) { holder.onViewAttachedToWindow(); } @Override public void onViewDetachedFromWindow(LifecycleAwareViewHolder holder) { holder.onViewDetachedFromWindow(); } @Override public void onViewRecycled(LifecycleAwareViewHolder holder) { holder.onRecycled(); } }
Handling Configuration Changes with Fragments
What is the best way to retain active objects—such as running Threads, Sockets, and AsyncTasks—across device configuration changes?
Solution 1: Bad Practice: Retain the Activity: Perhaps the hackiest and most widely abused workaround is to disable the default destroy-and-recreate behavior by setting the android:configChanges attribute in your Android manifest. This Solution is bad as
Solution 2: Override onRetainNonConfigurationInstance(): Prior to Honeycomb’s release, the recommended means of transferring active objects across Activity instances was to override the onRetainNonConfigurationInstance() and getLastNonConfigurationInstance() methods. Using this approach, transferring an active object across Activity instances was merely a matter of returning the active object in onRetainNonConfigurationInstance() and retrieving it in getLastNonConfigurationInstance().
Solution 3 and Recommended: Manage the Object Inside a Retained Fragment: Ever since the introduction of Fragments in Android 3.0, the recommended means of retaining active objects across Activity instances is to wrap and manage them inside of a retained “worker” Fragment. By default, Fragments are destroyed and recreated along with their parent Activitys when a configuration change occurs. Calling Fragment#setRetainInstance(true) allows us to bypass this destroy-and-recreate cycle, signaling the system to retain the current instance of the fragment when the activity is recreated. As we will see, this will prove to be extremely useful with Fragments that hold objects like running Threads, AsyncTasks, Sockets, etc.
(expand code)
/** * This Activity displays the screen's UI, creates a TaskFragment * to manage the task, and receives progress updates and results * from the TaskFragment when they occur. */ public class MainActivity extends Activity implements TaskFragment.TaskCallbacks { private static final String TAG_TASK_FRAGMENT = "task_fragment"; private TaskFragment mTaskFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); FragmentManager fm = getFragmentManager(); mTaskFragment = (TaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT); // If the Fragment is non-null, then it is currently being // retained across a configuration change. if (mTaskFragment == null) { mTaskFragment = new TaskFragment(); fm.beginTransaction().add(mTaskFragment, TAG_TASK_FRAGMENT).commit(); } } // The four methods below are called by the TaskFragment when new // progress updates or results are available. The MainActivity // should respond by updating its UI to indicate the change. @Override public void onPreExecute() { ... } @Override public void onProgressUpdate(int percent) { ... } @Override public void onCancelled() { ... } @Override public void onPostExecute() { ... } } /** * This Fragment manages a single background task and retains * itself across configuration changes. */ public class TaskFragment extends Fragment { /** * Callback interface through which the fragment will report the * task's progress and results back to the Activity. */ interface TaskCallbacks { void onPreExecute(); void onProgressUpdate(int percent); void onCancelled(); void onPostExecute(); } private TaskCallbacks mCallbacks; private DummyTask mTask; /** * Hold a reference to the parent Activity so we can report the * task's current progress and results. The Android framework * will pass us a reference to the newly created Activity after * each configuration change. */ @Override public void onAttach(Activity activity) { super.onAttach(activity); mCallbacks = (TaskCallbacks) activity; } /** * This method will only be called once when the retained * Fragment is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Retain this fragment across configuration changes. setRetainInstance(true); // Create and execute the background task. mTask = new DummyTask(); mTask.execute(); } /** * Set the callback to null so we don't accidentally leak the * Activity instance. */ @Override public void onDetach() { super.onDetach(); mCallbacks = null; } /** * A dummy task that performs some (dumb) background work and * proxies progress updates and results back to the Activity. * * Note that we need to check if the callbacks are null in each * method in case they are invoked after the Activity's and * Fragment's onDestroy() method have been called. */ private class DummyTask extends AsyncTask<Void, Integer, Void> { @Override protected void onPreExecute() { if (mCallbacks != null) { mCallbacks.onPreExecute(); } } /** * Note that we do NOT call the callback object's methods * directly from the background thread, as this could result * in a race condition. */ @Override protected Void doInBackground(Void... ignore) { for (int i = 0; !isCancelled() && i < 100; i++) { SystemClock.sleep(100); publishProgress(i); } return null; } @Override protected void onProgressUpdate(Integer... percent) { if (mCallbacks != null) { mCallbacks.onProgressUpdate(percent[0]); } } @Override protected void onCancelled() { if (mCallbacks != null) { mCallbacks.onCancelled(); } } @Override protected void onPostExecute(Void ignore) { if (mCallbacks != null) { mCallbacks.onPostExecute(); } } } }
When the MainActivity starts up for the first time, it instantiates and adds the TaskFragment to the Activity’s state. The TaskFragment creates and executes an AsyncTask and proxies progress updates and results back to the MainActivity via the TaskCallbacks interface. When a configuration change occurs, the MainActivity goes through its normal lifecycle events, and once created the new Activity instance is passed to the onAttach(Activity) method, thus ensuring that the TaskFragment will always hold a reference to the currently displayed Activity instance even after the configuration change. The resulting design is both simple and reliable; the application framework will handle re-assigning Activity instances as they are torn down and recreated, and the TaskFragment and its AsyncTask never need to worry about the unpredictable occurrence of a configuration change. Note also that it is impossible for onPostExecute() to be executed in between the calls to onDetach() and onAttach(), as explained in this StackOverflow answer and in my reply to Doug Stevenson in this Google+ post (there is also some discussion about this in the comments below).
Solution 4: Using androidLife Cycle.
ADB Tricks
Table: Adb commands
TOP COMMANDSExample:
alias cr='adb logcat | grep "E AndroidRuntime:"'Easy Finding Crash.
adb shell "dumpsys activity | grep top-activity"See the top activity.
adb shell "dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp'"See the Focus Activity.
PID=`adb shell ps | grep -i in.co.dipankar.lifecycletest | cut -c10-20`;adb logcat | grep $PIDsee the log only for my process.
ADB DEBUGGINGExample
adb devicese4b25377 device
adb forward tcp:8000 tcp:9000set up forwarding of host port 8000 to emulator/device port 9000
adb kill-serverkill the server if it is running. (terminal adb.exe process)
CONNECT BY WIFYExample
adb devicesList of devices attached
adb tcpip 5555restarting in TCP mode port: 5555
adb connect 102.169.0.9connected to ip
adb devicesremove the cable and see if it still visible.
adb usbrestarting ADB in USB mode.
ACTIVITY MANAGERExample
adb shell am start -a "android.intent.action.VIEW" -d "http://developer.android.com"specifying the action and data uri
adb shell am start -a "android.intent.action.SEND" --es "android.intent.extra.TEXT" "Hello World" -t "text/plain"specifying the action, mime type and an extra string NOTE THAT THERE IS -- for ES.
adb shell am start -n "com.example.application/.MainActivity"specifying an explicit component name
adb shell am start -n com.growingwiththeweb.example/com.growingwiththeweb.example.MainActivityStart activity_rssfeed
adb shell am force-stop com.some.package.name
adb shell am startservice com.some.package.name/.YourServiceSubClassName
adb shell am stopservice ...
adb shell input text ‘This%sis%sSPARTA!’Giving some inputs
PACKAGE MANGERExample
adb install test.apkJust install by puyll path
adb install -l test.apkforward lock application
adb install -r test.apkreplace existing application
adb install -t test.apkallow test packages
adb install -s test.apkinstall application on sdcard
adb install -d test.apkallow version code downgrade
adb install -p test.apkpartial application install
adb uninstall com.test.appuninstall by package name
adb uninstall -k com.test.appKeep the data and cache directories around after package removal.
adb shell pm list packagesList all packges
adb shell pm list packages -fSee their associated file.
adb shell pm list packages -d Filterto only show disabled packages.
adb shell pm list packages -e Filterto only show enabled packages.
adb shell pm list packages -s Filterto only show system packages.
adb shell pm list packages -3 Filterto only show third party packages.
adb shell pm list packages -iSee the installer for the packages.
adb shell pm list packages -uAlso include uninstalled packages.
adb shell pm list packages --user The user space to query.
adb shell pm path com.android.phoneprint path package:/system/priv-app/TeleService/TeleService.apk
adb shell pm clear com.test.abcDeletes all data associated with a package.
RECORINGExample
adb shell screencap /sdcard/screen.png; adb pull /sdcard/screen.pngTaking a screenshot of a device display
adb shell screenrecord /sdcard/demo.mp4 ; adb pull /sdcard/demo.mp4ecording the display of devices running Android 4.4 (API level 19) and higher.
adb shell screenrecord --bit-rate 5000000 /sdcard/demo.mp4having a bitrate
adb shell screenrecord --time-limit 120 /sdcard/demo.mp4
adb shell screenrecord --rotateRotates the output 90 degrees. This feature is experimental.
LOGCATExample
adb logcatSee all logcat
adb logcat *:Vlowest priority, filter to only show Verbose level
adb logcat *:Dfilter to only show Debug level
adb logcat *:Ifilter to only show Info level
adb logcat *:Wfilter to only show Warning level
adb logcat *:Efilter to only show Error level
adb logcat *:Ffilter to only show Fatal level
adb logcat *:SSilent, highest priority, on which nothing is ever printed
adb logcat -dDumps the log to the screen and exits.
adb logcat -v briefDisplay priority/tag and PID of the process issuing the message (default format).
adb logcat -v processDisplay PID only.)
adb logcat -v tagDisplay the priority/tag only.
adb logcat -v rawDisplay the raw log message, with no other metadata fields.
adb logcat -v timeDisplay the date, invocation time, priority/tag, and PID of the process issuing the message.
adb logcat -v threadtimeDisplay the date, invocation time, priority, tag, and the PID and TID of the thread issuing the message.
adb logcat -v longDisplay all metadata fields and separate messages with blank lines.
adb logcat -f test.logsWrites log message output to test.logs .
adb logcat | grep DIPGrep.
SYSTEM DATAExample
adb shell dumpsysdumps system data
adb shell dumpsys batteryadb shell dumpsys meminfo
adb shell dumpsys batterystatscollects battery data from your device
adb shell dumpsys batterystats --reseterases old collection data
adb shell dumpsys activity
adb shell dumpsys gfxinfo com.android.phonemeasuring com.android.phone ui performance
adb shell dumpstatedumps state
adb shell dumpstate > state.logsdumps state to a file
FILE MANAGER
adb pull /sdcard/demo.mp4download /sdcard/demo.mp4 to current dict
adb pull /sdcard/demo.mp4 e:\download /sdcard/demo.mp4 to drive E.
adb push test.apk /sdcardCopies /platform-tools/test.apk to /sdcard directory.
adb push d:\test.apk /sdcardCopies d:\test.apk to /sdcard directory.
adb shell ls -Rlist subdirectories recursively
adb shell ls -ado not hide entries starting with
PROCESS
adb shell ps | grep dipSee the process id
adb shell kill -9 13414
NETWORKExample
adb shell netstatSee all Port opened or not
adb shell ping www.google.comtest the connection and latency between two network connection.
adb shell netcfgconfigure and manage network connections via profiles
adb shell ip -f inet addr show wlan0show WiFi IP Address