Xamarin.Forms ListView放在StackLayout中

时间:2019-01-31 15:28:18

标签: c# xamarin xamarin.forms

我的Xamarin项目中有一个相当简单的XAML表单,该表单应该以垂直的StackLayout封装两个视图,顶部的CameraViewListView在底部。 CameraView将用尽大部分空间。 ListView不得超过4-5个项目,但会在运行时更改。这是到目前为止的XAML:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
         x:Class="Foo.CamPage"
         xmlns:ctrl="clr-namespace:Foo.Controls">
<ContentPage.Content>
    <StackLayout Orientation="Vertical">

        <ctrl:CameraView HeightRequest="2000"/>

        <StackLayout VerticalOptions="FillAndExpand">
            <ListView>
                  <ListView.ItemsSource>
                      <x:Array Type="{x:Type x:String}">
                        <x:String>Entry 1</x:String>
                        <x:String>Entry 2</x:String>
                      </x:Array>
                  </ListView.ItemsSource>
            </ListView>
        </StackLayout>

    </StackLayout>
</ContentPage.Content>

如您所见,我的第一个想法是将ListView放入另一个嵌套的StackLayout中。我还玩过HeightRequest中的CameraView,但是,我不想设置摄像机视图的特定高度,我希望ListView占用所需的空间和CameraView进行相应调整。可以在尺寸中定义百分比吗?

如果有帮助,请访问CameraView

namespace Foo.Controls
{
    public class CameraView : View
    {
        public static readonly BindableProperty CameraProperty =
            BindableProperty.Create(
                propertyName: "Camera",
                returnType: typeof(CameraOptions),
                declaringType: typeof(CameraView),
                defaultValue: CameraOptions.Rear);

        public CameraOptions Camera
        {
            get { return (CameraOptions)GetValue(CameraProperty); }
            set { SetValue(CameraProperty, value); }
        }

        public Func<Task<ImageSource>> TakePhotoAsync { set; get; }

        public Func<CameraOptions, bool> SwitchCamera { set; get; }
    }
}

这是关联的iOS渲染器:

[assembly: ExportRenderer(typeof(CameraView), typeof(CamPreviewRenderer))]
namespace Foo.iOS.Renderers
{
    public class CamPreviewRenderer : ViewRenderer<CameraView, UICamPreview>
    {
        UICamPreview uiCameraPreview;

        protected override void OnElementChanged(ElementChangedEventArgs<CameraView> e)
        {
            base.OnElementChanged(e);

            if (Control == null)
            {
                uiCameraPreview = new UICamPreview(e.NewElement.Camera);
                SetNativeControl(uiCameraPreview);
            }
            if (e.OldElement != null)
            {
                // Unsubscribe
                uiCameraPreview.Tapped -= OnCameraPreviewTapped;
            }
            if (e.NewElement != null)
            {
                // Subscribe
                uiCameraPreview.Tapped += OnCameraPreviewTapped;
            }
        }

        void OnCameraPreviewTapped(object sender, EventArgs e)
        {
            if (uiCameraPreview.IsPreviewing)
            {
                uiCameraPreview.CaptureSession.StopRunning();
                uiCameraPreview.IsPreviewing = false;
            }
            else
            {
                uiCameraPreview.CaptureSession.StartRunning();
                uiCameraPreview.IsPreviewing = true;
            }
        }
    }
}

这是UICamPreview

namespace Foo.iOS.Renderers
{
    public class UICamPreview : UIView
    {
        AVCaptureVideoPreviewLayer previewLayer;
        CameraOptions cameraOptions;

        public event EventHandler<EventArgs> Tapped;

        public AVCaptureSession CaptureSession { get; private set; }

        public bool IsPreviewing { get; set; }

        public UICamPreview(CameraOptions options)
        {
            cameraOptions = options;
            IsPreviewing = false;
            Initialize();
        }

        public override void Draw(CGRect rect)
        {
            base.Draw(rect);
            previewLayer.Frame = rect;
        }

        public override void TouchesBegan(NSSet touches, UIEvent evt)
        {
            base.TouchesBegan(touches, evt);
            OnTapped();
        }

        protected virtual void OnTapped()
        {
            var eventHandler = Tapped;
            if (eventHandler != null)
            {
                eventHandler(this, new EventArgs());
            }
        }

        void Initialize()
        {
            CaptureSession = new AVCaptureSession();
            previewLayer = new AVCaptureVideoPreviewLayer(CaptureSession)
            {
                Frame = Bounds,
                VideoGravity = AVLayerVideoGravity.ResizeAspectFill
            };

            var videoDevices = AVCaptureDevice.DevicesWithMediaType(AVMediaType.Video);
            var cameraPosition = (cameraOptions == CameraOptions.Front) ? AVCaptureDevicePosition.Front : AVCaptureDevicePosition.Back;
            var device = videoDevices.FirstOrDefault(d => d.Position == cameraPosition);

            if (device == null)
            {
                return;
            }

            NSError error;
            var input = new AVCaptureDeviceInput(device, out error);
            CaptureSession.AddInput(input);
            Layer.AddSublayer(previewLayer);
            CaptureSession.StartRunning();
            IsPreviewing = true;
        }
    }
}

任何提示表示赞赏。

2 个答案:

答案 0 :(得分:0)

将这些视图放入带有2行的Grid中。一个您想使用所需空间的地方将Height设置为Auto,另一个假设要占用剩余空间的视图将Height设置为*。像这样

<ContentPage.Content>
<StackLayout Orientation="Vertical">
 <Grid>
   <Grid.RowDefinitions>
   <RowDefinition Height="Auto"/>
   <RowDefinition Height="*" />
   </Grid.RowDefinitions>
    <ctrl:CameraView Grid.Row="0"/>

    <StackLayout VerticalOptions="FillAndExpand" Grid.Row="1">
        <ListView>
              <ListView.ItemsSource>
                  <x:Array Type="{x:Type x:String}">
                    <x:String>Entry 1</x:String>
                    <x:String>Entry 2</x:String>
                  </x:Array>
              </ListView.ItemsSource>
        </ListView>
    </Grid>
    </StackLayout>
   </StackLayout>
  </ContentPage.Content>

请记住,就布局而言,ListView是一个非常麻烦的问题。无论如何,您可能都需要设置“高度”的硬编码值。

答案 1 :(得分:0)

您尝试覆盖OnLayout时,通常的代码是这样的。

        protected override void OnLayout(bool changed, int l, int t, int r, int b)
        {
            try
            {
                base.OnLayout(changed, l, t, r, b);



                if (!changed)
                    return;



                int msw = 50;
                int msh = 50;

                int layoutWidth = r - l;
                int layoutHeight = b - t;


                msw = MeasureSpec.MakeMeasureSpec(layoutWidth, MeasureSpecMode.Exactly);
                msh = MeasureSpec.MakeMeasureSpec(layoutHeight, MeasureSpecMode.Exactly);

                view.Measure(msw, msh);
                view.Layout(0, 0, layoutWidth, layoutHeight);


            }
            catch (System.Exception ex)
            {
               Logger.WriteException(ex);
            }
            
      }