Search code examples
androidandroid-layoutandroid-canvas

Getting imageView null reference error - why so?


I'am drawing a re-sizeable rectangle on a image view.

The View class to draw the custom re-sizeable rectangle view:

public class DrawPolygonCanvas extends View implements OnTouchListener {

    private final String TAG = "DrawLineCanvas";
    private final String defaultROI = "Roi1:0.2,0.2|0.8,0.2|0.8,0.8|0.2,0.8";
    private Context context;
    private CanvasTouchManager canvasTouchManager = new CanvasTouchManager();
    private boolean isInitialized = false;
    private Paint circlePaint, linePaint;
    private String cameraROI = "";

    // Debug helpers to draw lines between the two touch points
    private HashMap<String, List<Vector2D>> polygons = new HashMap();

    public DrawPolygonCanvas(Context context) {
        super(context);
        init(context);
    }

    public DrawPolygonCanvas(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public DrawPolygonCanvas(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public DrawPolygonCanvas(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    private void init(Context context) {
        Log.e(TAG, "called init");
        this.context = context;
        Log.e(TAG, "calling setOnTouchListener");
        setOnTouchListener(this);
    }

    public void clearROI() {
        Log.e(TAG, "called clearROI");
        // Get ROI from the shared preferences.
        SharedPreferences prefs = UserSharedPref.initializeSharedPreferencesForCameraROI(context);
        cameraROI = prefs.getString(UserSharedPref.cameraROI, cameraROI);
        Log.e(TAG, "Reloaded ROI = " + cameraROI);
        convertString2Polygons();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.e(TAG, "called onDraw");
        super.onDraw(canvas);

        if (!isInitialized) {
            // Get ROI from the shared preferences.
            SharedPreferences prefs = UserSharedPref.initializeSharedPreferencesForCameraROI(context);
            cameraROI = prefs.getString(UserSharedPref.cameraROI, cameraROI);
            if (cameraROI.isEmpty()) {
                cameraROI = defaultROI;
            }
            convertString2Polygons();

            circlePaint = new Paint();
            circlePaint.setColor(0xFFFF0000);

            linePaint = new Paint();
            linePaint.setColor(0xFF00FF00);
            linePaint.setStyle(Paint.Style.STROKE);
            linePaint.setStrokeWidth(10);

            isInitialized = true;
        }

        Rect dest = new Rect(0, 0, getWidth(), getHeight());
        Paint paint = new Paint();
        paint.setFilterBitmap(true);


        exampleActivity ex = new exampleActivity();
        Bitmap bm = ex.getBitmap();

        canvas.drawBitmap(bm, null, dest, paint);

        try {
            for (Map.Entry<String, List<Vector2D>> entry : polygons.entrySet()) {

                List<Vector2D> polygon = entry.getValue();

                for (int i = 0; i < polygon.size() - 1; i++) {
                    canvas.drawLine(polygon.get(i).getX(), polygon.get(i).getY(), polygon.get(i + 1).getX(), polygon.get(i + 1).getY(), linePaint);
                }
                canvas.drawLine(polygon.get(polygon.size() - 1).getX(), polygon.get(polygon.size() - 1).getY(), polygon.get(0).getX(), polygon.get(0).getY(), linePaint);

                for (int i = 0; i < polygon.size(); i++) {
                    canvas.drawCircle(polygon.get(i).getX(), polygon.get(i).getY(), 40, circlePaint);
                }
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDetachedFromWindow() {
        Log.e(TAG, "called onDetachedFromWindow");
        super.onDetachedFromWindow();

        // Convert polygon back to string.
        convertPolygons2String();

        SharedPreferences.Editor editor = UserSharedPref.initializeSharedPreferencesForCameraROI(context).edit();
        editor.putString(UserSharedPref.cameraROI, cameraROI);
        editor.apply();
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.e(TAG, "called onTouch");
        canvasTouchManager.update(event);
        Vector2D vct = canvasTouchManager.getPoint();

        float minDist = 200000.0f;
        float radius = 200;

        int minIndex = -1;
        String key = "";

        for (Map.Entry<String, List<Vector2D>> entry : polygons.entrySet()) {

            List<Vector2D> points = entry.getValue();
            for (int i = 0; i < points.size(); i++) {
                float dist = points.get(i) == null ? 0 : Vector2D.subtract(vct, points.get(i)).getLength();
                if (dist >= radius) {
                    continue;
                }

                if (dist < minDist) {
                    minDist = dist;
                    minIndex = i;
                    Log.e(TAG, "Matching x = " + points.get(i).getX() + ", y = " + points.get(i).getY());
                    key = entry.getKey();
                }
            }
        }

        if (minIndex != -1 && !key.isEmpty()) {
            polygons.get(key).set(minIndex, vct);
        } else {
            Log.e("OK2", "Touch point is too far, dist = " + minDist);
        }

        invalidate();

        return true;
    }

    private void convertPolygons2String() {
        cameraROI = "";
        for (Map.Entry<String, List<Vector2D>> entry : polygons.entrySet()) {
            cameraROI += cameraROI.isEmpty() ? entry.getKey() + ":" : ";" + entry.getKey() + ":";
            List<Vector2D> points = entry.getValue();
            for (int i = 0; i < points.size(); i++) {
                float x = points.get(i).getX() / (float) getWidth();
                float y = points.get(i).getY() / (float) getHeight();

                cameraROI += String.format("%.2f", x);
                cameraROI += ",";
                cameraROI += String.format("%.2f", y);

                if (i < points.size() - 1) {
                    cameraROI += "|";
                }
            }
        }

        Log.e(TAG, "Converted string = " + cameraROI);
    }

    private void convertString2Polygons() {
        Log.e(TAG, "called  convertString2Polygons");
        String[] ROIs = cameraROI.split(";");
        if (cameraROI.isEmpty() || ROIs.length == 0) {
            Log.e(TAG, "No polygon is configured.");
            polygons.clear();
            return;
        }

        // Parse and add each polygon
        for (String roi : ROIs) {
            String[] polygonString = roi.split(":");
            if (polygonString.length != 2) {
                continue;
            }

            String[] vertices = polygonString[1].split("\\|");
            if (vertices.length < 3) {
                Log.d(TAG, polygonString[1] + " is not a valid polygon setting, which needs at least 3 vertices, e.g '0,5,0.1|0.3,0.4|0.5,0.6|0.7,0.8'.");
                return;
            }

            List<Vector2D> points = new ArrayList<>();
            for (String vertex : vertices) {
                Log.d(TAG, "Vertex = " + vertex);
                String[] vertexString = vertex.split(",");
                if (vertexString.length != 2) {
                    Log.d(TAG, vertex + " is not a valid vertex setting. An example should look like '0.5,0.5'.");
                    return;
                }

                try {
                    float x = Float.parseFloat(vertexString[0]);
                    float y = Float.parseFloat(vertexString[1]);

                    points.add(new Vector2D(x * getWidth(), y * getHeight()));

                    Log.d(TAG, "Adding [" + x + ", " + y + "]");
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                    Log.d(TAG, vertex + " is invalid. Vertex's X and Y coordinates should be float numbers.");
                    return;
                }
            }

            polygons.put(polygonString[0], points);
            Log.d(TAG, "Add configured polygon: " + polygonString[0] + " - " + polygonString[1]);
        }
    }
}

This is my XML file:

<RelativeLayout 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"
    tools:context=".exampleActivity">



    <ImageView
        android:id="@+id/office"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/office"
        android:src="@raw/office" />

    <com.lambdahash.sonic.example.draw.senapr.DrawPolygonCanvas
        android:id="@+id/DrawPolygonCanvas"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

and finally, this is how I'am attaching the view to xml file, the activity:

public class exampleActivity extends Activity {

private ImageView img;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);
        DrawPolygonCanvas canvas = (DrawPolygonCanvas) findViewById(R.id.DrawPolygonCanvas);

        img = (ImageView) findViewById(R.id.office);
       }

public Bitmap getBitmap(){
    Bitmap bm = ((BitmapDrawable) img.getDrawable()).getBitmap();
    return bm;
}


    public void onReset(View v) {
        DrawPolygonCanvas canvas = (DrawPolygonCanvas) findViewById(R.id.DrawPolygonCanvas);
        SharedPreferences.Editor editor = UserSharedPref.initializeSharedPreferencesForCameraROI(canvas.getContext()).edit();
        editor.putString(UserSharedPref.cameraROI, "");
        editor.apply();
        canvas.clearROI();
    }
}

Basically, the most important parts in this code are when there is a problem in the onDraw() in DrawPolygonCanvas , these lines, exampleActivity ex = new exampleActivity(); and Bitmap bm = ex.getBitmap(); are creating the error saying that:

java.lang.NullPointerException: Attempt to invoke virtual method 'android.graphics.drawable.Drawable android.widget.ImageView.getDrawable()' on a null object reference.

The imageView in the XML file is not getting initialised for some reason. How do I solve this problem?


Solution

  • exampleActivity ex = new exampleActivity();
    Bitmap bm = ex.getBitmap();
    

    Root cause: Because you are creating a new instance of ExampleActivity, so all activity callback methods such as onCreate, onStart, onResume, etc. won't be called. In you case because onCreate never called that why your img always null. As a result Android will throws a NullPointerException when you call getDrawable method on it.

    java.lang.NullPointerException: Attempt to invoke virtual method 'android.graphics.drawable.Drawable android.widget.ImageView.getDrawable()' on a null object reference.
    

    Tips: Never create an instance of an Activity in Android, all you need to do is register the activity in AndroidManifest.xml file then write your code in Activity callback methods.

    Solution: Back to your case, to get bitmap from Activity you can implement a listener interface to pass the bitmap from exampleActivity to DrawPolygonCanvas.

    DrawPolygonCanvas

    public class DrawPolygonCanvas extends View implements OnTouchListener{
    
        public interface OnGetBitmapListener {
            Bitmap onGetBitmap();
        }
    
        private OnGetBitmapListener mOnGetBitmapListener;
    
        public void setOnGetBitmapListener(OnGetBitmapListener listener) {
            mOnGetBitmapListener = listener;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            Log.e(TAG, "called onDraw");
            super.onDraw(canvas);
            paint.setFilterBitmap(true);
    
            // Add these lines
            Bitmap bm = null;
            if (mOnGetBitmapListener != null) {
                bm = mOnGetBitmapListener.onGetBitmap();
            }
    
            canvas.drawBitmap(bm, null, dest, paint);
        }
    }
    

    exampleActivity

    public class exampleActivity extends Activity implements DrawPolygonCanvas.OnGetBitmapListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_example);
    
            img = (ImageView) findViewById(R.id.office);
    
            DrawPolygonCanvas canvas = (DrawPolygonCanvas) findViewById(R.id.DrawPolygonCanvas);
            canvas.setOnGetBitmapListener(this);
        }
    
        @Override
        public Bitmap onGetBitmap() {
            Bitmap bm = ((BitmapDrawable) img.getDrawable()).getBitmap();
            return bm;
        }
    }