Layout Flip Animation in Android

Layout Flip Animation in Android

    Sometimes, we would like to build a 2-sides layout with 2 separate contents like playing cards and we can flip/switch them smoothly with animation.

    By this tutorial post, I will show you how to do a card flip animation with custom fragment animations. Card flips animate between views of content by showing an animation that emulates a card layout flipping over.
    And our output (flip animation) will be like this:

Create the animators

    Create the animations for the layout flips. You'll need two animators for when the front of the card animates out and to the left and in and from the left. You'll also need two animators for when the back of the card animates in and from the right and out and to the right. Now, create animator folder under res and add these animation files (XML):
card_flip_left_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Before rotating, immediately set the alpha to 0. -->
    <objectAnimator
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:propertyName="alpha"
        android:duration="0" />

    <!-- Rotate. -->
    <objectAnimator
        android:valueFrom="-180"
        android:valueTo="0"
        android:propertyName="rotationY"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="@integer/card_flip_time_full" />

    <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
    <objectAnimator
        android:valueFrom="0.0"
        android:valueTo="1.0"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_time_half"
        android:duration="1" />
</set>
card_flip_left_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Rotate. -->
    <objectAnimator
        android:valueFrom="0"
        android:valueTo="180"
        android:propertyName="rotationY"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="@integer/card_flip_time_full" />

    <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
    <objectAnimator
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_time_half"
        android:duration="1" />
</set>
card_flip_right_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Before rotating, immediately set the alpha to 0. -->
    <objectAnimator
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:propertyName="alpha"
        android:duration="0" />

    <!-- Rotate. -->
    <objectAnimator
        android:valueFrom="180"
        android:valueTo="0"
        android:propertyName="rotationY"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="@integer/card_flip_time_full" />

    <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
    <objectAnimator
        android:valueFrom="0.0"
        android:valueTo="1.0"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_time_half"
        android:duration="1" />
</set>
card_flip_right_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Rotate. -->
    <objectAnimator
        android:valueFrom="0"
        android:valueTo="-180"
        android:propertyName="rotationY"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="@integer/card_flip_time_full" />

    <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
    <objectAnimator
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_time_half"
        android:duration="1" />
</set>

Create 2 Fragment views

    Each side of the "card" layout is a Fragment which has a separate layout that can contain any content you want. You'll then use the two layouts in the Fragments that you'll later animate. The following layout is "front side" which shows an image:
fragment_card_front.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:contentDescription="@null"
        android:scaleType="fitXY"
        android:src="@drawable/android_team" />
</LinearLayout>
    And this is "back layout" which contains some texts:
fragment_card_back.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#a6c"
    android:orientation="vertical"
    android:padding="@dimen/activity_horizontal_margin">

    <TextView
        android:id="@android:id/text1"
        style="?android:textAppearanceLarge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/card_back_title"
        android:textColor="#fff"
        android:textStyle="bold" />

    <TextView
        style="?android:textAppearanceSmall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:lineSpacingMultiplier="1.2"
        android:text="@string/card_back_description"
        android:textAllCaps="true"
        android:textColor="#80ffffff"
        android:textStyle="bold" />

</LinearLayout>

Fragments programmatically code

    Create Fragments classes for the front and back of the "card" layout. These classes return the layouts that you created previously in the onCreateView() method of each Fragment. You can then create instances of this Fragment in the parent Activity where you want to show the layout:
FrontLayoutFragment.java
package info.devexchanges.animationfliplayout;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FrontLayoutFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_card_front, container, false);
    }
}
BackLayoutFragment.java
package info.devexchanges.animationfliplayout;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class BackLayoutFragment extends Fragment{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_card_back, container, false);
    }
}

Animate "Card" layout in the Activity

    Now, you'll need to display the Fragments inside of an Activity. To do this, first create the layout for your Activity. In my example, I create a RelativeLayout that I can add Fragments to later, I also put a Button inside it to flip this layout with animation when clicked:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btn_flip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:layout_marginRight="@dimen/activity_horizontal_margin"
        android:text="Flip Layout" />
