9

In this code, I would like to draw a line between the top of two ImageViews. However, when running the app, the custom view is shown solid black after calling invalidate().

Here is my code:

public class ArrowView extends RelativeLayout {
    public Paint paint;
    public Bitmap eraser;
    public Canvas cacheCanvas;

    public float leftX;
    public float leftY;

    public float rightX;
    public float rightY;

    public boolean update = false;

    public ImageView iv_leftArrow;
    public ImageView iv_rightArrow;

    private int w;
    private int h;

    LayoutInflater mInflater;

    public ArrowView(Context context) {
        super(context);
        this.setWillNotDraw(false);
        mInflater = LayoutInflater.from(context);
        init();
    }

    public ArrowView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setWillNotDraw(false);
        mInflater = LayoutInflater.from(context);
        init();
    }

    public ArrowView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setWillNotDraw(false);
        mInflater = LayoutInflater.from(context);
        init();
    }

    @Override
    public void onSizeChanged(int w, int h, int oldW, int oldH) {
        this.w = w;
        this.h = h;
        super.onSizeChanged(w, h, oldW, oldH);
    }

    public void init() {
        View v = mInflater.inflate(R.layout.arrow_view, this, true);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.BLACK);
        paint.setStrokeWidth(5);
        v.setBackgroundColor(Color.TRANSPARENT);

        eraser = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        iv_leftArrow = (ImageView) v.findViewById(R.id.iv_leftarrow);
        iv_rightArrow = (ImageView) v.findViewById(R.id.iv_rightArrow);

        cacheCanvas = new Canvas();
        cacheCanvas.setBitmap(eraser);
    }

    public void setCoordinates(float leftX, float leftY, float rightX, float rightY) {
        this.leftX = leftX;
        this.leftY = leftY;
        this.rightX = rightX;
        this.rightY = rightY;
    }

    @Override
    public void onDraw(Canvas c) {
        super.onDraw(c);
        setCoordinates(iv_leftArrow.getX() + iv_leftArrow.getWidth() / 2, iv_leftArrow.getY(), iv_rightArrow.getX() + iv_rightArrow.getWidth() / 2, iv_rightArrow.getY() );
        if (update) {
            c.drawLine(leftX, leftY, rightX, rightY, paint);
            update = false;
        }
        cacheCanvas.drawPath(p, paint);
    }
}

Is there any reason why the custom view is showing as solid black after calling invalidate()?

Pink Jazz
  • 784
  • 4
  • 13
  • 34
  • 1
    Where do you set "update" to true? – Mobile Developer Jun 02 '16 at 18:30
  • I set it from an my `CollectingDetail` activity just before calling `invalidate()`. Perhaps I will try calling it before calling `setArrow()` in CollectingDetail. – Pink Jazz Jun 03 '16 at 22:07
  • In the last line: `cacheCanvas.drawPath(p, paint);` how could you find the value of **p**? – Khairul Alam Licon Jun 06 '16 at 07:26
  • Looks like I got it to work. Swapping two lines of code to move the change of `update` before `setArrow()` did the trick. How can I award the bounty? – Pink Jazz Jun 07 '16 at 14:35
  • As far as I can tell the problem is: you set the background of the inflated view to transparent, but that is not the background of your custom view. Maybe try to set the background of your view to transparent by calling `setBackgroundColor(Color.TRANSPARENT)` (without the v instance). – racs Jun 09 '16 at 04:46

2 Answers2

2

Usually, the system handles resizing, hiding, showing and a ton of other things for your widgets automatically but it sometimes has issues if the underlying buffer for drawn pixels or backing data has changed or is stale (you swap the image resource on a View or the raw dataset changes). This occurs because there is no way that the OS can know that the data changed in the specific manner that it did.

In these cases where you are dealing with drawing, you have to tell the system that its underlying data is not in a good state with Widget.invalidate() and the re-drawing gets queued on the main thread just as you mentioned. Depending on the system implementation and Android version what is tracked for changes by the system varies but what I normally do is assume that system resources (byte arrays, char arrays, resource indexes, manual drawing on the context) are not tracked and need an invalidate and everything else will be handled by the system.

Source : When it's necessary to execute invalidate() on a View?

Community
  • 1
  • 1
gopigoppu
  • 219
  • 2
  • 15
0

As you know, invalidate() is used to update a view by a call to onDraw.

What I think you were doing was calling it before setting update to true - so your onDraw method ran with a false update by the sounds of it.

SQLiteNoob
  • 2,958
  • 3
  • 29
  • 44