Android自定义布局- onDraw()永远不会被调用。

时间:2021-11-25 08:08:56
public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TableView tv = new TableView(this);
        tv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
        setContentView(tv);      
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
}

public class TableView extends ViewGroup {
    private Paint oval;
    private RectF rect;

    public TableView(Context context) {
        super(context);
        oval= new Paint(Paint.ANTI_ALIAS_FLAG);
        oval.setColor(Color.GREEN);
    }


    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawOval(rect , oval);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int wspec = MeasureSpec.makeMeasureSpec(
                getMeasuredWidth(), MeasureSpec.EXACTLY);
        int hspec = MeasureSpec.makeMeasureSpec(
                getMeasuredHeight(), MeasureSpec.EXACTLY);
        for(int i=0; i<getChildCount(); i++){
            View v = getChildAt(i);
            v.measure(wspec, hspec);
        }
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        float w=r-l;
        float h=b-t;
        rect=new RectF(w/8,h/8,7*w/8,7*h/8);
        float theta = (float) (2 * Math.PI / getChildCount());
        for(int i=0; i< getChildCount(); i++) {
            View v = getChildAt(i);
            w = (rect.right-rect.left)/2;
            h = (rect.bottom-rect.top)/2;
            float half = Math.max(w, h)/2;
            float centerX = rect.centerX()+w*FloatMath.cos(theta);
            float centerY = rect.centerY()+h*FloatMath.sin(theta);
            v.layout((int)(centerX-half),(int)(centerY-half),(int)(centerX+half),(int)(centerY+half));
        }
    }
}

Well there are almost NONE good and deep tutorials and hardly any piece of data on how to do custom layouts right, so i tried to figure out how its done, what i am trying to implement is a Layout that paints a green oval at the center of the screen, and i want every child of this layout to be layed out around the oval.

几乎没有好和深度教程和几乎任何数据如何自定义布局吧,所以我试图弄清楚如何做,我想实现的是一个布局,描绘了一幅绿色的椭圆形在屏幕的中心,我希望每一个孩子的这种布局讲到的椭圆形。

you can think of that oval as a poker table that i want the children of this layout to seat around it.

你可以把它想象成一张牌桌,我想让这个布局的孩子们坐在它旁边。

What currently happens by this code is that i get a white app scren with no oval, so i debugged it and saw that onDraw never gets called...

这段代码所发生的事情是,我得到了一个没有oval的白色app scren,所以我调试了它,并且看到onDraw从来没有被调用…

3 questions:

三个问题:

  1. why is onDraw not getting called?
  2. 为什么onDraw不被调用?
  3. the sdk warns me that i shouldnt allocate new objects within onLayout method, so where should i calculate the RectF so it is ready for the onDraw call to come?
  4. sdk警告我,我不应该在onLayout方法中分配新的对象,那么我应该在哪里计算RectF,这样就可以为onDraw调用准备好了?
  5. does calling super.onDraw() would make all children paint themselves? or should i explicitly invoke their draw()?
  6. 调用super.onDraw()会让所有的孩子都自己画吗?或者我应该显式地调用它们的draw()?

If I got it all wrong and you guys can guide me in the right direction, or have any links to examples or tutorials related to this subject that would be helpful too!

如果我做错了,你们可以指导我正确的方向,或者有任何与这个主题相关的例子或教程的链接,这也很有帮助!

2 个解决方案

#1


120  

By default, onDraw() isn't called for ViewGroup objects. Instead, you can override dispatchDraw().

默认情况下,onDraw()不需要ViewGroup对象。相反,可以覆盖dispatchDraw()。

Alternatively, you can enable ViewGroup drawing by calling setWillNotDraw(false) in your TableView constructor.

或者,您可以通过在TableView构造函数中调用setWillNotDraw(false)来启用ViewGroup绘图。

See: Romain Guy's explanation

看:罗曼人的解释

EDIT:

编辑:

For #2: - Initialize it in the constructor, then just call rect.set() in your onLayout() method.

对于#2:-在构造函数中初始化它,然后在onLayout()方法中调用rect.set()。

For #3: - Yes, as far as I'm aware the super call will handle it, you shouldn't have to handle that unless you need to customize the way the children are drawn.

对于#3:-是的,就我所知,超级呼叫将会处理它,你不需要处理它,除非你需要自定义孩子的绘制方式。

#2


-1  

If you want that the canvas is be re-draw call invalidate(), and the method onDraw() will be re-executed

如果您想要将画布重新绘制为invalidate(),那么onDraw()方法将被重新执行。

#1


120  

By default, onDraw() isn't called for ViewGroup objects. Instead, you can override dispatchDraw().

默认情况下,onDraw()不需要ViewGroup对象。相反,可以覆盖dispatchDraw()。

Alternatively, you can enable ViewGroup drawing by calling setWillNotDraw(false) in your TableView constructor.

或者,您可以通过在TableView构造函数中调用setWillNotDraw(false)来启用ViewGroup绘图。

See: Romain Guy's explanation

看:罗曼人的解释

EDIT:

编辑:

For #2: - Initialize it in the constructor, then just call rect.set() in your onLayout() method.

对于#2:-在构造函数中初始化它,然后在onLayout()方法中调用rect.set()。

For #3: - Yes, as far as I'm aware the super call will handle it, you shouldn't have to handle that unless you need to customize the way the children are drawn.

对于#3:-是的,就我所知,超级呼叫将会处理它,你不需要处理它,除非你需要自定义孩子的绘制方式。

#2


-1  

If you want that the canvas is be re-draw call invalidate(), and the method onDraw() will be re-executed

如果您想要将画布重新绘制为invalidate(),那么onDraw()方法将被重新执行。