Hướng dẫn và ví dụ Android ViewPager2
1. Android ViewPager2 vs ViewPager
Android ViewPager là một thành phần giao diện được giới thiệu trong thư viện hỗ trợ của Android, nó cho phép người dùng vuốt (swipe) sang trái hoặc trang phải để xem một trang (màn hình) hoàn toàn mới.
ViewPager/ViewPager2
Một ViewPager/ViewPager2 mà mỗi trang của nó lấp đầy màn hình, người dùng sẽ vuốt (swipe) sang trái để di chuyển tới trang tiếp theo, hoặc vuốt sang phải để lùi lại một trang.
Một ViewPager/ViewPager2 nhìn thấy trên ứng dụng học ngôn ngữ của Duolingo.
ViewPager2 vs ViewPager
Như chúng ta đã biết, đội ngũ Android liên tục phát hành các bản cập nhật và cải tiến mới cho Android Framework. Một trong những thành phần nhận được các bản cập nhật lớn là ViewPager2. ViewPager2 là sự thay thế của ViewPager và có một số cải tiến về hiệu suất và các chức năng bổ sung.
Vậy có gì mới trong ViewPager2?
- ViewPager2 là một phiên bản ngẫu hứng của ViewPager cung cấp các chức năng bổ sung và giải quyết các vấn đề thường gặp trong ViewPager.
- ViewPager2 được xây dựng trên RecyclerView. Vì vậy, bạn sẽ có những lợi thế tuyệt vời mà RecyclerView có. Ví dụ: bạn có thể tận dụng DiffUtils để tính toán hiệu quả sự khác biệt giữa các tập dữ liệu và cập nhật ViewPager bằng hình ảnh động.
- ViewPager2 hỗ trợ hướng thẳng đứng (Vertical Orientation). Nếu sử dụng ViewPager, bạn phải viết các tùy biến bổ xung cho ViewPager để có được kết quả tương tự.
- ViewPager2 hỗ trợ Right-to-Left (RTL) và điều này sẽ được bật tự động dựa trên App Locale.
- Khi làm việc với một tập hợp các Fragment. Nếu một trong các Fragment thay đổi giao diện của nó, bạn chỉ cần gọi phương thức notifyDatasetChanged() để cập nhập giao diện của ứng dụng một cách đúng cách.
- ViewPager2 hỗ trợ sự chuyển đổi trang (transformation), tức là bạn có thể cung cấp hình ảnh động khi chuyển đổi giữa các trang. Bạn cũng có thể viết PageTransformer tùy chỉnh của riêng mình.
- PagerAdapter được thay thế bằng RecyclerView.
- FragmentStatePagerAdapter được thay thế bởi FragmentStateAdapter.
Library
ViewPager2 là một thành phần không sẵn có trong thư viện tiêu chuẩn của Android, vì vậy nếu bạn muốn sử dụng nó bạn phải cài đặt nó vào project của bạn.
Bạn có thể cài đặt ViewPager2 từ Palette của cửa sổ thiết kế.
Sau khi cài đặt xong bạn sẽ thấy thư viện ViewPager2 được khai báo trong build.gradle (Module: app).
implementation 'androidx.viewpager2:viewpager2:1.0.0'
2. Example: ViewPager2
Bây giờ chúng ta sẽ thực hành một ví dụ về ViewPager2 và sử dụng Fragment như một bản thiết kế của mỗi trang. Dưới đây là hình ảnh xem trước của ví dụ:
- Tập tin fragment_employee_page.xml là bản thiết kế giao diện của một trang (Page).
- Lớp EmployeePageFragment chứa dữ liệu của một đối tượng Employee và hiển thị nó lên một trang (Page).
Trên Android Studio tạo mới một project:
- File > New > New Project > Empty Activity
- Name: ViewPager2Example
- Package name: org.o7planning.viewpager2example
- Language: Java
Như đã đề cập ở trên ViewPager2 là một thành phần không có sẵn trong thư viện tiêu chuẩn của Android, vì vậy, bước tiếp theo bạn cần cài đặt nó vào project của bạn từ Palette của cửa sổ thiết kế.
Và tạo một Fragment (EmployeePageFragment), nó tương ứng với một trang (Page) của ViewPager2:
- File > New > Fragment > Fragment (Blank)
- Fragment Name: EmployeePageFragment
- Fragment Layout Name: fragment_page_employee
- Source Language: Java
Lúc này bạn sẽ nhìn thấy hai tập tin được tạo ra:
Mở tập tin fragment_employee_page.xml để thiết kế giao diện.
fragment_employee_page.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView_fullName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:background="#C4CFB9"
android:gravity="center_horizontal"
android:text="Fullname"
android:textSize="22sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView71"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:text="Position:"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView_fullName" />
<TextView
android:id="@+id/textView_position"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:text="(Position)"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView71"
app:layout_constraintTop_toBottomOf="@+id/textView_fullName" />
<TextView
android:id="@+id/textView72"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:text="Email:"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView71" />
<TextView
android:id="@+id/textView_email"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:text="(Email)"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView72"
app:layout_constraintTop_toBottomOf="@+id/textView71" />
</androidx.constraintlayout.widget.ConstraintLayout>
EmployeePageFragment.java
package org.o7planning.viewpager2example;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class EmployeePageFragment extends Fragment {
private static final String LOG_TAG = "AndroidExample";
private Employee employee;
private TextView textViewEmail;
private TextView textViewPosition;
private TextView textViewFullName;
private static int counter = 0;
// IMPORTANT:
// Required default public constructor.
// If configuration change.
// For example: User rotate the Phone,
// Android will create new Fragment (EmployeePageFragment) via default Constructor
// so this.employee will be null.
public EmployeePageFragment() {
}
public EmployeePageFragment(Employee employee) {
this.employee = employee;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = (ViewGroup) inflater.inflate(
R.layout.fragment_employee_page, container, false);
counter++;
if(counter % 2 == 0) {
view.setBackgroundColor(Color.parseColor("#ebdef0"));
} else {
view.setBackgroundColor(Color.parseColor("#e8f8f5"));
}
this.textViewFullName = view.findViewById(R.id.textView_fullName);
this.textViewPosition = view.findViewById(R.id.textView_position);
this.textViewEmail = view.findViewById(R.id.textView_email);
return view;
}
// Called when configuration change.
// For example: User rotate the Phone,
// Android will create new Fragment (EmployeePageFragment) object via default Constructor
// so this.employee will be null.
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
Log.i(LOG_TAG, "onSaveInstanceState: save employee data to Bundle");
// Convert employee object to Bundle.
Bundle dataBundle = this.employeeToBundle(this.employee);
outState.putAll(dataBundle);
super.onSaveInstanceState(outState);
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
Log.i(LOG_TAG, "onViewStateRestored");
super.onViewStateRestored(savedInstanceState);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
Log.i(LOG_TAG, "onViewCreated");
super.onViewCreated(view, savedInstanceState);
if(this.employee == null) {
Log.i(LOG_TAG, "Get employee data from savedInstanceState");
// The state was saved by onSaveInstanceState(Bundle outState) method.
this.employee = this.bundleToEmployee(savedInstanceState);
}
this.showInGUI(this.employee);
}
// Call where View ready.
private void showInGUI(Employee employee) {
this.textViewFullName.setText(employee.getFullName());
this.textViewPosition.setText(employee.getPosition());
this.textViewEmail.setText(employee.getEmail());
}
private Bundle employeeToBundle(Employee employee) {
Bundle bundle = new Bundle();
bundle.putString("fullName", employee.getFullName());
bundle.putString("position", employee.getPosition());
bundle.putString("email", employee.getEmail());
return bundle;
}
private Employee bundleToEmployee(Bundle savedInstanceState) {
String fullName = savedInstanceState.getString("fullName");
String position = savedInstanceState.getString("position");
String email = savedInstanceState.getString("email");
return new Employee(fullName, email, position);
}
}
EmployeeFragmentStateAdapter.java
package org.o7planning.viewpager2example;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import java.util.ArrayList;
import java.util.List;
public class EmployeeFragmentStateAdapter extends FragmentStateAdapter {
private List<Employee> employees;
public EmployeeFragmentStateAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
this.employees = this.intDatas();
}
private List<Employee> intDatas() {
Employee emp1 = new Employee("James Smith", "jamessmith@example.com", "Web Designer");
Employee emp2 = new Employee("Elizabeth Johnson", "elizabethjohnson@example.com", "Project Manager");
Employee emp3 = new Employee("Catherine Johnson", "catherinejohnson@example.com", "President of Sales");
List<Employee> list = new ArrayList<Employee>();
list.add(emp1);
list.add(emp2);
list.add(emp3);
return list;
}
@NonNull
@Override
public Fragment createFragment(int position) {
Employee employee = this.employees.get(position);
return new EmployeePageFragment(employee);
}
@Override
public int getItemCount() {
return this.employees.size();
}
}
Employee.java
package org.o7planning.viewpager2example;
public class Employee {
private String fullName;
private String email;
private String position;
public Employee(String fullName, String email, String position) {
this.fullName = fullName;
this.email = email;
this.position = position;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
}
Giao diện chính của ứng dụng:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2_employee"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
package org.o7planning.viewpager2example;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private ViewPager2 viewPager2Employee;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.viewPager2Employee = findViewById(R.id.viewPager2_employee);
// Employee FragmentStateAdapter.
EmployeeFragmentStateAdapter adapter = new EmployeeFragmentStateAdapter(this);
this.viewPager2Employee.setAdapter(adapter);
}
}
3. Example: Transformation
Để hiển thị hiệu ứng hoạt hình khi người dùng chuyển đổi giữa các trang, bạn cần viết một lớp thực hiện (implements) interface ViewPager2.PageTransformer và cung cấp nó cho đối tượng ViewPager2.
ViewPager2 viewPager = findViewById(R.id.pager);
...
viewPager.setPageTransformer(new YourPageTransformer());
Interface ViewPager2.PageTransformer chỉ có duy nhất một phương thức transformPage(), phương thức này được gọi một lần cho mỗi quá trình chuyển đổi (transition).
/**
* Apply a property transformation to the given page.
*
* @param page Apply the transformation to this page
* @param position Position of page relative to the current front-and-center
* position of the pager. 0 is front and center. 1 is one full
* page position to the right, and -2 is two pages to the left.
* Minimum / maximum observed values depend on how many pages we keep
* attached, which depends on offscreenPageLimit.
*
* @see #setOffscreenPageLimit(int)
*/
void transformPage(@NonNull View page, float position);
Tiếp tục với ví dụ ở trên, chúng ta sẽ thêm hiệu ứng hoạt hình vào cho nó.
Zoom Out Page Transformer
ZoomOutPageTransformer.java
package org.o7planning.viewpager2example.transformer;
import android.view.View;
import androidx.viewpager2.widget.ViewPager2;
public class ZoomOutPageTransformer implements ViewPager2.PageTransformer {
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0f);
} else if (position <= 1) { // [-1,1]
// Modify the default slide transition to shrink the page as well
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}
// Scale the page down (between MIN_SCALE and 1)
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
// Fade the page relative to its size.
view.setAlpha(MIN_ALPHA +
(scaleFactor - MIN_SCALE) /
(1 - MIN_SCALE) * (1 - MIN_ALPHA));
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0f);
}
}
}
MainActivity.java (Zoom Out Page Transformer)
package org.o7planning.viewpager2example;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import org.o7planning.viewpager2example.transformer.ZoomOutPageTransformer;
public class MainActivity extends AppCompatActivity {
private ViewPager2 viewPager2Employee;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.viewPager2Employee = findViewById(R.id.viewPager2_employee);
// Employee FragmentStateAdapter.
EmployeeFragmentStateAdapter adapter = new EmployeeFragmentStateAdapter(this);
this.viewPager2Employee.setAdapter(adapter);
// PageTransformer
this.viewPager2Employee.setPageTransformer(new ZoomOutPageTransformer());
}
}
Depth Page Transformer
DepthPageTransformer.java
package org.o7planning.viewpager2example.transformer;
import android.view.View;
import androidx.annotation.RequiresApi;
import androidx.viewpager2.widget.ViewPager2;
@RequiresApi(21)
public class DepthPageTransformer implements ViewPager2.PageTransformer {
private static final float MIN_SCALE = 0.75f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0f);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1f);
view.setTranslationX(0f);
view.setTranslationZ(0f);
view.setScaleX(1f);
view.setScaleY(1f);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Move it behind the left page
view.setTranslationZ(-1f);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0f);
}
}
}
MainActivity.java (Depth Page Transformer)
package org.o7planning.viewpager2example;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Build;
import android.os.Bundle;
import org.o7planning.viewpager2example.transformer.DepthPageTransformer;
public class MainActivity extends AppCompatActivity {
private ViewPager2 viewPager2Employee;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.viewPager2Employee = findViewById(R.id.viewPager2_employee);
// Employee FragmentStateAdapter.
EmployeeFragmentStateAdapter adapter = new EmployeeFragmentStateAdapter(this);
this.viewPager2Employee.setAdapter(adapter);
// PageTransformer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Api 21+.
this.viewPager2Employee.setPageTransformer(new DepthPageTransformer());
}
}
}
Các hướng dẫn lập trình Android
- Cấu hình Android Emulator trong Android Studio
- Hướng dẫn và ví dụ Android ToggleButton
- Tạo một File Finder Dialog đơn giản trong Android
- Hướng dẫn và ví dụ Android TimePickerDialog
- Hướng dẫn và ví dụ Android DatePickerDialog
- Bắt đầu với Android cần những gì?
- Cài đặt Android Studio trên Windows
- Cài đặt Intel® HAXM cho Android Studio
- Hướng dẫn và ví dụ Android AsyncTask
- Hướng dẫn và ví dụ Android AsyncTaskLoader
- Hướng dẫn lập trình Android cho người mới bắt đầu - Các ví dụ cơ bản
- Làm sao biết số số điện thoại của Android Emulator và thay đổi nó
- Hướng dẫn và ví dụ Android TextInputLayout
- Hướng dẫn và ví dụ Android CardView
- Hướng dẫn và ví dụ Android ViewPager2
- Lấy số điện thoại trong Android sử dụng TelephonyManager
- Hướng dẫn và ví dụ Android Phone Call
- Hướng dẫn và ví dụ Android Wifi Scanning
- Hướng dẫn lập trình Android Game 2D cho người mới bắt đầu
- Hướng dẫn và ví dụ Android DialogFragment
- Hướng dẫn và ví dụ Android CharacterPickerDialog
- Hướng dẫn lập trình Android cho người mới bắt đầu - Hello Android
- Hướng dẫn sử dụng Android Device File Explorer
- Bật tính năng USB Debugging trên thiết bị Android
- Hướng dẫn và ví dụ Android UI Layouts
- Hướng dẫn và ví dụ Android SMS
- Hướng dẫn lập trình Android với Database SQLite
- Hướng dẫn và ví dụ Google Maps Android API
- Hướng dẫn chuyển văn bản thành lời nói trong Android
- Hướng dẫn và ví dụ Android Space
- Hướng dẫn và ví dụ Android Toast
- Tạo một Android Toast tùy biến
- Hướng dẫn và ví dụ Android SnackBar
- Hướng dẫn và ví dụ Android TextView
- Hướng dẫn và ví dụ Android TextClock
- Hướng dẫn và ví dụ Android EditText
- Hướng dẫn và ví dụ Android TextWatcher
- Định dạng số thẻ tín dụng với Android TextWatcher
- Hướng dẫn và ví dụ Android Clipboard
- Tạo một File Chooser đơn giản trong Android
- Hướng dẫn và ví dụ Android AutoCompleteTextView và MultiAutoCompleteTextView
- Hướng dẫn và ví dụ Android ImageView
- Hướng dẫn và ví dụ Android ImageSwitcher
- Hướng dẫn và ví dụ Android ScrollView và HorizontalScrollView
- Hướng dẫn và ví dụ Android WebView
- Hướng dẫn và ví dụ Android SeekBar
- Hướng dẫn và ví dụ Android Dialog
- Hướng dẫn và ví dụ Android AlertDialog
- Hướng dẫn và ví dụ Android RatingBar
- Hướng dẫn và ví dụ Android ProgressBar
- Hướng dẫn và ví dụ Android Spinner
- Hướng dẫn và ví dụ Android Button
- Hướng dẫn và ví dụ Android Switch
- Hướng dẫn và ví dụ Android ImageButton
- Hướng dẫn và ví dụ Android FloatingActionButton
- Hướng dẫn và ví dụ Android CheckBox
- Hướng dẫn và ví dụ Android RadioGroup và RadioButton
- Hướng dẫn và ví dụ Android Chip và ChipGroup
- Sử dụng các tài sản ảnh và biểu tượng của Android Studio
- Thiết lập SD Card cho Android Emulator
- Ví dụ với ChipGroup và các Chip Entry
- Làm sao thêm thư viện bên ngoài vào dự án Android trong Android Studio?
- Làm sao loại bỏ các quyền đã cho phép trên ứng dụng Android
- Làm sao loại bỏ các ứng dụng ra khỏi Android Emulator?
- Hướng dẫn và ví dụ Android LinearLayout
- Hướng dẫn và ví dụ Android TableLayout
- Hướng dẫn và ví dụ Android FrameLayout
- Hướng dẫn và ví dụ Android QuickContactBadge
- Hướng dẫn và ví dụ Android StackView
- Hướng dẫn và ví dụ Android Camera
- Hướng dẫn và ví dụ Android MediaPlayer
- Hướng dẫn và ví dụ Android VideoView
- Phát hiệu ứng âm thanh trong Android với SoundPool
- Hướng dẫn lập trình mạng trong Android - Android Networking
- Hướng dẫn xử lý JSON trong Android
- Lưu trữ dữ liệu trên thiết bị với Android SharedPreferences
- Hướng dẫn lập trình Android với bộ lưu trữ trong (Internal Storage)
- Hướng dẫn lập trình Android với bộ lưu trữ ngoài (External Storage)
- Hướng dẫn sử dụng Intent trong Android
- Ví dụ về một Android Intent tường minh, gọi một Intent khác
- Ví dụ về Android Intent không tường minh, mở một URL, gửi một email
- Hướng dẫn sử dụng Service trong Android
- Hướng dẫn sử dụng thông báo trong Android - Android Notification
- Hướng dẫn và ví dụ Android DatePicker
- Hướng dẫn và ví dụ Android TimePicker
- Hướng dẫn và ví dụ Android Chronometer
- Hướng dẫn và ví dụ Android OptionMenu
- Hướng dẫn và ví dụ Android ContextMenu
- Hướng dẫn và ví dụ Android PopupMenu
- Hướng dẫn và ví dụ Android Fragment
- Hướng dẫn và ví dụ Android ListView
- Android ListView với Checkbox sử dụng ArrayAdapter
- Hướng dẫn và ví dụ Android GridView
Show More