2

I am trying to draw a line with Canvas inside of a Fragment. After some research it appeared that I just had to extend the View class and override onDraw, then return an instance of my new View class where I would normally inflate a layout. However when I access the fragment on my device all I see is a white screen. Here is what I have so far:

package com.example.testing;

import android.app.Fragment;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class LineTab extends Fragment 
{       

    private class DrawView extends View {
        Paint paint = new Paint();

        public DrawView(Context context) {
            super(context);
            paint.setColor(Color.BLACK);
        }
        public DrawView(Context context, AttributeSet attrs) {
            super(context, attrs);
            paint.setColor(Color.BLACK);
        }
        public DrawView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            paint.setColor(Color.BLACK);
        }

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

            canvas.drawLine(0, 0, 50, 50, paint);
            canvas.drawLine(50, 0, 0, 50, paint);
        }

        @Override 
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

            int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
            int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
            this.setMeasuredDimension(parentWidth, parentHeight);
        }
    }   

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        return new DrawView(this.getActivity());
    }
}

What am I missing here?

EDIT: For anyone else facing this problem, this code turned out to work fine. I just had issues displaying it within my activity.

austerity
  • 23
  • 1
  • 4
  • You may need to call the draw method on creation by invalidating the view. Typically that is how you invoke the onDraw() method. I'm not in a place to check documentation, by I think it is View.invalidate() or something similar. – Demonslay335 Nov 11 '13 at 01:53
  • I tried both view.invalidate() and view.postInvalidate() inside onCreateView to no avail. Should it be called somewhere else? – austerity Nov 11 '13 at 02:09
  • VIEWs onDraw() method will be call when they is coming attach to the screen, so I think that was the reason your invalidate() doesn't work, but I didn't tried before, just a tip for you, good lucky. – VinceStyling Nov 11 '13 at 03:01
  • I think onDraw() may be already called on creation I'm now reading. Perhaps the coordinates of the lines are off-screen? I've had that happen so many times in drawing with any programming language. Try doing something else to make sure onDraw() is being called correctly like filling the background, and do some logs. – Demonslay335 Nov 11 '13 at 03:02
  • Also have you tried a real device? I've read a lot of goofs and red herrings with using emulators, so it's just good to check. – Demonslay335 Nov 11 '13 at 03:04
  • Yes, I've tried it on a nexus 4. I don't think I'm drawing off screen, I added a line to change the background color to green and that isn't coming through either. Am I extending View incorrectly? Am I instancing my new View the right way? Or is it something else? – austerity Nov 11 '13 at 06:31

1 Answers1

2

A few things you need to do when creating your own type of Widget:

  • What size should the view have? You need to overwrite one of or both of onMeasure and onLayout. Check the Guide to customizing widgets that shows what should go into onMeasure.

  • Overwrite/implement all three constructors. This is more kind of optional in your case, where you dont get inflated via XML. But it's good style to do so.

This should bring you at least a step forward. Maybe there're other issues with that code. Let's see.

EDIT: The second way is to use a Drawable and a usual ImageView to draw on the screen. This is more direct and needs much less lines to code.

Here's the XML that puts an ImageView on the screen. Note, that it does not define android:src.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/drawLineImageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FFFFFFFF" />

</FrameLayout>

Now you can code the drawable and connect it to the ImageView:

public class DrawLineActivity extends Activity {
/* (non-Javadoc)
 * @see android.app.Activity#onCreate(android.os.Bundle)
 */
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.draw_line);

    // Create a simple drawable and set it in the ImageView
    ((ImageView) findViewById(R.id.drawLineImageView)).setImageDrawable(new MyDrawable());
}

private class MyDrawable extends Drawable {
    @Override
    public void draw(Canvas canvas) {
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        canvas.drawLine(0, 0, 50, 50, paint);
        canvas.drawLine(50, 0, 0, 50, paint);
    }

    @Override
    public int getOpacity() {return 0;}

    @Override
    public void setAlpha(int alpha) {}

    @Override
    public void setColorFilter(ColorFilter cf) {}
}
}
jboi
  • 11,324
  • 4
  • 36
  • 43
  • Thank you for the help, I've updated my code to include a simple override of onMeasure and implementation of all 3 constructors and I'm still seeing a blank screen. onDraw is definitely never being called, I added a Log inside that method to make sure. Do you think I need onLayout or is the problem somewhere else? – austerity Nov 11 '13 at 20:22
  • I expected, that `onDraw` is called automatically. Try to `invalidate()` the view. This should call it. If not, a last thing would be, to extend a `Drawable` and put that in an `ImageView`. This way it has worked for me. – jboi Nov 11 '13 at 20:50
  • Marking this as the answer. I had issues with my visualization of the fragment in my activity that didn't have to do with the code I posted, but overriding onMeasure turned out to be necessary as well. Thank you so much! – austerity Nov 11 '13 at 20:52
  • I was just conducting some example for the drawable. Hopefully somebody find it useful. – jboi Nov 11 '13 at 21:36