Як візуалізувати PDF-файл в Android


213

Android не має підтримки PDF у своїх бібліотеках. Чи є спосіб відобразити PDF-файли в додатках для Android?


5
Це хороший приклад для показу PDF-файлів. Щоб скористатися цим файлом, потрібно посилатись на файл Readme.txt. github.com/jblough/Android-Pdf-Viewer-Library
Рамеш Акула

1
ось приклад використання цієї бібліотеки: stackoverflow.com/a/16294833/2027232
Ніколя Тайлер

Ви HVA , щоб подивитися на stackoverflow.com/questions/22498937 / ...
Самет ÖZTOPRAK

Відповіді:


68

Оскільки API рівня 21 (Lollipop) Android забезпечує клас PdfRenderer :

// create a new renderer
 PdfRenderer renderer = new PdfRenderer(getSeekableFileDescriptor());

 // let us just render all pages
 final int pageCount = renderer.getPageCount();
 for (int i = 0; i < pageCount; i++) {
     Page page = renderer.openPage(i);

     // say we render for showing on the screen
     page.render(mBitmap, null, null, Page.RENDER_MODE_FOR_DISPLAY);

     // do stuff with the bitmap

     // close the page
     page.close();
 }

 // close the renderer
 renderer.close();

Для отримання додаткової інформації дивіться зразок програми .

Для старих API я рекомендую бібліотеку Android PdfViewer , це дуже швидкий і простий у використанні, ліцензований під ліцензією Apache License 2.0:

pdfView.fromAsset(String)
  .pages(0, 2, 1, 3, 3, 3) // all pages are displayed by default
  .enableSwipe(true)
  .swipeHorizontal(false)
  .enableDoubletap(true)
  .defaultPage(0)
  .onDraw(onDrawListener)
  .onLoad(onLoadCompleteListener)
  .onPageChange(onPageChangeListener)
  .onPageScroll(onPageScrollListener)
  .onError(onErrorListener)
  .enableAnnotationRendering(false)
  .password(null)
  .scrollHandle(null)
  .load();

12
Бібліотека PDFView чудова, але зауважте, що вона належить до загальної громадськості GNU, а не до меншої GPL. Це може ускладнити включення до комерційного програмного забезпечення.
Сем

Так, @Sam має рацію. Особисто мені довелося використовувати PDF.js через проблеми з ліцензією у старих версіях Android, але візуалізація надзвичайно повільна і важко змусити її працювати через зв’язок Java-JavaScript.
Miloš Černilovský

1
Для старих версій чи є спосіб показати їх, не завантажуючи і не зберігаючи файл десь у додатку?
Гохан Арік

1
Не могли б ви зробити більш детальний приклад, використовуючи PDFView Library? Що ми маємо вкласти pdfName? Що робити функції onDraw, onLoadі onPageChangeслухач виглядає? Дякую за допомогу :)
Трієт Доан

2
До відповіді додано нову бібліотеку PDF, що має ліцензію Apache License 2.0.
Miloš Černilovský

61

Взято з мого блогу:

public class MyPdfViewActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    WebView mWebView=new WebView(MyPdfViewActivity.this);
    mWebView.getSettings().setJavaScriptEnabled(true);
    mWebView.getSettings().setPluginsEnabled(true);
    mWebView.loadUrl("https://docs.google.com/gview?embedded=true&url="+LinkTo);
    setContentView(mWebView);
  }
}

70
Це справді не дуже чисто. Наприклад, якщо Google коли-небудь вирішить змінити URL-адресу Google Документів, ваш додаток буде зламано, і вам доведеться натиснути оновлення, щоб користувачі змогли переглянути документи знову.
Марк

3
це дозволяє мені переглядати першу сторінку, а посилання "Завантажити" внизу не активне ... будь-які ідеї? Крім того, схоже, що setPluginsEnabled більше не входить до класу WebView.
whyoz

7
Це не працюватиме, якщо немає підключення до Інтернету, і для нього потрібен дозвіл INTERNET
Mani

4
Це працювало раніше. Але останнім часом на ній постійно відображається "На жаль, виникла проблема з переглядом цього документа" Я думаю, що Google змінив деякі речі на їх стороні, що спричинило цю проблему.
Лі Йі Гонг

