Android PDF Reader with ListView.


This is a tutorial about implementing a basic PDF Reader using ListView and third party PDF Rendering library.

We will list PDF items from external storage into a ListView. When you click a PDF item, a new PDF Reader activity is opened and you can read the PDF with advanced capabilities like zooming, scrolling, swiping and pagination.

Android PDF Reader ListView

Android PDF Reader ListView

Android PDF Reader ListView

Gradle Scripts

Given we are using a third party PDF Renderer, we need to add dependencies in the app level build.gradle.

1. Build.gadle

  • Go over and dependencies. We are using android-pdf-viewer library.
    apply plugin: 'com.android.application'

    android {
        compileSdkVersion 24
        buildToolsVersion "24.0.1"

        defaultConfig {
            applicationId "com.tutorials.hp.listviewpdf"
            minSdkVersion 15
            targetSdkVersion 24
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:24.1.1'
        compile 'com.android.support:design:24.1.1'
        compile 'com.android.support:cardview-v7:24.1.1'
        compile 'com.github.barteksc:android-pdf-viewer:1.4.0'
    }

LAYOUTS

We'll work with 4 layouts:

1. activity_main.xml

  • Our MainActivity layout.
  • Root layout is CordinatorLayout.
  • This layout defines:

    1. appbar
    2. Toolbar
    3. FloatingActionButton.
  • We also include our content_main.xml here.
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout 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"
        android:fitsSystemWindows="true"
        tools:context="com.tutorials.hp.listviewpdf.MainActivity">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

        </android.support.design.widget.AppBarLayout>

        <include layout="@layout/content_main" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            android:src="@android:drawable/ic_dialog_email" />

    </android.support.design.widget.CoordinatorLayout>

2. content_main.xml

  • Still part of our MainActivity layout.
  • Root layout is RelativeLayout
  • It gets included inside the activity_main.xml.
  • Will hold our ListView onto which our PDFs will be displayed.
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout 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"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="com.tutorials.hp.listviewpdf.MainActivity"
        tools:showIn="@layout/activity_main">

        <ListView
            android:id="@+id/lv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />
    </RelativeLayout>

3. model.xml

  • Will model our custom rows for our listview.
  • The listview should contain images and text. The images will be an icon to indicate a PDF. Thus the user knows the list contains PDFs.
  • At the root tag is a ``android.support.v7.widget.CardView.
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal" android:layout_width="match_parent"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_margin="10dp"
        card_view:cardCornerRadius="5dp"
        card_view:cardElevation="5dp"
        android:layout_height="200dp">

            <LinearLayout
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <ImageView
                    android:id="@+id/pdfImage"
                    android:src="@drawable/pdf_icon"
                    android:layout_width="150dp"
                    android:layout_height="wrap_content" />

                <LinearLayout
                    android:orientation="vertical"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:textAppearance="?android:attr/textAppearanceLarge"
                        android:text="Name"
                        android:id="@+id/nameTxt"
                        android:padding="10dp"
                        android:textColor="@color/colorAccent"
                        android:textStyle="bold"
                        android:layout_alignParentLeft="true"
                        />

                </LinearLayout>

                </LinearLayout>
    </android.support.v7.widget.CardView>

4. activity_pdf.xml

  • Now this layout will be our PDF render layout.
  • It's our PDF Viewer layout.
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.tutorials.hp.listviewpdf.PDFActivity">

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

        <com.github.barteksc.pdfviewer.ScrollBar
            android:id="@+id/scrollBar"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true" />
    </RelativeLayout>

JAVA CLASSES

Let's now look at our 4 Java classes.

1. PDFDoc.java

This is our data object. Our POJO class.

POJO stands for Plain Old Java Object. We use this class to represent our data entity. In this case a PDF document.

This class will hold the PDF's:

  • name
  • path
    package com.tutorials.hp.listviewpdf;

    public class PDFDoc {
        String name,path;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }
    }

2. CustomAdapter.java

This is our adapter class. It will derive from android.widget.BaseAdapter. This is what makes the class an adapter.

Then we'll override a couple of methods given that BaseAdapter is an abstract class.

An adapter class basically adapts data to custom views. Like in our case we have a ListView with images and text. well that's a custom listview. So we need the adapter class to help in binding the data to this listview.

