[Android] Hướng dẫn thêm hiểu ứng tải dữ liệu Shimmer vào ứng dụng Android

Đăng bởi: Admin | Lượt xem: 1076 | Chuyên mục: Android

Shimmer là một thư viện do Facebook phát triển, giúp chúng ta thêm hiệu ứng tải dữ liệu cho ứng dụng Android một cách dễ dàng thay vì những màn hình với progress bar nhàm chán. Trong bài viết hôm nay, chúng ta sẽ cùng tìm hiểu cách cài đặt và sử dụng thư viện này nhé!


1. Thư viện Shimmer

Để có được hiệu ứng này trên bất kỳ layout nào, bạn phải đặt layout bên trong ShimmerFrameLayout. Để sử dụng animation này, bạn phải gọi startShimmerAnimation() trên ShimmerFrameLayout

Dưới đây là đoạn mã để có được hiệu ứng Shimmer. Đầu tiên, đặt bố cục của bạn bên trong ShimmerFrameLayout.

<com.facebook.shimmer.ShimmerFrameLayout
     android:id=“@+id/shimmer_view_container”
     android:layout_width=“wrap_content”
     android:layout_height="wrap_content"
     shimmer:duration="1000">
 
     <View
        android:layout_width="100dp"
        android:layout_height="8dp"
        android:background="#dddddd" />
 
</com.facebook.shimmer.ShimmerFrameLayout>

Để bắt đầu hiệu ứng, gọi startShimmerAnimation() từ activity của bạn.

2. Dữ liệu JSON

 Mẫu JSON có chứa một danh sách các công thức nấu ăn:

[{
    "id": 1,
    "name": "Salmon Teriyaki",
    "description": "Put the ginger and garlic into a bowl and mix with the soy sauce, maple syrup, mirin and a drizzle of olive oil",
    "price": 140,
    "chef": "Gordon Ramsay",
    "thumbnail": "https://api.androidhive.info/images/food/1.jpg",
    "timestamp": "2 min ago"
}, {
    "id": 2,
    "name": "Grilled Mushroom",
    "description": "Combine butter, dill and garlic salt, brush over mushrooms.",
    "price": 150,
    "chef": "Ravi Tamada",
    "thumbnail": "https://api.androidhive.info/images/food/2.jpg",
    "timestamp": "5 min ago"
}
]

3. Ví dụ

Bước 1

Tạo project mới bằng cách: File -> New Project và lựa chọn Basic Activity từ template

Bước 2

Thêm các dependency vào build.gradle và rebuild lại project

build.gradle
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
 
    // Shimmer
    implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'

    // RecyclerView
    implementation 'com.android.support:recyclerview-v7:26.1.0'
 
    // glide image library
    implementation 'com.github.bumptech.glide:glide:3.7.0'
 
    // volley http library
    implementation 'com.android.volley:volley:1.0.0'
    implementation 'com.google.code.gson:gson:2.6.2'
}

Bước 3

Thêm đoạn màu, dimen như ở dưới

colors.xml

<!--?xml version="1.0" encoding="utf-8"?-->
<resources>
    <color name="colorPrimary">#d91248</color>
    <color name="colorPrimaryDark">#d91248</color>
    <color name="colorAccent">#3ad23e</color>
    <color name="placeholder_bg">#dddddd</color>
    <color name="item_name">#0c0c0c</color>
    <color name="description">#1a1a1a</color>
    <color name="chef">#777</color>
    <color name="timestamp">#777</color>
</resources>

dimens.xml

<!--?xml version="1.0" encoding="utf-8"?-->
<resources>
    <dimen name="activity_padding">16dp</dimen>
    <dimen name="placeholder_image">50dp</dimen>
    <dimen name="placeholder_text_height">8dp</dimen>
    <dimen name="activity_padding_horizontal">16dp</dimen>
    <dimen name="padding_10">10dp</dimen>
    <dimen name="name">15dp</dimen>
    <dimen name="chef">12dp</dimen>
    <dimen name="timestamp">11dp</dimen>
    <dimen name="description">15dp</dimen>
    <dimen name="price">13dp</dimen>
</resources>

Bước 4

Tạo mới layout với tên là: recipe_placeholder_item.xml. Trong file này, thiết kế giao diện tương tự như item thực tế.

