简体   繁体   中英

Android View Instantiation Order

EDIT: I was missing a key concept, the message queue, while creating my custom view and trying to update the view without using it turned out being the source of the issue I was having. This gets cleared up here:

Android: Writing Custom Views that Support The Message Queue

It turns out that you need to let the runtime/android call OnMeasure after OnCreate exits ("like normal"), and in the mean time put any updates to the view in the message queue via the .post method.

So, you can grab a handle to the view, via FindViewById, in OnCreate as usual, and then post any updates to it (either through overrides like setText, or your own set functions) within a runnable (see above link).


I've been working on a custom view in android for quite some time now and have throughly experimented with constructors, attributes, layout parameters, and drawing methods (OnMeasure, OnLayout, ...).

The parent-most viewgroup (the one declared in the layout file for rendering) inherits from RelativeLayout and contains three child views, all custom/inherited viewgroups as well, and so on.

I'm implementing a custom calendar (I know, very original and exciting), I'll call CustomCalendar. So, in my layout file I have the statment

<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"
 ...
/>

This view resides along side a few others that are defined in the layout file as well, and thus the source of my complication, which will be stated now.

I'm trying to figure out how to get this view's dimensions before OnMeasure is called (where I can get them no problem, however because the layout dimensions of the children views are determined from this views dimensions, I cannot instantiate the childeren views until the first time OnMeasure is called, and this proves to be after I need it; after OnCreate exits).

That is, after calling SetContentView in OnCreate, and then instantiating the custom view from it via FindViewById(Resource.Id.Calendar1), the child views do not get instantiated as well if its done from the OnMeasure override (again, b/c OnMeasure isn't called until after OnCreate returns).

If I read the attributes passed into the constructor, alls I get from layout_width and layout_height are the constant -1 for fill_parent. I cannot just use the entire screen either, because there are other views to compensate for.


Therefore, I'm looking for the best approach in determining the dimensions of a custom view from within its constructor so that I can instantiate all of the view's childern from it (the constructor) with the proper layout dimensions so that the views are available from OnCreate (for updating with content) after SetContentView is called and before returning from OnCreate.

Another means to the same end will be fine with me, though I'm kinda looking to do this the way the infrastructure intends it to be (if there is such a thing).

You can't get dimensions in the constructor. At best, if they are defined in XML, you could get the layout width and height from the AttributeSet.

What you should be doing is overriding onMeasure and onLayout. You can do whatever calculations and setup you need in onMeasure, then pass the calculated values to measureChild(view, widthSpec, heightSpec) . and you can mess with layout stuff in onLayout then call childView.layout(left, top, right, bottom) .

I think I had this problem. I'm not sure of the timing myself, but I did the following to get the dimensions

ViewTreeObserver viewTreeObserver = mOverlayFrameLayout.getViewTreeObserver();
                if (viewTreeObserver.isAlive()) {
                  viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                    public void onGlobalLayout() {
                        mOverlayFrameLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                        //do some things
                    }
                  });
                }

EDIT Dont do this (see OP's edit note).


It ends up being that onMeasure is where this problem is solved...

Ya just got to call Measure on the view from OnCreate, which requires constructing the width and height MeasureSpec parameters that Measure uses:

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);

        ...
    }
}

Then just recursively do the same for all nested/child views.

I have a static bool variable in each custom view that keeps track if the view has been initialized, and check it in onMeasure to determine if I should initialize and measure them.

    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;
        }
    }

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