简体   繁体   中英

Android custom view not repainting, though onDraw is being called

I have custom view called ArrowView .

When this view is part of a layout which is part of an ArrayAdapter everything is fine. (It is repainted as the underlying data changes.)

When I use that same layout as part of another Adapter the ArrowView doesn't repaint.

I have debugged it, and found that onDraw is called, with different values for rot , when I expect it to be - it is just that the screen is not updating.

Do I need to alter my ArrowView so that it repaints properly?

public class ArrowView extends View {

    private Path path;
    public final Paint paint;
    public final Paint circlePaint;
    private float rot = 30;
    private float length = 0.5f;
    private float width = 1.0f;
    public final static int SIZE = 96;
    private float size = SIZE;
    private float xOff;
    private float yOff;

    public ArrowView(Context context, AttributeSet attrs) {
        super(context, attrs);

        path = new Path();
        path.moveTo( 0.0f,  -0.5f);
        path.lineTo( 0.5f,   0.0f);
        path.lineTo( 0.25f,  0.0f);
        path.lineTo( 0.25f,  0.5f);
        path.lineTo(-0.25f,  0.5f);
        path.lineTo(-0.25f,  0.0f);
        path.lineTo(-0.5f,   0.0f);
        path.close();

        paint = new Paint();
        paint.setARGB(150, 0, 150, 0);

        circlePaint = new Paint();
        circlePaint.setARGB(50, 150, 150, 150);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        canvas.translate(xOff, yOff);
        canvas.scale(size, size);
        canvas.save();
        canvas.translate(0.5f, 0.5f);
        canvas.drawCircle(-0.0f, -0.0f, 0.5f, circlePaint);
        canvas.save();
        canvas.rotate(rot, 0.0f, 0.0f);
        canvas.save();
        canvas.scale(width, length);
        canvas.drawPath(path, paint);
        canvas.restore();
        canvas.restore();
        canvas.restore();
        canvas.restore();
    }

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

        //Get the width measurement
        int widthSize = getMeasurement(widthMeasureSpec,
                SIZE);

        //Get the height measurement
        int heightSize = getMeasurement(heightMeasureSpec,
                SIZE);

        size = min(widthSize, heightSize) * 0.9f;
        xOff = 0.0f;
        yOff = 0.05f * size;

        //MUST call this to store the measurements
        setMeasuredDimension(widthSize, heightSize);
    }

    public static int getMeasurement(int measureSpec, int contentSize) {
        int specMode = View.MeasureSpec.getMode(measureSpec);
        int specSize = View.MeasureSpec.getSize(measureSpec);
        int resultSize = 0;
        switch (specMode) {
            case View.MeasureSpec.UNSPECIFIED:
                //Big as we want to be
                resultSize = contentSize;
                break;
            case View.MeasureSpec.AT_MOST:
                //Big as we want to be, up to the spec
                resultSize = min(contentSize, specSize);
                break;
            case View.MeasureSpec.EXACTLY:
                //Must be the spec size
                resultSize = specSize;
                break;
        }

        return resultSize;
    }

    public void setLength(float length) {
        this.length = length;
        invalidate();
    }

    public float getLength() {
        return length;
    }

    public void setWidth(float width) {
        this.width = width;
        invalidate();
    }

    public void setRot(float rot) {
        this.rot = rot;
        invalidate();
    }

}

I also faced the same problem when i want to update an view from the non-ui thread and fragment; invalidate() method not updating the view. So as per the sdk reference

http://developer.android.com/intl/es/reference/android/view/View.html postInvalidate() will solve the issue.

This can be done in two ways or more!!!.

Calling runOnUi thread in setter method

runOnUiThread(new Runnable() {
    public void run() {
     this.length = length;
    this.invalidate();
    }
});

Or Just call postInvalidate() in setter method.

if onDraw() is called but the view doesn't update itself, the cause may be that it is called on the wrong thread.

In your adapter be sure to handle your view from a UI Thread

 getActivity().runOnUiThread(new Runnable() {
    @Override
    public void run() {
    mAdapter.updateRot(newRot);
 });

 public void updateRot(double newRot){
     arrowView.setRot(newRot);
     notifyDataSetChanged();
 }

from the adapter use notifyDataSetChanged() to refresh the view

notifyDataSetChanged(): Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM