Android预览pdf文档

谷歌为我们提供了PdfRender工具类对pdf文档进行渲染,首先看一下PdfRender的构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Creates a new instance.
* <p>
* <strong>Note:</strong> The provided file descriptor must be <strong>seekable</strong>,
* i.e. its data being randomly accessed, e.g. pointing to a file.
* </p>
* <p>
* <strong>Note:</strong> This class takes ownership of the passed in file descriptor
* and is responsible for closing it when the renderer is closed.
* </p>
* <p>
* If the file is from an untrusted source it is recommended to run the renderer in a separate,
* isolated process with minimal permissions to limit the impact of security exploits.
* </p>
*
* @param input Seekable file descriptor to read from.
*
* @throws java.io.IOException If an error occurs while reading the file.
* @throws java.lang.SecurityException If the file requires a password or
* the security scheme is not supported.
*/
public PdfRenderer(@NonNull ParcelFileDescriptor input){
//略...
}

可以看构造方法中的参数是ParcelFileDescriptor的一个实例,那么ParcelFileDescriptor类是做什么的呢?ParcelFileDescriptor是Android 提供的一种数据结构,支持数据的写入和写出。我们通过ParcelFileDescriptor#open 建立文件和ParcelFileDescriptor的联系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Create a new ParcelFileDescriptor accessing a given file.
*
* @param file The file to be opened.
* @param mode The desired access mode, must be one of
* {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or
* {@link #MODE_READ_WRITE}; may also be any combination of
* {@link #MODE_CREATE}, {@link #MODE_TRUNCATE},
* {@link #MODE_WORLD_READABLE}, and
* {@link #MODE_WORLD_WRITEABLE}.
* @return a new ParcelFileDescriptor pointing to the given file.
* @throws FileNotFoundException if the given file does not exist or can not
* be opened with the requested mode.
* @see #parseMode(String)
*/
public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
final FileDescriptor fd = openInternal(file, mode);
if (fd == null) return null;
return new ParcelFileDescriptor(fd);
}

从该方法中可以看出ParcelFileDescriptor#open实际操作的是FileDescriptor,仔细阅读FileDescriptor的注释,FileDescriptor是文件操作符。FileDescriptor可以用来表示开放文件、开放套接字。以FileDecriptor 表示文件来说,当FileDescroptor表示某文件 时,我们可以通俗的将FileDescriptor看成是该文件。但是,我们不能直接通过FileDescription对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputSteam,再对文件进行操作。这里有对FileDescription详细描述

1
2
3
4
5
6
7
8
 ParcelFileDescriptor mFileDescriptor;
PdfRenderer mPdfRenderer;
//根据文件对象创建
mFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
// This is the PdfRenderer we use to render the PDF.
if (mFileDescriptor != null) {
mPdfRenderer = new PdfRenderer(mFileDescriptor);
}

打开pdf指定页面和生成当前页面的Bitmap展示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private PdfRenderer.Page mCurrentPage;
private void showPage(int index) {
if (mPdfRenderer.getPageCount() <= index) {
return;
}
// Make sure to close the current page before opening another one.
if (null != mCurrentPage) {
mCurrentPage.close();
}
// Use `openPage` to open a specific page in PDF.
mCurrentPage = mPdfRenderer.openPage(index);
// Important: the destination bitmap must be ARGB (not RGB).
Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(),Bitmap.Config.ARGB_8888);
// Here, we render the page onto the Bitmap.
// To render a portion of the page, use the second and third parameter. Pass nulls to get
// the default result.
// Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter.
mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
// We are ready to show the Bitmap to user.
mImageView.setImageBitmap(bitmap);
updateUi();
}

完整实例:

页面R.layout.fragment_pdf_renderer_basic:

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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".PdfRendererBasicFragment">

<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/white"
android:scaleType="fitCenter"
android:contentDescription="@null"/>

<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:measureWithLargestChild="true"
android:orientation="horizontal">

<Button
android:id="@+id/previous"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/previous" />

<Button
android:id="@+id/next"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/next" />
</LinearLayout>

</LinearLayout>

渲染pdf代码,pdf文档来源于asset目录下:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
public class PdfRendererBasicFragment extends Fragment implements View.OnClickListener {

/**
* Key string for saving the state of current page index.
*/
private static final String STATE_CURRENT_PAGE_INDEX = "current_page_index";

/**
* The filename of the PDF.
*/
private static final String FILENAME = "sample.pdf";

/**
* File descriptor of the PDF.
*/
private ParcelFileDescriptor mFileDescriptor;

/**
* {@link android.graphics.pdf.PdfRenderer} to render the PDF.
*/
private PdfRenderer mPdfRenderer;

/**
* Page that is currently shown on the screen.
*/
private PdfRenderer.Page mCurrentPage;

/**
* {@link android.widget.ImageView} that shows a PDF page as a {@link android.graphics.Bitmap}
*/
private ImageView mImageView;

/**
* {@link android.widget.Button} to move to the previous page.
*/
private Button mButtonPrevious;

/**
* {@link android.widget.Button} to move to the next page.
*/
private Button mButtonNext;

/**
* PDF page index
*/
private int mPageIndex;

public PdfRendererBasicFragment() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_pdf_renderer_basic, container, false);
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Retain view references.
mImageView = (ImageView) view.findViewById(R.id.image);
mButtonPrevious = (Button) view.findViewById(R.id.previous);
mButtonNext = (Button) view.findViewById(R.id.next);
// Bind events.
mButtonPrevious.setOnClickListener(this);
mButtonNext.setOnClickListener(this);

mPageIndex = 0;
// If there is a savedInstanceState (screen orientations, etc.), we restore the page index.
if (null != savedInstanceState) {
mPageIndex = savedInstanceState.getInt(STATE_CURRENT_PAGE_INDEX, 0);
}
}

@Override
public void onStart() {
super.onStart();
try {
openRenderer(getActivity());
showPage(mPageIndex);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getActivity(), "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}

@Override
public void onStop() {
try {
closeRenderer();
} catch (IOException e) {
e.printStackTrace();
}
super.onStop();
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (null != mCurrentPage) {
outState.putInt(STATE_CURRENT_PAGE_INDEX, mCurrentPage.getIndex());
}
}

/**
* Sets up a {@link android.graphics.pdf.PdfRenderer} and related resources.
*/
private void openRenderer(Context context) throws IOException {
// In this sample, we read a PDF from the assets directory.
File file = new File(context.getCacheDir(), FILENAME);
if (!file.exists()) {
// Since PdfRenderer cannot handle the compressed asset file directly, we copy it into
// the cache directory.
InputStream asset = context.getAssets().open(FILENAME);
FileOutputStream output = new FileOutputStream(file);
final byte[] buffer = new byte[1024];
int size;
while ((size = asset.read(buffer)) != -1) {
output.write(buffer, 0, size);
}
asset.close();
output.close();
}
mFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
// This is the PdfRenderer we use to render the PDF.
if (mFileDescriptor != null) {
mPdfRenderer = new PdfRenderer(mFileDescriptor);
}
}

/**
* Closes the {@link android.graphics.pdf.PdfRenderer} and related resources.
*
* @throws java.io.IOException When the PDF file cannot be closed.
*/
private void closeRenderer() throws IOException {
if (null != mCurrentPage) {
mCurrentPage.close();
}
mPdfRenderer.close();
mFileDescriptor.close();
}

/**
* Shows the specified page of PDF to the screen.
*
* @param index The page index.
*/
private void showPage(int index) {
if (mPdfRenderer.getPageCount() <= index) {
return;
}
// Make sure to close the current page before opening another one.
if (null != mCurrentPage) {
mCurrentPage.close();
}
// Use `openPage` to open a specific page in PDF.
mCurrentPage = mPdfRenderer.openPage(index);
// Important: the destination bitmap must be ARGB (not RGB).
Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(),
Bitmap.Config.ARGB_8888);
// Here, we render the page onto the Bitmap.
// To render a portion of the page, use the second and third parameter. Pass nulls to get
// the default result.
// Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter.
mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
// We are ready to show the Bitmap to user.
mImageView.setImageBitmap(bitmap);
updateUi();
}

/**
* Updates the state of 2 control buttons in response to the current page index.
*/
private void updateUi() {
int index = mCurrentPage.getIndex();
int pageCount = mPdfRenderer.getPageCount();
mButtonPrevious.setEnabled(0 != index);
mButtonNext.setEnabled(index + 1 < pageCount);
getActivity().setTitle(getString(R.string.app_name_with_index, index + 1, pageCount));
}

/**
* Gets the number of pages in the PDF. This method is marked as public for testing.
*
* @return The number of pages.
*/
public int getPageCount() {
return mPdfRenderer.getPageCount();
}

@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.previous: {
// Move to the previous page
showPage(mCurrentPage.getIndex() - 1);
break;
}
case R.id.next: {
// Move to the next page
showPage(mCurrentPage.getIndex() + 1);
break;
}
}
}

}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×