1

在我的项目中,用户可以查看 PDF 文档,我希望他们可以选择通过 onDraw 和 Paint 对文档中的每一页进行注释。我希望首先打开文档以供查看,并选择通过 WhatsApp 绘画功能之类的按钮打开和关闭绘画/绘画功能。

我有一个扩展我的 PDFView 的 PaintView 类,但是当我打开 PDF 时,会立即调用 onDraw,允许我在 PDF 上绘图,但无法关闭此功能并在页面之间滑动。当我将 initDraw 移动到一个按钮时,我的 PaintView 类中有一个空指针。

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Canvas.drawColor(int)' on a null object reference
    at com.example.dissertation814.pdfViewer.PaintView.onDraw(PaintView.java:60)

我的观众活动:

public class PdfViewerActivity extends AppCompatActivity {

private boolean isDrawInit = false;
private PaintView paintView;

//firebase auth
private FirebaseAuth mAuth;

//variables
public String currentUserAccount;
public String teacherAccountNav = "Teacher";

PDFView pdfView;

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pdf_viewer);

    //PDFView to display PDFs
    pdfView = findViewById(R.id.pdfView);

    //use best quality
    pdfView.useBestQuality(true);

    //get data from intent
    Intent i = this.getIntent();
    Uri uri = i.getParcelableExtra("FILE_PATH_URI");

    //Get the pdf file
    assert uri != null;
    File file = new File(Objects.requireNonNull(uri.getPath()));

    if(file.canRead()){
        //load pdf file
        pdfView.fromFile(file)
                .defaultPage(0)
                .enableSwipe(true)
                .swipeHorizontal(true)
                .pageSnap(true)

                .onDrawAll(new OnDrawListener() {
                    @Override
                    public void onLayerDrawn(Canvas canvas, float pageWidth, float pageHeight, int displayedPage) {
                        Bitmap  bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);

                        Paint paint = new Paint();
                        paint.setColor(Color.BLACK);
                        canvas.drawBitmap(bitmap, 0,0, paint);
                    }
                })

                .onLoad(new OnLoadCompleteListener() {
            @Override
            public void loadComplete(int nbPages) {
                Toast.makeText(PdfViewerActivity.this, "No. of pages: " + nbPages, Toast.LENGTH_SHORT).show();
            }
        }).load();
    }

}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
public void onInitDrawClick(View view){

}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
protected void onResume() {
    super.onResume();
    if(!isDrawInit){
        initDraw();
        isDrawInit = true;
    }
}

//initialise paint view
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
private void initDraw(){
        paintView = findViewById(R.id.paintView);
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
        paintView.init(metrics);
}

//user finger path from paint view class
static class FingerPath{
    int colour;
    int strokeWidth;
    Path path;

    FingerPath(int colour, int strokeWidth, Path path){
        this.colour = colour;
        this.strokeWidth = strokeWidth;
        this.path = path;
    }
}

我的 PaintView 类:

public class PaintView extends PDFView {
private Paint mPaint;
private Canvas mCanvas;
private Bitmap mBitmap;

private ArrayList<PdfViewerActivity.FingerPath> paths = new ArrayList<>();
private Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);

private static final float TOUCH_TOLERANCE = 4;

private Path mPath;
private float mX;
private float mY;
public int brushColour = Color.BLACK;
public int brushSize = 10;


public PaintView(Context context, AttributeSet set) {
    super(context, set);
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setDither(true);
    mPaint.setColor(Color.BLACK);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setXfermode(null);
    mPaint.setAlpha(0xff);
}

public void init (DisplayMetrics metrics){
    int height = (int) (metrics.heightPixels);
    int width = metrics.widthPixels;

    mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    mCanvas = new Canvas(mBitmap);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();
    mCanvas.drawColor(Color.TRANSPARENT);

    for(PdfViewerActivity.FingerPath fp : paths){
        mPaint.setColor(fp.colour);
        mPaint.setStrokeWidth(fp.strokeWidth);
        mPaint.setMaskFilter(null);

        mCanvas.drawPath(fp.path, mPaint);
    }

    canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
    canvas.restore();
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();

    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            touchStart(x,y);
            invalidate();
            break;
            case MotionEvent.ACTION_MOVE:
                touchMove(x,y);
                invalidate();
                break;
                case MotionEvent.ACTION_UP:
                    touchUp();
                    break;
    }
    return true;
}

