3

I'm trying to populate a LinearLayout inside simple_pdf_example.xml with 10 printed_order_element2.xml just so I can generate a PDF with a ListView (which is actually a LinearLayout).

The problem is that when I do linearLayoutView.addView(v) 10 times, I don't see v inside the LinearLayout. I just see the original item I added in the xml just to see if the LinearLayout was rendering.

SimplePDFSaver.java:

package com.mypackage.example;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;

import android.content.Intent;
import android.graphics.pdf.PdfDocument;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;

import java.io.File;
import java.io.FileOutputStream;


public class SimplePDFSaver extends AppCompatActivity {
    private static final String TAG = "SimplePDFSaver";

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

    public void generatePDF() {
        LayoutInflater inflater = getLayoutInflater();
        View pdfLayout = inflater.inflate(R.layout.simple_pdf_example,
            findViewById(android.R.id.content),
            false);


        int sizeSpec = View.MeasureSpec.makeMeasureSpec(2480, View.MeasureSpec.EXACTLY);
        int sizeSpec2 = View.MeasureSpec.makeMeasureSpec(3508, View.MeasureSpec.EXACTLY);
        pdfLayout.measure(sizeSpec, sizeSpec2);

        int width = pdfLayout.getMeasuredWidth();
        int height = pdfLayout.getMeasuredHeight();
        pdfLayout.layout(0, 0, width, height);

        LinearLayout linearLayoutView = pdfLayout.findViewById(R.id.linearLayoutView);

        for (int i=0; i<10; i++) {
            View v = getLayoutInflater().inflate(R.layout.printed_order_element2, null);
            linearLayoutView.addView(v);
        }

        Runnable r = new Runnable() {
            @Override
            public void run() {

                PdfDocument document = new PdfDocument();
                PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(2480, 3508, 0).create();
                PdfDocument.Page page = document.startPage(pageInfo);

                pdfLayout.draw(page.getCanvas());
                document.finishPage(page);

                FileOutputStream outStream;
                File file = new File(getExternalFilesDir(null), "file.pdf");
                try {
                    outStream = new FileOutputStream(file);
                    document.writeTo(outStream);
                    document.close();
                    outStream.flush();
                    outStream.getFD().sync();
                    outStream.close();

                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    Uri uri = FileProvider.getUriForFile(SimplePDFSaver.this, BuildConfig.APPLICATION_ID + ".provider", file);
                    intent.setDataAndType(uri, "application/pdf");
                    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    startActivity(intent);
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.d(TAG, e.toString());
                }
            }
        };
        new Thread(r).start();
    }
}

simple_pdf_example.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/linearLayoutView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView10">


        <TextView
            android:id="@+id/textView13"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Just to see if linearLayoutView rendered" />
    </LinearLayout>

    <TextView
        android:id="@+id/textView10"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="There should be a list of items below:"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

printed_order_element2.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView6"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView" />
</LinearLayout>

This is what I get:

enter image description here

Cœur
  • 37,241
  • 25
  • 195
  • 267
Guerlando OCs
  • 1,886
  • 9
  • 61
  • 150
  • 1
    You are adding the views on the Background Thread. Here is the answer how to trigger the Main Thread: https://stackoverflow.com/questions/11123621/running-code-in-main-thread-from-another-thread – Maxim M Feb 10 '20 at 21:45
  • 1
    @GuessWho I modified the code so the layout inflating and inserting happens on the main thread but the problem persists. I think this is not the problem. Please take a look at mu updated code – Guerlando OCs Feb 10 '20 at 22:19
  • @[Guerlando OCs] maybe another problem could be that when you add the view you do not specify the LayoutParams so the Android system does not know how to position you item. Try this out: `linearLayoutView.addView(v, 0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));` – Maxim M Feb 11 '20 at 12:50
  • @GuessWho unfortunately nothing changes – Guerlando OCs Feb 11 '20 at 14:55

3 Answers3

2

I believe the problem lies in this section of code:

public void generatePDF() {
    LayoutInflater inflater = getLayoutInflater();
    View pdfLayout = inflater.inflate(R.layout.simple_pdf_example,
            findViewById(android.R.id.content),
            false);

All of the rest of your code uses this inflated pdfLayout view:

LinearLayout linearLayoutView = pdfLayout.findViewById(R.id.linearLayoutView);

for (int i=0; i<10; i++) {
    View v = getLayoutInflater().inflate(R.layout.printed_order_element2, null);
    linearLayoutView.addView(v);
}
pdfLayout.draw(page.getCanvas());

The problem is that your pdfLayout view is never put on the screen. Your setContentView() call in onCreate() inflates a different layout, and the inflated pdfLayout is never attached to any view!

When you call inflate(int, View, boolean) and pass false as the third argument, you are telling the system to treat the second argument (the View) as the parent only in order to parse LayoutParams on the root of the inflated layout. The inflated layout is not added to the parent! You have to manually call parent.addView() with the inflated view.


How to fix it? It's hard to say, exactly, since I've got a few questions. But maybe just asking the questions will reveal the answer.

It's strange to see both a call to setContentView() and a call to inflate() that passes in android.R.id.content as the parent. Could you just call setContentView(R.layout.simple_pdf_example) in your onCreate(), and avoid the second inflate() call altogether?

If so, then you'd replace all calls to pdfLayout.findViewById() with direct findViewById() calls (since what was previously pdfLayout is now just your Activity's main content).

Ben P.
  • 52,661
  • 6
  • 95
  • 123
  • I forgot to say that I don't want the simple_pdf_example to be rendered on screen, just on the PDF. That's why I followed the tips on https://stackoverflow.com/questions/7854664/render-view-to-bitmap-off-screen-in-android. It looks like you're saying that I should render to the activity, rigth? If you see the answer, in order to render offscreen, I pass `findViewById(android.R.id.content)` just for measurements, and `false` because I don't want to attach it to the root. – Guerlando OCs Feb 12 '20 at 23:47
  • I guess I'm confused by the screenshot. If you don't expect anything to be rendered on the screen, then what are you expecting to "see"? Is that a screenshot **not** of your device, but instead of some PDF viewer? – Ben P. Feb 13 '20 at 00:28
  • What is producing "Just to see if linearLayoutView rendered"? – Ben P. Feb 13 '20 at 00:29
  • Yes, the screenshot if from the generated PDF. The "Just to see..." text is hardcoded into `simple_pdf_example.xml`, please take a look – Guerlando OCs Feb 13 '20 at 00:50
  • I wonder if the fact that `pdfLayout` isn't attached to anything means that it never does a layout/drawing pass once you add the child views. – Ben P. Feb 13 '20 at 02:05
  • THANKS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! it worked. I must wait 18 hours to award your bounty so please wait a little. See my answer to this question so you understand what I modified. – Guerlando OCs Feb 13 '20 at 03:43
1

Your linearLayoutView lies in pdfLayout, but when you inflate pdfLayout you don't add it to the parent. Please, try this way:

View pdfLayout = inflater.inflate(R.layout.simple_pdf_example, findViewById(android.R.id.content));
Bracadabra
  • 3,609
  • 3
  • 26
  • 46
  • It didn't work, same output as in the image. I updated my code with this addition though, don't know why I made the other way – Guerlando OCs Feb 10 '20 at 23:05
  • Your linearLayoutView has 0 width also. – Bracadabra Feb 10 '20 at 23:08
  • This is default behaviour when I make it "match constraint". The fact that I see the text inside it ("Just to see if linearLayoutView rendered") also confirms that the width it not 0. Anyways I tried `wrap_content` and I still have the same output – Guerlando OCs Feb 10 '20 at 23:43
0

Simply adding

    pdfLayout.measure(sizeSpec, sizeSpec2);
    pdfLayout.layout(0, 0, width, height);

after the for loop fixed everything. Thanks to Ben P. https://stackoverflow.com/users/8298909/ben-p

The problem is that the layout needs to me measured or layout() must be executed. I don't know for sure, but it has to do with it. I don't know if the 2 lines are needed but you can try to use only one and see what works

Guerlando OCs
  • 1,886
  • 9
  • 61
  • 150