1
Для мене це було дійсно повільним завантаженням та прокруткою документа.
nilsi

33

Я зробив гібридний підхід з деяких відповідей на це та інших подібних дописів:

Це рішення перевіряє, чи встановлено додаток для читання PDF, чи виконує таке: - Якщо встановлено зчитувач, завантажте файл PDF на пристрій та запустіть програму для читання PDF - Якщо не встановлено жодного зчитувача, запитайте користувача, чи хоче він переглянути PDF-файл в Інтернеті через Google Диск

ПРИМІТКА! У цьому рішенні використовується DownloadManagerклас Android , який був представлений в API9 (Android 2.3 або Gingerbread). Це означає, що він не працює на Android 2.2 або новіших версіях.

Я написав повідомлення в блозі про це тут , але я надав повний код нижче для повноти:

public class PDFTools {
    private static final String GOOGLE_DRIVE_PDF_READER_PREFIX = "http://drive.google.com/viewer?url=";
    private static final String PDF_MIME_TYPE = "application/pdf";
    private static final String HTML_MIME_TYPE = "text/html";

    /**
     * If a PDF reader is installed, download the PDF file and open it in a reader. 
     * Otherwise ask the user if he/she wants to view it in the Google Drive online PDF reader.<br />
     * <br />
     * <b>BEWARE:</b> This method
     * @param context
     * @param pdfUrl
     * @return
     */
    public static void showPDFUrl( final Context context, final String pdfUrl ) {
        if ( isPDFSupported( context ) ) {
            downloadAndOpenPDF(context, pdfUrl);
        } else {
            askToOpenPDFThroughGoogleDrive( context, pdfUrl );
        }
    }

    /**
     * Downloads a PDF with the Android DownloadManager and opens it with an installed PDF reader app.
     * @param context
     * @param pdfUrl
     */
    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
    public static void downloadAndOpenPDF(final Context context, final String pdfUrl) {
        // Get filename
        final String filename = pdfUrl.substring( pdfUrl.lastIndexOf( "/" ) + 1 );
        // The place where the downloaded PDF file will be put
        final File tempFile = new File( context.getExternalFilesDir( Environment.DIRECTORY_DOWNLOADS ), filename );
        if ( tempFile.exists() ) {
            // If we have downloaded the file before, just go ahead and show it.
            openPDF( context, Uri.fromFile( tempFile ) );
            return;
        }

        // Show progress dialog while downloading
        final ProgressDialog progress = ProgressDialog.show( context, context.getString( R.string.pdf_show_local_progress_title ), context.getString( R.string.pdf_show_local_progress_content ), true );

        // Create the download request
        DownloadManager.Request r = new DownloadManager.Request( Uri.parse( pdfUrl ) );
        r.setDestinationInExternalFilesDir( context, Environment.DIRECTORY_DOWNLOADS, filename );
        final DownloadManager dm = (DownloadManager) context.getSystemService( Context.DOWNLOAD_SERVICE );
        BroadcastReceiver onComplete = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if ( !progress.isShowing() ) {
                    return;
                }
                context.unregisterReceiver( this );

                progress.dismiss();
                long downloadId = intent.getLongExtra( DownloadManager.EXTRA_DOWNLOAD_ID, -1 );
                Cursor c = dm.query( new DownloadManager.Query().setFilterById( downloadId ) );

                if ( c.moveToFirst() ) {
                    int status = c.getInt( c.getColumnIndex( DownloadManager.COLUMN_STATUS ) );
                    if ( status == DownloadManager.STATUS_SUCCESSFUL ) {
                        openPDF( context, Uri.fromFile( tempFile ) );
                    }
                }
                c.close();
            }
        };
        context.registerReceiver( onComplete, new IntentFilter( DownloadManager.ACTION_DOWNLOAD_COMPLETE ) );

        // Enqueue the request
        dm.enqueue( r );
    }

    /**
     * Show a dialog asking the user if he wants to open the PDF through Google Drive
     * @param context
     * @param pdfUrl
     */
    public static void askToOpenPDFThroughGoogleDrive( final Context context, final String pdfUrl ) {
        new AlertDialog.Builder( context )
            .setTitle( R.string.pdf_show_online_dialog_title )
            .setMessage( R.string.pdf_show_online_dialog_question )
            .setNegativeButton( R.string.pdf_show_online_dialog_button_no, null )
            .setPositiveButton( R.string.pdf_show_online_dialog_button_yes, new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    openPDFThroughGoogleDrive(context, pdfUrl); 
                }
            })
            .show();
    }

    /**
     * Launches a browser to view the PDF through Google Drive
     * @param context
     * @param pdfUrl
     */
    public static void openPDFThroughGoogleDrive(final Context context, final String pdfUrl) {
        Intent i = new Intent( Intent.ACTION_VIEW );
        i.setDataAndType(Uri.parse(GOOGLE_DRIVE_PDF_READER_PREFIX + pdfUrl ), HTML_MIME_TYPE );
        context.startActivity( i );
    }
    /**
     * Open a local PDF file with an installed reader
     * @param context
     * @param localUri
     */
    public static final void openPDF(Context context, Uri localUri ) {
        Intent i = new Intent( Intent.ACTION_VIEW );
        i.setDataAndType( localUri, PDF_MIME_TYPE );
        context.startActivity( i );
    }
    /**
     * Checks if any apps are installed that supports reading of PDF files.
     * @param context
     * @return
     */
    public static boolean isPDFSupported( Context context ) {
        Intent i = new Intent( Intent.ACTION_VIEW );
        final File tempFile = new File( context.getExternalFilesDir( Environment.DIRECTORY_DOWNLOADS ), "test.pdf" );
        i.setDataAndType( Uri.fromFile( tempFile ), PDF_MIME_TYPE );
        return context.getPackageManager().queryIntentActivities( i, PackageManager.MATCH_DEFAULT_ONLY ).size() > 0;
    }

}

