2016-05-27 2 views
5

Я пытаюсь использовать PdfRenderer, и требование состоит в том, чтобы с ним иметь Zoom и прокрутку, но в Android PdfRenderer не предоставляется поддержка Zoom и прокрутки, доступна только поддержка навигации по страницам.Как реализовать поддержку PdfRenderer Zoom и Scroll?

Но я предполагаю, что поддержка масштабирования и прокрутки может быть реализована, так как PdfRenderer использует растровое изображение для отображения содержимого с использованием imageview.

Как реализовать поддержку масштабирования и прокрутки с помощью Google PdfRenderer образец?

PS: Я использую этот PdfRenderer образец предоставленный Google, https://github.com/googlesamples/android-PdfRendererBasic

ответ

3

Я использовал идею @ yan-yankelevich и написал код на Java. Большая проблема заключалась в поиске правильного масштабирования и соответствующих значений размера битмапа. Не забывайте, что PdfRenderer работает только с API 21+.

Фрагмент с PDF Bitmap fragment_pdf_renderer.xml:

<?xml version="1.0" encoding="utf-8"?> 
<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:background="@color/white" 
    android:orientation="vertical" 
    tools:context=".PdfRendererFragment"> 

    <ScrollView 
     android:layout_width="match_parent" 
     android:layout_height="0dp" 
     android:layout_weight="1"> 

     <HorizontalScrollView 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content"> 

      <ImageView 
       android:id="@+id/image" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:background="@android:color/white" 
       android:contentDescription="@null" /> 
     </HorizontalScrollView> 
    </ScrollView> 

    <LinearLayout 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:background="@color/from_divider_gray" 
     android:gravity="center_vertical" 
     android:orientation="horizontal"> 

     <Button 
      android:id="@+id/previous" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:layout_weight="1" 
      android:text="@string/previous_page" 
      android:textSize="13sp" /> 

     <Button 
      android:id="@+id/next" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:layout_weight="1" 
      android:text="@string/next_page" 
      android:textSize="13sp" /> 

     <ImageButton 
      android:id="@+id/zoomout" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_margin="0dp" 
      android:padding="8dp" 
      android:src="@drawable/ic_zoom_out_black_36dp" /> 

     <ImageButton 
      android:id="@+id/zoomin" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_margin="0dp" 
      android:padding="8dp" 
      android:src="@drawable/ic_zoom_in_black_36dp" /> 
    </LinearLayout> 

</LinearLayout> 

enter image description here

PdfRendererFragment:

