Tuesday, 6 November 2018

Android Architecture Components : View Model

Hi ,
Today I am going to share another demo in Android architecture series i.e. View Model  . Firstly let me explain a short introduction of View Model:

  • Survives Configuration changes i.e. Screen-rotation
  • Not same as onSaveInstanceState (it is used to store small amount of data)
  • View Model used for large data such as :Bitmap or user list
  • store and manage UI related data
  • Destroyed only if the user activity is completely destroyed, in onCleared()
  • Communication layer between DB and UI
For more understanding , please refer : Android Developer View Model


So as I previously said, View Model is used to retain the data of configuration changes. So here I am going to explain a demo code , to check that View Model is really helpful to retain data while configuration changes (switching between landscape and portrait mode) or not .

In my demo code , I am taking one integer array , and getting one random number from that array .That random number will show on text-view. I will take 2 text-views , to hold the random number fetching from integer array. Among these two text-views , one will show the number normally while configuration changes and other text-view will show the number using view-model.So let's start:

-> Firstly I am adding following dependencies in build.gradle(app) file :

implementation "android.arch.lifecycle:runtime:1.1.1"
implementation "android.arch.lifecycle:extensions:1.1.1"
annotationProcessor "android.arch.lifecycle:compiler:1.1.1"

-> Take one layout named : view_model_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textview7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="25sp"
        android:textColor="#085937"
        android:textStyle="bold"
        android:text="TextView" />

    <TextView
        android:id="@+id/textview8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textview7"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="30dp"
        android:textSize="25sp"
        android:textColor="#085937"
        android:textStyle="bold"
        android:text="TextView" />

</RelativeLayout>



-> Before moving to activity java class, I have sub-classed , ViewModel class , with name :
RandomNumViewModel.java  , please take a look on code:

package com.development.view_model;

import android.arch.lifecycle.ViewModel;

import java.util.Random;

public class RandomNumViewModel extends ViewModel {

private int[] items = new int[]{1, 2, 3, 44, 65, 87, 22, 65, 87, 22, 90, 63, 4, 8, 50};

private Random random = new Random();

/**
* mRandomNumber , this variable should retain its value,
* on configuration change
*/
private int mRandomNumber = 0;


/**
* used to create a random number from array
* @return
*/
public int createNumber(){
mRandomNumber = items[random.nextInt(items.length)];
return mRandomNumber;
}


/**
* used to get the random number
* @return
*/
public int getRandArrayElement(){
if(mRandomNumber == 0){
mRandomNumber = createNumber();
return mRandomNumber;
}else{
return mRandomNumber;
}
}

}


-> Now take a look on my java file , I have named it as : ViewModelActivity.java

package com.development.view_model;

import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import com.development.R;

import java.util.Random;

public class ViewModelActivity extends AppCompatActivity {
TextView txt,txt1;
private int[] items = new int[]{1, 2, 3, 44, 65, 87, 22, 65, 87, 22, 90, 63, 4, 8, 50};
private Random random = new Random();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_model_layout);
txt = (TextView)findViewById(R.id.textview7);
txt1 = (TextView)findViewById(R.id.textview8);
int num = getRandArrayElement();

/**
* it's value should change on configuration change
*/
txt.setText(""+num); // normal way to set data on textview


/**
* Setting textview using View Model
* it's value should retain on configuration change
*/
RandomNumViewModel randomNumViewModel =
ViewModelProviders.of(this).get(RandomNumViewModel.class);

int num1 = randomNumViewModel.getRandArrayElement();

txt1.setText(""+num1);
}

public int getRandArrayElement(){
return items[random.nextInt(items.length)];
}
}


That's it , build and run the code, you can check that the value of one text-view will change , but value of another text-view will not change , while configuration changes (switching between landscape and portrait mode) , I have run my code , you can check the screen shots for various steps of screen-rotation:







       Portrait Mode

     



 Landscape Mode