</RelativeLayout>
    In the programmatically code, set the content view to be the layout that you just created. It's also good idea to show a default fragment when the activity is created, I will display the front of the "card" layout by default:
public class MainActivity extends AppCompatActivity implements FragmentManager.OnBackStackChangedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            getFragmentManager()
                    .beginTransaction()
                    .add(R.id.container, new FrontLayoutFragment())
                    .commit();
        } else {
            isShowingBackLayout = (getFragmentManager().getBackStackEntryCount() > 0);
        }

        getFragmentManager().addOnBackStackChangedListener(this);

        View btnFlip = findViewById(R.id.btn_flip);
        btnFlip.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                flipCard();
            }
        });
    }
}
    Now that you have the front of the card showing, you can show the back of the "card" layout with the flip animation at an appropriate time. Create a method to show the other side of the card that does the following things (this method named flipCard() and will be invoked when the Button clicked):
  • Sets the custom animations that you created earlier for the fragment transitions. 
  • Replaces the currently displayed fragment with a new fragment and animates this event with the custom animations that you created. 
  • Adds the previously displayed fragment to the fragment back stack so when the user presses the Back button, the card flips back over.
This is the remaining code of MainActivity:
        private boolean isShowingBackLayout = false;

        private void flipCard() {
        if (isShowingBackLayout) {
            getFragmentManager().popBackStack();
            return;
        }
        isShowingBackLayout = true;

        getFragmentManager().beginTransaction()
                // Replace the default fragment animations with animator resources representing
                // rotations when switching to the back of the card, as well as animator
                // resources representing rotations when flipping back to the front (e.g. when
                // the system Back button is pressed).
                .setCustomAnimations(
                        R.animator.card_flip_right_in, R.animator.card_flip_right_out,
                        R.animator.card_flip_left_in, R.animator.card_flip_left_out)

                // Replace any fragments currently in the container view with a fragment
                // representing the next page (indicated by the just-incremented currentPage
                // variable).
                .replace(R.id.container, new BackLayoutFragment())

                // Add this transaction to the back stack, allowing users to press Back
                // to get to the front of the card.
                .addToBackStack(null)
                .commit();
    }

    @Override
    public void onBackStackChanged() {
        isShowingBackLayout = (getFragmentManager().getBackStackEntryCount() > 0);
    }
    As you can see at the DEMO output above, when the back layout is showing, if user press Back button, the front layout will be inflated. Conversely, app will be closed!

Conclusions

    By adding animation to fragments transaction process, we now able to make a card flip animation easily. Hope this trick is helpful with your own work. Further, you can visit this tag link to read all posts about creating animation in Android. Finally, you can download full source code by click the button below!


    References to original post on Android Developer blog.

Create a lock screen inside Android application

    Like some application which focuses on the protection of user data, if we would like to access content, we must enter password first. This lock screen always show every time we open the app, ensure that strangers cannot access your data. Maybe in some cases to protect user information, developer can use this way.

    In this post, I will make a lock activity by designing in XML. There is an another solution is using a third-party library, please read my previous post.

Designing layout for lock activity

    User must input number-password to pass this lock screen, so design a keypad for this activity like this:
activity_lock.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="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/activity_horizontal_margin"
    tools:context="info.devexchanges.lockscreen.LockActivity">

    <LinearLayout
        android:id="@+id/lock_keypad"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/btn1"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="1" />

            <Button
                android:id="@+id/btn2"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="2" />

            <Button
                android:id="@+id/btn3"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="3" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/btn4"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="4" />

            <Button
                android:id="@+id/btn5"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="5" />

            <Button
                android:id="@+id/btn6"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="6" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/btn7"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="7" />

            <Button
                android:id="@+id/btn8"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="8" />

            <Button
                android:id="@+id/btn9"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="9" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/btn_space"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:enabled="false" />

            <Button
                android:id="@+id/btn0"
                style="@style/Borderless_Button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="0" />

            <ImageView
                android:id="@+id/btn_clear"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:contentDescription="@null"
                android:src="@android:drawable/ic_input_delete"
                android:tint="@color/colorPrimary" />
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/dot_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/lock_keypad"
        android:layout_marginBottom="@dimen/activity_horizontal_margin"
        android:gravity="center"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/dot_1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@null"
            android:src="@drawable/dot_disable" />

        <ImageView
            android:id="@+id/dot_2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@null"
            android:src="@drawable/dot_disable" />

        <ImageView
            android:id="@+id/dot_3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@null"
            android:src="@drawable/dot_disable" />

        <ImageView
            android:id="@+id/dot_4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@null"
            android:src="@drawable/dot_disable" />

    </LinearLayout>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/dot_layout"
        android:layout_centerInParent="true"
        android:layout_marginBottom="@dimen/activity_horizontal_margin"
        android:src="@drawable/lock" />