8

Завантажте вихідний код тут ( Відобразити PDF-файл у моєму додатку для Android )

Додайте цю залежність у свій клас: компілюйте 'com.github.barteksc: android-pdf-viewer: 2.0.3'

activity_main.xml

<RelativeLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    xmlns:android="http://schemas.android.com/apk/res/android" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="@color/colorPrimaryDark"
        android:text="View PDF"
        android:textColor="#ffffff"
        android:id="@+id/tv_header"
        android:textSize="18dp"
        android:gravity="center"></TextView>

    <com.github.barteksc.pdfviewer.PDFView
        android:id="@+id/pdfView"
        android:layout_below="@+id/tv_header"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


    </RelativeLayout>

MainActivity.java

import android.app.Activity;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import com.github.barteksc.pdfviewer.PDFView;
import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener;
import com.github.barteksc.pdfviewer.listener.OnPageChangeListener;
import com.github.barteksc.pdfviewer.scroll.DefaultScrollHandle;
import com.shockwave.pdfium.PdfDocument;

import java.util.List;

public class MainActivity extends Activity implements OnPageChangeListener,OnLoadCompleteListener{
    private static final String TAG = MainActivity.class.getSimpleName();
    public static final String SAMPLE_FILE = "android_tutorial.pdf";
    PDFView pdfView;
    Integer pageNumber = 0;
    String pdfFileName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        pdfView= (PDFView)findViewById(R.id.pdfView);
        displayFromAsset(SAMPLE_FILE);
    }

    private void displayFromAsset(String assetFileName) {
        pdfFileName = assetFileName;

        pdfView.fromAsset(SAMPLE_FILE)
                .defaultPage(pageNumber)
                .enableSwipe(true)

                .swipeHorizontal(false)
                .onPageChange(this)
                .enableAnnotationRendering(true)
                .onLoad(this)
                .scrollHandle(new DefaultScrollHandle(this))
                .load();
    }


    @Override
    public void onPageChanged(int page, int pageCount) {
        pageNumber = page;
        setTitle(String.format("%s %s / %s", pdfFileName, page + 1, pageCount));
    }


    @Override
    public void loadComplete(int nbPages) {
        PdfDocument.Meta meta = pdfView.getDocumentMeta();
        printBookmarksTree(pdfView.getTableOfContents(), "-");

    }

    public void printBookmarksTree(List<PdfDocument.Bookmark> tree, String sep) {
        for (PdfDocument.Bookmark b : tree) {

            Log.e(TAG, String.format("%s %s, p %d", sep, b.getTitle(), b.getPageIdx()));

            if (b.hasChildren()) {
                printBookmarksTree(b.getChildren(), sep + "-");
            }
        }
    }

}