Here in above screenshots, we can clearly see , that the second text-view , which is using View-Model , to show (retain) the number value , doesn't change while screen-rotation , but the first one changes its value. 

Tuesday, 25 September 2018

Android Architecture Components : Data Binding Part 3

Hi ,
Today I am going to share the third part of Data Binding Demo. You can check out previous posts for more understanding.
In this post I am sharing the data binding in RecyclerView and Adapters.

-> Firstly I created one layout named : friend_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</layout>

-> Now I created one another layout for thumbnail in Grid , named : post_row_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="post"
            type="info.databinding.model.Post" />
    </data>

    <android.support.constraint.ConstraintLayout 
        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">

        <ImageView
            android:id="@+id/thumbnail"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleType="centerCrop"
            android:profileImage="@{post.profileImage}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="H,1:1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </android.support.constraint.ConstraintLayout>

</layout>

-> I have created the thumbnail layout named: post_row_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="post"
            type="info.databinding.model.Post" />
    </data>

    <android.support.constraint.ConstraintLayout
        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">

        <ImageView
            android:id="@+id/thumbnail"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleType="centerCrop"
            android:profileImage="@{post.profileImage}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="H,1:1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </android.support.constraint.ConstraintLayout>

</layout>

-> Now let's move to entity part , named : Post.java

public class Post {
    String profileImage;
    public String getProfileImage() {
        return profileImage;
    }

    public void setProfileImage(String profileImage) {
        this.profileImage = profileImage;
    }

    @BindingAdapter({"android:profileImage"})
    public static void loadImage(ImageView view, String imageUrl) {
        // If you consider Picasso, follow the below
        Picasso.with(view.getContext()).
                load(imageUrl).
                placeholder(R.drawable.ic_launcher_background).
                into(view);
    }

}

 -> The adapter class will be named : PostsAdapter.java

public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> {
    ArrayList<Post> list = new ArrayList<>();
    Activity activity ;
    LayoutInflater mLayoutInflator;

    class ViewHolder extends RecyclerView.ViewHolder{
       PostRowLayoutBinding postBinding;
        public ViewHolder(PostRowLayoutBinding itemBinding) {
            super(itemBinding.getRoot());
            this.postBinding = itemBinding;
        }
    }
    public PostsAdapter(ArrayList<Post> list, Activity friendsListActivity) {
        this.list = list;
        this.activity = friendsListActivity;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (mLayoutInflator == null) {
            mLayoutInflator = LayoutInflater.from(parent.getContext());
        }
        PostRowLayoutBinding binding = DataBindingUtil.inflate(mLayoutInflator,R.layout.post_row_layout,parent,false);
        return new ViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
      
       holder.postBinding.setPost(list.get(position));
       holder.postBinding.thumbnail.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Toast.makeText(activity,position+" Friends clicked!!",Toast.LENGTH_LONG).show();
           }
       });
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

}

Now the activity class , is named as : FriendsListActivity.class

public class FriendsListActivity extends AppCompatActivity {