</RelativeLayout>
    Expected output:
    Now, you must have a main activity, it's will start when launching app. It's layout depend on your work, in this simple project, it only include a TextView:
activity_main.xml
<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="info.devexchanges.lockscreen.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/activity_horizontal_margin"
        android:text="Hello World!" />
</LinearLayout>

Main activity programmatically code

     Because the lock screen always display when app launching, so in onStart() of MainActivity, we'll check if users haven't typed true before, starting LockActivity immediately by Intent! In order to detect users passed the lock screen or not, we'll use SharedPreferences. Source code simple like this:
MainActivity.java
package info.devexchanges.lockscreen;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.widget.TextView;

import butterknife.BindView;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.toolbar)
    Toolbar toolbar;

    @BindView(R.id.text)
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        setSupportActionBar(toolbar);
    }

    @SuppressLint("SetTextI18n")
    @Override
    protected void onStart() {
        super.onStart();
        if (!isPass()) {
            Intent intent = new Intent(this, LockActivity.class);
            startActivity(intent);
        } else {
            textView.setText("The code you typed is right!");
        }
    }

    private boolean isPass() {
        SharedPreferences prefs = getSharedPreferences("PASS_CODE", MODE_PRIVATE);
        return prefs.getBoolean("is_pass", false);
    }

    @Override
    protected void onStop() {
        super.onStop();

        SharedPreferences.Editor editor = getSharedPreferences("PASS_CODE", MODE_PRIVATE).edit();
        editor.putBoolean("is_pass", false);
        editor.apply();
    }
}
As you can see, we must remove password when the main activity closed (by override onStop() to guaranteed that the lock activity always shown when app resumed!

Lock activity programmatically code

    In this activity, we must:
  • Handle click event of all buttons in the keypad (to get input code - password)
  • Update dots layout when input code changed
  • Check input code right or wrong. If right, return to the main activity and if wrong, show a Toast to warning.
    And this is it's source code:
LockActivity.java
package info.devexchanges.lockscreen;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.util.List;

import butterknife.BindViews;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class LockActivity extends AppCompatActivity {

    @BindViews({R.id.btn0, R.id.btn1, R.id.btn2, R.id.btn3, R.id.btn4, R.id.btn5, R.id.btn6,
            R.id.btn7, R.id.btn8, R.id.btn9, R.id.btn_clear})
    List<View> btnNumPads;

    @BindViews({R.id.dot_1, R.id.dot_2, R.id.dot_3, R.id.dot_4})
    List<ImageView> dots;

    private static final String TRUE_CODE = "2869";
    private static final int MAX_LENGHT = 4;
    private String codeString = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_lock);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.btn_clear)
    public void onClear() {
        if (codeString.length() > 0) {
            //remove last character of code
            codeString = removeLastChar(codeString);

            //update dots layout
            setDotImagesState();
        }
    }

    @OnClick({R.id.btn0, R.id.btn1, R.id.btn2, R.id.btn3, R.id.btn4, R.id.btn5, R.id.btn6,
            R.id.btn7, R.id.btn8, R.id.btn9})
    public void onClick(Button button) {
        getStringCode(button.getId());
        if (codeString.length() == MAX_LENGHT) {
            if (codeString.equals(TRUE_CODE)) {
                Toast.makeText(this, "Code is right", Toast.LENGTH_SHORT).show();
                setIsPass();
                finish();
            } else {
                Toast.makeText(this, "Wrong Pass code", Toast.LENGTH_SHORT).show();
                //vibrate the dots layout
                shakeAnimation();
            }
        } else if (codeString.length() > MAX_LENGHT){
            //reset the input code
            codeString = "";
            getStringCode(button.getId());
        }
        setDotImagesState();
    }

    private void shakeAnimation() {
        Animation shake = AnimationUtils.loadAnimation(this, R.anim.vibrate_anim);
        findViewById(R.id.dot_layout).startAnimation(shake);
        Toast.makeText(this, "Wrong Password", Toast.LENGTH_SHORT).show();
    }

    private void getStringCode(int buttonId) {
        switch (buttonId) {
            case R.id.btn0:
                codeString += "0";
                break;
            case R.id.btn1:
                codeString += "1";
                break;
            case R.id.btn2:
                codeString += "2";
                break;
            case R.id.btn3:
                codeString += "3";
                break;
            case R.id.btn4:
                codeString += "4";
                break;
            case R.id.btn5:
                codeString += "5";
                break;
            case R.id.btn6:
                codeString += "6";
                break;
            case R.id.btn7:
                codeString += "7";
                break;
            case R.id.btn8:
                codeString += "8";
                break;
            case R.id.btn9:
                codeString += "9";
                break;
            default:
                break;
        }
    }

    private void setDotImagesState() {
        for (int i = 0; i < codeString.length(); i++) {
            dots.get(i).setImageResource(R.drawable.dot_enable);
        }
        if (codeString.length()<4) {
            for (int j = codeString.length(); j<4; j++) {
                dots.get(j).setImageResource(R.drawable.dot_disable);
            }
        }
    }

    private String removeLastChar(String s) {
        if (s == null || s.length() == 0) {
            return s;
        }
        return s.substring(0, s.length() - 1);
    }

    private void setIsPass() {
        SharedPreferences.Editor editor = getSharedPreferences("PASS_CODE", MODE_PRIVATE).edit();
        editor.putBoolean("is_pass", true);
        editor.apply();
    }
}
    This is output when app running:

Shaking animation

    As you see at the output video, when user type a wrong code, the dots layout will be shake. This is xml file to make this animation:
res\anim\shake_anim.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration="70"
        android:fromDegrees="-5"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="5"
        android:repeatMode="reverse"
        android:interpolator="@android:anim/linear_interpolator"
        android:toDegrees="5" />
    <translate
        android:fromXDelta="-10"
        android:toXDelta="10"
        android:repeatCount="5"
        android:repeatMode="reverse"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="70" />
</set>
    And this is output:

    You can make other better animation than this by research more about animation in Android!
     In this project, I use ButterKnife to make finViewbyId work become easier! In order to use this library, please add this dependency to your app-level build.gradle:
compile 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'

Conclusions

    Make a lock screen inside your own application is a not hard work, you can design it better and should pay attention to developing animation and further, vibrating device when user type a wrong code! The fact that there are many third-party libraries which can help you make this layout without write XML codes yourself. Please read my previous post to learn about using one of them. Finally, you can get full project code on Github.

Android RecyclerView dynamically load more items when scroll to end with bottom ProgressBar

Android RecyclerView dynamically load more items when scroll to end with bottom ProgressBar

    Load more data when scrolling to bottom of list view/table view is one the most popular design style which available in a lot of application, for example: Facebook, Google+,... By my previous post, you've learn the way to do this trick with ListView. As you are already known, RecyclerView is a new component introduced in Android Lollipop, this component increases performances respect to ListView. Moreover, respect to ListView, RecyclerView is much more customizable.
    Today, with this post, I would like to talk about making an endless RecyclerView and when data is loading, it will show a ProgressBar at the bottom.

