Viết Một Ứng dụng Gallery Cho Android Với Glide
Vietnamese (Tiếng Việt) translation by Dai Phong (you can also view the original English article)



1. Glide Là Gì?
Glide là một thư viện mã nguồn mở phổ biến cho Android, dùng để tải hình ảnh, video và GIF động. Với Glide bạn có thể tải và hiển thị media từ nhiều nguồn khác nhau, chẳng hạn như máy chủ từ xa hoặc hệ thống tập tin cục bộ.
Mặc định, Glide sử dụng một cài đặt tùy biến của HttpURLConnection để tải hình ảnh thông qua internet. Tuy nhiên, Glide cũng cung cấp các plugin cho các thư viện kết nối mạng phổ biến khác như Volley hay OkHttp.
2. Vậy, Tại sao Sử dụng Glide?
Phát triển tính năng tải và hiển thị media của bạn trong Java có thể là một khó khăn thật sự: bạn phải lo về bộ nhớ đệm, giải mã, quản lý kết nối mạng, luồng, xử lý ngoại lệ và vân vân. Glide là một thư viện dễ sử dụng, được cân nhắc kỹ lưỡng, có tài liệu hướng dẫn đầy đủ, và được kiểm thử một cách kỹ lưỡng, có thể giúp bạn tiết kiệm rất nhiều thời gian quý báu và bạn khỏi phải nhức ốc để tự mình làm.
Trong hướng dẫn này, chúng ta sẽ tìm hiểu về Glide 3 bằng cách xây dựng một ứng dụng gallery (thư viện hình ảnh) đơn giản. Nó sẽ tải hình ảnh thông qua Internet và hiển thị chúng dưới dạng hình thumbnail (thu nhỏ) trong RecyclerView và khi người dùng nhấp vào bất kỳ hình ảnh nào, nó sẽ mở ra một activity chi tiết có chứa hình ảnh lớn hơn.
3. Tạo Một Dự án Studio Android
Khởi động Studio Android của bạn và tạo một dự án mới với activity rỗng gọi là MainActivity
.
4. Khai báo Các Phụ thuộc
Sau khi tạo một dự án mới, chỉ định các phụ thuộc sau đây trong build.gradle
của bạn.
repositories { mavenCentral() // jcenter() works as well because it pulls from Maven Central } dependencies { // Glide compile 'com.github.bumptech.glide:glide:3.7.0' // Recyclerview compile 'com.android.support:recyclerview-v7:25.1.1' }
Hoặc với Maven:
<dependency> <groupId>com.github.bumptech.glide</groupId> <artifactId>glide</artifactId> <version>3.7.0</version> </dependency> <dependency> <groupId>com.google.android</groupId> <artifactId>support-v4</artifactId> <version>r7</version> </dependency>
Hãy đảm bảo rằng bạn đồng bộ dự án của bạn sau khi bổ sung các phụ thuộc cho Glide.
Thư viện Tích hợp
Nếu bạn muốn sử dụng một thư viện kết nối mạng như OkHttp hoặc Volley trong dự án của mình để thực hiện các hoạt động kết nối mạng, thì bạn nên chỉ định cụ thể thư viện bạn đang sử dụng cho Glide (thay vì thư viện mặc định là HttpURLConnection).
Volley
dependencies { compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.github.bumptech.glide:volley-integration:1.4.0@aar' compile 'com.mcxiaoke.volley:library:1.0.8' }
OkHttp
dependencies { // okhttp 3 compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar' compile 'com.squareup.okhttp3:okhttp:3.2.0' // okhttp 2 compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar' compile 'com.squareup.okhttp:okhttp:2.2.0' }
Bạn có thể truy cập hướng dẫn chính thức về tích hợp thư viện với Glide để biết thêm thông tin.
5. Thêm Quyền Internet
Vì Glide sẽ thực hiện một yêu cầu kết nối mạng để tải hình ảnh thông qua internet, nên chúng ta cần bao gồm quyền INTERNET
trong AndroidManifest.xml của chúng ta.
<uses-permission android:name="android.permission.INTERNET" />
6. Tạo Layout
Chúng ta sẽ bắt đầu bằng cách tạo ra RecyclerView
của chúng ta.
<?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:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_images" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout>
Tạo Layout Cho Phần tử Tuỳ biến
Tiếp theo, hãy tạo layout XML sẽ được sử dụng cho mỗi phần tử (ImageView
) bên trong RecyclerView
.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_photo" android:adjustViewBounds="true" android:layout_height="200dp" android:scaleType="centerCrop" android:layout_margin="2dp" android:layout_width="match_parent"/> </LinearLayout>
Bây giờ thì chúng ta đã tạo layout, bước tiếp theo là tạo adapter RecyclerView
để nạp dữ liệu. Trước khi chúng ta làm điều đó, hãy tạo ra mô hình dữ liệu đơn giản của chúng ta.
7. Tạo một Mô hình Dữ liệu
Chúng ta sẽ định nghĩa một mô hình dữ liệu đơn giản cho RecyclerView
của chúng ta. Mô hình này cài đặt Parcelable để vận chuyển dữ liệu với hiệu quả cao từ thành phần này sang thành phần khác. Trong trường hợp của chúng ta, dữ liệu sẽ được vận chuyển từ SpaceGalleryActivity
đến SpacePhotoActivity
.
import android.os.Parcel; import android.os.Parcelable; public class SpacePhoto implements Parcelable { private String mUrl; private String mTitle; public SpacePhoto(String url, String title) { mUrl = url; mTitle = title; } protected SpacePhoto(Parcel in) { mUrl = in.readString(); mTitle = in.readString(); } public static final Creator<SpacePhoto> CREATOR = new Creator<SpacePhoto>() { @Override public SpacePhoto createFromParcel(Parcel in) { return new SpacePhoto(in); } @Override public SpacePhoto[] newArray(int size) { return new SpacePhoto[size]; } }; public String getUrl() { return mUrl; } public void setUrl(String url) { mUrl = url; } public String getTitle() { return mTitle; } public void setTitle(String title) { mTitle = title; } public static SpacePhoto[] getSpacePhotos() { return new SpacePhoto[]{ new SpacePhoto("http://i.imgur.com/zuG2bGQ.jpg", "Galaxy"), new SpacePhoto("http://i.imgur.com/ovr0NAF.jpg", "Space Shuttle"), new SpacePhoto("http://i.imgur.com/n6RfJX2.jpg", "Galaxy Orion"), new SpacePhoto("http://i.imgur.com/qpr5LR2.jpg", "Earth"), new SpacePhoto("http://i.imgur.com/pSHXfu5.jpg", "Astronaut"), new SpacePhoto("http://i.imgur.com/3wQcZeY.jpg", "Satellite"), }; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeString(mUrl); parcel.writeString(mTitle); } }
8. Tạo Adapter
Chúng ta sẽ tạo một adapter để đưa dữ liệu vào RecyclerView. Chúng ta cũng sẽ cài đặt một listener để lắng nghe sự kiện nhấp chuột để mở activity chi tiết—SpacePhotoActivity
—truyền vào nó một đối tượng của SpacePhoto
. Activity chi tiết sẽ hiển thị hình ảnh đầy đủ. Chúng ta sẽ tạo nó trong phần sau.
public class MainActivity extends AppCompatActivity { // ... private class ImageGalleryAdapter extends RecyclerView.Adapter<ImageGalleryAdapter.MyViewHolder> { @Override public ImageGalleryAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { Context context = parent.getContext(); LayoutInflater inflater = LayoutInflater.from(context); View photoView = inflater.inflate(R.layout.item_photo, parent, false); ImageGalleryAdapter.MyViewHolder viewHolder = new ImageGalleryAdapter.MyViewHolder(photoView); return viewHolder; } @Override public void onBindViewHolder(ImageGalleryAdapter.MyViewHolder holder, int position) { SpacePhoto spacePhoto = mSpacePhotos[position]; ImageView imageView = holder.mPhotoImageView; } @Override public int getItemCount() { return (mSpacePhotos.length); } public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public ImageView mPhotoImageView; public MyViewHolder(View itemView) { super(itemView); mPhotoImageView = (ImageView) itemView.findViewById(R.id.iv_photo); itemView.setOnClickListener(this); } @Override public void onClick(View view) { int position = getAdapterPosition(); if(position != RecyclerView.NO_POSITION) { SpacePhoto spacePhoto = mSpacePhotos[position]; Intent intent = new Intent(mContext, SpacePhotoActivity.class); intent.putExtra(SpacePhotoActivity.EXTRA_SPACE_PHOTO, spacePhoto); startActivity(intent); } } } private SpacePhoto[] mSpacePhotos; private Context mContext; public ImageGalleryAdapter(Context context, SpacePhoto[] spacePhotos) { mContext = context; mSpacePhotos = spacePhotos; } } }
9. Tải Hình ảnh Từ Một URL
Đây là nơi chúng ta cần Glide thực hiện công việc của nó—để kéo về các hình ảnh từ internet và hiển thị chúng trong từng ImageView
, sử dụng phương thức onBindViewHolder()
của RecyclerView khi người dùng cuộn ứng dụng.
// ... @Override public void onBindViewHolder(MyViewHolder holder, int position) { Photo photo = mPhotoList.get(position); ImageView imageView = holder.mPhotoImageView; Glide.with(mContext) .load(spacePhoto.getUrl()) .placeholder(R.drawable.ic_cloud_off_red) .into(imageView); } // ...
Từng bước một, đây là những gì các cuộc gọi đến Glide đang thực hiện:
-
with(Context context)
: chúng ta bắt đầu quá trình tải bằng cách trước tiên truyền context của chúng ta vào phương thứcwith()
. -
load(String string)
: nguồn hình ảnh được chỉ định như là một đường dẫn thư mục, một URI hay một URL. -
placeholder(int resourceId)
: một id tài nguyên ứng dụng cục bộ, thường được gọi là drawable, đó sẽ là một placeholder (hình giữ chỗ) cho đến khi hình ảnh được tải và hiển thị.
-
into(ImageView imageView)
: ImageVeiw đích mà hình ảnh sẽ được đặt ở đó.
Lưu ý rằng Glide cũng có thể tải hình ảnh cục bộ, chỉ cần truyền id của tài nguyên Android, đường dẫn tập tin hoặc Uri như là một đối số vào phương thức load()
.
Thay đổi Kích thước Và Biến đổi Hình ảnh
Bạn có thể thay đổi kích thước hình ảnh trước khi nó được hiển thị trong ImageView
với phương thức .override(int width, int height)
của Glide. Điều này giúp ích cho việc tạo hình thumbnail trong ứng dụng của bạn khi tải một kích thước hình ảnh khác nhau từ máy chủ. Lưu ý rằng các tham số được tính bằng pixel chứ không phải là dp.
Có sẵn các phép biến đổi hình ảnh sau:
-
fitCenter()
: giãn hình ảnh một cách thống nhất (duy trì tỷ lệ của hình ảnh) sao cho hình ảnh phù hợp với khu vực nhất định. Toàn bộ hình ảnh sẽ được hiển thị, nhưng có thể có khoảng dư theo chiều dọc hoặc ngang. -
centerCrop()
: giãn hình ảnh một cách thống nhất (duy trì tỷ lệ của hình ảnh) sao cho hình ảnh lấp đầy một vùng nhất định, với càng nhiều hình ảnh hiển thị càng tốt. Nếu cần, hình ảnh sẽ được cắt theo chiều ngang hoặc chiều dọc để cho vừa vặn.
10. Khởi tạo Adapter
Ở đây chúng ta tạo ra RecyclerView
với GridLayoutManager
như là trình quản lý layout, khởi tạo adapter của chúng ta, và ràng buộc nó vào RecyclerView
.
// ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this, 2); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_images); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(layoutManager); ImageGalleryAdapter adapter = new ImageGalleryAdapter(this, SpacePhoto.getSpacePhotos()); recyclerView.setAdapter(adapter); } // ...
11. Tạo Activity Chi tiết
Tạo một activity mới và đặt tên nó là SpacePhotoActivity
. Chúng ta lấy extra SpacePhoto
và tải hình ảnh với Glide như chúng ta đã làm trước đây. Ở đây chúng ta mong muốn tập tin hoặc URL là một Bitmap
, vì vậy chúng ta sẽ sử dụng asBitmap()
để làm cho Glide nhận một Bitmap
. Nếu không việc tải sẽ không thành công và callback .error()
sẽ được kích hoạt—làm cho tài nguyên drawable trả về từ callback error được hiển thị. Bạn cũng có thể sử dụng asGif()
nếu bạn muốn đảm bảo rằng hình ảnh đã được nạp của bạn là một ảnh GIF. (Tôi sẽ sớm giải thích cách GIF hoạt động trong Glide.)
import android.graphics.Bitmap; import android.graphics.Color; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.graphics.Palette; import android.view.ViewGroup; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; public class SpacePhotoActivity extends AppCompatActivity { public static final String EXTRA_SPACE_PHOTO = "SpacePhotoActivity.SPACE_PHOTO"; private ImageView mImageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_photo_detail); mImageView = (ImageView) findViewById(R.id.image); SpacePhoto spacePhoto = getIntent().getParcelableExtra(EXTRA_SPACE_PHOTO); Glide.with(this) .load(spacePhoto.getUrl()) .asBitmap() .error(R.drawable.ic_cloud_off_red) .diskCacheStrategy(DiskCacheStrategy.SOURCE) .into(mImageView); } }
Lưu ý rằng chúng ta cũng khởi tạo một bộ nhớ đệm duy nhất cho các hình ảnh được tải: DiskCacheStrategy.SOURCE
. Tôi sẽ giải thích thêm về bộ nhớ đệm trong phần sau.
Layout Chi tiết
Đây là một layout để hiển thị activity chi tiết. Nó chỉ hiển thị một ImageView
có thể cuộn sẽ hiển thị phiên bản đầy đủ của hình ảnh được tải.
<?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"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/activity_character" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="center_vertical"> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="fitCenter"/> </LinearLayout> </ScrollView> </LinearLayout>
12. Bộ nhớ đệm Trong Glide
Nếu bạn theo dõi kỹ, bạn sẽ thấy rằng khi bạn xem lại hình ảnh đã được tải trước đó, nó sẽ tải nhanh hơn trước. Điều gì làm cho nó nhanh hơn? Hệ thống bộ nhớ đệm của Glide, đó là những gì.
Sau khi một hình ảnh đã được tải một lần từ internet, Glide sẽ lưu nó trong bộ nhớ đệm và trên đĩa, tiết kiệm được các yêu cầu mạng lặp đi lặp lại và cho phép truy vấn hình ảnh nhanh hơn. Vì vậy, Glide trước tiên sẽ kiểm tra xem một hình ảnh có sẵn trong bộ nhớ hoặc trên đĩa hay không trước khi tải nó từ mạng.
Tuy nhiên, tùy thuộc vào ứng dụng của bạn, bạn có thể cần tránh bộ nhớ đệm—ví dụ: nếu những hình ảnh được hiển thị có thể thay đổi thường xuyên và không được tải lại.
Vậy Làm thế nào Bạn Vô hiệu hoá Bộ nhớ đệm?
Bạn có thể tránh bộ nhớ đệm bằng cách gọi .skipMemoryCache(true)
. Nhưng hãy lưu ý rằng hình ảnh vẫn sẽ được lưu trữ trong đĩa—để ngăn chặn điều đó, bạn sử dụng phương thức .diskCacheStrategy(DiskCacheStrategy strategy)
, nó nhận một trong những giá trị enum sau đây:
-
DiskCacheStrategy.NONE
: không có dữ liệu nào được lưu vào bộ nhớ đệm. -
DiskCacheStrategy.SOURCE
: dữ liệu ban đầu được lưu vào bộ nhớ đệm. -
DiskCacheStrategy.RESULT
: lưu kết quả của dữ liệu sau khi chuyển đổi vào bộ nhớ đệm. -
DiskCacheStrategy.ALL
: lưu trữ cả dữ liệu ban đầu và dữ liệu đã chuyển đổi.
Để tránh lưu vào bộ nhớ và bộ nhớ đệm của ổ đĩa, chỉ cần lần lượt gọi cả hai phương thức:
Glide.with(this) .load(spacePhoto.getUrl()) .asBitmap() .skipMemoryCache(true) .diskCacheStrategy(DiskCacheStrategy.NONE) .into(imageView);
13. Các Listener Cho Yêu cầu
Trong Glide, bạn có thể cài đặt một RequestListener
để theo dõi trạng thái của yêu cầu mà bạn thực hiện khi tải hình ảnh. Chỉ một trong những phương thức này sẽ được gọi.
-
onException()
: được kích hoạt bất cứ khi nào một ngoại lệ xuất hiện để bạn có thể xử lý ngoại lệ trong phương thức này. -
onResourceReady()
: được kích hoạt khi hình ảnh được tải thành công.
Quay trở lại ứng dụng gallery của chúng ta, hãy sửa đổi màn hình một chút bằng cách sử dụng đối tượng RequestListener
giúp đặt bitmap vào ImageView
và đồng thời thay đổi màu nền của layout bằng cách chiết xuất màu tối và nổi của hình ảnh bằng cách sử dụng Android Palette API
// ... @Override protected void onCreate(Bundle savedInstanceState) { // ... Glide.with(this) .load(spacePhoto.getUrl()) .asBitmap() .error(R.drawable.ic_cloud_off_red) .listener(new RequestListener<String, Bitmap>() { @Override public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) { return false; } @Override public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) { onPalette(Palette.from(resource).generate()); mImageView.setImageBitmap(resource); return false; } public void onPalette(Palette palette) { if (null != palette) { ViewGroup parent = (ViewGroup) mImageView.getParent().getParent(); parent.setBackgroundColor(palette.getDarkVibrantColor(Color.GRAY)); } } }) .diskCacheStrategy(DiskCacheStrategy.SOURCE) .into(mImageView); } // ...
Ở đây bạn cũng có thể ẩn một hộp tiến độ nếu có. Với sự thay đổi cuối cùng này, hãy đảm bảo bao gồm phụ thuộc Palette trong build.gradle
của bạn:
dependencies { // ... compile 'com.android.support:palette-v7:25.1.1' }
14. Thử nghiệm Ứng dụng
Cuối cùng, bạn đã có thể chạy ứng dụng! Nhấp vào một hình thumbnail để xem một phiên bản đầy đủ của hình ảnh.



15. Hiệu ứng động
Nếu bạn chạy ứng dụng, bạn sẽ nhận thấy một hiệu ứng cắt ngang đang xảy ra khi hình ảnh đang được hiển thị. Glide kích hoạt hiệu ứng này mặc định, nhưng bạn có thể vô hiệu hóa nó bằng cách gọi dontAnimate()
, điều này sẽ chỉ làm cho hình ảnh được hiển thị mà không có bất kỳ hiệu ứng nào. Bạn còn có thể tùy biến hiệu ứng cắt ngang bằng cách gọi crossFade(int duration)
, truyền vào duration (khoảng thời gian) bằng mili giây để tăng tốc hoặc làm chậm nó xuống—300 mili giây là mặc định.
Hình GIF Động
Rất dễ để hiển thị ảnh GIF động trong ứng dụng của bạn bằng Glide. Nó hoạt động giống như hiển thị hình ảnh bình thường.
ImageView gifImageView = (ImageView) findViewById(R.id.iv_gif); Glide.with(this) .load("http://i.imgur.com/Vth6CBz.gif") .asGif() .placeholder(R.drawable.ic_cloud_off_red) .error(R.drawable.ic_cloud_off_red) .into(gifImageView);
Nếu bạn kỳ vọng hình ảnh đó là GIF, hãy gọi asGif()
—điều này đảm bảo rằng Glide nhận một GIF, nếu không việc tải sẽ không thành công và Drawable
được truyền vào phương thức .error()
sẽ được hiển thị.
Phát Video
Không may, Glide không hỗ trợ tải và hiển thị video thông qua URL. Thay vào đó, nó chỉ có thể tải và hiển thị video đã được lưu trữ trên điện thoại. Hiển thị một video bằng cách truyền URI của nó vào phương thức load()
.
Glide.with(context) .load(Uri.fromFile(new File("your/video/file/path")) .into(imageView)
Tóm tắt
Làm tốt lắm! Trong hướng dẫn này, bạn đã xây dựng một ứng dụng gallery hoàn chỉnh với Glide và song song đó học được cách làm việc của thư viện và cách bạn có thể tích hợp nó vào dự án của riêng bạn. Bạn cũng đã học được cách hiển thị cả hình ảnh cục bộ và từ xa, cách hiển thị ảnh GIF động và video, cách để áp dụng các phép biến đổi hình ảnh như thay đổi kích thước. Không chỉ vậy, bạn còn thấy nó dễ dàng thế nào để kích hoạt bộ nhớ đệm, xử lý lỗi và tuỳ biến các listener.
Để tìm hiểu thêm về Glide, bạn có thể tham khảo tài liệu chính thức của nó. Để tìm hiểu thêm về viết ứng dụng Android, hãy kiểm tra một số các khóa học và hướng dẫn khác của chúng tôi ở đây trên Envato Tuts+!
- Android SDKCách Thực hiện Cuộc gọi và Sử dụng SMS trong Ứng dụng AndroidChike Mgbemena
- Android SDKAndroid Things: Dự án Đầu tiên Của BạnPaul Trebilcox-Ruiz
- Android SDKTạo Ứng dụng Xem Video trên Cardboard 360 của AndroidPaul Trebilcox-Ruiz
- Android SDKChụp ảnh Với Ứng dụng Android Của BạnAshraff Hathibelagal