    ArrayList<Post> aryList =new ArrayList<>();
    FriendLayoutBinding dataBinding;
    RecyclerView recyclerView;
    PostsAdapter mAdapter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        dataBinding = DataBindingUtil.setContentView(this, R.layout.friend_layout);
        initViews();
    }

    private void initViews() {
        recyclerView = dataBinding.recyclerView;
        recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
        recyclerView.addItemDecoration(new GridSpacingItemDecoration(3, Utils.dpToPx(4,getApplicationContext()), true));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setNestedScrollingEnabled(false);
        mAdapter = new PostsAdapter(addFriends(), this);
        recyclerView.setAdapter(mAdapter);
    }

    private ArrayList<Post> addFriends() {
        Post post = new Post();
        post.setProfileImage("https://www.ienglishstatus.com/wp-content/uploads/2018/04/profile-images-for-lover.jpg");
        aryList.add(post);

        Post post1 = new Post();
        post1.setProfileImage("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRLGja2adSQA0iRqBPSP16wx-9QCHK7P0ADoFRAjKfo4elX5unVpg");
        aryList.add(post1);

        Post post2 = new Post();
        post2.setProfileImage("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSm92-cGpGAlpNqJPHzWhSA_p83pkxvQjsLXMrGN1uX8Fw36Zha");
        aryList.add(post2);

        Post post3 = new Post();
        post3.setProfileImage("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRRMXMX3L3ENBdFykfCjGVXomO8qXa082hm80ZAJJIYjNw6pVap");
        aryList.add(post3);

        Post post4 = new Post();
        post4.setProfileImage("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRaAPF9U-hEs5XN_WcGS25NZvaWTAQCklIoi8OJ_aKQMHmf0RdF");
        aryList.add(post4);

        Post post5 = new Post();
        post5.setProfileImage("https://avatarfiles.alphacoders.com/665/66525.jpg");
        aryList.add(post5);

        Post post6 = new Post();
        post6.setProfileImage("https://dp.profilepics.in/profile-pictures-for-facebook-whatsapp/stylish-profile-pics/stylish-profile-pics-for-whatsapp-facebook-05.jpg");
        aryList.add(post6);

        Post post7 = new Post();
        post7.setProfileImage("https://www.androidcrush.com/wp-content/uploads//2016/10/Hope-Whatsapp-Images-Free-Download-1.png");
        aryList.add(post7);

        Post post8 = new Post();
        post8.setProfileImage("https://i.pinimg.com/originals/53/54/f7/5354f750a2816333f42efbeeacb4e244.jpg");
        aryList.add(post8);

        Post post9 = new Post();
        post9.setProfileImage("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSDEDfz0OsWGMtJJH1MuG0Deaxmr7kEsgwU9XtLbW4pZLe8_nYuLQ");
        aryList.add(post9);

        return aryList;
    }
}

Here I have added some random image urls to show the thumbnails in grid.That's it.

Monday, 24 September 2018

Android Architecture Components : Data Binding Part 2

Hi ,

In previous post , I have explained about data binding at initial level, now going forward I am going to share the demo to bind ImageView , with the help of Data Binding.

Now firstly enable the dataBinding element in build.gradle .

Add Picasso dependency in build.gradle as well .

implementation 'com.squareup.picasso:picasso:2.5.2'

After that , create an entity class , named ProfilePicEntity :

public class ProfilePicEntity extends BaseObservable {
    String profileImage;

    public String getProfileImage() {
        return profileImage;
    }

    public void setProfileImage(String profileImage) {
        this.profileImage = profileImage;
        notifyPropertyChanged(BR.profile);
    }

    @BindingAdapter({"android:profileImage"})
    public static void loadImage(ImageView view, String imageUrl) {
        // If you consider Picasso, follow the below
        Picasso.with(view.getContext()).
                load(imageUrl).
                placeholder(R.drawable.ic_launcher_background).
                into(view);
    }

}

Here in above class , I have one variable , with its getter/setter methods and one another method to load image .This method is helpful to load image when we pass url in ImageView setProfileImage method.
 @BindingAdapter({"android:profileImage"}) -> this is used to define the attribute which is used in layout to setImage by ImageView Tag.

Now Let's move to layout named : image_view_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <import type="com.demo.databinding.utils.Utils" />
        <import type="android.view.View"/>
        <variable
            name="profile"
            type="com.demo.databinding.model.ProfilePicEntity">              </variable>

        <variable name="handlers"
           type="com.demo.databinding.ImageViewActivity.MyHandlers"> </variable>
    </data>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:padding="20dp">


            <ImageView
                android:id="@+id/imageView"
                android:layout_width="300dp"
                android:layout_height="300dp"
                android:layout_gravity="center_horizontal"
                android:scaleType="fitXY"
                android:profileImage="@{profile.profileImage}"
               tools:srcCompat="@tools:sample/backgrounds/scenic[3]" />

            <Button
                android:id="@+id/button2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Change Image"
                android:onClick="@{(view)->   handlers.onClickButton(imageView)}"
                android:layout_margin="10dp"/>
        </LinearLayout>