private void touchUp(){
    mPath.lineTo(mX,mY);
}

private void touchMove(float x, float y){
    float dx = Math.abs(x - mX);
    float dy = Math.abs(y - mY);


    if(dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE){
        mPath.quadTo(mX, mY, (x+mX)/2, (y+mY)/2);
        mX = x;
        mY = y;
    }
}

private void touchStart(float x, float y){
    mPath = new Path();
    PdfViewerActivity.FingerPath fp = new PdfViewerActivity.FingerPath(brushColour, brushSize, mPath);
    paths.add(fp);

    mPath.reset();
    mPath.moveTo(x,y);


    mX = x;
    mY = y;
}

public void clear(){
    paths.clear();
    invalidate();
}

我的 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:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/black"
     tools:context=".pdfViewer.PdfViewerActivity">

<Button
    android:id="@+id/initDraw"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="init"
    app:layout_constraintBottom_toTopOf="@+id/relativeLayout"
    app:layout_constraintEnd_toStartOf="@+id/homeButton"
    app:layout_constraintStart_toEndOf="@+id/backButton"
    app:layout_constraintTop_toTopOf="parent"
    android:onClick="onInitDrawClick"/>

<ImageButton
    android:id="@+id/backButton"
    android:layout_width="60dp"
    android:layout_height="60dp"
    android:background="@color/black"
    android:contentDescription="@string/back_button"
    android:onClick="onBackClicked"
    android:src="@drawable/backward_arrow"
    app:layout_constraintBottom_toTopOf="@+id/relativeLayout"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.112"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.8" />

<ImageButton
    android:id="@+id/homeButton"
    android:layout_width="60dp"
    android:layout_height="60dp"
    android:layout_marginEnd="64dp"
    android:layout_marginRight="64dp"
    android:background="@color/black"
    android:onClick="onHomeClicked"
    android:src="@drawable/ic_home_black_24dp"
    app:layout_constraintBottom_toTopOf="@+id/relativeLayout"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="1.0"
    app:layout_constraintStart_toEndOf="@+id/backButton"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.8" />

<RelativeLayout
    android:id="@+id/relativeLayout"
    android:layout_width="match_parent"
    android:layout_height="800dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.919"
    android:paddingLeft="16dp"
    android:paddingRight="16dp">

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

    <com.example.dissertation814.pdfViewer.PaintView
        android:id="@+id/paintView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/transparent"/>


</RelativeLayout>

 </androidx.constraintlayout.widget.ConstraintLayout>
4

1 回答 1

1

onDraw是一种方法,因此它不是您初始化的东西。我认为您也不应该尝试禁用该方法。尽管您可以覆盖它,但这使您可以控制绘制的内容。

考虑另一种解决您的问题的方法。您无需启用或禁用该onDraw方法,而是控制哪个视图来处理用户输入。


解决方案:

true在方法中返回时onTouchEvent,您声明没有高于该视图的视图(在视图层次结构中)需要处理此输入。

您应该做的是检查绘图功能是否应该打开。如果绘图功能被禁用,则返回false. 否则,如果启用了绘图功能,则处理输入,然后返回true

例子:

@Override
public boolean onTouchEvent(MotionEvent event) {
    // Check whether or not the drawing feature is disabled
    if (drawingIsEnabled == false) { 
         // Let parent views process this input
         return false;
    }

    float x = event.getX();
    float y = event.getY();

    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            touchStart(x,y);
            invalidate();
            break;
        case MotionEvent.ACTION_MOVE:
            touchMove(x,y);
            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            touchUp();
            break;
    }

    // Prevent parent views from processing this input
    return true;
}

返回false时,输入会进一步向上传递到视图层次结构,以便父视图有机会处理输入。(这将允许您滑动页面)

但是,如果您 return true,您将阻止父视图处理输入。(这将防止父视图在您绘图时滑动页面,这会很烦人)


希望这可以帮助!

于 2020-08-10T10:06:21.087 回答