ImageSlider khá phổ biến trong thế giới Web. Và với ứng dụng Android, bạn cũng có thể thực hiện điều tương tự. Vì chúng ta sẽ cho phép người dùng vuốt sang trái hoặc phải để chuyển sang ảnh trước hoặc sau, và ViewPager cung cấp chính xác khả năng đó, nên chắc chắn chúng ta sẽ dùng nó luôn thay vì tự code một View. Đồng thời, chúng ta sẽ tạo một ImageSliderAdapter extends trực tiếp PagerAdapter, thay vì dùng Fragment(State)PagerAdapter và đặt mỗi hình ảnh vào trong một Fragment. Và như thường lệ, xin mời các bạn tạo một Android Project mới với Android Studio và xin quyền INTERNET trong AndroidManifest. Ngoài ra, chúng ta sẽ load ảnh từ unsplash.com thông qua thư viện Glide. Nếu bạn chưa biết cách tích hợp Glide vào project, hãy thêm các dòng sau vào tập tin build.gradle module app:
1 2 |
implementation 'com.github.bumptech.glide:glide:4.8.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' |
Trong trường hợp các bạn muốn tạo ImageSlider trong Flutter, hãy tham khảo bài viết này.
1. Tạo ImageSlider bằng ViewPager và PagerAdapter:
Đầu tiên các bạn tạo một Activity chứa một ViewPager. Và tốt nhất, bạn nên bọc nó trong một FrameLayout. Dưới đây là code đơn giản của tôi:
1 2 3 4 5 6 7 8 9 10 11 |
<!--activity_main.xml--> <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.view.ViewPager android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/imageSlider"/> </FrameLayout> |
Và chúng ta tiến hành các thao tác gán View khá quen thuộc trong tập tin MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 |
/* MainActivity.java */ public class MainActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager imageSlider = findViewById(R.id.imageSlider); } } |
Bây giờ, chúng ta sẽ dựng một class ImageSliderAdapter. Class này có một constructor nhận tham số là một String[] urls chính là các URLs trỏ tới địa chỉ các ảnh mà chúng ta sẽ cho hiển thị lên ViewPager. Nếu bạn nào muốn dùng với Fragment, bạn chỉ cần extends Fragment(State)PagerAdapter và chỉ định contentView cho Fragment là xong. Còn trong bài viết này, tôi sẽ hướng dẫn các bạn dùng trực tiếp ImageView trên mỗi trang của ViewPager thay vì gián tiếp qua một Fragment. Quay trở lại code Java, bạn tạo class ImageSliderAdapter extends PagerAdapter và override 2 abstract methods getCount và isViewFromObject sau khi đã thiết lập constructor như bên dưới:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class ImageSliderAdapter extends PagerAdapter { private String[] urls; public ImageSliderAdapter(String[] urls) { this.urls = urls; } @Override public int getCount() { return urls.length; } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } } |
Tạm thời, class ImageSliderAdapter của chúng ta đã không còn lỗi. Và bây giờ, chúng ta sẽ định nghĩa giao diện mỗi trang của ViewPager bằng cách override tiếp hàm Object instantiateItem(ViewGroup container, int position). Hàm này tương tự như BaseAdapter#getView(int position, View convertView, ViewGroup parent):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class ImageSliderAdapter extends PagerAdapter { private String[] urls; public ImageSliderAdapter(String[] urls) { this.urls = urls; } @Override public int getCount() { return urls.length; } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { // Tạo ImageView final Context context = container.getContext(); final AppCompatImageView imageView = new AppCompatImageView(context); container.addView(imageView); // Load ảnh vào ImageView bằng Glide Glide.with(context).load(urls[position]).into(imageView); // Return return imageView; } } |
Tuy nhiên, bấy nhiêu đó là chưa đủ, vì ta còn phải override tiếp hàm destroyView(ViewGroup container, int position, Object object) nữa. Và tham số thứ ba, Object, chính là giá trị mà instantiateItem(ViewGroup container, int position) return ứng với position tương ứng.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class ImageSliderAdapter extends PagerAdapter { private String[] urls; public ImageSliderAdapter(String[] urls) { this.urls = urls; } @Override public int getCount() { return urls.length; } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { // Tạo ImageView, container chính là ViewPager của chúng ta final Context context = container.getContext(); final AppCompatImageView imageView = new AppCompatImageView(context); container.addView(imageView); // Load ảnh vào ImageView bằng Glide Glide.with(context).load(urls[position]).into(imageView); // Return return imageView; } @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { // container chính là ViewPager, còn Object chính là return của instantiateItem ứng với position container.removeView((View) object); } } |
Vậy là xong. Cuối cùng, bạn thêm tạo một instance của ImageSliderAdapter và gán vào cho ViewPager imageSlider trong MainActivity, và đừng quên các URLs trỏ tới các ảnh. Dưới đây là phần code của tôi bao gồm luôn một số ảnh đến từ unsplash.com:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String[] urls = { "https://images.unsplash.com/photo-1544614342-c48ab91d79fc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1544638627-725124bda50d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1544638635-8a5838b796c6?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1544633662-2b2afce79046?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" }; ViewPager imageSlider = findViewById(R.id.imageSlider); ImageSliderAdapter imageSliderAdapter = new ImageSliderAdapter(urls); imageSlider.setAdapter(imageSliderAdapter); } } |
Bây giờ, có lẽ bạn đang thắc mắc là tại sao instantiateItem lại có class là Object thay vì trực tiếp là một View. Đơn giản là PagerAdapter đảm bảo cho bạn extend tùy ý. Ở đây, tôi dùng View nên sẽ return View, nhưng đối với Fragment(State)PagerAdapter, chúng sẽ return android.app.Fragment đối với thư viện Support v13, hay android.support.v4.app.Fragment trong thư viện Support v4. Và trong trường hợp khác, bạn lại có thể wrap một View trong một class khác do bạn tùy ý đặt. Do đó, để đảm bảo cho mọi trường hợp, instantiateItem là Object. Tuy nhiên, bao giờ bạn cũng phải thao tác với View dù return bất cứ instance của class nào. Đơn giản, vì ViewPager có nhiệm vụ hiển thị nội dung, và đương nhiên, phải “dính dáng” tới View.
2. Tạo dot indicator:
Nếu tiếng Anh của bạn chưa tốt, thì dot indicator có nghĩa là các chấm chỉ thị việc imageSlider của bạn (tức ViewPager) đang hiển thị nội dung ở vị trí nào (position). Việc tạo chúng cũng không có gì quá phức tạp. Đầu tiên, trong acitivity_main.xml, bạn tạo tiếp một LinearLayout có layout_gravity ở bottom|center, và đây là lí do tôi đặt ViewPager nằm trong một FrameLayout ngay từ đầu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<!--activity_main.xml--> <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.view.ViewPager android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/imageSlider"/> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center|bottom" android:orientation="horizontal" android:id="@+id/indicator" /> </FrameLayout> |
Bây giờ bạn không tiếp tục đặt thêm vài View con bên trong LinearLayout, vì bạn luôn phải đảm bảo số lượng các chấm phải đúng bằng số lượng các item trong ImageSliderAdapter, tức chiều dài của mảng String[] urls. Vì vậy bạn không nên đặt các View bằng tay trong XML mà nên thêm bằng hàm addView(View).
Để tạo một dot, bạn cần một View đóng vai trò là một “chấm”. Ở đây, bạn sẽ cần định nghĩa chính xác kích thước của nó thay vì wrap_content (sẽ trả về 0) hay match_parent. Tôi tạo luôn View trong Java thay vì từ XML, đồng thời cũng định nghĩa background của nó là một ShapeDrawable cũng trong Java thay cho XML:
1 2 3 4 5 6 7 8 9 10 |
View createDot(Context context) { View dot = new View(context); ViewGroup.MarginLayoutParams dotParams = new ViewGroup.MarginLayoutParams(20, 20); dotParams.setMargins(20, 10, 20, 10); dot.setLayoutParams(dotParams); ShapeDrawable drawable = new ShapeDrawable(new OvalShape()); drawable.setTint(Color.white); dot.setBackground(drawable); return dot; } |
Mỗi “chấm” sẽ nằm trong LinearLayout bên trên. Như vậy, code của tôi trong MainActivity tạm thời sẽ như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public class MainActivity extends AppCompatActivity { static final int COLOR_INACTIVE = Color.WHITE; static final int COLOR_ACTIVE = Color.BLUE; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String[] urls = { "https://images.unsplash.com/photo-1544614342-c48ab91d79fc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1544638627-725124bda50d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1544638635-8a5838b796c6?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1544633662-2b2afce79046?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" }; ViewPager imageSlider = findViewById(R.id.imageSlider); ImageSliderAdapter imageSliderAdapter = new ImageSliderAdapter(urls); imageSlider.setAdapter(imageSliderAdapter); // Indicator: LinearLayout indicator = findViewById(R.id.indicator); for (int i = 0; i < urls.length; i++) { // COLOR_ACTIVE ứng với chấm ứng với vị trí hiện tại của ViewPager, // COLOR_INACTIVE ứng với các chấm còn lại // ViewPager có vị trí mặc định là 0, vì vậy color ở vị trí i == 0 sẽ là COLOR_ACTIVE View dot = createDot(indicator.getContext(), i == 0 ? COLOR_ACTIVE : COLOR_INACTIVE); indicator.addView(dot); } } View createDot(Context context, @ColorInt int color) { View dot = new View(context); Linearayout.MarginLayoutParams dotParams = new LinearLayout.MarginLayoutParams(20, 20); dotParams.setMargins(20, 10, 20, 10); dot.setLayoutParams(dotParams); ShapeDrawable drawable = new ShapeDrawable(new OvalShape()); drawable.setTint(color); dot.setBackground(drawable); return dot; } } |
Cuối cùng, tới phần quan trọng nhất: Thay đổi màu của “chấm” ứng với vị trí hiện tại của ViewPager. Cũng không có gì quá phức tạp, bạn chỉ cần dùng hàm ViewPager#addOnPageChangeListener mà thôi. Phần này tôi cũng sẽ code luôn trong onCreate ở bên dưới LinearLayout indicator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
public class MainActivity extends AppCompatActivity { static final int COLOR_INACTIVE = Color.WHITE; static final int COLOR_ACTIVE = Color.BLUE; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String[] urls = { "https://images.unsplash.com/photo-1544614342-c48ab91d79fc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1544638627-725124bda50d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1544638635-8a5838b796c6?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1544633662-2b2afce79046?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" }; ViewPager imageSlider = findViewById(R.id.imageSlider); ImageSliderAdapter imageSliderAdapter = new ImageSliderAdapter(urls); imageSlider.setAdapter(imageSliderAdapter); // Indicator: LinearLayout indicator = findViewById(R.id.indicator); for (int i = 0; i < urls.length; i++) { // COLOR_ACTIVE ứng với chấm ứng với vị trí hiện tại của ViewPager, // COLOR_INACTIVE ứng với các chấm còn lại // ViewPager có vị trí mặc định là 0, vì vậy color ở vị trí i == 0 sẽ là COLOR_ACTIVE View dot = createDot(indicator.getContext(), i == 0 ? COLOR_ACTIVE : COLOR_INACTIVE); indicator.addView(dot); } // Thay đổi màu các chấm khi ViewPager thay đổi vị trí: imageSlider.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { for (int i = 0; i < size; i++) { // Duyệt qua từng "chấm" trong indicator // Nếu i == position, tức i đang là vị trí hiện tại của ViewPager, // ta sẽ đổi màu "chấm" thành COLOR_ACTIVE, nếu không // thì sẽ đổi thành màu COLOR_INACTIVE indicator.getChildAt(i).getBackground().mutate().setTint(color(i == position ? COLOR_ACTIVE : COLOR_INACTIVE)); } } @Override public void onPageSelected(int position) { } @Override public void onPageScrollStateChanged(int state) { } }); } View createDot(Context context, @ColorInt int color) { View dot = new View(context); Linearayout.MarginLayoutParams dotParams = new LinearLayout.MarginLayoutParams(20, 20); dotParams.setMargins(20, 10, 20, 10); dot.setLayoutParams(dotParams); ShapeDrawable drawable = new ShapeDrawable(new OvalShape()); drawable.setTint(color); dot.setBackground(drawable); return dot; } } |
Vậy là xong phần code. Bạn có thể cho chạy thử để kiểm tra.
Bạn đã biết ImageSlider khá phổ biến trong thế giới Web. Nhưng bạn có biết chúng ta có thể dễ dàng ImageSlider mang lên ứng dụng Android? Hãy tham khảo cách thực hiện tại http://eitguide.net/tao-imageslider-trong-ung-dung-android-voi-viewpager/
Eitguide Androidさんの投稿 2019年1月24日木曜日