</layout>

Here in above layout , we have defined the entity class variable name as -> profile and Handler is named as -> handlers

Now I have created a java class named: ImageViewActivity.java

public class ImageViewActivity extends AppCompatActivity {

    ImageViewLayoutBinding imageViewLayoutBinding;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        imageViewLayoutBinding = DataBindingUtil.setContentView
                (this, R.layout.image_view_layout);

        ProfilePicEntity profilePicEntity = new ProfilePicEntity();
        profilePicEntity.setProfileImage("https://cdn.iconscout.com/icon/free/png-256/avatar-373-456325.png");

        imageViewLayoutBinding.setProfile(profilePicEntity);

        MyHandlers myHandlers = new MyHandlers(this);
        imageViewLayoutBinding.setHandlers(myHandlers);
    }

    public class MyHandlers{
        public MyHandlers(ImageViewActivity activity) {
        }

        public void onClickButton(ImageView view){
            Toast.makeText(getApplicationContext(),"Button is Clicked!",Toast.LENGTH_LONG).show();
            ProfilePicEntity profilePicEntity = new ProfilePicEntity();
            profilePicEntity.setProfileImage("https://www.attractivepartners.co.uk/wp-content/uploads/2017/06/profile.jpg");
            imageViewLayoutBinding.setProfile(profilePicEntity);
        }

    }

}


Above in java class , I am setting one image url at loading time and after click the button to change the image , again setting another image url on that imageView. That's it . Paste this code and run it , let me know if you face any problem . In next post I will share the data Binding in Adapters. Till then Happy Coding :)






  

Android Architecture Components : Data Binding Part 1

Hi ,

Today I am going to share the demo for data binding . According to android official docs: The Data Binding Library is a support library that allows you to bind UI components in your layout to data sources in your app using a declarative format rather than programmatically.

So as it is mentioned , this library is basically used to bind view of layout to view of activity, without using findViewById() .

Let's start with few coding, here I am showing a simple demo with 3 textviews and 1 profile picture form layout :

-> Firstly we need to download lib in Android SDK Manager : Support Repository 

-> Add Data Binding element into build.gradle :


android {
    dataBinding {
        enabled = true    }

-> Now let's design the layout , here you have to notice one thing , that parent tag of all layout should be :  <layout> </layout> only. This is used to create the by default binding class for each layout.If you forgot to add this tag as the parent tag , so we would not be able to get the binding class of this layout , in our activity class :

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <import type="com.demo.databinding.utils.Utils" />
        <import type="android.view.View"/>
        <variable
            name="user"
            type="com.demo.databinding.model.User"></variable>

        <variable 
            name="handlers"
            type="com.demo.databinding.MainActivity.MyHandlers"></variable>
    </data>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="20dp">


        <TextView
            android:id="@+id/textView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{Utils.capitalize(user.name)}"
            android:onClick="@{(view)-> handlers.onTextClicked(view)}"
            android:layout_margin="10dp"
            android:textSize="18sp"
            android:clickable="true"
            android:textColor="@color/colorAccent"/>

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.email}"
            android:onClick="@{(view)-> handlers.showEmail(view,user.email)}"
            android:layout_margin="10dp"
            android:textSize="18sp"
            android:textColor="@color/colorAccent"/>

        <TextView
            android:id="@+id/textView3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.phoneNumber}"
            android:onClick="@{() -> handlers.onClickNumber()}"
            android:layout_margin="10dp"
            android:textSize="18sp"
            android:textColor="@color/colorAccent"/>

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Submit"
            android:layout_margin="10dp"
            android:textSize="18sp"
            android:onClick="@{()-> handlers.onSubmitButtonClicked(textView2, textView,textView3)}"
            android:textColor="@color/colorAccent"/>


    </LinearLayout>
    </ScrollView>