Adding dependencies to project

    The first work after creating a new Android Studio project is adding RecyclerView denpendency to your app level build.gradle. In this sample project, I set each RecyclerView item as a CardView, so you must add it's dependency, too:
dependencies {
    compile 'com.android.support:appcompat-v7:25.1.0'
    compile 'com.android.support:recyclerview-v7:25.1.0'
    compile 'com.android.support:cardview-v7:25.1.0'
}

Create layout files

    Now we must provide some necessary XML files to define layouts. Because of we will set a ProgressBar at the bottom of RecyclerView when data is loading, it will have two item types. The normal item that to show info of user and loading item that place at bottom to show progress bar.
    Layout for the normal item:
item_recycler_view_row.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    card_view:cardUseCompatPadding="true">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="?android:selectableItemBackground"
        android:padding="10dp">

        <TextView
            android:id="@+id/txt_email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/black"
            android:textSize="16sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/txt_phone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/txt_email"
            android:textColor="@android:color/black"
            android:textSize="12sp" />
    </RelativeLayout>
</android.support.v7.widget.CardView>
    Layout for the loading item:
item_loading.xml
<?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="wrap_content"
    android:orientation="vertical">

    <ProgressBar
        android:id="@+id/progressBar1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />

</LinearLayout>
    And you must put a RecyclerView object to the main activity:
activity_main.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="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

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

Create a POJO class

    We've just create all necessary layouts for this project, now please move to programmatically code. Firstly, provide a POJO class (model object) simply like this:
Contact.java
package info.devexchanges.endlessrecyclerview;

public class Contact {
    private String email;
    private String phone;

    public String getEmail() {
        return email;
    }

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

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

Define an interface for callbacks

    Now, make an interface named OnLoadMoreListener with an abstract method onLoadMore() which will be invoked when we scroll the RecyclerView to end:
OnLoadMoreListener.java
package info.devexchanges.endlessrecyclerview;

public interface OnLoadMoreListener {
    void onLoadMore();
}

Configuration in RecyclerView adapter

    In this RecyclerView adapter (I named as ContactAdapter), we have 2 item types then must create two ViewHolder like below:
// "Loading item" ViewHolder
private class LoadingViewHolder extends RecyclerView.ViewHolder {
        public ProgressBar progressBar;

        public LoadingViewHolder(View view) {
            super(view);
            progressBar = (ProgressBar) view.findViewById(R.id.progressBar1);
        }
    }

// "Normal item" ViewHolder
private class UserViewHolder extends RecyclerView.ViewHolder {
        public TextView phone;
        public TextView email;

        public UserViewHolder(View view) {
            super(view);
            phone = (TextView) view.findViewById(R.id.txt_phone);
            email = (TextView) view.findViewById(R.id.txt_email);
        }
    }
In this adapter, declare two constants that is delegate for two item type of RecyclerView:
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_LOADING = 1;
Provide an OnLoadMoreListener variable and set an "add method":
private OnLoadMoreListener onLoadMoreListener;
public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {
        this.onLoadMoreListener = mOnLoadMoreListener;
    }
In the constructor of this adapter class, we handle scroll event of the RecyclerView here. This is the most important step, firstly, you must get LayoutManager of RecyclerView, detecting scroll to bottom in onScroll():
    private boolean isLoading;
    private Activity activity;
    private List<Contact> contacts;
    private int visibleThreshold = 5;
    private int lastVisibleItem, totalItemCount;

    public ContactAdapter(RecyclerView recyclerView, List<Contact> contacts, Activity activity) {
        this.contacts = contacts;
        this.activity = activity;

        final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                totalItemCount = linearLayoutManager.getItemCount();
                lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
                if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
                    if (onLoadMoreListener != null) {
                        onLoadMoreListener.onLoadMore();
                    }
                    isLoading = true;
                }
            }
        });
    }
