-1

编辑:我在创建自定义视图并尝试在不使用它的情况下更新视图时错过了一个关键概念,即消息队列,结果证明这是我遇到的问题的根源。这在这里得到了澄清:

Android:编写支持消息队列的自定义视图

事实证明,您需要让运行时/android 在 OnCreate 退出后调用 OnMeasure(“像正常一样”​​),同时通过 .post 方法将任何更新放入消息队列中。

因此,您可以像往常一样在 OnCreate 中通过 FindViewById 获取视图的句柄,然后在可运行文件中发布任何更新(通过 setText 之类的覆盖或您自己的设置函数)(参见上面的链接)。


我在 android 中开发自定义视图已经有一段时间了,并且已经彻底试验了构造函数、属性、布局参数和绘图方法(OnMeasure、OnLayout,...)。

最父视图组(在布局文件中声明用于渲染的视图组)继承自 RelativeLayout 并包含三个子视图,以及所有自定义/继承的视图组,等等。

我正在实现一个自定义日历(我知道,非常原始且令人兴奋),我将调用 CustomCalendar。所以,在我的布局文件中,我有声明

<MonoDroid.CustomViews.CustomCalendar
  xmlns:calendar="http://schemas.android.com/apk/res/net.monocross.appname"
  android:id="+id/Calendar1"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  calendar:row_count="5"
  calendar:col_count="7"
 ...
/>

这个视图与布局文件中定义的其他一些视图一起存在,因此是我的复杂性的来源,现在将说明。

我试图弄清楚如何在调用 OnMeasure 之前获取该视图的尺寸(在那里我可以得到它们没有问题,但是因为子视图的布局尺寸是由该视图尺寸确定的,所以我无法实例化子视图,直到第一次调用 OnMeasure,这证明是在我需要它之后;在 OnCreate 退出之后)。

也就是说,在 OnCreate 中调用 SetContentView,然后通过 FindViewById(Resource.Id.Calendar1) 从中实例化自定义视图后,如果从 OnMeasure 覆盖完成子视图(再次,b/c OnMeasure直到 OnCreate 返回后才被调用)。

如果我读取传递给构造函数的属性,我从 layout_width 和 layout_height 得到的都是 fill_parent 的常量 -1。我也不能只使用整个屏幕,因为还有其他视图需要补偿。


因此,我正在寻找从其构造函数中确定自定义视图尺寸的最佳方法,以便我可以使用适当的布局尺寸从它(构造函数)实例化所有视图的子项,以便视图可以从在调用 SetContentView 之后和从 OnCreate 返回之前的 OnCreate(用于更新内容)。

达到同一目的的另一种方法对我来说很好,尽管我有点希望按照基础设施的预期方式来做这件事(如果有这样的事情)。

4

3 回答 3

1

您无法在构造函数中获取尺寸。充其量,如果它们是在 XML 中定义的,您可以从 AttributeSet 中获取布局的宽度和高度。

您应该做的是覆盖 onMeasure 和 onLayout。您可以在 onMeasure 中进行所需的任何计算和设置,然后将计算值传递给measureChild(view, widthSpec, heightSpec). 你可以在 onLayout 中弄乱布局,然后调用childView.layout(left, top, right, bottom).

于 2012-10-16T21:08:19.837 回答
1

我想我有这个问题。我自己不确定时间,但我做了以下操作来获得尺寸

ViewTreeObserver viewTreeObserver = mOverlayFrameLayout.getViewTreeObserver();
                if (viewTreeObserver.isAlive()) {
                  viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                    public void onGlobalLayout() {
                        mOverlayFrameLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                        //do some things
                    }
                  });
                }
于 2012-10-16T21:10:54.643 回答
0

编辑不要这样做(参见 OP 的编辑说明)。


它最终是 onMeasure 是解决这个问题的地方......

Ya 只需从 OnCreate 调用视图上的 Measure,这需要构造 Measure 使用的宽度和高度 MeasureSpec 参数:

public class Activity1 : Activity
{
    ...

    private CustomCalendar _calendar;

    public override void OnCreate()
    {
        SetContentView(Resource.Layout.Calendar);

        _calendar = FindViewById<CustomCalendar>(Resource.Id.Calendar1);           

        int widthMeasureSpec = View.MeasureSpec.MakeMeasureSpec(Resources.DisplayMetrics.WidthPixels, MeasureSpecMode.Exactly);
        int heightMeasureSpec = View.MeasureSpec.MakeMeasureSpec(Resources.DisplayMetrics.HeightPixels, MeasureSpecMode.Exactly);

        _calendar.Measure(widthMeasureSpec, heightMeasureSpec);

        ...
    }
}

然后对所有嵌套/子视图递归地执行相同的操作。

我在每个自定义视图中都有一个静态布尔变量,用于跟踪视图是否已初始化,并在 onMeasure 中检查它以确定我是否应该初始化和测量它们。

    protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        base.OnMeasure(widthMeasureSpec, heightMeasureSpec);

        if (!_initialized)
        {
            _WIDTH = MeasureSpec.GetSize(widthMeasureSpec);
            _HEIGHT = MeasureSpec.GetSize(heightMeasureSpec);

            InitViews();

            MeasureSpecMode widthMode = MeasureSpec.GetMode(widthMeasureSpec);
            MeasureSpecMode heightMode = MeasureSpec.GetMode(heightMeasureSpec);

            widthMeasureSpec = MeasureSpec.MakeMeasureSpec(_childView.LayoutParameters.Width, widthMode);
            heightMeasureSpec = MeasureSpec.MakeMeasureSpec(_childView.LayoutParameters.Height, heightMode);

            _childView.Measure(widthMeasureSpec, heightMeasureSpec);

            _initialized = true;
        }
    }
于 2012-10-17T18:01:03.883 回答