我想创建一个页面布局,在其中显示多个TableView
s 以及其他视图,例如Label
每个视图之间的a 。
为此,我需要具备以下能力:
TableView
不默认为垂直扩展布局选项(默认看起来不可更改),以便它们可以StackLayout
垂直堆叠。UITableView
部分页眉/页脚,以便它们之间没有过多的垂直间距。我通过以下CustomTableView
控制实现了这一点:
public class CustomTableView : TableView
{
public bool NoHeader { get; set; }
public bool NoFooter { get; set; }
protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
{
if(!VerticalOptions.Expands)
{
// Call OnSizeRequest that is overwritten in custom renderer
var baseOnSizeRequest = GetVisualElementOnSizeRequest();
return baseOnSizeRequest(widthConstraint, heightConstraint);
}
return base.OnSizeRequest(widthConstraint, heightConstraint);
}
public Func<double, double, SizeRequest> GetVisualElementOnSizeRequest()
{
var handle = typeof(VisualElement).GetMethod(
"OnSizeRequest",
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new Type[] { typeof(double), typeof(double) },
null)?.MethodHandle;
var pointer = handle.Value.GetFunctionPointer();
return (Func<double, double, SizeRequest>)Activator.CreateInstance(
typeof(Func<double, double, SizeRequest>), this, pointer);
}
}
以及用于此的自定义 iOS 渲染器,它遵循此 SO 答案中隐藏UITableView
页眉/页脚的建议:
public class CustomTableViewRenderer : TableViewRenderer
{
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
Control.LayoutIfNeeded();
var size = new Size(Control.ContentSize.Width, Control.ContentSize.Height);
return new SizeRequest(size);
}
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
{
base.OnElementChanged(e);
if(e.NewElement is CustomTableView view)
{
Control.ScrollEnabled = false;
SetSource();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var view = (CustomTableView)Element;
if(e.PropertyName == TableView.HasUnevenRowsProperty.PropertyName)
{
SetSource();
}
}
private void SetSource()
{
var view = (CustomTableView)Element;
if(view.NoFooter || view.NoHeader)
{
Control.Source = new CustomTableViewModelRenderer(view);
}
else
{
Control.Source = Element.HasUnevenRows ? new UnEvenTableViewModelRenderer(Element) :
new TableViewModelRenderer(Element);
}
}
public class CustomTableViewModelRenderer : UnEvenTableViewModelRenderer
{
private readonly CustomTableView _view;
public CustomTableViewModelRenderer(CustomTableView model)
: base(model)
{
_view = model;
}
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
if(_view.HasUnevenRows)
{
return UITableView.AutomaticDimension;
}
return base.GetHeightForRow(tableView, indexPath);
}
public override nfloat GetHeightForHeader(UITableView tableView, nint section)
{
if(_view.NoHeader)
{
return 0.00001f;
}
return base.GetHeightForHeader(tableView, section);
}
public override UIView GetViewForHeader(UITableView tableView, nint section)
{
if(_view.NoHeader)
{
return new UIView(new CGRect(0, 0, 0, 0));
}
return base.GetViewForHeader(tableView, section);
}
public override nfloat GetHeightForFooter(UITableView tableView, nint section)
{
if(_view.NoFooter)
{
return 0.00001f;
}
return 10f;
}
public override UIView GetViewForFooter(UITableView tableView, nint section)
{
if(_view.NoFooter)
{
return new UIView(new CGRect(0, 0, 0, 0));
}
return base.GetViewForFooter(tableView, section);
}
}
}
然后我创建了一个简单的Xamarin.Forms
ContentPage
来实现这个:
public class TablePage : ContentPage
{
public TablePage()
{
Table1 = new PageTableView
{
BackgroundColor = Color.Cyan,
Root = new TableRoot
{
new TableSection("Table 1")
{
new LabelEntryTableCell("Label A"),
new LabelEntryTableCell("Label B")
}
}
};
var label1 = new Label
{
Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Vivamus accumsan lacus orci. Nulla in enim erat.",
Margin = new Thickness(15, 5, 15, 0),
BackgroundColor = Color.Yellow
};
Table2 = new PageTableView
{
BackgroundColor = Color.Red,
Root = new TableRoot
{
new TableSection("Table 2")
{
new LabelEntryTableCell("Label C"),
new LabelEntryTableCell("Label D")
}
}
};
var label2 = new Label
{
Text = "Duis vulputate mattis elit. " +
"Donec vulputate lorem vitae elit posuere, quis consequat ligula imperdiet.",
Margin = new Thickness(15, 5, 15, 0),
BackgroundColor = Color.Green
};
StackLayout = new StackLayout
{
Children = { Table1, label1, Table2, label2 },
Spacing = 0
};
BackgroundColor = Color.Gray;
Title = "Custom Table View";
Content = new ScrollView
{
Content = StackLayout
};
}
public PageTableView Table1 { get; set; }
public PageTableView Table2 { get; set; }
public StackLayout StackLayout { get; set; }
}
public class PageTableView : CustomTableView
{
public PageTableView()
{
Intent = TableIntent.Settings;
HasUnevenRows = true;
RowHeight = -1;
VerticalOptions = LayoutOptions.Start;
NoFooter = true;
}
}
public class LabelEntryTableCell : ViewCell
{
public LabelEntryTableCell(string labelText)
{
var label = new Label { Text = labelText };
var entry = new Entry();
View = new StackLayout
{
Children = { label, entry },
BackgroundColor = Color.White,
Padding = 10
};
}
}
这一切似乎都按预期工作,但是,当第TablePage
一个显示CustomTableView
内容被切断时。然后我可以将设备旋转到横向,问题会自行纠正,按照我的意图显示我的布局。然后将设备旋转回纵向仍然显示正确的布局,CustomTableView
并且在该方向上不再有更多信息被切断。这种情况每次都持续发生。在此处查看此演示:
我还可以通过将其添加到以下内容来解决问题TablePage
:
protected override void OnAppearing()
{
base.OnAppearing();
// This will also fix the problem, but you see a flash of the old layout
// when the page first displays.
StackLayout.RedrawLayout();
}
...
public class CustomStackLayout : StackLayout
{
public void RedrawLayout()
{
InvalidateLayout();
}
}
How do I correct this problem? My custom control/renderer/page works as intended whenever I do the rotation/OnAppearing hack to correct it and I am not sure why. It seems like I need to call some re-draw method on the UITableView
.
Sample Project
If you want to see the full source/project for this isolated test scenario you can find it on GitHub here.
I knew this issue in iOS. When the tableView first loaded, we can't get the correct contentSize even if we have written LayoutIfNeeded()
. But after the page is appearing then we refresh it, we can get the correct contentSize. This is why
This will also fix the problem, but you see a flash of the old layout
解决方案 1:我的建议是我们可以尝试在 tableView 显示时延迟一点毫秒刷新 tableView:
在 TablePage 中,我订阅了一个 MessagingCenter 来刷新布局
MessagingCenter.Subscribe<object>(this, "Refresh", (sender) =>
{
StackLayout.RedrawLayout();
});
然后我在渲染器中发送消息:
//I define this bool field to make sure this message only be sent at the first time after tableView loaded
private bool shouldRefresh = true;
public override async void WillDisplay(UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
if (tableView.NumberOfRowsInSection(0) - 1 == indexPath.Row)
{
if (shouldRefresh)
{
await Task.Delay(10);
MessagingCenter.Send<object>(this, "Refresh");
shouldRefresh = false;
}
}
}
Solutin2:我们也可以尝试定义一个估计的rowHeight,在tableView第一次加载时给出一个contentSize:
public override nfloat EstimatedHeight(UITableView tableView, NSIndexPath indexPath)
{
return 100;
}
但是在这个解决方案中,高度必须由我们自己预定义和计算。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句