简体   繁体   中英

Only one custom view is drawing

I am trying to display several the same custom views on the screen. Here is my View class:

public class DraggableText extends View {
private int cStart;
private int cEnd;
private Paint mTextPaint, mBgPaint;
private RectF mRect;
private Point mPos;

public DraggableText(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
            R.styleable.DraggableText, 0, 0);

    try {
        cStart = Color.parseColor(a
                .getString(R.styleable.DraggableText_gradientStart));
        cEnd = Color.parseColor(a
                .getString(R.styleable.DraggableText_gradientEnd));
    } finally {
        a.recycle();
    }
    init();
}

private void init() {
    mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    Shader s = new LinearGradient(0, 0, 0, getHeight(), cStart, cEnd,
            Shader.TileMode.CLAMP);
    mBgPaint.setShader(s);

    mRect = new RectF();
    mPos = new Point(0, 0);
}

public void setPos(Point p){
    mPos = p;
    invalidate();
    requestLayout();
}

public Point getPos(){
    return mPos;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int desiredWidth = (widthSize / 2) - 80;
    int desiredHeight = 65;

    int width;
    int height;

    if (widthMode == MeasureSpec.EXACTLY) {
        width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
        width = Math.min(desiredWidth, widthSize);
    } else {
        width = desiredWidth;
    }

    if (heightMode == MeasureSpec.EXACTLY) {
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        height = Math.min(desiredHeight, heightSize);
    } else {
        height = desiredHeight;
    }

    setMeasuredDimension(width, height);
}

@Override
protected void onDraw(Canvas c) {
    super.onDraw(c);
     mRect.set(mPos.x, mPos.y, getWidth(), getHeight());
     c.drawRoundRect(mRect, 8, 8, mBgPaint);
}

}

In the Activity in onCreate callback I do that:

    word1 = (DraggableText) findViewById(R.id.word1);
    word2 = (DraggableText) findViewById(R.id.word2);
    word3 = (DraggableText) findViewById(R.id.word3);
    word4 = (DraggableText) findViewById(R.id.word4);

    word1.post(new Runnable() {
        @Override
        public void run() {
            word1.setPos(new Point(20, 20));
            word2.setPos(new Point(word1.getWidth() + 20, 20));
            word3.setPos(new Point(20, word1.getHeight() + 10));
            word4.setPos(new Point(word1.getWidth() + 20, word1.getHeight() + 10));
        }
    });

I post this runnable to have access to getWidth and getHeight of views. It is not a point. If I remove this code and just use setPos with whatever values I always get the same. Only one view on the screen (or all, but on top of formers).

Where is bug which causing drawing only one view?

The problem is that your views haven't actually finished inflation and initial layout by the time your post() runnable executes. You want to interact with a ViewTreeObserver instead. In your Activity's onResume, try this:

@Override
public void onResume(){
    super.onResume();
    findViewById(R.id.word1).getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                @Override public void onGlobalLayout() {
                        DraggableText word1 = (DraggableText) findViewById(R.id.word1);
                        DraggableText word2 = (DraggableText) findViewById(R.id.word2);
                        DraggableText word3 = (DraggableText) findViewById(R.id.word3);
                        DraggableText word4 = (DraggableText) findViewById(R.id.word4);
                        word1.setPos(new Point(20, 20));
                        word2.setPos(new Point(word1.getWidth() + 20, 20));
                        word3.setPos(new Point(20, word1.getHeight() + 10));
                        word4.setPos(new Point(word1.getWidth() + 20, word1.getHeight() + 10));
                        word1.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    }
                }
            });
}

Here's more info on ViewTreeObserver .

FOLLOW UP: Ah, my bad. I didn't read your code very thoroughly I guess. I'm actually not sure there's a convenient 100% framework way of doing what you ask. requestLayout() only queues the next layout of your view, it doesn't force it synchronously. You could try two things:

1 - Create two different runnables: the first to Post the position change to word1. the second to post the remaining position changes.

2 - Instead of relying on the framework's getWidth\\getHeight, track position and dimension in your own member variables within your custom view. This way, you can manually update them when you call setPos().

Also, you may consider using a different layout mechanism entirely. Instead of getting into so many details, try using a RelativeLayout container. Then, for example, you could just change the margins of word1, and the other words would follow accordingly.

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