підказка: assetFileNamepdf-файл повинен існувати у assetsпапці
ahmednabil88

7

Нарешті я зміг змінити код butelo, щоб відкрити будь-який файл PDF у файловій системі Android за допомогою pdf.js. Код можна знайти на моєму GitHub

Що я зробив, було змінено pdffile.jsна читання HTML-аргументу, fileяк це:

var url = getURLParameter('file');

function getURLParameter(name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20'))||null}

Отже, вам потрібно лише додати шлях до файлу після index.htmlподібного:

Uri path = Uri.parse(Environment.getExternalStorageDirectory().toString() + "/data/test.pdf");
webView.loadUrl("file:///android_asset/pdfviewer/index.html?file=" + path);

Оновіть pathзмінну, щоб вказати на дійсний PDF у файловій системі Adroid.


привіт Пол, я також використовував цей приклад, але він показує порожній екран на WebView в API рівня 16, чи можете у вас виникнути проблеми з цим питанням ??
Riddhish.Chaudhari

Де зберігається pdf-файл? Ви не можете завантажити pdf з папки активів. Ви можете завантажити або з SD-картки, або із захищеного внутрішнього сховища програми. Також перевірте свою логіт-коду на наявність помилок, пов’язаних із переглядом веб-сторінок.
Шантану Пол

Uri шлях = Uri.parse (Environment.getExternalStorageDirectory (). ToString () + "/example4.pdf"); webView.loadUrl ("файл: ///android_asset/pdfviewer/index.html? file =" + шлях);
Riddhish.Chaudhari

@Paul Я використовую наведений вище код, він добре працює з API 19, але нижче він не працює. Чому ??
Riddhish.Chaudhari

Будь ласка, перегляньте це для сумісності зі старими версіями Android: github.com/pauldmps/Android-pdf.js/isissue/1 github.com/pauldmps/Android-pdf.js/isissue/2
Shantanu Paul

4

Ви можете скористатися простим методом імпорту

implementation 'com.github.barteksc:android-pdf-viewer:2.8.2'

і XML-код є

<com.github.barteksc.pdfviewer.PDFView
        android:id="@+id/pdfv"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </com.github.barteksc.pdfviewer.PDFView>

а просто оголосити та додати файл у папку ресурсу та просто призначити ім’я

   PDFView  pdfView=findViewById(R.id.pdfv);       
    pdfView.fromAsset("agl.pdf").load();

Як ви декларуєте PDFView у Котліні?
Микола Фермер

@NicholasFarmer перевірити це посилання stackoverflow.com/questions/56613766 / ...
Акшай Шетті

6
Будьте обережні, це може додати 16 ~ 18 Мб поверх вашої програми. Ця бібліотека використовує, PdfiumAndroidщо вже є 18,4 МБ
П'єр

підказка: agl.pdfфайл повинен існувати у assetsпапці
ahmednabil88

1

Щоб додати трохи світла до цього, мені доведеться перейти з рішенням pdf.js від Mozilla. Ось посилання на вже добре написану реалізацію цього: https://bitbucket.org/butelo/pdfviewer/ .

Ось зміни, які я додав у своїй діяльності Android:

private String getInternalPDFURL(String interalPDFName){
    return "file:///android_asset/pdfviewer/index.html?pdf=" + interalPDFName + ".pdf";
}

Ось зміни, внесені мною pdffile.js:

var url = '../' + getPDFURL();

function getPDFURL(){
    var query = window.location.search.substring(1);
    var vars = query.split("=");
    var pdfPage = vars[1];
    return pdfPage;
}

1

Я використовував наведений нижче код, щоб відкрити та надрукувати PDF за допомогою Wi-Fi. Я надсилаю весь свій код, і я сподіваюся, що він корисний.

public class MainActivity extends Activity {

    int Result_code = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button mButton = (Button)findViewById(R.id.button1);

        mButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

                 PrintManager printManager = (PrintManager)getSystemService(Context.PRINT_SERVICE);
                String jobName =  " Document";
                printManager.print(jobName, pda, null);
            }
        });
    }


    public void openDocument(String name) {

        Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
        File file = new File(name);
        String extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString());
        String mimetype = android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

        if (extension.equalsIgnoreCase("") || mimetype == null) {
            // if there is no extension or there is no definite mimetype, still try to open the file
            intent.setDataAndType(Uri.fromFile(file), "text/*");
        }
        else {
            intent.setDataAndType(Uri.fromFile(file), mimetype);
        }

        // custom message for the intent
        startActivityForResult((Intent.createChooser(intent, "Choose an Application:")), Result_code);
        //startActivityForResult(intent, Result_code);
        //Toast.makeText(getApplicationContext(),"There are no email clients installed.", Toast.LENGTH_SHORT).show();
    }


    @SuppressLint("NewApi")
    PrintDocumentAdapter pda = new PrintDocumentAdapter(){

        @Override
        public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback){
            InputStream input = null;
            OutputStream output = null;

            try {
                String filename = Environment.getExternalStorageDirectory()    + "/" + "Holiday.pdf";
                File file = new File(filename);
                input = new FileInputStream(file);
                output = new FileOutputStream(destination.getFileDescriptor());

                byte[] buf = new byte[1024];
                int bytesRead;

                while ((bytesRead = input.read(buf)) > 0) {
                     output.write(buf, 0, bytesRead);
                }

                callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
            }
            catch (FileNotFoundException ee){
                //Catch exception
            }
            catch (Exception e) {
                //Catch exception
            }
            finally {
                try {
                    input.close();
                    output.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras){

            if (cancellationSignal.isCanceled()) {
                callback.onLayoutCancelled();
                return;
            }

           // int pages = computePageCount(newAttributes);

            PrintDocumentInfo pdi = new PrintDocumentInfo.Builder("Name of file").setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).build();

            callback.onLayoutFinished(pdi, true);
        }
    };
}

