java - 使用 "id"而不是 "android:id"会导致 NullPointerException

标签 java android canvas nullpointerexception

我创建了一个View 的子类,用于绘制三角形。 TriangleDrawView 如下所示。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
import android.util.AttributeSet;
import android.view.View;

public class TriangleDrawView extends View {

    private Paint mPaint;
    private Path mPath;
    private int mDrawColor;
    private int mBackgroundColor;
    private Canvas mExtraCanvas;
    private Bitmap mExtraBitmap;

    private int paddingX, paddingY;
    private int drawableRectX,drawableRectY;
    private int triangleBoundX,triangleBoundY;
    private int total_width,total_height;
    private double angle_a,angle_b,angle_c;
    private int xA,xB,xC,yA,yB,yC;

    public TriangleDrawView(Context context) {
        this(context,null);
    }

    public TriangleDrawView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        mBackgroundColor = ResourcesCompat.getColor(getResources(),
                R.color.opaque_orange, null);
        mDrawColor = ResourcesCompat.getColor(getResources(),
                R.color.opaque_yellow, null);

        // Holds the path we are currently drawing.
        mPath = new Path();
        // Set up the paint with which to draw.
        mPaint = new Paint();
        mPaint.setColor(mDrawColor);
        // Smoothes out edges of what is drawn without affecting shape.
        mPaint.setAntiAlias(true);
        // Dithering affects how colors with higher-precision device
        // than the are down-sampled.
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.STROKE); // default: FILL
        mPaint.setStrokeJoin(Paint.Join.ROUND); // default: MITER
        mPaint.setStrokeCap(Paint.Cap.ROUND); // default: BUTT
        mPaint.setStrokeWidth(5); // default: Hairline-width (really thin)

        angle_a = Math.PI/3;
        angle_b = Math.PI/3;
        angle_c = Math.PI/3;

        paddingX = 100;
        paddingY = 100;
    }


    @Override
    protected void onSizeChanged(int width, int height,
                                 int oldWidth, int oldHeight) {
        super.onSizeChanged(width, height, oldWidth, oldHeight);

        // Create bitmap, create canvas with bitmap, fill canvas with color.
        mExtraBitmap = Bitmap.createBitmap(width, height,
                Bitmap.Config.ARGB_8888);
        mExtraCanvas = new Canvas(mExtraBitmap);
        // Fill the Bitmap with the background color.
        mExtraCanvas.drawColor(mBackgroundColor);

        total_width = width;
        total_height = height;
        drawTriangle(angle_a,angle_b,angle_c);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // Draw the bitmap that stores the path the user has drawn.
        // Initially the user has not drawn anything
        // so we see only the colored bitmap.
        canvas.drawBitmap(mExtraBitmap, 0, 0, null);
    }


    private void initTriangleBounds(double angle_b, double angle_c, int total_width,int total_height, int paddingX, int paddingY){
        drawableRectX = total_width-2*paddingX;
        drawableRectY=total_height-2*paddingY;

        int x_,y_;
        x_ = total_width/2;
        y_ = total_height/2;

        if(angle_b<Math.PI/2 && angle_c<Math.PI/2 ){
            if(drawableRectX >= ( drawableRectY/Math.tan(angle_b) + drawableRectY/Math.tan(angle_c) ) ){
                triangleBoundY =  drawableRectY;
                triangleBoundX = (int)( drawableRectY/Math.tan(angle_b) + drawableRectY/Math.tan(angle_c) );
            }else{
                triangleBoundX = drawableRectX;
                triangleBoundY = (int) ( drawableRectX/( 1/Math.tan(angle_b) +1/Math.tan(angle_c) ) );
            }

            xB = x_ - triangleBoundX/2;
            yB = y_ + triangleBoundY/2;

            xC = x_ + triangleBoundX/2;
            yC = yB;

            yA = y_ - triangleBoundY/2;
            xA = xB + (int)(triangleBoundY/Math.tan(angle_b));

        }else if(angle_b>=Math.PI/2){
            if( drawableRectX <= ( drawableRectY/Math.tan(angle_c) )){
                triangleBoundY =  drawableRectY;
                triangleBoundX = (int)( drawableRectY/Math.tan(angle_c));
            }else{
                triangleBoundX = drawableRectX;
                triangleBoundY = (int)( drawableRectX * Math.tan(angle_c));
            }

            xA = x_ - triangleBoundX/2;
            yA = y_ - triangleBoundY/2;

            xC = x_ + triangleBoundX/2;
            yC = y_ + triangleBoundY/2;

            xB = xA + (int)(triangleBoundY/Math.tan(Math.PI - angle_b));
            yB = yC;
        }else if(angle_c>=Math.PI/2){
            if( drawableRectX <= ( drawableRectY/Math.tan(angle_b) )){
                triangleBoundY =  drawableRectY;
                triangleBoundX = (int)( drawableRectY/Math.tan(angle_b));
            }else{
                triangleBoundX = drawableRectX;
                triangleBoundY = (int)( drawableRectX * Math.tan(angle_b));
            }

            xA = x_ + triangleBoundX/2;
            yA = y_ - triangleBoundY/2;

            xB = x_ - triangleBoundX/2;
            yB = y_ + triangleBoundY/2;

            xC = xA - (int)(triangleBoundY/Math.tan(Math.PI - angle_c));
            yC = yB;
        }
    }

    public void drawTriangle(double angle_a, double angle_b, double angle_c){
        this.angle_a = angle_a;
        this.angle_b = angle_b;
        this.angle_c = angle_c;
        initTriangleBounds(angle_b,angle_c,total_width,total_height,paddingX,paddingY);

        mPath.moveTo(xA, yA);
        mPath.quadTo(xA,yA,xB,yB);
        mPath.quadTo(xB,yB,xC,yC);
        mPath.quadTo(xC,yC,xA,yA);
        mExtraCanvas.drawPath(mPath, mPaint);
        invalidate();
    }


}