</layout>

The above layout (activity_main.xml), is having few textviews and button with one imageview,here we have used some tags, take a look:

-> <layout> - This tag is used as a parent tag of a layout, for data-binding it is compulsory.
-> <data> -  This tag is used to import any class for src to layout .
-> @{} - > It is used to define the particular callback on call any event.

Here in textviews we are setting different types of Strings/Numbers , which are managed by JAVA class , which we will show you further :

  1. android:text="@{Utils.capitalize(user.name)}"
  2. android:text="@{user.email}"
  3. android:text="@{user.phoneNumber}"
In above it is clear that User is an entity and name, email and phoneNumber has been set here like this.


Also in these 3 textviews we have different types of call onClick event , that are:

  1. onClick="@{(view)-> handlers.onTextClicked(view)}"
  2. onClick="@{(view)-> handlers.showEmail(view,user.email)}"
  3. onClick="@{() -> handlers.onClickNumber()}"
In Pt 1: We are passing the same textview object on click that textView.

In Pt.2: We are passing that textView and  email , which are setted by JAVA class (will explain you later)

In Pt. 3: We are calling this method without any parameters.

Now Let's take a look on User.class(entity class) :


public class User extends BaseObservable {
    String name;
    String email;
    String phoneNumber;

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
        notifyPropertyChanged(BR.email);
    }

    @Bindable
    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        notifyPropertyChanged(BR.phoneNumber);
    }
   
}

-> We have added a method in Utils class as well:

 public static String capitalize(String text) {
        return text.toUpperCase();
    }

-> Now let's move to MainActivity.class:

public class MainActivity extends AppCompatActivity {

    User user;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding dataBinding = DataBindingUtil.setContentView(this,R.layout.activity_main);

        user = new User();
        user.setName("XYZ");
        user.setEmail("xyz@gmail.com");
        user.setPhoneNumber("9876543210");
       

        dataBinding.setUser(user);
        MyHandlers handlers = new MyHandlers(MainActivity.this);
        dataBinding.setHandlers(handlers);
    }

    public class MyHandlers{
        public MyHandlers(MainActivity activity) {
        }

        public void onTextClicked(View view){
            Toast.makeText(getApplicationContext(),"Text is Clicked!",Toast.LENGTH_LONG).show();
        }

        public void showEmail(View view, String email){
            Toast.makeText(getApplicationContext(),email+" is entered !!",Toast.LENGTH_LONG).show();
        }

        public void onClickNumber(){
            Toast.makeText(getApplicationContext(),"Phone number is clicked!!",Toast.LENGTH_LONG).show();
        }

        public void onSubmitButtonClicked(TextView name, TextView email, TextView phoneNumber){
            User user  =  new User();
            user.setName("PQR");
            user.setEmail("pqr@gmail.com");
            user.setPhoneNumber("1234567890");
            name.setText("PQR");
            email.setText("pqr@gmail.com");
            phoneNumber.setText("1234567890");
            Toast.makeText(getApplicationContext(),"Submit is clicked!!",Toast.LENGTH_LONG).show();

        }

        
    }

}


-> Above in MainActivity , we have set Content View , we have to call setContentView from DataBindingUtil class.

After that we will set all the variables of Entity class, in our case it is User.java.
then set it to ActivityMainBinding  class(it is a generated class mainly used for Binding data) 
Set the Handlers  object to this class also. That's it. Run the code and enjoy the data binding experience :) .For ImageView setting I will explain in next post ,till then Happy Coding :) 






Advanced Kotlin Coroutines : Introduction

 Hi,  Today I am unwraping the topic in Kotin world i.e. Coroutine . If you want to get started with Kotlin coroutine and ease your daily de...