Giới thiệu
Hướng dẫn này trình bày cách triển khai tính năng phân trang văn bản (text pagination) trong Android, cho phép chia nội dung dài thành nhiều trang để hiển thị dễ đọc hơn.
Các bước thực hiện
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.
Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingTop="16dp"
android:paddingRight="16dp"
android:paddingBottom="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_back"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent" />
<Button
android:id="@+id/btn_forward"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent" />
</LinearLayout>
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Bước 3: Logic chính (src/MainActivity.java)
package app.com.sample;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.Html;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
private Pagination mPagination;
private CharSequence mText;
private int mCurrentIndex = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.tv);
// Chuẩn bị nội dung văn bản có định dạng
Spanned htmlString = Html.fromHtml(getString(R.string.html_string));
Spannable spanString = new SpannableString(getString(R.string.long_string));
// Áp dụng style cho đoạn đầu
spanString.setSpan(new ForegroundColorSpan(Color.BLUE), 0, 24, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString.setSpan(new RelativeSizeSpan(2f), 0, 24, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString.setSpan(new StyleSpan(Typeface.MONOSPACE.getStyle()), 0, 24, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// Áp dụng style cho đoạn cuối
spanString.setSpan(new ForegroundColorSpan(Color.BLUE), 700, spanString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString.setSpan(new RelativeSizeSpan(2f), 700, spanString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString.setSpan(new StyleSpan(Typeface.MONOSPACE.getStyle()), 700, spanString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mText = TextUtils.concat(htmlString, spanString);
// Khởi tạo phân trang sau khi layout hoàn tất
mTextView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mTextView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mPagination = new Pagination(
mText,
mTextView.getWidth(),
mTextView.getHeight(),
mTextView.getPaint(),
mTextView.getLineSpacingMultiplier(),
mTextView.getLineSpacingExtra(),
mTextView.getIncludeFontPadding()
);
update();
}
});
// Nút quay lại trang trước
findViewById(R.id.btn_back).setOnClickListener(v -> {
mCurrentIndex = (mCurrentIndex > 0) ? mCurrentIndex - 1 : 0;
update();
});
// Nút chuyển trang tiếp theo
findViewById(R.id.btn_forward).setOnClickListener(v -> {
mCurrentIndex = (mCurrentIndex < mPagination.size() - 1) ? mCurrentIndex + 1 : mPagination.size() - 1;
update();
});
}
private void update() {
final CharSequence text = mPagination.get(mCurrentIndex);
if (text != null) mTextView.setText(text);
}
}
Bước 4: Lớp phân trang (src/Pagination.java)
package app.com.sample;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import java.util.ArrayList;
import java.util.List;
class Pagination {
private final boolean mIncludePad;
private final int mWidth;
private final int mHeight;
private final float mSpacingMult;
private final float mSpacingAdd;
private final CharSequence mText;
private final TextPaint mPaint;
private final List<CharSequence> mPages;
Pagination(CharSequence text, int pageW, int pageH, TextPaint paint,
float spacingMult, float spacingAdd, boolean inclidePad) {
this.mText = text;
this.mWidth = pageW;
this.mHeight = pageH;
this.mPaint = paint;
this.mSpacingMult = spacingMult;
this.mSpacingAdd = spacingAdd;
this.mIncludePad = inclidePad;
this.mPages = new ArrayList<>();
layout();
}
private void layout() {
final StaticLayout layout = new StaticLayout(
mText, mPaint, mWidth, Layout.Alignment.ALIGN_NORMAL,
mSpacingMult, mSpacingAdd, mIncludePad
);
final int lines = layout.getLineCount();
final CharSequence text = layout.getText();
int startOffset = 0;
int height = mHeight;
for (int i = 0; i < lines; i++) {
if (height < layout.getLineBottom(i)) {
// Vượt quá chiều cao trang, tạo trang mới
addPage(text.subSequence(startOffset, layout.getLineStart(i)));
startOffset = layout.getLineStart(i);
height = layout.getLineTop(i) + mHeight;
}
if (i == lines - 1) {
// Trang cuối cùng
addPage(text.subSequence(startOffset, layout.getLineEnd(i)));
return;
}
}
}
private void addPage(CharSequence text) {
mPages.add(text);
}
int size() {
return mPages.size();
}
CharSequence get(int index) {
return (index >= 0 && index < mPages.size()) ? mPages.get(index) : null;
}
}
Bước 5: Cấu hình Manifest (androidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.com.sample">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
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>
Chạy ứng dụng
Kết nối thiết bị Android, nhấn Run trong Android Studio. Sử dụng hai nút transparan bên trái/phải để điều hướng giữa các trang.