Add some necessary methods to complete ContactAdapter:
    @Override
    public int getItemViewType(int position) {
        return contacts.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == VIEW_TYPE_ITEM) {
            View view = LayoutInflater.from(activity).inflate(R.layout.item_recycler_view_row, parent, false);
            return new UserViewHolder(view);
        } else if (viewType == VIEW_TYPE_LOADING) {
            View view = LayoutInflater.from(activity).inflate(R.layout.item_loading, parent, false);
            return new LoadingViewHolder(view);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof UserViewHolder) {
            Contact contact = contacts.get(position);
            UserViewHolder userViewHolder = (UserViewHolder) holder;
            userViewHolder.phone.setText(contact.getEmail());
            userViewHolder.email.setText(contact.getPhone());
        } else if (holder instanceof LoadingViewHolder) {
            LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
            loadingViewHolder.progressBar.setIndeterminate(true);
        }
    }

    @Override
    public int getItemCount() {
        return contacts == null ? 0 : contacts.size();
    }

    public void setLoaded() {
        isLoading = false;
    }

Activity programmatically code

    In onCreate() method of activity, we must call setOnLoadMoreListener() and get new data inside onLoadMore(). This is full code of the activity:
MainActivity.java
package info.devexchanges.endlessrecyclerview;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class MainActivity extends AppCompatActivity {

    private List<Contact> contacts;
    private ContactAdapter contactAdapter;
    private Random random;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        contacts = new ArrayList<>();
        random = new Random();

        //set dummy data
        for (int i = 0; i < 10; i++) {
            Contact contact = new Contact();
            contact.setPhone(phoneNumberGenerating());
            contact.setEmail("DevExchanges" + i + "@gmail.com");
            contacts.add(contact);
        }

        //find view by id and attaching adapter for the RecyclerView
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        contactAdapter = new ContactAdapter(recyclerView, contacts, this);
        recyclerView.setAdapter(contactAdapter);

        //set load more listener for the RecyclerView adapter
        contactAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore() {
                if (contacts.size() <= 20) {
                    contacts.add(null);
                    contactAdapter.notifyItemInserted(contacts.size() - 1);
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            contacts.remove(contacts.size() - 1);
                            contactAdapter.notifyItemRemoved(contacts.size());

                            //Generating more data
                            int index = contacts.size();
                            int end = index + 10;
                            for (int i = index; i < end; i++) {
                                Contact contact = new Contact();
                                contact.setPhone(phoneNumberGenerating());
                                contact.setEmail("DevExchanges" + i + "@gmail.com");
                                contacts.add(contact);
                            }
                            contactAdapter.notifyDataSetChanged();
                            contactAdapter.setLoaded();
                        }
                    }, 5000);
                } else {
                    Toast.makeText(MainActivity.this, "Loading data completed", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    private String phoneNumberGenerating() {
        int low = 100000000;
        int high = 999999999;
        int randomNumber = random.nextInt(high - low) + low;

        return "0" + randomNumber;
    }
}

Running application

    And this is output for our code:

Conclusions

    Over here, I've just present the way to put a progress bar at the bottom of RecyclerView and loading more data when scroll to end. This design is very popular in mobile application development, so I hope this post is helpful with your own work. Further, you can visit my previous post to learn solution with ListView or read related tutorial posts on other site like:
Android (Home screen) widget - Part 4: Update widget via Service

Android (Home screen) widget - Part 4: Update widget via Service

    The fact that, most of widgets that you can see on your device are auto-update information, for example: weather widget, location widget,... Service is the Context which is used here to change the widget data. With auto update feature, your widget become lively and useful!

    Today, by this post, I would like to present an updating widget, which displaying current time in HH:mm format.

Creating a Service class

    We'll get current time in the device in this class (which a subclass of Service). Override onStartCommand() method like this:
UpdateService.java
package info.devexchanges.updatewidget;

import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.widget.RemoteViews;

import java.util.Calendar;

public class UpdateService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        String time = getCurrentDateTime();

        RemoteViews view = new RemoteViews(getPackageName(), R.layout.widget_updating);
        view.setTextViewText(R.id.txt_widget, time);
        ComponentName theWidget = new ComponentName(this, UpdatingWidget.class);
        AppWidgetManager manager = AppWidgetManager.getInstance(this);
        manager.updateAppWidget(theWidget, view);

        return super.onStartCommand(intent, flags, startId);
    }

    private String getCurrentDateTime() {
        Calendar c = Calendar.getInstance();
        int minute = c.get(Calendar.MINUTE);
        int hour = c.get(Calendar.HOUR_OF_DAY);

        return hour + ":" + minute;
    }
}
    As you can see at the code above, remember update data (current time) to a TextView in your widget after get time here!