0

У веб-перегляді Android немає жодного способу попереднього перегляду PDF-документа. Якщо ви хочете переглянути base64 pdf. Це потрібно для сторонньої бібліотеки.

build.Gradle

compile 'com.github.barteksc:android-pdf-viewer:2.7.0'

dialog_pdf_viewer

<?xml version="1.0" encoding="utf-8"?>

<!--
  ~ Copyright (c) 2017.
  ~ Samet Öztoprak
  ~ All rights reserved.
  -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/dialog_pdf_viewer_close"
        style="@style/ExitButtonImageViewStyle"
        android:src="@drawable/popup_exit" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/white"
        android:orientation="vertical">

        <com.github.barteksc.pdfviewer.PDFView
            android:id="@+id/pdfView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </LinearLayout>

    <View style="@style/HorizontalLine" />

    <com.pozitron.commons.customviews.ButtonFont
        android:id="@+id/dialog_pdf_viewer_button"
        style="@style/ButtonPrimary2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="15dp"
        android:text="@string/agreed" />

</LinearLayout>

DailogPDFViewer.java

public class DialogPdfViewer extends Dialog {
    PDFView pdfView;
    byte[] decodedString;

    public interface OnDialogPdfViewerListener {
        void onAgreeClick(DialogPdfViewer dialogFullEula);

        void onCloseClick(DialogPdfViewer dialogFullEula);
    }

    public DialogPdfViewer(Context context, String base64, final DialogPdfViewer.OnDialogPdfViewerListener onDialogPdfViewerListener) {
        super(context);

        setContentView(R.layout.dialog_pdf_viewer);
        findViewById(R.id.dialog_pdf_viewer_close).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onDialogPdfViewerListener.onCloseClick(DialogPdfViewer.this);
            }
        });

        findViewById(R.id.dialog_pdf_viewer_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onDialogPdfViewerListener.onAgreeClick(DialogPdfViewer.this);
            }
        });

        decodedString = Base64.decode(base64.toString(), Base64.DEFAULT);

        pdfView = ((PDFView) findViewById(R.id.pdfView));
        pdfView.fromBytes(decodedString).load();

        setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
                    onDialogPdfViewerListener.onCloseClick(DialogPdfViewer.this);
                }
                return true;
            }
        });

    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.