<?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"
    android:padding="@dimen/activity_padding">
 
    <View
        android:id="@+id/thumbnail"
        android:layout_width="@dimen/placeholder_image"
        android:layout_height="@dimen/placeholder_image"
        android:layout_marginRight="@dimen/activity_padding"
        android:background="@color/placeholder_bg" />
 
    <View
        android:id="@+id/name"
        android:layout_width="150dp"
        android:layout_height="10dp"
        android:layout_marginBottom="10dp"
        android:layout_toRightOf="@id/thumbnail"
        android:background="@color/placeholder_bg" />
 
    <View
        android:layout_width="100dp"
        android:layout_height="@dimen/placeholder_text_height"
        android:layout_below="@id/name"
        android:layout_toRightOf="@id/thumbnail"
        android:background="@color/placeholder_bg" />
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/thumbnail"
        android:layout_marginBottom="40dp"
        android:layout_marginTop="20dp"
        android:orientation="vertical">
 
        <View
            android:layout_width="match_parent"
            android:layout_height="@dimen/placeholder_text_height"
            android:layout_marginRight="100dp"
            android:background="@color/placeholder_bg" />
 
        <View
            android:layout_width="match_parent"
            android:layout_height="@dimen/placeholder_text_height"
            android:layout_marginRight="50dp"
            android:layout_marginTop="10dp"
            android:background="@color/placeholder_bg" />
 
        <View
            android:layout_width="match_parent"
            android:layout_height="@dimen/placeholder_text_height"
            android:layout_marginRight="160dp"
            android:layout_marginTop="10dp"
            android:background="@color/placeholder_bg" />
 
    </LinearLayout>
 
</RelativeLayout>

Bước 5

Thêm placeholder vào giao diện chính

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:shimmer="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    tools:context="info.androidhive.shimmer.MainActivity">
 
    <com.facebook.shimmer.ShimmerFrameLayout
        android:id="@+id/shimmer_view_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="vertical"
        shimmer:duration="800">
 
        <!-- Adding 3 rows of placeholders -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
 
            <include layout="@layout/recipe_placeholder_item" />
 
            <include layout="@layout/recipe_placeholder_item" />
 
            <include layout="@layout/recipe_placeholder_item" />
 
        </LinearLayout>
 
    </com.facebook.shimmer.ShimmerFrameLayout>
 
</android.support.constraint.ConstraintLayout>

Bước 6

Mở MainActivity.java và bắt đầu animation Shimmer bằng cách gọi phương thức startShimmerAnimation() trong onResume(), tạm dừng animation ở onPause() 

MainActivity.java

import com.facebook.shimmer.ShimmerFrameLayout;
 
public class MainActivity extends AppCompatActivity {
 
    private ShimmerFrameLayout mShimmerViewContainer;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        mShimmerViewContainer = findViewById(R.id.shimmer_view_container);
    }
 
    @Override
    public void onResume() {
        super.onResume();
        mShimmerViewContainer.startShimmerAnimation();
    }
 
    @Override
    public void onPause() {
        mShimmerViewContainer.stopShimmerAnimation();
        super.onPause();
    }
}

Chạy thử ta được kết quả như sau:

Bước 7

Tạo 1 class với tên là MyApplication.java kế thừa từ class Application. Đây là 1 class singleton, trong đó thư viện Volley sẽ đc khởi tạo

MyApplication.java

package info.androidhive.shimmer;
 
/**
 * Created by ravi on 18/01/18.
 */
 
import android.app.Application;
import android.text.TextUtils;
 
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
 
public class MyApplication extends Application {
 
    public static final String TAG = MyApplication.class
            .getSimpleName();
 
    private RequestQueue mRequestQueue;
 
    private static MyApplication mInstance;
 
    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }
 
    public static synchronized MyApplication getInstance() {
        return mInstance;
    }
 
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }
 
        return mRequestQueue;
    }
 
    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }
 
    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }
 
    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

Bước 8

Mở AndroidManifest.xml và thêm class MyApplication vào thẻ <application> và thêm permission INTERNET

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.shimmer">
 
    <uses-permission android:name="android.permission.INTERNET"/>
 
    <!-- add .MyApplication class -->
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:name=".MyApplication"
        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>

Bước 9

