Blog Post

React Native UI Component for Android

Reinhard Hafenscher
Illustration: React Native UI Component for Android

Last week, we announced that our PSPDFKit React Native library now comes with a native UI component for iOS. Today, we’re happy to share that we now support this on Android as well. We already talked about how our new component works in our original announcement blog post, so here we’ll focus on some of the Android-specific challenges we faced.

The Default ReactActivity

The very first issue we ran into was that, by default, your React Native app will use a ReactActivity to host your application. This posed a problem when trying to integrate our PdfFragment, since it requires the hosting activity to extend from FragmentActivity. As a workaround, we provide a ReactFragmentActivity, which works as a drop-in replacement for the ReactActivity. If your app is using react-native-navigation, everything will simply work, since react-native-navigation comes with Activity subclasses that extend from AppCompatActivity.

Integrating a Fragment into the View Hierarchy

With that out of the way, it was time to actually integrate the PdfFragment, which, as it turns out, is simpler than we thought. Our PdfView uses the FragmentManager provided by the hosting Activity to add a PdfFragment, thereby attaching the view to itself.

The only issue we ran into here was that when the configuration changes, the entire view hierarchy is recreated. This causes a crash, because the FragmentManager will try to reattach the Fragment to a view with the ID we used when adding it. But it can’t do this, because React Native hasn’t yet created those views. The way we solved this is by requiring you to specify a unique fragmentTag when adding a PSPDFKitView to your React component.

Our PdfView also contains many supplemental views — such as our PdfThumbnailBar — that aren’t contained in the PdfFragment. With all the views correctly attached, we ran into our next issue.

The React Native Layout System

React Native provides its own layout engine. For this to work, the ReactRootView actually swallows all layout events dispatched by the Android system. This works perfectly as long as all your layouts are created using React Native. However, where this breaks down is when your native UI component contains subviews that require their own layout.

viewHierarchy

As you can see above, there are quite a few subviews in our PdfView that need the layout events dispatched by Android. There’s already an issue for this in the React Native repo, but since we don’t know when a proper fix may be ready, we decided to ship this with our own makeshift workaround inside our PdfView. What our workaround does — regardless of if it’s necessary or not — is lay out all children of the PdfView on every frame again. That looks something like this:

Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
            child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
        }
        getViewTreeObserver().dispatchOnGlobalLayout();
    }
    Choreographer.getInstance().postFrameCallback(this);
});

ℹ️ Note: This may cause your app to misbehave or performance to degrade, so use it at your own risk.

PSPDFKit

The final issue we encountered was the way our own framework is structured internally. Our native module’s present method uses a PdfActivity to present your PDFs. The PdfActivity contains a PdfFragment responsible for rendering the document, as well as a bunch of supplemental views that provide access to different features, such as the table of contents.

What this means is that our native UI component would actually need to reimplement all the features our PdfActivity already provides. Since that isn’t feasible at this time, we decided to expose a minimal API required to access the basic functionality of our PdfFragment — namely displaying and annotating documents.

Since our PSPDFKitView component doesn’t provide its own UI, we expose an enterAnnotationCreationMode function that allows you to start the annotation creation mode from your React UI. Here’s an example of how to use this:

<PSPDFKitView
  ref="pdfView"
  document="file:///android_asset/Annual Report.pdf"
  pageIndex={4}
  fragmentTag="PDF1"
  onStateChanged={event => {
    this.setState({
      currentPageIndex: event.currentPageIndex,
      pageCount: event.pageCount,
      annotationCreationActive: event.annotationCreationActive,
    });
  }}
  style={{ flex: 1}}
/>
<Button
  onPress={() => {
    if (this.state.annotationCreationActive) {
      this.refs.pdfView.exitCurrentlyActiveMode();
    } else {
      this.refs.pdfView.enterAnnotationCreationMode();
    }
  }}
  title={this.state.annotationCreationActive ? "Exit" : "Create Annotations"}
/>

ℹ️ Note: You can find all provided methods in the documentation for PSPDFKitView.

You can find example usages of this and all the other things we talked about in our Catalog.

Conclusion

While there’s still a lot of work to do on our native UI component, we now have a basic version you can start using today. If you have any questions about PSPDFKit for React Native, please don’t hesitate to reach out to us. We’re happy to help!

Related Products
PSPDFKit for React Native

Product Page
Guides
Example Projects

Share Post
Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
PRODUCTS  |  Android • Releases

Android 2024.1 Update: Advanced Content Editing and Digital Signatures, Plus Expanded Jetpack Compose Support

TUTORIALS  |  Android • How To

How to Persist Zoom While Scrolling through a Document Using the PSPDFKit Android Library

CUSTOMER STORIES  |  Case Study • React Native • iOS • Android

Case Study: How Trinoor Uses PSPDFKit to Drive Operational Excellence with Flexible, Mobile Applications for the Energy and Utilities Market