#xamarin.forms #bottom-sheet
Вопрос:
Я попытался реализовать нижний лист для своего приложения Xamarin forms. Я реализовал несколько методов, включая nugets. В моем основном представлении сзади у меня есть представление списка. На нижнем листе обычные рамки и надписи. Моя проблема в том, что последние один или два элемента не отображаются или частично отображаются в представлении списка почти во всех вариантах.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:yummy="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
x:Class="RSMapp.Views.RSMview">
<ContentPage.ToolbarItems>
<ToolbarItem Name="MenuItem" Command="{Binding filterCommand}" Order="Primary" Icon="filter.png" Text="" Priority="0" IsEnabled="{Binding IsEnabled}" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<RelativeLayout>
<StackLayout BackgroundColor="#d4d4d4" x:Name="mainView" Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand">
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<yummy:PancakeView HeightRequest="55" BackgroundColor="{Binding mainBgColor}" Padding="10" HorizontalOptions="FillAndExpand" x:Name="dropdownLayout" CornerRadius="0,0,15,15" VerticalOptions="CenterAndExpand" Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="AUTO"/>
</Grid.ColumnDefinitions>
<Frame IsVisible="{Binding isMonthVisible}" HasShadow="{OnPlatform Android=true, iOS=false}" HorizontalOptions="FillAndExpand" BackgroundColor="WhiteSmoke" Padding="10,0,10,0" CornerRadius="15" Grid.Column="0">
<Picker
Title="{Binding SelectedItem.mName}"
IsVisible="{Binding isMonthVisible}"
TitleColor="Black"
VerticalTextAlignment="Center"
Margin="0,0,20,0"
Focused="Picker_Focused"
TextColor="#565656"
ItemsSource="{Binding MyList}"
SelectedItem="{Binding SelectedItem}"
ItemDisplayBinding="{Binding mName}" />
</Frame>
<StackLayout WidthRequest="60" IsVisible="{Binding extraBtnVisible}" VerticalOptions="Center" HorizontalOptions="Center" Grid.Column="1">
<ImageButton HeightRequest="50" WidthRequest="30" Command="{Binding regionClassCommand}" VerticalOptions="Center" IsVisible="{Binding extraBtnVisible}" BackgroundColor="#F44336" Source="extra.png"/>
</StackLayout>
</Grid>
</yummy:PancakeView>
<StackLayout BackgroundColor="#d4d4d4" Grid.Row="1" Orientation="Vertical">
<ListView IsVisible="{Binding hasLifeData}" ItemTapped="RSMListView_ItemTapped"
SeparatorVisibility="None"
IsPullToRefreshEnabled="False"
ItemsSource="{Binding rsmLifeRankingList}"
x:Name="RSMListView"
Margin="0,0,0,2"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Margin="2,4,2,4" BackgroundColor="White" IsVisible="True" HasShadow="{OnPlatform Android='true',iOS='false'}" CornerRadius="10" Padding="2,4,2,4">
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="White" Orientation="Vertical">
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout IsVisible="{Binding noData}" HorizontalOptions="CenterAndExpand" VerticalOptions="StartAndExpand">
<Image Source="nodata" HorizontalOptions="Center"/>
</StackLayout>
</StackLayout>
</Grid>
</StackLayout>
<yummy:PancakeView CornerRadius="10,10,0,0" Padding="0" BackgroundColor="#faf9f8"
x:Name="bottomSheet" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Height,Factor=.96,Constant=0}" RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1,Constant=0}" RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=0}">
<yummy:PancakeView.GestureRecognizers>
<PanGestureRecognizer PanUpdated="OnPanUpdated" />
</yummy:PancakeView.GestureRecognizers>
<StackLayout BackgroundColor="Gray" Spacing="0">
<BoxView HeightRequest="5" CornerRadius="2" Margin="0,6,0,8" WidthRequest="80" BackgroundColor="LightGray" HorizontalOptions="Center"/>
<StackLayout Orientation="Vertical">
<StackLayout IsVisible="{Binding hasLifeData}" BackgroundColor="WhiteSmoke" Orientation="Vertical" Grid.Row="1">
<StackLayout IsVisible="{Binding layoutVisible}" Spacing="0" Orientation="Vertical">
<yummy:PancakeView Padding="10,0,10,10" CornerRadius="0,0,15,15" BackgroundColor="{StaticResource colorLife}">
<StackLayout Orientation="Vertical">
</StackLayout>
</yummy:PancakeView>
<ScrollView Padding="0,5,0,5">
<StackLayout Margin="2,0,2,0" Orientation="Vertical">
</StackLayout>
</ScrollView>
</StackLayout>
</StackLayout>
</StackLayout>
</StackLayout>
</yummy:PancakeView>
</RelativeLayout>
</ContentPage.Content>
</ContentPage>
Код за
public partial class RSMview : ContentPage
{
private RSMViewModel vm;
private string company;
private string lifColor = "#f2b22c";
private double x, y;
public RSMview()
{
InitializeComponent();
}
public RSMview(string company, string userRegion)
{
InitializeComponent();
this.company = company;
BindingContext = new RSMViewModel();
vm = BindingContext as RSMViewModel;
Title = userRegion " (RSM)";
ToolbarStatusBarTheme.ToolbarWithStatusBar(company);
if (NetworkCheck.IsInternet())
{
if (company.Equals("G"))
{
dropdownLayout.BackgroundColor = Color.FromHex(genColor);
vm.GetGenRSMData(userRegion, "G");
}
else if (company.Equals("L"))
{
dropdownLayout.BackgroundColor = Color.FromHex(lifColor);
vm.GetLifeRSMData(userRegion, "L");
}
}
}
private async void RSMListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var item = (LifeRSMData)e.Item;
await Navigation.PushAsync(new SMviewHead(company, item));
}
protected override void OnAppearing()
{
base.OnAppearing();
if (Device.Idiom == TargetIdiom.Phone)
{
CrossDeviceOrientation.Current.LockOrientation(Plugin.DeviceOrientation.Abstractions.DeviceOrientations.Portrait);
}
}
void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
// Handle the pan
switch (e.StatusType)
{
case GestureStatus.Running:
// Translate and ensure we don't y e.TotalY pan beyond the wrapped user interface element bounds.
var translateY = Math.Max(Math.Min(0, y e.TotalY), -Math.Abs((Height * .25) - Height));
bottomSheet.TranslateTo(bottomSheet.X, translateY, 20);
break;
case GestureStatus.Completed:
// Store the translation applied during the pan
y = bottomSheet.TranslationY;
//at the end of the event - snap to the closest location
var finalTranslation = Math.Max(Math.Min(0, -1000), -Math.Abs(GetClosestLockState(e.TotalY y)));
//depending on Swipe Up or Down - change the snapping animation
if (isSwipeUp(e))
{
bottomSheet.TranslateTo(bottomSheet.X, finalTranslation, 250, Easing.SpringIn);
}
else
{
bottomSheet.TranslateTo(bottomSheet.X, finalTranslation, 250, Easing.SpringOut);
}
//dismiss the keyboard after a transition
//SearchBox.Unfocus();
y = bottomSheet.TranslationY;
break;
}
}
public bool isSwipeUp(PanUpdatedEventArgs e)
{
if (e.TotalY < 0)
{
return true;
}
return false;
}
//TO-DO: Make this cleaner
private double GetClosestLockState(double TranslationY)
{
//Play with these values to adjust the locking motions - this will change depending on the amount of content ona apge
var lockStates = new double[] { 0, .5, .88 };
//get the current proportion of the sheet in relation to the screen
var distance = Math.Abs(TranslationY);
var currentProportion = distance / Height;
//calculate which lockstate it's the closest to
var smallestDistance = 10000.0;
var closestIndex = 0;
for (var i = 0; i < lockStates.Length; i )
{
var state = lockStates[i];
var absoluteDistance = Math.Abs(state - currentProportion);
if (absoluteDistance < smallestDistance)
{
smallestDistance = absoluteDistance;
closestIndex = i;
}
}
var selectedLockState = lockStates[closestIndex];
var TranslateToLockState = GetProportionCoordinate(selectedLockState);
return TranslateToLockState;
}
private double GetProportionCoordinate(double proportion)
{
return proportion * Height;
}
private void DismissBottomSheet()
{
//SearchBox.Unfocus();
var finalTranslation = Math.Max(Math.Min(0, -1000), -Math.Abs(GetProportionCoordinate(0)));
bottomSheet.TranslateTo(bottomSheet.X, finalTranslation, 450, Easing.SpringOut);
//SearchBox.Text = string.Empty;
}
private void Picker_Focused(object sender, FocusEventArgs e)
{
DismissBottomSheet();
}
private void OpenBottomSheet()
{
var finalTranslation = Math.Max(Math.Min(0, -1000), -Math.Abs(GetProportionCoordinate(.85)));
bottomSheet.TranslateTo(bottomSheet.X, finalTranslation, 150, Easing.SpringIn);
}
}
Модель представления
public class RSMViewModel : BaseViewModelHelper
{
public RSMViewModel()
{
btnImage = "upbtn.png";
extraBtnVisible = false;
isKpiEnable = false;
}
private void CreateToplayout(string company, string sList)
{
if (company.Equals("L"))
{
if (sList != null)
{
var itemList = JsonConvert.DeserializeObject<List<SummaryLifeRegionalData>>(sList);
//var itemList = mList.rsmLifeData;
if (itemList.Count > 0)
{
foreach (var item in itemList)
{
FYR_NOP_TO_BE_PAID = item.FYR_NOP_TO_BE_PAID.ToString();
....
}
}
else
{
}
}
else
{
}
}
else if (company.Equals("G"))
{
if (sList != null)
{
var mList = JsonConvert.DeserializeObject<SummaryRegionalHeaderData>(sList);
var itemList = mList.rsmMotorData;
if (itemList.Count > 0)
{
foreach (var item in itemList)
{
RENEWAL_VEHICLE_COUNT = item.RENEWAL_VEHICLE_COUNT.ToString();
....
}
}
else
{
}
}
else
{
}
}
}
public void GetLifeRSMData(string p_region, string cType)
{
extraBtnVisible = false;
this.p_region = p_region;
this.companyType = cType;
IsEnabled = false;
SelectedItem = getCurrentMonth(cType);
}
public MonthData SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
Task.Run(async () =>
{
AcrDialogClass.ShowLoadingDialog("Loading...");
if (firstRun)
{
string selectedSelectedItem = _selectedItem.ToString();
string getSubStringMonth = selectedSelectedItem.Substring(1, 2);
string getSubStringYear = selectedSelectedItem.Substring(9, 4);
await ChangeListAsync(getSubStringYear, getSubStringMonth, p_region, companyType);
string sList = null;
if (companyType.Equals("L"))
{
mainBgColor = "#f2b22c";
sList = await _apiServices.GetLifeSummaryDataRM(getSubStringYear, getSubStringMonth, p_region);
}
else if (companyType.Equals("G"))
{
mainBgColor = "#F44336";
sList = await _apiServices.getRSMsummaryData(getSubStringYear, getSubStringMonth, p_region, null);
}
CreateToplayout(companyType, sList);
}
else
{
firstRun = true;
}
AcrDialogClass.HideLoadingDialog();
});
OnPropertyChanged();
}
}
private async Task ChangeListAsync(string getSubStringYear, string getSubStringMonth, string p_region, string companyType)
{
if (companyType.Equals("L"))
{
kpiImage = "kpi_chart.png";
isKpiEnable = false;
IsEnabled = false;
//lifeValue = await _apiServices.getLifeRSMDataList(getSubStringYear, getSubStringMonth, p_region, authenticateData1);
lifeValue = await _apiServices.GetLifeRSMDataList(getSubStringYear, getSubStringMonth, p_region);
if (lifeValue != null)
{
hasLifeData = true;
hasGenData = false;
noData = false;
IsEnabled = true;
layoutVisible = true;
layoutVisibleGen = false;
foreach (LifeRSMData ss in lifeValue)
{
ss.TOTAL_PREM_TO_BE_PAID_STRING = ss.TOTAL_PREM_TO_BE_PAID.ToString("N", CultureInfo.InvariantCulture);
ss.LIFE_TARGET_S = ss.LIFE_TARGET.ToString("N", CultureInfo.InvariantCulture);
ss.LIFE_ACHIVEMENT_S = ss.LIFE_ACHIVEMENT.ToString("N", CultureInfo.InvariantCulture);
Device.BeginInvokeOnMainThread(() =>
{
createChart(ss);
});
}
rsmLifeRankingList = lifeValue;
}
else
{
hasLifeData = false;
hasGenData = false;
noData = true;
IsEnabled = false;
layoutVisible = false;
layoutVisibleGen = false;
rsmLifeRankingList.Clear();
}
}
else if (companyType.Equals("G"))
{
kpiImage = "kpi_chart.png";
isKpiEnable = false;
IsEnabled = false;
genValue = await _apiServices.getGenRSMDataList(getSubStringYear, getSubStringMonth, p_region, null);
if (genValue != null)
{
hasGenData = true;
hasLifeData = false;
noData = false;
IsEnabled = true;
layoutVisible = false;
layoutVisibleGen = true;
foreach (GenRSMData ss in genValue)
{
ss.RENEWAL_PREMIUM_TO_BE_PAID_S = ss.RENEWAL_PREMIUM_TO_BE_PAID.ToString("N", CultureInfo.InvariantCulture);
ss.GENERAL_TARGET_S = ss.GENERAL_TARGET.ToString("N", CultureInfo.InvariantCulture);
ss.GENERAL_ACHIVEMENT_S = ss.GENERAL_ACHIVEMENT.ToString("N", CultureInfo.InvariantCulture);
createChartGen(ss);
}
rsmGenRankingList = genValue;
}
else
{
hasGenData = false;
hasLifeData = false;
noData = true;
IsEnabled = false;
layoutVisible = false;
layoutVisibleGen = false;
rsmGenRankingList.Clear();
}
}
else
{
}
}
private void createChart(LifeRSMData ss)
{
var labels = new List<string>();
var lineEntries = new List<EntryChart>();
float achPercentg = (float)((ss.LIFE_ACHIVEMENT / ss.LIFE_TARGET) * 100);
lineEntries.Add(new EntryChart(0, 100));
lineEntries.Add(new EntryChart(1, achPercentg));
labels.Add("Tar:");
labels.Add("Ach:");
var lineDataSet4 = new BarDataSet(lineEntries, "")
{
Colors = new List<Color>{
Color.FromHex("#248acf"), Color.FromHex("#36DBBB")
}
};
var lineData4 = new BarChartData(new List<IBarDataSet>() { lineDataSet4 });
AxisLeft = new YAxisConfig();
AxisLeft.DrawGridLines = false;
AxisLeft.DrawAxisLine = true;
AxisLeft.Enabled = true;
AxisLeft.AxisMinimum = 0;
AxisLeft.AxisMaximum = 100;
AxisRight = new YAxisConfig();
AxisRight.DrawAxisLine = false;
AxisRight.DrawGridLines = false;
AxisRight.Enabled = false;
XAxis = new XAxisConfig();
XAxis.Granularity = 1f;
XAxis.XAXISPosition = XAXISPosition.BOTTOM;
XAxis.DrawGridLines = false;
XAxis.DrawLabels = true;
XAxis.AxisValueFormatter = new TextByIndexXAxisFormatter(labels);
Legend = new LegendXF();
Legend.Enabled = false;
DescriptionChart = new ChartDescription();
DescriptionChart.Enabled = false;
DescriptionChart.Text = "";
ss.AxisLeft = AxisLeft;
ss.AxisRight = AxisRight;
ss.XAxis = XAxis;
ss.Legend = Legend;
ss.DescriptionChart = DescriptionChart;
ss.chartData = lineData4;
}
public ICommand filterCommand
{
get
{
return new Command(() =>
{
if (IsEnabled)
{
if (companyType != null)
{
if (companyType.Equals("L"))
{
UserDialogs.Instance.ActionSheet(new ActionSheetConfig().SetTitle("Sort using...")
.Add("Due", () => this.SortData("DueA", companyType), "fup.png")
.Add("Due", () => this.SortData("DueD", companyType), "fdown.png")
.Add("Achievement", () => this.SortData("AchA", companyType), "fup.png")
.Add("Achievement", () => this.SortData("AchD", companyType), "fdown.png").SetUseBottomSheet(true));
}
else if (companyType.Equals("G"))
{
UserDialogs.Instance.ActionSheet(new ActionSheetConfig().SetTitle("Sort using...")
.Add("Due", () => this.SortData("DueA", companyType), "fup.png")
.Add("Due", () => this.SortData("DueD", companyType), "fdown.png").SetUseBottomSheet(true));
}
}
}
});
}
}
public ICommand regionClassCommand
{
get
{
return new Command(() =>
{
Application.Current.MainPage.Navigation.PushAsync(new RSMclassView(p_region, SelectedItem));
});
}
}
private void SortData(string caseSwitch, string companyType)
{
}
private MonthData getCurrentMonth(string cType)
{
DateTime now = DateTime.Now;
string currMonth_2 = now.AddMonths(-2).ToString("(MM)/MMM yyyy");
string currMonth_1 = now.AddMonths(-1).ToString("(MM)/MMM yyyy");
string currMonth0 = now.ToString("(MM)/MMM yyyy");
string currMonth1 = now.AddMonths(1).ToString("(MM)/MMM yyyy");
if (cType.Equals("L"))
{
MyList.Add(new MonthData { mName = currMonth_2 });
MyList.Add(new MonthData { mName = currMonth_1 });
MyList.Add(new MonthData { mName = currMonth0 });
MyList.Add(new MonthData { mName = currMonth1 });
}
else
{
MyList.Add(new MonthData { mName = currMonth_2 });
MyList.Add(new MonthData { mName = currMonth_1 });
MyList.Add(new MonthData { mName = currMonth0 });
}
return new MonthData { mName = currMonth0 };
}
}
Комментарии:
1. Не могли бы вы, пожалуйста, отправить почтовый индекс
RSMViewModel
?2. Я обновил модель представления
3. Каков код
LifeRSMData
,SummaryLifeRegionalData
,LifeRSMData
? Если это удобно для вас,не могли бы вы опубликовать базовую демонстрационную версию на github или onedriver и поделиться ссылкой здесь, чтобы мы могли вам лучше помочь?
Ответ №1:
Вы можете настроить ListView
отображение над нижней рамкой. Пожалуйста, удалите внешний элемент ItemListView
и добавьте следующие свойства для ItemListView
:
RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1.0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1.0}"
Весь код является :
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
BackgroundColor="Yellow"
x:Class="TestBottomSheet.MainPage">
<RelativeLayout>
<ListView x:Name="ItemListView" HasUnevenRows="true"
RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1.0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.91}"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame HasShadow="True" BackgroundColor="White" CornerRadius="15" Margin="5">
<StackLayout Orientation="Vertical">
<Label Text="{Binding title}" FontSize="Medium"/>
<Label Text="{Binding id}" FontSize="Default"/>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Frame HasShadow="true" CornerRadius="8" Padding="1,4,1,0" BackgroundColor="#faf9f8"
x:Name="bottomSheet" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Height,Factor=.92,Constant=0}" RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1,Constant=0}" RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=0}">
<Frame.GestureRecognizers>
<PanGestureRecognizer PanUpdated="OnPanUpdated" />
</Frame.GestureRecognizers>
<StackLayout Spacing="5">
<BoxView HeightRequest="5" CornerRadius="2" WidthRequest="50" BackgroundColor="Gray" HorizontalOptions="Center"/>
<StackLayout BackgroundColor="White" HeightRequest="25">
</StackLayout>
<ListView x:Name="ItemListView2" HasUnevenRows="true" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame HasShadow="True" BackgroundColor="FloralWhite" CornerRadius="15" Margin="5">
<StackLayout Orientation="Vertical">
<Label Text="{Binding id}" FontSize="Default"/>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</Frame>
</RelativeLayout>
</ContentPage>
В результате получается:
Комментарии:
1. Я также дал тот же коэффициент=.92 для ограничения высоты ListView. теперь идеально