Thêm RecyclerView vào dưới ShimmerFrameLayout.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:shimmer="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    tools:context="info.androidhive.shimmer.MainActivity">
 
    <com.facebook.shimmer.ShimmerFrameLayout
        android:id="@+id/shimmer_view_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="vertical"
        shimmer:duration="800">
 
        <!-- Adding 3 rows of placeholders -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
 
            <include layout="@layout/layout_placeholder_row" />
 
            <include layout="@layout/layout_placeholder_row" />
 
            <include layout="@layout/layout_placeholder_row" />
 
        </LinearLayout>
 
    </com.facebook.shimmer.ShimmerFrameLayout>
 
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical" />
 
</android.support.constraint.ConstraintLayout>

Bước 10

Tạo 1 class với tên Recipe.java là 1 class POJO được sử dụng để tuần tự hóa JSON.

Recipe.java

package info.androidhive.shimmer;
 
public class Recipe {
    int id;
    String name;
    String description;
    double price;
    String thumbnail;
    String chef;
    String timestamp;
 
    public Recipe() {
    }
 
    public int getId() {
        return id;
    }
 
    public String getName() {
        return name;
    }
 
    public String getDescription() {
        return description;
    }
 
    public double getPrice() {
        return price;
    }
 
    public String getThumbnail() {
        return thumbnail;
    }
 
    public String getChef() {
        return chef;
    }
 
    public String getTimestamp() {
        return timestamp;
    }
}

Bước 11

Tạo một layout item

recipe_list_item.xml

recipe_list_item.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"
    android:background="@android:color/white"
    android:clickable="true"
    android:foreground="?attr/selectableItemBackground"
    android:padding="@dimen/activity_padding_horizontal">
 
    <ImageView
        android:id="@+id/thumbnail"
        android:layout_width="@dimen/placeholder_image"
        android:layout_height="@dimen/placeholder_image"
        android:layout_marginRight="@dimen/padding_10"
        android:scaleType="centerCrop" />
 
    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/thumbnail"
        android:ellipsize="end"
        android:fontFamily="sans-serif-medium"
        android:maxLines="1"
        android:textColor="@color/item_name"
        android:textSize="@dimen/name" />
 
    <TextView
        android:id="@+id/chef"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/name"
        android:layout_toRightOf="@id/thumbnail"
        android:maxLines="1"
        android:textColor="@color/chef"
        android:textSize="@dimen/chef" />
 
    <TextView
        android:id="@+id/timestamp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/chef"
        android:layout_toRightOf="@id/thumbnail"
        android:maxLines="1"
        android:text="2 min ago"
        android:textColor="@color/timestamp"
        android:textSize="@dimen/timestamp" />
 
    <TextView
        android:id="@+id/description"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/thumbnail"
        android:layout_marginTop="@dimen/activity_padding_horizontal"
        android:ellipsize="end"
        android:maxLines="3"
        android:textColor="@color/description"
        android:textSize="@dimen/description" />
 
    <TextView
        android:id="@+id/price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/description"
        android:layout_marginTop="@dimen/padding_10"
        android:textColor="@color/colorPrimary"
        android:textSize="@dimen/price"
        android:textStyle="bold" />
 
</RelativeLayout>

Bước 12

Tạo adapter

RecipeListAdapter.java

package info.androidhive.shimmer;
 
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
 
import com.bumptech.glide.Glide;
 
import java.util.List;
 
public class RecipeListAdapter extends RecyclerView.Adapter<RecipeListAdapter.MyViewHolder> {
    private Context context;
    private List<Recipe> cartList;
 
    public class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView name, description, price, chef, timestamp;
        public ImageView thumbnail;
 
        public MyViewHolder(View view) {
            super(view);
            name = view.findViewById(R.id.name);
            chef = view.findViewById(R.id.chef);
            description = view.findViewById(R.id.description);
            price = view.findViewById(R.id.price);
            thumbnail = view.findViewById(R.id.thumbnail);
            timestamp = view.findViewById(R.id.timestamp);
        }
    }
 
 
    public RecipeListAdapter(Context context, List<Recipe> cartList) {
        this.context = context;
        this.cartList = cartList;
    }
 
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recipe_list_item, parent, false);
 
        return new MyViewHolder(itemView);
    }
 
    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        final Recipe recipe = cartList.get(position);
        holder.name.setText(recipe.getName());
        holder.chef.setText("By " + recipe.getChef());
        holder.description.setText(recipe.getDescription());
        holder.price.setText("Price: ₹" + recipe.getPrice());
        holder.timestamp.setText(recipe.getTimestamp());
 
        Glide.with(context)
                .load(recipe.getThumbnail())
                .into(holder.thumbnail);
    }
    // recipe
    @Override
    public int getItemCount() {
        return cartList.size();
    }
}

