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

在Xamarin Forms中实现播放歌曲时显示歌词的技术咨询

Synchronized Lyric Display for Xamarin.Forms (PCL) Audio Player

Hey there! Let's break down how to add synchronized lyric display to your existing Xamarin.Forms audio player. I'll walk you through the key components you'll need to implement:

1. Choose a Lyric File Format

The most common and easy-to-parse format is LRC files. They follow a simple structure that ties lyrics to precise timestamps:

[00:01.23] First line of lyrics
[00:04.56] Second line of lyrics
[00:07.89] Third line of lyrics

Each line starts with a [mm:ss.xx] timestamp, followed by the corresponding lyric text.

2. Create a Lyric Data Model

First, define a lightweight model to store individual lyric lines and their timestamps:

public class LyricLine
{
    public double Timestamp { get; set; } // Timestamp in seconds (e.g., 1.23 for 00:01.23)
    public string Text { get; set; }
    public bool IsCurrent { get; set; } // For highlighting active lyric in UI
}

3. Parse LRC Files

Write a helper method to convert raw LRC content into a sorted list of LyricLine objects:

using System.Text.RegularExpressions;

public static List<LyricLine> ParseLrc(string lrcContent)
{
    var lyricLines = new List<LyricLine>();
    var lines = lrcContent.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
    
    foreach (var line in lines)
    {
        // Extract all timestamps from the line (supports multiple timestamps per lyric)
        var timestampMatches = Regex.Matches(line, @"\[(\d{2}):(\d{2})\.(\d{2})\]");
        foreach (Match match in timestampMatches)
        {
            var minutes = double.Parse(match.Groups[1].Value);
            var seconds = double.Parse(match.Groups[2].Value);
            var milliseconds = double.Parse(match.Groups[3].Value) / 100;
            var totalSeconds = minutes * 60 + seconds + milliseconds;
            
            // Strip timestamp tags to get clean lyric text
            var lyricText = Regex.Replace(line, @"\[\d{2}:\d{2}\.\d{2}\]", "").Trim();
            if (!string.IsNullOrEmpty(lyricText))
            {
                lyricLines.Add(new LyricLine { Timestamp = totalSeconds, Text = lyricText });
            }
        }
    }
    
    // Sort lines by timestamp to ensure playback order is correct
    return lyricLines.OrderBy(l => l.Timestamp).ToList();
}

For loading LRC files, you can embed them as resources, fetch them from local storage, or pull them remotely. In PCL, use dependency service if you need platform-specific file access logic.

4. Sync Lyrics with Audio Playback

Next, tie lyric display to your audio player's current position. Most Xamarin audio implementations (platform-specific players like Android's MediaPlayer or libraries like Plugin.MediaManager) expose a way to track playback position. Here's a sample sync workflow:

private List<LyricLine> _currentLyrics;
private int _currentLyricIndex = -1;
private Timer _lyricUpdateTimer;

// Call this when a song is selected (after loading its matching LRC file)
private void LoadLyricsForSong(string lrcContent)
{
    _currentLyrics = ParseLrc(lrcContent);
    _currentLyricIndex = -1;
    
    // Reset and start a timer to check playback position every 100ms
    _lyricUpdateTimer?.Dispose();
    _lyricUpdateTimer = new Timer(UpdateActiveLyric, null, 0, 100);
}

private void UpdateActiveLyric(object state)
{
    if (_currentLyrics == null || !AudioPlayer.IsPlaying) return;
    
    // Get current playback position in seconds (adjust based on your player's API)
    var currentPosition = AudioPlayer.CurrentPosition.TotalSeconds;
    
    // Find the latest lyric line where timestamp <= current playback position
    var matchingIndex = _currentLyrics.FindLastIndex(l => l.Timestamp <= currentPosition);
    
    if (matchingIndex != _currentLyricIndex && matchingIndex >= 0)
    {
        // Update previous line to not be active
        if (_currentLyricIndex >= 0)
            _currentLyrics[_currentLyricIndex].IsCurrent = false;
        
        _currentLyricIndex = matchingIndex;
        _currentLyrics[_currentLyricIndex].IsCurrent = true;
        
        // Update UI on the main thread (critical for Xamarin.Forms)
        Device.BeginInvokeOnMainThread(() =>
        {
            // Option 1: Update a single label for current lyric
            CurrentLyricLabel.Text = _currentLyrics[_currentLyricIndex].Text;
            
            // Option 2: Refresh ListView and scroll to active line
            LyricListView.ItemsSource = null;
            LyricListView.ItemsSource = _currentLyrics;
            LyricListView.ScrollTo(_currentLyrics[_currentLyricIndex], ScrollToPosition.Center, false);
        });
    }
}

Replace AudioPlayer with your actual audio player instance—platform-specific players will have different methods to retrieve current playback position.

5. Build the Lyric UI

You have two intuitive options for displaying lyrics:

Option 1: Single Label for Active Lyric

Add a prominent label to your Audio.xaml to show only the current lyric:

<Label x:Name="CurrentLyricLabel" 
       FontSize="Large" 
       HorizontalOptions="Center" 
       VerticalOptions="CenterAndExpand"
       Margin="20" />

Option 2: Scrollable ListView with Highlighted Line

Use a ListView to show all lyrics, with the active line styled differently. Add value converters to change font size/color based on the IsCurrent property:

<ContentPage.Resources>
    <ResourceDictionary>
        <local:BoolToColorConverter x:Key="BoolToColorConverter" />
        <local:BoolToFontSizeConverter x:Key="BoolToFontSizeConverter" />
    </ResourceDictionary>
</ContentPage.Resources>

<ListView x:Name="LyricListView" 
          HasUnevenRows="True"
          SeparatorVisibility="None">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Label Text="{Binding Text}" 
                       FontSize="{Binding IsCurrent, Converter={StaticResource BoolToFontSizeConverter}}"
                       TextColor="{Binding IsCurrent, Converter={StaticResource BoolToColorConverter}}"
                       Margin="10"
                       HorizontalTextAlignment="Center" />
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Example value converter for text color:

public class BoolToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? Color.FromHex("#2196F3") : Color.Gray;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

6. Cleanup Resources

Don't forget to dispose of the timer when the page is dismissed to avoid memory leaks:

protected override void OnDisappearing()
{
    base.OnDisappearing();
    _lyricUpdateTimer?.Dispose();
}

Key Tips

  • Handle Edge Cases: Add checks for missing LRC files, malformed content, and songs with no lyrics.
  • Performance: A 100ms timer interval balances responsiveness and resource usage.
  • Platform File Access: Use Xamarin's dependency service to load LRC files from device storage if needed in your PCL.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:05:26