Widget XML files

    Like another widgets at previous parts, your must create widget layout and property files (in XML).
    The layout for this widget:
layout\widget_updating.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/txt_widget"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_margin="8dp"
        android:background="#09C"
        android:text="@string/appwidget_text"
        android:textColor="#ffffff"
        android:textSize="24sp"
        android:textStyle="bold|italic"/>

</RelativeLayout>
    And define the AppWidgetProviderInfo object in an XML like this:
xml\updating_widget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget_updating"
    android:minHeight="80dp"
    android:minWidth="80dp"
    android:previewImage="@mipmap/ic_launcher"
    android:resizeMode="vertical"
    android:updatePeriodMillis="0">
</appwidget-provider>

AppWidgetProvider class

    Service does not start by itself. We need to start the service (in every minute for this example) in the AppWidgetProvider.
    But why we do not just use updatePeriodMillis atribute? The official document say that:
If the device is asleep when it is time for an update (as defined by updatePeriodMillis), then the device will wake up in order to perform the update. If you don't update more than once per hour, this probably won't cause significant problems for the battery life. If, however, you need to update more frequently and/or you do not need to update while the device is asleep, then you can instead perform updates based on an alarm that will not wake the device. To do so, set an alarm with an Intent that your AppWidgetProvider receives, using the AlarmManager. Set the alarm type to either ELAPSED_REALTIME or RTC, which will only deliver the alarm when the device is awake. Then set updatePeriodMillis to zero ("0").
    In previous examples, we used getActivity() and getBroadcast() methods. This time we are going to use getService():
UpdatingWidget.java
package info.devexchanges.updatewidget;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;

public class UpdatingWidget extends AppWidgetProvider {
    private PendingIntent pendingIntent;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        final Intent i = new Intent(context, UpdateService.class);

        if (pendingIntent == null) {
            pendingIntent = PendingIntent.getService(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
        }
        manager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), 60000, pendingIntent);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
    }
}
    The minimum interval time is 60000 milliseconds for AlarmManager. If you need to call your service less than 60 second with an alarm manager, please read this discussion on StackOverflow. But you should pay attention: this action drains the battery and makes users delete your app.

Running application

    The widget will be installed after you run the application. Drag it to Home screen and you'll have this result:

    I cropped the video to not keep you wait 4 minutes.

Conclusions

    Through 4 parts in this series, I hope you now have general knowledge about developing a widget in Android - which can be understand as an extension of the application. For further reading, check these helpful document and tutorial:
Android (Home screen) widget - Part 3: Configurable widget