Bước 13

Chỉnh sửa MainActivity.java

package info.androidhive.shimmer;
 
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
 
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.facebook.shimmer.ShimmerFrameLayout;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
 
import org.json.JSONArray;
 
import java.util.ArrayList;
import java.util.List;
 
public class MainActivity extends AppCompatActivity {
 
    private static final String TAG = MainActivity.class.getSimpleName();
    private RecyclerView recyclerView;
    private List<Recipe> cartList;
    private RecipeListAdapter mAdapter;
 
    private ShimmerFrameLayout mShimmerViewContainer;
 
    // URL to fetch menu json
    // this endpoint takes 2 sec before giving the response to add
    // some delay to test the Shimmer effect
    private static final String URL = "https://api.androidhive.info/json/shimmer/menu.php";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        mShimmerViewContainer = findViewById(R.id.shimmer_view_container);
 
        recyclerView = findViewById(R.id.recycler_view);
        cartList = new ArrayList<>();
        mAdapter = new RecipeListAdapter(this, cartList);
 
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.addItemDecoration(new MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16));
        recyclerView.setAdapter(mAdapter);
 
        // making http call and fetching menu json
        fetchRecipes();
    }
 
    /**
     * method make volley network call and parses json
     */
    private void fetchRecipes() {
        JsonArrayRequest request = new JsonArrayRequest(URL,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        if (response == null) {
                            Toast.makeText(getApplicationContext(), "Couldn't fetch the menu! Pleas try again.", Toast.LENGTH_LONG).show();
                            return;
                        }
 
                        List<Recipe> recipes = new Gson().fromJson(response.toString(), new TypeToken<List<Recipe>>() {
                        }.getType());
 
                        // adding recipes to cart list
                        cartList.clear();
                        cartList.addAll(recipes);
 
                        // refreshing recycler view
                        mAdapter.notifyDataSetChanged();
 
                        // stop animating Shimmer and hide the layout
                        mShimmerViewContainer.stopShimmerAnimation();
                        mShimmerViewContainer.setVisibility(View.GONE);
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // error in getting json
                Log.e(TAG, "Error: " + error.getMessage());
                Toast.makeText(getApplicationContext(), "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
 
        MyApplication.getInstance().addToRequestQueue(request);
    }
 
    @Override
    public void onResume() {
        super.onResume();
        mShimmerViewContainer.startShimmerAnimation();
    }
 
    @Override
    public void onPause() {
        mShimmerViewContainer.stopShimmerAnimation();
        super.onPause();
    }
}

Trong đó: 

  • fetchRecipes() tìm nạp JSON bằng cách thực hiện cuộc gọi http của Volley. JSON được phân tích cú pháp bằng cách sử dụng serializer Gson.
  • Một khi JSON phân tích cú pháp và thêm vào bộ điều hợp RecyclerView, danh sách sẽ được hiển thị và ShimmerFrameLayout bị giấu làm cho danh sách thực tế hiển thị trên màn hình

Kết luận

Mong rằng qua bài viết các bạn đã biết cách cài đặt và sử dụng thư viện Shimmer và có thể áp dụng vào các dự án trong tương lai.

Cảm ơn các bạn đã đọc bài viết.

Chào thân ái và quyết thắng!

vncoder logo

Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!



Khóa học liên quan

Khóa học: Android

Học Kotlin cơ bản
Số bài học:
Lượt xem: 17137
Đăng bởi: Admin
Chuyên mục: Android

Học lập trình Flutter cơ bản
Số bài học:
Lượt xem: 55457
Đăng bởi: Admin
Chuyên mục: Android

Lập trình Android cơ bản
Số bài học:
Lượt xem: 22283
Đăng bởi: Admin
Chuyên mục: Android