如何通过按钮在 Android 中初始化 onDraw
How to initialise onDraw in Android via a button
在我的项目中,用户可以查看 PDF 文档,我希望他们可以选择通过 onDraw 和 Paint 在文档中的每个页面上进行注释。我希望首先打开文档以供查看,并可以选择通过 WhatsApp 绘画功能等按钮打开和关闭 drawing/painting 功能。
我有一个 PaintView class 扩展我的 PDFView 但是当我打开一个 PDF 时,onDraw 被立即调用,允许我在 PDF 上绘制但不能关闭这个功能并滑动页之间。当我将 initDraw 移动到一个按钮时,我在我的 PaintView class.
中得到一个空指针
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)
我的观众activity:
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 class:
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>
onDraw
是一种方法,因此它不是您初始化的东西。我认为您也不应该尝试禁用该方法。虽然您可以重写它,这让您可以控制绘制的内容。
考虑另一种解决您问题的方法。您可以控制哪个视图处理用户输入,而不是启用或禁用 [=11=] 方法。
解法:
当在方法 onTouchEvent
中 returning true
时,您声明在该视图之上的视图(在视图层次结构中)都不需要处理此输入。
您应该做的是检查绘图功能是否应该打开。如果绘图功能被禁用,您 return false
。否则,如果启用了绘图功能,则处理输入,然后 return 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;
}
当 returning false
时,输入将进一步向上传递到视图层次结构,以便父视图将有机会处理输入。 (这将允许您滑动页面)
但是,如果您 return true
,您将阻止父视图处理输入。 (这将防止父视图在您绘制时滑动页面,这会很烦人)
希望对您有所帮助!
在我的项目中,用户可以查看 PDF 文档,我希望他们可以选择通过 onDraw 和 Paint 在文档中的每个页面上进行注释。我希望首先打开文档以供查看,并可以选择通过 WhatsApp 绘画功能等按钮打开和关闭 drawing/painting 功能。
我有一个 PaintView class 扩展我的 PDFView 但是当我打开一个 PDF 时,onDraw 被立即调用,允许我在 PDF 上绘制但不能关闭这个功能并滑动页之间。当我将 initDraw 移动到一个按钮时,我在我的 PaintView class.
中得到一个空指针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)
我的观众activity:
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 class:
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>
onDraw
是一种方法,因此它不是您初始化的东西。我认为您也不应该尝试禁用该方法。虽然您可以重写它,这让您可以控制绘制的内容。
考虑另一种解决您问题的方法。您可以控制哪个视图处理用户输入,而不是启用或禁用 [=11=] 方法。
解法:
当在方法 onTouchEvent
中 returning true
时,您声明在该视图之上的视图(在视图层次结构中)都不需要处理此输入。
您应该做的是检查绘图功能是否应该打开。如果绘图功能被禁用,您 return false
。否则,如果启用了绘图功能,则处理输入,然后 return 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;
}
当 returning false
时,输入将进一步向上传递到视图层次结构,以便父视图将有机会处理输入。 (这将允许您滑动页面)
但是,如果您 return true
,您将阻止父视图处理输入。 (这将防止父视图在您绘制时滑动页面,这会很烦人)
希望对您有所帮助!