Android:自定义 View 在错误的 x、y 坐标处绘制

标签 android ondraw viewgroup onmeasure

我正在尝试创建一个自定义 View ,从 View 组继承,并以自定义方式在该 View 组内布局自定义 subview 。基本上,我正在尝试创建一个类似于 Outlook 中的日历 View ,其中每个事件占用的屏幕高度与其长度有关。

我在 ViewGroup 的构造函数中初始化了一个 View 的 ArrayList,覆盖了 onMeasure、onLayout 和 onDraw,并且一切正常,除了...渲染的 View 都从 (0,0) 开始渲染,即使我设置了它们的左和权属性为其他值。它们的宽度和高度没问题,只有顶部和左侧是错误的。

这是代码,为了清晰和简单起见,我对其进行了缩写:

public class CalendarDayViewGroup extends ViewGroup {
    private Context mContext;
    private int mScreenWidth = 0;

    private ArrayList<Event> mEvents;
    private ArrayList<View> mEventViews;

    // CalendarGridPainter is a class that draws the background grid. 
    // this one works fine so I didn't write its actual code here.
    // it just takes a Canvas and draws lines on it.
    // I also tried commenting out this class and got the same result,
    // so this is DEFINITELY not the problem.
    private CalendarGridPainter mCalendarGridPainter;

    public CalendarDayViewGroup(Context context, Date date) {
        super(context);
        init(date, context);
    }

    //... other viewGroup constructors go here...

    private void init(Date date, Context context) {
        mContext = context;
        // the following line loads events from a database
        mEvents = AppointmentsRepository.getByDateRange(date, date);

        // inflate all event views
        mEventViews = new ArrayList<>();
        LayoutInflater inflater = LayoutInflater.from(mContext);
        for (int i = 0; i < mEvents.size(); i++) {
            View view = getSingleEventView(mEvents.get(i), inflater);
            mEventViews.add(view);
        }

        // set this flag so that the onDraw event is called
        this.setWillNotDraw(false);
    }

    private View getSingleEventView(Event event, LayoutInflater inflater) {
        View view = inflater.inflate(R.layout.single_event_view, null);
        // [set some properties in the view's sub-views]
        return view;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));

        // get screen width and create a new GridPainter if needed
        int screenWidth = MeasureSpec.getSize(widthMeasureSpec);
        if (mScreenWidth != screenWidth)
        {
            mScreenWidth = screenWidth;
            mCalendarGridPainter = new CalendarGridPainter(screenWidth);
        }

        int numChildren = mEvents.size();
        for (int i = 0; i < numChildren; i++) {
            View child = mEventViews.get(i);
            Event event = mEvents.get(i);

            // event width is the same as screen width
            int specWidth = MeasureSpec.makeMeasureSpec(mScreenWidth, MeasureSpec.EXACTLY);

            // event height is calculated by its length, the calculation was ommited here for simplicity
            int eventHeight = 350; // actual calculation goes here...
            int specHeight = MeasureSpec.makeMeasureSpec(eventHeight, MeasureSpec.EXACTLY);
            child.measure(specWidth, specHeight);
        }
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int numChildren = mEvents.size();
        for (int i = 0; i < numChildren; i++) {
            View child = mEventViews.get(i);
            Event event = mEvents.get(i);

            int eventLeft = 0;
            int eventTop = (i + 1) * 200; // test code, make each event start 200 pixels after the previous one
            int eventWidth = eventLeft + child.getMeasuredWidth();
            int eventHeight = eventTop + child.getMeasuredHeight();
            child.layout(eventLeft, eventTop, eventWidth, eventHeight);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
         // draw background grid
        mCalendarGridPainter.paint(canvas);
        // draw events
        for (View view : mEventViews) {
            view.draw(canvas);
        }
        super.onDraw(canvas);
    }
}

最佳答案

出于某种原因,似乎使用 ViewGroup 绘制子项的方式是 ViewGroup 将 Canvas 转换为子项的位置,然后在 0,0 处绘制子项.

但事实证明,ViewGroup 将为您处理所有 child 的绘制。我认为如果您简化您的 onDraw() 方法,您应该准备就绪:

@Override
protected void onDraw(Canvas canvas) {
     // draw background grid
    mCalendarGridPainter.paint(canvas);
    // draw events
    super.onDraw(canvas);
}

现在我正在进一步查看您的代码,我注意到您在 ViewGroup 的代码中扩充了您的 subview 。最好在 ViewGroup 之外执行所有这些操作,使用 addView() 添加这些 View ,然后使用 getChildCount()getChildAt()onLayout() 期间访问 subview 。

关于Android:自定义 View 在错误的 x、y 坐标处绘制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34368271/

相关文章:

java - Flutter如何解析JSON Mongodb数据

android - 使用 android.support.v7.widget.RecyclerView 的自定义实现时没有预览

android - 未调用扩展 ImageView onDraw 方法

android - 使用移动手势绘制矩形

java - Android ViewGroup.setScaleX() 导致 View 被裁剪

android - 在没有GPS的情况下定位android中的当前位置

android - 是否可以扫描所有支持 WiFi 的 Android 设备,而不仅仅是热点?

android - SweepGradient Android - 如何设置渐变的起始角度

android - 创建新 View 时动态隐藏 View stub

android ViewGroup高度不能是wrap_content