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

Xamarin.Android自定义ScrollPicker执行Drop后Label不可见问题

问题描述

我开发了名为ScrollPicker的自定义控件,内部封装ScrollView实现滚动能力,控件子元素仅包含Label,支持滚动、点击以及拖拽放置(Drop)操作。
在Android平台运行应用测试时,滚动、点击功能均正常,Drop功能也可正常触发,但执行Drop操作后,对应的Label会变为不可见状态:该Label仍占据原有布局位置,且在该不可见Label的位置再次执行Drop操作时,Drop处理逻辑仍会正常执行。


相关代码

ScrollPicker XAML 定义

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DebugTest.MyView.ApplyView.ScrollPicker">
  <ContentView.Content>
        <ScrollView x:Name="ScrollView" x:FieldModifier="Public">
            <StackLayout x:Name="MainView" x:FieldModifier="Public">
                
            </StackLayout>
        </ScrollView>
  </ContentView.Content>
</ContentView>

ScrollPicker C# 后台代码

public partial class ScrollPicker : ContentView
{
    public static readonly BindableProperty OrientationProperty = BindableProperty.Create("Orientation", typeof(StackOrientation), typeof(ScrollPicker), StackOrientation.Vertical, propertyChanged: Orientation_Changed);
    public static readonly BindableProperty ItemSourceProperty = BindableProperty.Create("ItemSource", typeof(List<string>), typeof(ScrollPicker), propertyChanged: ItemSource_Changed);
    private static void Orientation_Changed(BindableObject bindable, object oldValue, object newValue)
    {
        if(((StackOrientation)newValue) == StackOrientation.Horizontal)
        {
            ((ScrollPicker)bindable).ScrollView.Orientation = ScrollOrientation.Horizontal;
            ((ScrollPicker)bindable).MainView.Orientation = StackOrientation.Horizontal;
        }
        else
        {
            ((ScrollPicker)bindable).ScrollView.Orientation = ScrollOrientation.Vertical;
            ((ScrollPicker)bindable).MainView.Orientation = StackOrientation.Vertical;
        }
    }
    private static void ItemSource_Changed(BindableObject bindable, object oldValue, object newValue)
    {
        var view = ((ScrollPicker)bindable);
        view.ItemSourceList = (List<string>)newValue;
        view.ResetLabelList();
    }
    public StackOrientation Orientation
    {
        get { return (StackOrientation)GetValue(ItemSourceProperty); }
        set { SetValue(ItemSourceProperty, value); }
    }
    public List<string> ItemSource
    {
        get { return (List<string>)GetValue(ItemSourceProperty); }
        set { SetValue(ItemSourceProperty, value); }
    }
    public DropGestureRecognizer DropGR;
    public event EventHandler<IndexArgs> ItemClickEvent;
    public event EventHandler<IndexArgs> DropEvent;
    public List<string> ItemSourceList;
    public List<Label> LabelList = new List<Label>();
    public ScrollPicker()
    {
        InitializeComponent();
        ItemClickEvent += ScrollPicker_ItemClickEvent;
        DropEvent += ScrollPicker_DropEvent;
    }

    private void ScrollPicker_DropEvent(object sender, EventArgs e)
    {
        
    }

    private void CE_Click(Element obj)
    {
        if(LabelList != null && LabelList.Count != 0)
        {
            if(LabelList.Exists(label => label == obj))
            {
                int index = LabelList.IndexOf((Label)obj);
                ItemClickEvent.Invoke(obj, new IndexArgs(index));
            }
        }
    }
    private void ScrollPicker_ItemClickEvent(object sender, EventArgs e)
    {
        
    }
    private void ResetLabelList()
    {
        MainView.Children.Clear();
        LabelList.Clear();
        if (ItemSourceList != null && ItemSourceList.Count != 0)
        {
            for(int i=0;i< ItemSourceList.Count;i++)
            {
                //创建Label
                Label temp = CreateSubView(ItemSourceList[i]);
                //绑定CE
                ClickEffect CE = new ClickEffect();
                CE.Click += CE_Click;
                Global.ClickEffect_BindClickEffect(temp, CE);
                //添加Drop
                DropGR = new DropGestureRecognizer();
                DropGR.DragOver += DropGR_DragOver;
                DropGR.DragLeave += DropGR_DragLeave;
                DropGR.Drop += DropGR_Drop;
                temp.GestureRecognizers.Add(DropGR);
                //添加视图
                LabelList.Add(temp);
                MainView.Children.Add(temp);

                if (i == 0) temp.Opacity = 0.1;
            }
        }
    }
    private void DropGR_DragOver(object sender, DragEventArgs e)
    {

    }
    private void DropGR_DragLeave(object sender, DragEventArgs e)
    {
        
    }
    private void DropGR_Drop(object sender, DropEventArgs e)
    {
        var view = (sender as GestureRecognizer).Parent as View;
        int index = MainView.Children.IndexOf(view);
        DropEvent.Invoke(view, new IndexArgs(index));
    }

    private Label CreateSubView(string text)
    {
        Label label = new Label()
        {
            Text = text,
            FontSize = 20,
            WidthRequest = 75,
            VerticalTextAlignment = TextAlignment.Start,
            HorizontalTextAlignment = TextAlignment.Start,
        };
        return label;
    }
}
public abstract partial class ScrollPickerItemView : ContentView
{

}
public class IndexArgs: EventArgs
{
    public int Index;
    public IndexArgs(int index =0)
    {
        Index = index;
    }
}
public enum ScrollPickerOrientation
{
    Vertical,
    Horizontal
}

页面调用代码

......
<AbsoluteLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,0.1" BackgroundColor="#66CCFF">
    <Label AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,0.2,1" 
           Text="Project:" FontSize="Large" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
    <applyview:ScrollPicker x:Name="ProjectPicker" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="1,0,0.8,1" Orientation="Horizontal" 
                            ItemClickEvent="ProjectPicker_ItemClickEvent" DropEvent="ProjectPicker_DropEvent"/>
</AbsoluteLayout>

问题成因与解决方案

问题成因

  1. 核心问题是Xamarin.Forms Android平台拖拽手势的原生行为特性:当Drop事件触发时,Android原生拖拽系统会默认将接收Drop事件的目标视图透明度设置为0,且这个原生修改不会同步到Xamarin.Forms层面的Opacity属性绑定,因此会出现视图看不见但仍占布局位置、手势仍可响应的现象。
  2. 代码存在隐藏bug:Orientation属性的get/set逻辑写错,错误绑定到了ItemSourceProperty上,会导致方向设置功能异常。

解决方案

  • 修复属性绑定错误,将Orientation属性的get/set逻辑修正为绑定OrientationProperty:
// 修正后代码
public StackOrientation Orientation
{
    get { return (StackOrientation)GetValue(OrientationProperty); }
    set { SetValue(OrientationProperty, value); }
}
  • 处理Drop后的透明度恢复,在DropGR_Drop方法中手动将触发Drop的目标视图Opacity重置为1,解决不可见问题:
private void DropGR_Drop(object sender, DropEventArgs e)
{
    var view = (sender as GestureRecognizer).Parent as View;
    // 修复Android平台Drop后视图被原生设置为透明的问题
    view.Opacity = 1;
    int index = MainView.Children.IndexOf(view);
    DropEvent.Invoke(view, new IndexArgs(index));
}
  • 可选优化:如果需要自定义拖拽过程中的视觉反馈,可以在DragOver和DragLeave事件中手动控制视图透明度,覆盖原生默认样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 20:33:17