Как вы используете RelativeLayout с элементами управления, положение которых зависит от их собственного размера?

У меня довольно много других проблем с макетом, которые требуют большого количества дополнительных вызовов InvalidateLayout(), поэтому я начинаю сомневаться, понимаю ли я, как работает RelativeLayout.

Вот очень простой пример пользовательского интерфейса, которому нужна метка, выровненная по правому краю:

public class MainPage : ContentPage {
    public MainPage() {
        var layout = new RelativeLayout();

        var label = new Label() {
            Text = "I want to be right-aligned."
        };
        layout.Children.Add(label,
            Constraint.RelativeToParent((rl) => rl.Width - label.Width),
            Constraint.Constant(10));

        var button = new Button() {
            Text = "Invalidate"
        };
        button.Clicked += (object sender, EventArgs e) => layout.ForceLayout();
        layout.Children.Add(button,
            Constraint.Constant(10),
            Constraint.Constant(10));

        Content = layout;
    }
}

Я ожидаю, что это начнется с правильного выравнивания метки, но оно не выравнивает метку правильно, пока не будет принудительно выполнен другой проход макета. Переопределяя такие методы, как OnSizeRequest() в моем пользовательском элементе управления, я определил, что это связано с тем, что вызовы OnSizeRequest() не происходят до тех пор, пока не будут вызваны лямбда-выражения ограничения RelativeLayout. Итак, когда страница выложена, ширина метки равна -1. Когда ForceLayout() вызывается позже, метка имеет возможность выполнить свою логику компоновки и правильно установить свойство Width, поэтому она размещается правильно.

В более широком контексте я пытаюсь сделать кнопку, которая при нажатии исчезает, а метка перемещается на то место, где она была. Он должен быть выровнен по нижнему правому углу моего макета, но я обнаружил, что изменение Opacity или IsVisible только непоследовательно обновляет макет. Единственным последовательным поведением является то, что RelativeLayout действительно любит запрашивать размер элемента управления, прежде чем он получит возможность изменить свой размер.

Я неправильно интерпретирую, как использовать RelativeLayout, или это ошибка в его логике?


person OwenP    schedule 24.02.2015    source источник


Ответы (1)


Углубившись в (текущую) реализацию RelativeLayout, я обнаружил истину, которую не ожидал: она не обращается к методу GetSizeRequest() представления и не вызывает его метод Layout() до того, как будут вычислены ограничения, потому что эти ограничения могут повлиять на окончательный размер элемента управления. Следствие: в то время как ограничения вычисляются, границы элемента управления отражают его старое положение и размер.

Чтобы «исправить» это, вызовите GetSizeRequest() представления внутри ограничений, которым нужен самый актуальный размер элемента управления:

public class MainPage : ContentPage {
    public MainPage() {
        var layout = new RelativeLayout();

        var label = new Label() {
            Text = "I want to be right-aligned."
        };
        Func<RelativeLayout, double> getLabelWidth = (parent) => label.GetSizeRequest(parent.Width, parent.Height).Request.Width;
        layout.Children.Add(label,
            Constraint.RelativeToParent((rl) => rl.Width - getLabelWidth(rl)),
            Constraint.Constant(10));

        var button = new Button() {
            Text = "Invalidate"
        };
        button.Clicked += (object sender, EventArgs e) => layout.ForceLayout();
        layout.Children.Add(button,
            Constraint.Constant(10),
            Constraint.Constant(10));

        Content = layout;
    }
}
person OwenP    schedule 25.02.2015