Android (Home screen) widget - Part 3: Configurable widget

    In Part 2, I had presented one more example of widget: Broadcast Widget (which can update it's interface when clicked). Today, I would like to talk about a widget type that can be configurable at creation, this mean when you drag it to Home screen to use, a "configuration Activity" will be launched and you will perform a setting for your widget.

   In this sample project, we will allow users to choose a link and whenever it’s clicked we open this link on the browser.

Create layout for configuration Activity

    The most important work of creating this type of widget is developing the configuration Activity. Firstly, define a simple layout includes a Spinner to allow user selected one value from a set and a one value from a Button to confirm this work:
activity_config.xml
<?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:padding="@dimen/activity_horizontal_margin">

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn_go"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:text="@string/add" />
</LinearLayout>

Activity programmatically code

    Now, looking at this source code of ConfigActivity:
ConfigActivity.java
package info.devexchanges.configurablewidget;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.RemoteViews;
import android.widget.Spinner;

import java.util.ArrayList;

public class ConfigActivity extends AppCompatActivity {

    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    private AppWidgetManager widgetManager;
    private RemoteViews remoteViews;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setResult(RESULT_CANCELED);

        setContentView(R.layout.activity_config);

        final Spinner spinner = (Spinner)findViewById(R.id.spinner);
        View btnCreate = findViewById(R.id.btn_go);

        //create data
        ArrayList<String> spnOptions = new ArrayList<>();
        spnOptions.add("Go to my site");
        spnOptions.add("Go to Google page");

        //set adapter for the spinner
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, spnOptions);
        spinner.setAdapter(adapter);

        //initializing RemoteViews and AppWidgetManager
        widgetManager = AppWidgetManager.getInstance(this);
        remoteViews = new RemoteViews(this.getPackageName(), R.layout.widget_configurable);

        // Find the widget id from the intent
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        }
        if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            finish();
            return;
        }
        btnCreate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String selectedUrl;
                if (spinner.getSelectedItemPosition() == 0) {
                    // Go to my website with this selection (position = 1)
                    selectedUrl = "http://www.devexchanges.info";
                } else {
                    selectedUrl = "https://www.google.com";
                }
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(selectedUrl));
                PendingIntent pending = PendingIntent.getActivity(ConfigActivity.this, 0, intent, 0);
                remoteViews.setOnClickPendingIntent(R.id.text_view, pending);
                if (spinner.getSelectedItemPosition() == 0) {
                    remoteViews.setTextViewText(R.id.text_view, "Click to visit my site");
                } else {
                    remoteViews.setTextViewText(R.id.text_view, "Click to visit Google");
                }
                widgetManager.updateAppWidget(mAppWidgetId, remoteViews);
                Intent resultValue = new Intent();

                // Set the results as expected from a 'configure activity'.
                resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
                setResult(RESULT_OK, resultValue);
                finish();
            }
        });
    }
}
    In onCreate() method, the first thing we do is setting setResult(RESULT_CANCELED). Why? Android triggers the configuration Activity that belongs to your widget and awaits result data. If the user did not configure as we expected, let’s say she pressed back button without entering a data, we don’t need to create a widget.
    At the Spinner, we set data to make 2 options for user to choose (go to DevExchanges home page or Google), after click the Button, we update TextView content on the widget and set the Set the RemoteViews to based on appWidgetIds.

Modifying the widget xml

    The last thing we do is modify the XML of the widget. With that modification, Android OS will know this widget has a configuration Activity. So before creating the widget, it will trigger the Activity:
res\xml\configurable_widget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:configure="info.devexchanges.configurablewidget.ConfigActivity"
    android:initialLayout="@layout/widget_configurable"
    android:minHeight="40dp"
    android:minWidth="40dp"
    android:previewImage="@mipmap/ic_launcher"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="86400000" />
    As you notice we didn’t talk about widget class yet. We do not need to add any code for widget class because all actions done by ConfigActivity. But we have to create it anyway:
ConfigurableWidget.java
package info.devexchanges.configurablewidget;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;

public class ConfigurableWidget extends AppWidgetProvider {
    
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

    }
}
    And this is the layout file for our widget:
res\layout\widget_configurable.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:padding="8dp"
        android:background="@color/colorPrimary"
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_gravity="center"
        android:textColor="@android:color/white"
        android:textStyle="bold" />

</RelativeLayout>

Running the application

    Before launching the app, make sure you add ConfigurableWidget as a receiver to your AndroidManifest.xml like other previous examples:
<application
        android:allowBackup="true"
        ....>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name=".ConfigurableWidget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/configurable_widget_info" />
        </receiver>

        <activity android:name=".ConfigActivity">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>
    </application>

    After app installed, move to "WIDGETS" tab, you will see it:
    And when you drag it to Home page to use, the configuration activity will be launched:
    When click on it:

     If you select other options at the configuration activity,  the widget text will be different:

Conclusions

    I've just provided one more example about Android widget, hope you can understand the way to configure widget before using it. Up to next part, I will talk about updating widget via a Service - the most popular and important feature of this topic! Coming soon!