现在,这个 TriangleDrawView 包含在 Activity 中,如下所示。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
    tools:context="my.canvas2.TestingActivity">

    <view
        class="my.canvas2.TriangleDrawView"
        id="@+id/viewTriangle"
        android:layout_width="273dp"
        android:layout_height="302dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:onClick="onClick"/>
</android.support.constraint.ConstraintLayout>

当 Activity 开始时,它工作正常,如下面的屏幕截图所示。 enter image description here

我需要做的是调用TriangleDrawViewpublic void drawTriangle(double angle_a, double angle_b, double angle_c)方法。因此,在 Activity 中按钮的 onClick 方法中,我调用了 drawTriangle 方法,如下所示。

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class TestingActivity extends AppCompatActivity {

    private TriangleDrawView triangleDrawView;

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


        triangleDrawView = findViewById(R.id.viewTriangle);
    }

    public void onClick(View view) {
        double pi = Math.PI;
        //Method implemented
        triangleDrawView.drawTriangle(pi/2,pi/3,pi/6);
    }
}

但是,当单击 按钮时,应用程序崩溃并显示 NullPointerException。这是 LogCat 输出;

FATAL EXCEPTION: main
   Process: my.canvas2, PID: 15645
   java.lang.IllegalStateException: Could not execute method for android:onClick
       at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293)
       at android.view.View.performClick(View.java:6294)
       at android.view.View$PerformClick.run(View.java:24770)
       at android.os.Handler.handleCallback(Handler.java:790)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:164)
       at android.app.ActivityThread.main(ActivityThread.java:6494)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
    Caused by: java.lang.reflect.InvocationTargetException
       at java.lang.reflect.Method.invoke(Native Method)
       at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
       at android.view.View.performClick(View.java:6294) 
       at android.view.View$PerformClick.run(View.java:24770) 
       at android.os.Handler.handleCallback(Handler.java:790) 
       at android.os.Handler.dispatchMessage(Handler.java:99) 
       at android.os.Looper.loop(Looper.java:164) 
       at android.app.ActivityThread.main(ActivityThread.java:6494) 
       at java.lang.reflect.Method.invoke(Native Method) 
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void my.canvas2.TriangleDrawView.drawTriangle(double, double, double)' on a null object reference
       at my.canvas2.TestingActivity.onClick(TestingActivity.java:24)
       at java.lang.reflect.Method.invoke(Native Method) 
       at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288) 
       at android.view.View.performClick(View.java:6294) 
       at android.view.View$PerformClick.run(View.java:24770) 
       at android.os.Handler.handleCallback(Handler.java:790) 
       at android.os.Handler.dispatchMessage(Handler.java:99) 
       at android.os.Looper.loop(Looper.java:164) 
       at android.app.ActivityThread.main(ActivityThread.java:6494) 
       at java.lang.reflect.Method.invoke(Native Method) 
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 

更新

NullPointerExceptionthis 中有很好的解释。问题。但这并没有解决我的问题。我通过Mike M的答案成功解决了问题和 Tomas Jablonskis .

The id attribute on the element must have the android prefix. That is, android:id="@+id/viewTriangle". I'd be surprised if your IDE isn't giving at least a warning about that.

-Mike M

Your view triangleDrawView is not being initialized, because you use id attribute in your XML on element, that is a reason why .findViewById(id) does not find and initialize your View properly.

Changing id=... to android:id=... should fix your problem.

-Tomas Jablonskis

最佳答案

您的看法 triangleDrawView未初始化,因为您使用 id XML 中的属性上<view ... >元素,这就是 .findViewById(id) 的原因未找到并初始化您的View适本地。

改变id=...android:id=...应该可以解决你的问题。

关于java - 使用 "id"而不是 "android:id"会导致 NullPointerException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49703582/

相关文章:

java - 一对多 order by 与案例 hibernate spring

java - .zip 文件下载为 f.txt 文件 - spring boot

c# - 如何通过蓝牙从 Unity C# android 应用程序向 Arduino 发送字符串?

java - ServerSocket 在单独的线程中抛出 java.net.BindException : bind failed: EACCES (Permission denied) on Android

java - java中的简单雨算法

java - Tomcat8 UTF-8 显示为问号

Java类路径错误: ./filename.jar:无法执行二进制文件

android - 扩展 Android View 类以添加阴影

javascript - 在 Chrome 中绘制文本陷阱

javascript - Canvas 油漆桶/洪水填充工具卡在循环中