/** 
* This fragment has a big {@ImageView} that shows PDF pages, and 2 
* {@link android.widget.Button}s to move between pages. We use a 
* {@link android.graphics.pdf.PdfRenderer} to render PDF pages as 
* {@link android.graphics.Bitmap}s. 
*/ 
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) 
public class PdfRendererFragment 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. 
    */ 
    public String FILENAME; 
    public String PURCHASE_ID; 
    public int TICKETS_NUMBER; 

    /** 
    * 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; 
    private ImageView mButtonZoomin; 
    private ImageView mButtonZoomout; 
    private Button mButtonNext; 
    private float currentZoomLevel = 12; 

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

    public PdfRendererFragment() { 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     return inflater.inflate(R.layout.fragment_pdf_renderer, 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); 
     mButtonZoomin = view.findViewById(R.id.zoomin); 
     mButtonZoomout = view.findViewById(R.id.zoomout); 

     // Bind events. 
     mButtonPrevious.setOnClickListener(this); 
     mButtonNext.setOnClickListener(this); 
     mButtonZoomin.setOnClickListener(this); 
     mButtonZoomout.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 onActivityCreated(@Nullable Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 
     FILENAME = getActivity().getIntent().getExtras().getString("pdfFilename"); 
     TICKETS_NUMBER = getActivity().getIntent().getExtras().getInt("tickets_number"); 
     PURCHASE_ID = getActivity().getIntent().getExtras().getString("purchaseGuid"); 
    } 

    @Override 
    public void onStart() { 
     super.onStart(); 
     try { 
      openRenderer(getActivity()); 
      showPage(mPageIndex); 
     } catch (IOException e) { 
      e.printStackTrace(); 
      Toast.makeText(getActivity(), getString(R.string.ticket_file_not_found, FILENAME), Toast.LENGTH_SHORT).show(); 
      App app = (App) getActivity().getApplicationContext(); 
      TicketUtil.downloadTicket(app, PURCHASE_ID); 
      getActivity().finish(); 
     } 
    } 

    @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 = TicketUtil.getTicketFile(context, PURCHASE_ID); 
     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(); 
      mCurrentPage = null; 
     } 
     if (null != mPdfRenderer) { 
      mPdfRenderer.close(); 
     } 
     if (null != mFileDescriptor) { 
      mFileDescriptor.close(); 
     } 
    } 

    /** 
    * Zoom level for zoom matrix depends on screen density (dpiAdjustedZoomLevel), but width and height of bitmap depends only on pixel size and don't depend on DPI 
    * 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). 
     int newWidth = (int) (getResources().getDisplayMetrics().widthPixels * mCurrentPage.getWidth()/72 * currentZoomLevel/40); 
     int newHeight = (int) (getResources().getDisplayMetrics().heightPixels * mCurrentPage.getHeight()/72 * currentZoomLevel/64); 
     Bitmap bitmap = Bitmap.createBitmap(
       newWidth, 
       newHeight, 
       Bitmap.Config.ARGB_8888); 
     Matrix matrix = new Matrix(); 

     float dpiAdjustedZoomLevel = currentZoomLevel * DisplayMetrics.DENSITY_MEDIUM/getResources().getDisplayMetrics().densityDpi; 
     matrix.setScale(dpiAdjustedZoomLevel, dpiAdjustedZoomLevel); 
//  Toast.makeText(getActivity(), "width " + String.valueOf(newWidth) + " widthPixels " + getResources().getDisplayMetrics().widthPixels, Toast.LENGTH_LONG).show(); 
//  matrix.postTranslate(-rect.left/mCurrentPage.getWidth(), -rect.top/mCurrentPage.getHeight()); 

     // 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, matrix, 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(); 
     if (pageCount == 1) { 
      mButtonPrevious.setVisibility(View.GONE); 
      mButtonNext.setVisibility(View.GONE); 
     } else { 
      mButtonPrevious.setEnabled(0 != index); 
      mButtonNext.setEnabled(index + 1 < pageCount); 
     } 
     if (currentZoomLevel == 2) { 
      mButtonZoomout.setActivated(false); 
     } else { 
      mButtonZoomout.setActivated(true); 
     } 
    } 

    /** 
    * 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 
       currentZoomLevel = 12; 
       showPage(mCurrentPage.getIndex() - 1); 
       break; 
      } 
      case R.id.next: { 
       // Move to the next page 
       currentZoomLevel = 12; 
       showPage(mCurrentPage.getIndex() + 1); 
       break; 
      } 
      case R.id.zoomout: { 
       // Move to the next page 
       --currentZoomLevel; 
       showPage(mCurrentPage.getIndex()); 
       break; 
      } 
      case R.id.zoomin: { 
       // Move to the next page 
       ++currentZoomLevel; 
       showPage(mCurrentPage.getIndex()); 
       break; 
      } 
     } 
    } 
} 

Принесите ваше внимание на тот факт, что уровень масштабирования зависит от вашей плотности экрана, но ширина и высота Bitmap (в пикселях) зависит только от вашего уровня масштабирования. Кроме того, вам нужно настроить ваши размеры так, чтобы при масштабировании по умолчанию (для меня это был pdf-рендеринг в полноэкранном режиме, а значение равно 12). PDF-растровое изображение в PDF больше не требуется и не меньше, чем нужно в вашем представлении.

int newWidth = (int) (getResources().getDisplayMetrics().widthPixels * mCurrentPage.getWidth()/72 * currentZoomLevel/40); 
int newHeight = (int) (getResources().getDisplayMetrics().heightPixels * mCurrentPage.getHeight()/72 * currentZoomLevel/64); 
Bitmap bitmap = Bitmap.createBitmap(
       newWidth, 
       newHeight, 
       Bitmap.Config.ARGB_8888); 
  1. Я обнаружил, что увеличение 12 подходит моему экрану и 40 и 64 являются коэффициентами, которые делают Bitmap правильного размера.
  2. mCurrentPage.getWidth() возвращает ширину в точках Postscript, где каждый pt составляет 1/72 дюйма.
  3. 72 (DPI) - разрешение по умолчанию PDF.

PS. Если вам нужно одновременно вертикальной и горизонтальной прокрутки Scrollview vertical and horizontal in android

4

Решение я использовал, столкнувшись с этой ситуацией был:

  • Загрузите страницу pdfRenderer в ImageView

  • Поместите свой ImageView в ScrollView (управляемый прокруткой тадам), и этот ScrollView в FrameLayout

  • Добавьте две кнопки (вне прокрутки), чтобы управлять увеличением и уменьшением масштаба (каждая кнопка запускает масштабную анимацию на моем ImageView). Кроме того, можно управлять с помощью детектора жеста, но я имел трудное время с поведением прокрутки при этом

  • Добавьте две кнопок для управления изменением страницы (до сих пор за пределами ScrollView)

  • Для приятного эффекта я добавил FadeIn/FADEOUT анимации на мои кнопки, FadeIn запуска на OnTouchEvents (если нет анимации не играет), и FadeOut срабатывания при FadeIn анимации над

Надежда я помог, АКС мне, если вам нужна более подробная информация, но вы должны знать где начать работу

Вот пример кода (который не учитывает навигацию по страницам и т. Д., Но влияет только на изменение масштаба и прокрутку, так как остальные находятся в образце кода Google, который вы связали) Код: C# (но очень легко конвертировать в Java)

private Button _zoomInButton; 
private Button _zoomOutButton; 
private ImageView _pdfViewContainer; 
private float _currentZoomLevel; 
private float _zoomFactor; 
private float _maxZoomLevel; 
private float _minZoomLevel; 

private void Init(View view) // the content of this method must go in your OnViewCreated method, here the view being the frameLayout you will find in xml 
{ 
    _zoomInButton = view.FindViewById<Button>(Resource.Id.PdfZoomInButton); 
    _zoomOutButton = view.FindViewById<Button>(Resource.Id.PdfZoomOutButton); 
    _pdfViewContainer = view.FindViewById<ImageView>(Resource.Id.PdfViewContainer); 

    _zoomInButton.Click += delegate { ZoomIn(); }; //for you (in Java) this must looks like setOnClickListener(this); and in the onClick metghod you just have to add a case for R.id.PdfZoomInButton containing a call to ZoomIn(); 
    _zoomOutButton.Click += delegate { ZoomOut(); }; 

    _minZoomLevel = 0.9f; 
    _maxZoomLevel = 1.2f; 
    _zoomFactor = 0.1f; 
} 

private void ZoomIn() 
{ 
    if (_currentZoomLevel + _zoomFactor < _maxZoomLevel) 
    { 
     ScaleAnimation scale = new ScaleAnimation(_currentZoomLevel, _currentZoomLevel + _zoomFactor, _currentZoomLevel, _currentZoomLevel + _zoomFactor, Dimension.RelativeToSelf, 0.5f, Dimension.RelativeToSelf, 0.5f); 
     scale.Duration = 50; 
     scale.FillAfter = true; 
     _pdfViewContainer.StartAnimation(scale); 
     _currentZoomLevel += _zoomFactor; 
    } 
} 

private void ZoomOut() 
{ 
    if (_currentZoomLevel - _zoomFactor > _minZoomLevel) 
    { 
     ScaleAnimation scale = new ScaleAnimation(_currentZoomLevel, _currentZoomLevel - _zoomFactor, _currentZoomLevel, _currentZoomLevel - _zoomFactor, Dimension.RelativeToSelf, 0.5f, Dimension.RelativeToSelf, 0.5f); 
     scale.Duration = 50; 
     scale.FillAfter = true; 
     _pdfViewContainer.StartAnimation(scale); 
     _currentZoomLevel -= _zoomFactor; 
    } 
} 

Xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:id="@+id/PdfContainer"> 
    <ScrollView xmlns:tools="http://schemas.android.com/tools" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:scrollbarAlwaysDrawVerticalTrack="true" 
     android:id="@+id/PdfScrollView"> 
     <ImageView 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      android:scaleType="fitCenter" 
      android:adjustViewBounds="true" 
      android:scrollbars="vertical" 
      android:src="@drawable/mediaIconPDF" 
      android:id="@+id/PdfViewContainer" /> 
    </ScrollView> 
    <LinearLayout 
     android:id="@+id/PdfRightLayout" 
     android:layout_gravity="right" 
     android:orientation="vertical" 
     android:gravity="center" 
     android:layout_width="50dp" 
     android:layout_height="match_parent" 
     android:weightSum="1"> 
     <Button 
      android:id="@+id/PdfZoomInButton" 
      android:layout_width="match_parent" 
      android:layout_height="50dp" 
      android:text="+" /> 
     <space 
      android:layout_width="match_parent" 
      android:layout_height="0dp" 
      android:layout_weight="0.2" /> 
     <Button 
      android:id="@+id/PdfZoomOutButton" 
      android:layout_width="match_parent" 
      android:layout_height="50dp" 
      android:text="-" /> 
    </LinearLayout> 
    <LinearLayout 
     android:id="@+id/PdfBottomLayout" 
     android:layout_gravity="bottom" 
     android:orientation="horizontal" 
     android:layout_width="match_parent" 
     android:layout_height="50dp" 
     android:background="@color/vogofTransparentGrey" 
     android:weightSum="1"> 
     <Button 
      android:id="@+id/PdfPreviousPage" 
      android:layout_width="0dp" 
      android:layout_weight="0.15" 
      android:layout_height="match_parent" 
      android:text="Prev" /> 
     <TextView 
      android:id="@+id/PdfCurrentPageLabel" 
      android:layout_width="0dp" 
      android:layout_weight="0.7" 
      android:gravity="center" 
      android:layout_height="match_parent" 
      /> 
     <Button 
      android:id="@+id/PdfNextPage" 
      android:layout_width="0dp" 
      android:layout_weight="0.15" 
      android:layout_height="match_parent" 
      android:text="Next" /> 
    </LinearLayout> 
</FrameLayout> 

с этим, некоторое время, чтобы понять это и немного усилий, вы должны быть в состоянии, чтобы получить желаемый результат. Хороший день

+0

Tha nks для ответа. можете ли вы разместить свой примерный код. –

+0

Выполнено, но это не сработает, если вы просто скопируете его как есть в XamarinC# (но очень легко перевести) –

+0

Нет, это не помогло :-(Я застрял с этим !! –

0

Начиная с этого решения: https://stackoverflow.com/a/46002017/5049286 я нашел хороший способ, чтобы избежать начального коэффициента масштабирования и другие фиксированные коэффициенты, только изменили этот метод:

private void showPage(int index) { 

    if (mPdfRenderer.getPageCount() <= index) { 
     return; 
    } 
    if (null != mCurrentPage) { 
     mCurrentPage.close(); 
    } 
    mCurrentPage = mPdfRenderer.openPage(index); 

    int newWidth = (int) (mVerticalScrollView.getWidth() * 
    currentZoomLevel); 
    int newHeight = (int) (newWidth * 
    ((float)mCurrentPage.getHeight()/(float)mCurrentPage.getWidth())); 

    Bitmap bitmap = Bitmap.createBitmap(
     newWidth, 
     newHeight, 
     Bitmap.Config.ARGB_8888); 

    mCurrentPage.render(bitmap, null, null, 
    PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY); 
    mImageView.setImageBitmap(bitmap); 

    updateUi(); 

}

с помощью этого решения currentZoomLevel начинается от 1.0f ххх (вы решаете предел) и выводимого изображения при трансфокации 1.0f вписываться в Scrollview и пропорции поддерживаемыми ...