We also handle OnClick event for our ListView here. Hence opening the PDF Activity to view/read the PDF document.

    package com.tutorials.hp.listviewpdf;

    import android.content.Context;
    import android.content.Intent;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;

    import java.util.ArrayList;

    /**
     * Created by Oclemy on 7/28/2016 for ProgrammingWizards Channel and http://www.camposha.com.
     */
    public class CustomAdapter extends BaseAdapter {

        Context c;
        ArrayList<PDFDoc> pdfDocs;

        public CustomAdapter(Context c, ArrayList<PDFDoc> pdfDocs) {
            this.c = c;
            this.pdfDocs = pdfDocs;
        }

        @Override
        public int getCount() {
            return pdfDocs.size();
        }

        @Override
        public Object getItem(int i) {
            return pdfDocs.get(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            if(view==null)
            {
                //INFLATE CUSTOM LAYOUT
                view= LayoutInflater.from(c).inflate(R.layout.model,viewGroup,false);
            }

            final PDFDoc pdfDoc= (PDFDoc) this.getItem(i);

            TextView nameTxt= (TextView) view.findViewById(R.id.nameTxt);
            ImageView img= (ImageView) view.findViewById(R.id.pdfImage);

            //BIND DATA
            nameTxt.setText(pdfDoc.getName());
            img.setImageResource(R.drawable.pdf_icon);

            //VIEW ITEM CLICK
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                   openPDFView(pdfDoc.getPath());
                }
            });
            return view;
        }

        //OPEN PDF VIEW
        private void openPDFView(String path)
        {
            Intent i=new Intent(c,PDFActivity.class);
            i.putExtra("PATH",path);
            c.startActivity(i);
        }
    }

3. PDFActivity.java

This is class is an activity because we derive from android.support.v7.app.AppCompatActivity, which is a support version of android.app.activity.

This class is our PDF Renderer/PDF Viewer. Users will read PDF documents here.

The first activity(MainActivity) will be responsible for listing pdf documents. This activity, however, will be responsible or viewing them.

    package com.tutorials.hp.listviewpdf;

    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.Toast;

    import com.github.barteksc.pdfviewer.PDFView;
    import com.github.barteksc.pdfviewer.ScrollBar;
    import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener;

    import java.io.File;

    public class PDFActivity extends AppCompatActivity {

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

            //PDFVIEW SHALL DISPLAY OUR PDFS
            PDFView pdfView= (PDFView) findViewById(R.id.pdfView);
            //SCROLLBAR TO ENABLE SCROLLING
            ScrollBar scrollBar = (ScrollBar) findViewById(R.id.scrollBar);
            pdfView.setScrollBar(scrollBar);
            //VERTICAL SCROLLING
            scrollBar.setHorizontal(false);
            //SACRIFICE MEMORY FOR QUALITY
            //pdfView.useBestQuality(true)

            //UNPACK OUR DATA FROM INTENT
            Intent i=this.getIntent();
            String path=i.getExtras().getString("PATH");

            //GET THE PDF FILE
            File file=new File(path);

            if(file.canRead())
            {
                //LOAD IT
                pdfView.fromFile(file).defaultPage(1).onLoad(new OnLoadCompleteListener() {
                    @Override
                    public void loadComplete(int nbPages) {
                        Toast.makeText(PDFActivity.this, String.valueOf(nbPages), Toast.LENGTH_LONG).show();
                    }
                }).load();

            }

        }
    }

4. MainActivity.java

This is our launcher activity. This means when the app is run it is this activity that gets executed by default.

This class derives from android.support.v7.app.AppCompatActivity.

We proceed and override the OnCreate(0 method.

We will read our android external storage to obtain PDF documents from downloads directory here.

We will then list these pdf documents in the mainactivity.

    package com.tutorials.hp.listviewpdf;

    import android.os.Bundle;
    import android.os.Environment;
    import android.support.design.widget.FloatingActionButton;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.View;
    import android.widget.ListView;

    import java.io.File;
    import java.util.ArrayList;

    public class MainActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
            final ListView lv= (ListView) findViewById(R.id.lv);

            FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    lv.setAdapter(new CustomAdapter(MainActivity.this,getPDFs()));

                }
            });
        }

        private ArrayList<PDFDoc> getPDFs()

        {
            ArrayList<PDFDoc> pdfDocs=new ArrayList<>();
            //TARGET FOLDER
            File downloadsFolder= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

            PDFDoc pdfDoc;

            if(downloadsFolder.exists())
            {
                //GET ALL FILES IN DOWNLOAD FOLDER
                File[] files=downloadsFolder.listFiles();

                //LOOP THRU THOSE FILES GETTING NAME AND URI
                for (int i=0;i<files.length;i++)
                {
                    File file=files[i];

                    if(file.getPath().endsWith("pdf"))
                    {
                        pdfDoc=new PDFDoc();
                        pdfDoc.setName(file.getName());
                        pdfDoc.setPath(file.getAbsolutePath());

                        pdfDocs.add(pdfDoc);
                    }

                }
            }

            return pdfDocs;
        }
    }

AndroidManifest.xml

Add this permission in your androidmanifest:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Download the source code here.

How do You Feel after reading this?

According to scientists, we humans have 8 primary innate emotions: joy, acceptance, fear, surprise, sadness, disgust, anger, and anticipation. Feel free to tell us how you feel about this article using these emotes or via the comment section. This feedback helps us gauge our progress.

Help me Grow.

I set myself some growth ambitions I desire to achieve by this year's end regarding this website and my youtube channel. Am halfway. Help me reach them by:




Recommendations


What do You Think


Previous Post Next Post