Андроид

РЕДАКТИРОВАТЬ: Мне не хватало ключевой концепции, очереди сообщений, при создании моего пользовательского представления и попытке обновить представление без использования его оказалось источником проблемы, с которой я столкнулся. Здесь выясняется:

Android: запись пользовательских просмотров, поддерживающих очередь сообщений

Оказывается, вы должны позволить runtime/android вызывать OnMeasure после выхода OnCreate ( "как обычно" ), и в то же время помещать любые обновления в представление в очереди сообщений с помощью метода .post.

Итак, вы можете захватить дескриптор представления, через FindViewById, в OnCreate, как обычно, а затем публиковать любые обновления (либо с помощью переопределений, как setText, либо ваших собственных функций набора) в пределах runnable (см. ссылку выше).


Я работаю над пользовательским представлением в андроиде довольно давно и на самом деле экспериментировал с конструкторами, атрибутами, параметрами макета и методами рисования (OnMeasure, OnLayout,...).

Основная группа представлений parent-most (объявленная в файле макета для рендеринга) наследует 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 (где я могу получить их без проблем, однако из-за того, что размеры макета представлений детей определяются из этих размеров представлений, я не могу создать экземпляр childeren до первого вызова OnMeasure, и это оказывается после того, как он мне понадобится, после выхода OnCreate).

То есть, вызывая SetContentView в OnCreate, а затем создавая экземпляр пользовательского представления из него через FindViewById (Resource.Id.Calendar1), дочерние представления также не получают экземпляр, если он сделан из переопределения OnMeasure (опять же, b/c OnMeasure не вызывается до тех пор, пока не вернется OnCreate).

Если я прочитал атрибуты, переданные в конструктор, все, что я получаю от layout_width и layout_height, являются константой -1 для fill_parent. Я не могу просто использовать весь экран, потому что есть другие виды для компенсации.


Поэтому я ищу наилучший подход при определении размеров пользовательского представления внутри его конструктора, чтобы я мог создать экземпляр из него (конструктора) с соответствующими размерами макета, чтобы представления доступны для OnCreate (для обновления с содержимым) после вызова SetContentView и перед возвратом из OnCreate.

Другим средством для того же самого конца будет хорошо со мной, хотя я как бы желаю сделать это так, как это хочет инфраструктура (если есть такая вещь).

Ответ 1

Вы не можете получить размеры в конструкторе. В лучшем случае, если они определены в XML, вы можете получить ширину и высоту макета из AttributeSet.

Что вы должны делать, это переопределить onMeasure и onLayout. Вы можете выполнять любые вычисления и настройки, необходимые в onMeasure, а затем передать рассчитанные значения measureChild(view, widthSpec, heightSpec). и вы можете возиться с материалами макета в onLayout, а затем вызвать childView.layout(left, top, right, bottom).

Ответ 2

Я думаю, у меня была эта проблема. Я не уверен в сроках сам, но я сделал следующее, чтобы получить размеры

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

Ответ 3

РЕДАКТИРОВАТЬ Не делайте этого (см. примечание по редактированию OP).


В результате получается, что onMeasure - это решение этой проблемы...

Я просто получил вызов Measure на представлении из OnCreate, который требует создания width и height Параметры MeasureSpec, которые использует Measure:

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

        ...
    }
}

Затем просто рекурсивно сделать то же самое для всех вложенных/дочерних представлений.

У меня есть статическая переменная bool в каждом пользовательском представлении, которая отслеживает, если вид был инициализируется и проверяет его в 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;
        }
    }