Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
314faa7
commit 07419bb
Showing
54 changed files
with
8,669 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
using System; | ||
using Xamarin.Forms; | ||
|
||
namespace Expandable | ||
{ | ||
public class ExpandableView : StackLayout | ||
{ | ||
public const string ExpandAnimationName = "expandAnimation"; | ||
|
||
public static readonly BindableProperty PrimaryViewProperty = BindableProperty.Create(nameof(PrimaryView), typeof(View), typeof(ExpandableView), null, propertyChanged: (bindable, oldValue, newValue) => | ||
{ | ||
(bindable as ExpandableView).SetPrimaryView(oldValue as View); | ||
(bindable as ExpandableView).OnShouldHandleTapToExpandChanged(); | ||
}); | ||
|
||
public static readonly BindableProperty SecondaryViewTemplateProperty = BindableProperty.Create(nameof(SecondaryViewTemplate), typeof(DataTemplate), typeof(ExpandableView), null, propertyChanged: (bindable, oldValue, newValue) => | ||
{ | ||
(bindable as ExpandableView).SetSecondaryView(true); | ||
(bindable as ExpandableView).OnIsExpandedChanged(); | ||
}); | ||
|
||
public static readonly BindableProperty IsExpandedProperty = BindableProperty.Create(nameof(IsExpanded), typeof(bool), typeof(ExpandableView), default(bool), BindingMode.TwoWay, propertyChanged: (bindable, oldValue, newValue) => | ||
{ | ||
(bindable as ExpandableView).SetSecondaryView(); | ||
(bindable as ExpandableView).OnIsExpandedChanged(); | ||
}); | ||
|
||
public static readonly BindableProperty ShouldHandleTapToExpandProperty = BindableProperty.Create(nameof(ShouldHandleTapToExpand), typeof(bool), typeof(ExpandableView), true, propertyChanged: (bindable, oldValue, newValue) => | ||
{ | ||
(bindable as ExpandableView).OnShouldHandleTapToExpandChanged(); | ||
}); | ||
|
||
public static readonly BindableProperty SecondaryViewHeightRequestProperty = BindableProperty.Create(nameof(SecondaryViewHeightRequest), typeof(double), typeof(ExpandableView), 0.0); | ||
|
||
private readonly TapGestureRecognizer _defaultTapGesture; | ||
private bool _shouldIgnoreAnimation; | ||
private double _lastVisibleHeight = -1; | ||
private double _startHeight; | ||
private double _endHeight; | ||
private View _secondaryView; | ||
|
||
public ExpandableView() | ||
{ | ||
_defaultTapGesture = new TapGestureRecognizer | ||
{ | ||
Command = new Command(() => IsExpanded = !IsExpanded) | ||
}; | ||
} | ||
|
||
public View PrimaryView | ||
{ | ||
get => GetValue(PrimaryViewProperty) as View; | ||
set => SetValue(PrimaryViewProperty, value); | ||
} | ||
|
||
public DataTemplate SecondaryViewTemplate | ||
{ | ||
get => GetValue(SecondaryViewTemplateProperty) as DataTemplate; | ||
set => SetValue(SecondaryViewTemplateProperty, value); | ||
} | ||
|
||
public bool IsExpanded | ||
{ | ||
get => (bool)GetValue(IsExpandedProperty); | ||
set => SetValue(IsExpandedProperty, value); | ||
} | ||
|
||
public bool ShouldHandleTapToExpand | ||
{ | ||
get => (bool)GetValue(ShouldHandleTapToExpandProperty); | ||
set => SetValue(ShouldHandleTapToExpandProperty, value); | ||
} | ||
|
||
public double SecondaryViewHeightRequest | ||
{ | ||
get => (double)GetValue(SecondaryViewHeightRequestProperty); | ||
set => SetValue(SecondaryViewHeightRequestProperty, value); | ||
} | ||
|
||
public View SecondaryView | ||
{ | ||
get => _secondaryView; | ||
private set | ||
{ | ||
if (_secondaryView != null) | ||
{ | ||
_secondaryView.SizeChanged -= OnSubViewSizeChanged; | ||
Children.Remove(_secondaryView); | ||
} | ||
if (value != null) | ||
{ | ||
if (value is Layout layout) | ||
{ | ||
layout.IsClippedToBounds = true; | ||
} | ||
value.HeightRequest = 0; | ||
value.IsVisible = false; | ||
Children.Add(_secondaryView = value); | ||
} | ||
} | ||
} | ||
|
||
protected override void OnBindingContextChanged() | ||
{ | ||
base.OnBindingContextChanged(); | ||
_lastVisibleHeight = -1; | ||
} | ||
|
||
private void OnIsExpandedChanged() | ||
{ | ||
if (SecondaryView == null) | ||
{ | ||
return; | ||
} | ||
|
||
SecondaryView.SizeChanged -= OnSubViewSizeChanged; | ||
|
||
var isExpanding = SecondaryView.AnimationIsRunning(ExpandAnimationName); | ||
SecondaryView.AbortAnimation(ExpandAnimationName); | ||
|
||
if (IsExpanded) | ||
{ | ||
SecondaryView.IsVisible = true; | ||
} | ||
|
||
_startHeight = 0; | ||
_endHeight = SecondaryViewHeightRequest > 0 | ||
? SecondaryViewHeightRequest | ||
: _lastVisibleHeight; | ||
|
||
var shouldInvokeAnimation = true; | ||
|
||
if(IsExpanded) | ||
{ | ||
if(_endHeight <= 0) | ||
{ | ||
shouldInvokeAnimation = false; | ||
SecondaryView.HeightRequest = -1; | ||
SecondaryView.SizeChanged += OnSubViewSizeChanged; | ||
} | ||
} | ||
else | ||
{ | ||
_lastVisibleHeight = _startHeight = SecondaryViewHeightRequest > 0 | ||
? SecondaryViewHeightRequest | ||
: !isExpanding | ||
? SecondaryView.Height | ||
: _lastVisibleHeight; | ||
_endHeight = 0; | ||
} | ||
|
||
_shouldIgnoreAnimation = Height < 0; | ||
|
||
if(shouldInvokeAnimation) | ||
{ | ||
InvokeAnimation(); | ||
} | ||
} | ||
|
||
private void OnShouldHandleTapToExpandChanged() | ||
{ | ||
if(PrimaryView == null) | ||
{ | ||
return; | ||
} | ||
PrimaryView.GestureRecognizers.Remove(_defaultTapGesture); | ||
if(ShouldHandleTapToExpand) | ||
{ | ||
PrimaryView.GestureRecognizers.Add(_defaultTapGesture); | ||
} | ||
} | ||
|
||
private void SetPrimaryView(View oldView) | ||
{ | ||
if(oldView != null) | ||
{ | ||
Children.Remove(oldView); | ||
} | ||
Children.Insert(0, PrimaryView); | ||
} | ||
|
||
private void SetSecondaryView(bool forceUpdate = false) | ||
{ | ||
if(IsExpanded && (SecondaryView == null || forceUpdate)) | ||
{ | ||
SecondaryView = CreateSecondaryView(); | ||
} | ||
} | ||
|
||
private View CreateSecondaryView() | ||
{ | ||
var template = SecondaryViewTemplate; | ||
if(template is DataTemplateSelector selector) | ||
{ | ||
template = selector.SelectTemplate(BindingContext, this); | ||
} | ||
return template?.CreateContent() as View; | ||
} | ||
|
||
private void OnSubViewSizeChanged(object sender, EventArgs e) | ||
{ | ||
if (SecondaryView.Height <= 0) return; | ||
SecondaryView.SizeChanged -= OnSubViewSizeChanged; | ||
SecondaryView.HeightRequest = 0; | ||
_endHeight = SecondaryView.Height; | ||
InvokeAnimation(); | ||
} | ||
|
||
private void InvokeAnimation() | ||
{ | ||
if (_shouldIgnoreAnimation) | ||
{ | ||
SecondaryView.HeightRequest = _endHeight; | ||
SecondaryView.IsVisible = IsExpanded; | ||
return; | ||
} | ||
|
||
new Animation(v => SecondaryView.HeightRequest = v, _startHeight, _endHeight) | ||
.Commit(SecondaryView, | ||
ExpandAnimationName, | ||
finished: (v, r) => | ||
{ | ||
if (!IsExpanded) | ||
{ | ||
SecondaryView.IsVisible = false; | ||
}; | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<RootNamespace>Expandable</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Xamarin.Forms" Version="3.0.0.446417" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Remove="MainPage.xaml.cs" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Any raw assets you want to be deployed with your application can be placed in | ||
this directory (and child directories) and given a Build Action of "AndroidAsset". | ||
|
||
These files will be deployed with you package and will be accessible using Android's | ||
AssetManager, like this: | ||
|
||
public class ReadAsset : Activity | ||
{ | ||
protected override void OnCreate (Bundle bundle) | ||
{ | ||
base.OnCreate (bundle); | ||
|
||
InputStream input = Assets.Open ("my_asset.txt"); | ||
} | ||
} | ||
|
||
Additionally, some Android functions will automatically load asset files: | ||
|
||
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); |
100 changes: 100 additions & 0 deletions
100
ExpandableViewSample.Android/ExpandableViewSample.Android.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
<ProjectGuid>{3885ACF0-209F-4173-BFBF-7A1C0CD59415}</ProjectGuid> | ||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | ||
<TemplateGuid>{c9e5eea5-ca05-42a1-839b-61506e0a37df}</TemplateGuid> | ||
<OutputType>Library</OutputType> | ||
<RootNamespace>ExpandableViewSample.Droid</RootNamespace> | ||
<AssemblyName>ExpandableViewSample.Android</AssemblyName> | ||
<AndroidApplication>True</AndroidApplication> | ||
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile> | ||
<AndroidResgenClass>Resource</AndroidResgenClass> | ||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> | ||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix> | ||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix> | ||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk> | ||
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion> | ||
<NuGetPackageImportStamp> | ||
</NuGetPackageImportStamp> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>full</DebugType> | ||
<Optimize>false</Optimize> | ||
<OutputPath>bin\Debug</OutputPath> | ||
<DefineConstants>DEBUG;</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
<AndroidLinkMode>None</AndroidLinkMode> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>pdbonly</DebugType> | ||
<Optimize>true</Optimize> | ||
<OutputPath>bin\Release</OutputPath> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
<AndroidManagedSymbols>true</AndroidManagedSymbols> | ||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Reference Include="Mono.Android" /> | ||
<Reference Include="System" /> | ||
<Reference Include="System.Core" /> | ||
<Reference Include="System.Xml.Linq" /> | ||
<Reference Include="System.Xml" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<PackageReference Include="Xamarin.Forms" Version="3.0.0.446417" /> | ||
<PackageReference Include="Xamarin.Android.Support.Design" Version="27.0.2" /> | ||
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="27.0.2" /> | ||
<PackageReference Include="Xamarin.Android.Support.v4" Version="27.0.2" /> | ||
<PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="27.0.2" /> | ||
<PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="27.0.2" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="MainActivity.cs" /> | ||
<Compile Include="Resources\Resource.Designer.cs" /> | ||
<Compile Include="Properties\AssemblyInfo.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Include="Resources\AboutResources.txt" /> | ||
<None Include="Assets\AboutAssets.txt" /> | ||
<None Include="Properties\AndroidManifest.xml" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<AndroidResource Include="Resources\layout\Tabbar.axml" /> | ||
<AndroidResource Include="Resources\layout\Toolbar.axml" /> | ||
<AndroidResource Include="Resources\values\styles.xml" /> | ||
<AndroidResource Include="Resources\values\colors.xml" /> | ||
<AndroidResource Include="Resources\mipmap-anydpi-v26\icon.xml" /> | ||
<AndroidResource Include="Resources\mipmap-anydpi-v26\icon_round.xml" /> | ||
<AndroidResource Include="Resources\mipmap-hdpi\Icon.png" /> | ||
<AndroidResource Include="Resources\mipmap-hdpi\launcher_foreground.png" /> | ||
<AndroidResource Include="Resources\mipmap-mdpi\Icon.png" /> | ||
<AndroidResource Include="Resources\mipmap-mdpi\launcher_foreground.png" /> | ||
<AndroidResource Include="Resources\mipmap-xhdpi\Icon.png" /> | ||
<AndroidResource Include="Resources\mipmap-xhdpi\launcher_foreground.png" /> | ||
<AndroidResource Include="Resources\mipmap-xxhdpi\Icon.png" /> | ||
<AndroidResource Include="Resources\mipmap-xxhdpi\launcher_foreground.png" /> | ||
<AndroidResource Include="Resources\mipmap-xxxhdpi\Icon.png" /> | ||
<AndroidResource Include="Resources\mipmap-xxxhdpi\launcher_foreground.png" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Folder Include="Resources\drawable\" /> | ||
<Folder Include="Resources\drawable-hdpi\" /> | ||
<Folder Include="Resources\drawable-xhdpi\" /> | ||
<Folder Include="Resources\drawable-xxhdpi\" /> | ||
<Folder Include="Resources\drawable-xxxhdpi\" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\ExpandableViewSample\ExpandableViewSample.csproj"> | ||
<Project>{F9D508F8-CF53-4DC6-8C9A-D0C72B3D6284}</Project> | ||
<Name>ExpandableViewSample</Name> | ||
</ProjectReference> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System; | ||
|
||
using Android.App; | ||
using Android.Content.PM; | ||
using Android.Runtime; | ||
using Android.Views; | ||
using Android.Widget; | ||
using Android.OS; | ||
|
||
namespace ExpandableViewSample.Droid | ||
{ | ||
[Activity(Label = "ExpandableViewSample", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] | ||
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity | ||
{ | ||
protected override void OnCreate(Bundle bundle) | ||
{ | ||
TabLayoutResource = Resource.Layout.Tabbar; | ||
ToolbarResource = Resource.Layout.Toolbar; | ||
|
||
base.OnCreate(bundle); | ||
|
||
global::Xamarin.Forms.Forms.Init(this, bundle); | ||
LoadApplication(new App()); | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.