You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms Android无需NuGet实现相机多拍及ListView绑定

嘿,我来帮你搞定这个多图拍摄并绑定到ListView的问题!你的现有代码只能拍一张,主要是因为用了固定的文件名(每次都会覆盖),而且没有维护拍摄的图片路径集合。下面是不用任何NuGet包的完整解决方案:

第一步:修改安卓端拍摄逻辑(MainActivity.cs)

首先,我们需要替换固定的文件对象,改用动态生成的唯一文件名,同时维护一个集合存储所有拍摄的照片路径,还要处理异步等待拍摄完成的逻辑:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Provider;
using Java.IO;
using Xamarin.Forms;

namespace YourAppNamespace.Droid
{
    [Activity(Label = "YourApp", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        private List<string> _capturedImagePaths = new List<string>();
        private File _currentCaptureFile;
        private TaskCompletionSource<bool> _onCaptureCompleted;
        private TaskCompletionSource<bool> _onPermissionGranted;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);
            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App());

            // 绑定拍摄触发事件
            (Application.Current as App).ShouldTakePicture += async () =>
            {
                bool keepCapturing = true;
                while (keepCapturing)
                {
                    // 检查安卓6.0+动态权限
                    if (!await CheckAndRequestPermissions())
                    {
                        await Application.Current.MainPage.DisplayAlert("权限不足", "需要相机和存储权限才能拍摄照片", "确定");
                        break;
                    }

                    // 生成唯一文件名,避免覆盖
                    string fileName = $"photo_{DateTime.Now:yyyyMMddHHmmssfff}.jpg";
                    _currentCaptureFile = new File(
                        Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures),
                        fileName
                    );

                    // 创建相机Intent
                    var intent = new Intent(MediaStore.ActionImageCapture);
                    intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(_currentCaptureFile));

                    // 检查是否有可用相机应用
                    if (intent.ResolveActivity(PackageManager) != null)
                    {
                        _onCaptureCompleted = new TaskCompletionSource<bool>();
                        StartActivityForResult(intent, 0);
                        await _onCaptureCompleted.Task;

                        // 询问用户是否继续拍摄
                        keepCapturing = await Application.Current.MainPage.DisplayAlert(
                            "继续拍摄?",
                            "是否再拍一张照片?",
                            "是",
                            "否"
                        );
                    }
                    else
                    {
                        await Application.Current.MainPage.DisplayAlert("错误", "未找到可用的相机应用", "确定");
                        keepCapturing = false;
                    }
                }

                // 拍摄完成后更新App中的照片集合
                (Application.Current as App).UpdateCapturedImages(_capturedImagePaths);
            };
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);

            if (requestCode == 0 && resultCode == Result.Ok)
            {
                // 确认拍摄文件存在
                if (_currentCaptureFile?.Exists() ?? false)
                {
                    _capturedImagePaths.Add(_currentCaptureFile.Path);
                    _onCaptureCompleted?.SetResult(true);
                }
                else
                {
                    _onCaptureCompleted?.SetResult(false);
                }
            }
            else
            {
                // 用户取消拍摄
                _onCaptureCompleted?.SetResult(false);
            }
            _onCaptureCompleted = null;
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
        {
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            if (requestCode == 1)
            {
                bool allGranted = grantResults.All(r => r == Permission.Granted);
                _onPermissionGranted?.SetResult(allGranted);
                _onPermissionGranted = null;
            }
        }

        private async Task<bool> CheckAndRequestPermissions()
        {
            string[] requiredPermissions = { Manifest.Permission.Camera, Manifest.Permission.WriteExternalStorage };
            bool allGranted = true;

            foreach (var perm in requiredPermissions)
            {
                if (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(this, perm) != Permission.Granted)
                {
                    allGranted = false;
                    break;
                }
            }

            if (!allGranted)
            {
                _onPermissionGranted = new TaskCompletionSource<bool>();
                Android.Support.V4.App.ActivityCompat.RequestPermissions(this, requiredPermissions, 1);
                return await _onPermissionGranted.Task;
            }

            return true;
        }
    }
}

第二步:在App类中添加数据绑定支持

在共享项目的App.cs中维护照片路径集合,并实现INotifyPropertyChanged来通知UI更新:

using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;

namespace YourAppNamespace
{
    public partial class App : Application, INotifyPropertyChanged
    {
        private List<string> _capturedImages = new List<string>();
        public List<string> CapturedImages
        {
            get => _capturedImages;
            set
            {
                _capturedImages = value;
                OnPropertyChanged();
            }
        }

        public event Func<Task> ShouldTakePicture;

        public App()
        {
            InitializeComponent();
            MainPage = new MainPage();
        }

        public void UpdateCapturedImages(List<string> newPaths)
        {
            CapturedImages.Clear();
            CapturedImages.AddRange(newPaths);
            OnPropertyChanged(nameof(CapturedImages));
        }

        // 实现INotifyPropertyChanged接口
        public new event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

第三步:在Xamarin.Forms页面绑定ListView

在页面XAML中添加ListView和拍摄按钮,并绑定到CapturedImages集合:

<?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="YourAppNamespace.MainPage">
    <StackLayout>
        <!-- 触发拍摄的按钮 -->
        <Button Text="开始拍摄" Clicked="StartCapture_Clicked" Margin="10"/>
        
        <!-- 显示拍摄的照片列表 -->
        <ListView ItemsSource="{Binding CapturedImages}" Margin="10">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal" Padding="5">
                            <Image Source="{Binding .}" HeightRequest="80" WidthRequest="80" Aspect="AspectFill"/>
                            <Label Text="{Binding .}" VerticalOptions="Center" Margin="10,0"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

然后在页面代码后台添加按钮点击事件:

using System;
using Xamarin.Forms;

namespace YourAppNamespace
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            BindingContext = App.Current; // 设置绑定上下文为App实例
        }

        private async void StartCapture_Clicked(object sender, EventArgs e)
        {
            if ((App.Current as App).ShouldTakePicture != null)
            {
                await (App.Current as App).ShouldTakePicture.Invoke();
            }
        }
    }
}

权限配置

在安卓项目的AndroidManifest.xml中添加必要权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" android:required="true" />

这样,用户点击按钮后可以连续拍摄多张照片,每次拍摄后会询问是否继续,所有拍摄的照片路径都会自动显示在ListView里。

内容的提问来源于stack exchange,提问作者Pritish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:12:10