user6952624
user6952624

Reputation: 41

Activity with a custom view crashes when launched

I am trying to create a drawing activity with a custom view but the app crashes when I try to launch the activity. It does not crash when I remove myDrawView from the layout file. My guess would be that the layout file somehow fails to connect with the custom draw view I've created.

Here is the activity:

public class PaintActivity extends AppCompatActivity {

    MyDrawView myDrawView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_paint);
        RelativeLayout parent = (RelativeLayout) findViewById(R.id.drawing);
        myDrawView = new MyDrawView(this);
        parent.addView(myDrawView);
    }
    public void back(View view){

        super.onBackPressed();
    }
}

The layout file:

<?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=".view.PaintActivity">

    <ImageButton
        android:id="@+id/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="44dp"
        android:onClick="back"
        app:layout_constraintBottom_toTopOf="@+id/drawing"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/back" />

    <info.jonas.notes.view.MyDrawView
        android:id="@+id/drawing"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="63dp"
        android:layout_marginEnd="61dp"
        android:layout_marginLeft="61dp"
        android:layout_marginRight="61dp"
        android:layout_marginStart="61dp"
        android:layout_weight="1"
        android:background="#FFFFFFFF"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/back" />

</android.support.constraint.ConstraintLayout>

The MyDrawView.java class:

public class MyDrawView extends View {

    private Bitmap  mBitmap;
    private Canvas  mCanvas;
    private Path    mPath;
    private Paint   mBitmapPaint;
    private Paint   mPaint;

    public MyDrawView(Context c) {
        super(c);

        mPath = new Path();
        mBitmapPaint = new Paint(Paint.DITHER_FLAG);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(0xFF000000);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(3);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);

        canvas.drawPath(mPath, mPaint);
    }

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

    private void touch_start(float x, float y) {
        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
    }
    private void touch_move(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 touch_up() {
        mPath.lineTo(mX, mY);
        // commit the path to our offscreen
        mCanvas.drawPath(mPath, mPaint);
        // kill this so we don't double draw
        mPath.reset();
    }

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

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }
        return true;
    }

    public void clear(){
        mBitmap.eraseColor(Color.TRANSPARENT);
        invalidate();
        System.gc();
    }}

Here is the stack trace:

10-07 15:12:26.797 20682-20682/info.androidhive.sqlite E/AndroidRuntime: FATAL EXCEPTION: main
    Process: info.androidhive.sqlite, PID: 20682
    java.lang.RuntimeException: Unable to start activity ComponentInfo{info.androidhive.sqlite/info.jonas.notes.view.PaintActivity}: android.view.InflateException: Binary XML file line #20: Binary XML file line #20: Error inflating class info.jonas.notes.view.MyDrawView
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: android.view.InflateException: Binary XML file line #20: Binary XML file line #20: Error inflating class info.jonas.notes.view.MyDrawView
     Caused by: android.view.InflateException: Binary XML file line #20: Error inflating class info.jonas.notes.view.MyDrawView
     Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]
        at java.lang.Class.getConstructor0(Class.java:2327)
        at java.lang.Class.getConstructor(Class.java:1725)
        at android.view.LayoutInflater.createView(LayoutInflater.java:615)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:790)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
        at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:287)
        at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:139)
        at info.jonas.notes.view.PaintActivity.onCreate(PaintActivity.java:18)
        at android.app.Activity.performCreate(Activity.java:7136)
        at android.app.Activity.performCreate(Activity.java:7127)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Please help me find the problem.

Upvotes: 0

Views: 1275

Answers (4)

Zafer Celaloglu
Zafer Celaloglu

Reputation: 1418

there are 2 issues in this piece of code:

1st: you need to add missing constructors

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

    public MyDrawView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

2nd: wrong casting

findViewById(R.id.drawing); is not a RelativeLayout, it is your custom view which is MyDrawView

Upvotes: 0

Siddharth jain
Siddharth jain

Reputation: 447

As per the stacktrace your MyDrawView needs to override one more constructor.

public class MyDrawView extends View {

private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint   mBitmapPaint;
private Paint mPaint;

public MyDrawView(Context c) {
    super(c);
    init();
}

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

public void init() {
    mPath = new Path();
    mBitmapPaint = new Paint(Paint.DITHER_FLAG);

    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setDither(true);
    mPaint.setColor(0xFF000000);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeWidth(3);
}
.........
.........
//Rest of the code

Upvotes: 0

TheWanderer
TheWanderer

Reputation: 17834

You have two problems I see. The first is in your stacktrace, and the second you'd have run into after fixing the first.

First, you need to add another constructor to your custom View. Otherwise, there's no way for Android to apply any attributes from XML. That's where the crash is happening. Change your current constructor to:

public MyDrawView(Context c, AttributeSet attrs) {
    super(c, attrs);

    mPath = new Path();
    mBitmapPaint = new Paint(Paint.DITHER_FLAG);

    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setDither(true);
    mPaint.setColor(0xFF000000);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeWidth(3);
}

And add another constructor:

public MyDrawView(Context c) {
    this(c, null);
}

The second issue is, well, your entire onCreate() method:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_paint);
    RelativeLayout parent = (RelativeLayout) findViewById(R.id.drawing); 
    myDrawView = new MyDrawView(this);
    parent.addView(myDrawView);
}

R.id.drawing is your custom View, which doesn't extend RelativeLayout. In fact, you don't even have a RelativeLayout in your XML. Then you're creating a new instance of your View and attempting to add it to this non-existent RelativeLayout? None of that code is needed if you already have your custom View in XML. Remove it all.

If you need a reference to your View:

myDrawView = findViewById(R.id.drawing);

Upvotes: 3

extmkv
extmkv

Reputation: 2045

You need to override other constructor used when the XML is inflated:

  public MyDrawView(Context context, AttributeSet attrs) {....

The attrs contains the view attributes and if you want you can create your own attributes too.

Upvotes: 0

Related Questions