吸顶大法 -- UWP中的工具栏吸顶的实现方式之一
wptr33 2025-05-23 20:39 30 浏览
如果一个页面中有很长的列表/内容,很多应用都会在用户向下滚动时隐藏页面的头,给用户留出更多的阅读空间,同时提供一个方便的吸顶工具栏,比如淘宝中的店铺页面。
下面是一个比较简单的实现,如果有同学有更好的实现,欢迎留言,让我们共同进步。
首先假设我们的页面整体包含3部分;
- 页面头:随页面滚动慢慢消失/重现
- 工具栏: 开始时随页面滚动,在页面头消失后,吸顶,固定不动
- 可滚动内容:一个listview
结构代码如下,为了区别清楚,我是用不同的背景色做区分:
1 <Page
2 x:Class="App3.MainPage"
3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5 xmlns:local="using:App3"
6 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
8 mc:Ignorable="d">
9
10 <Page.Resources>
11 <Style TargetType="ListViewItem">
12 <!-- 让listview item拉伸 -->
13 <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
14 </Style>
15 </Page.Resources>
16
17 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
18 <Grid.RowDefinitions>
19 <!-- 头部高 -->
20 <RowDefinition Height="120" x:Name="headerRow"></RowDefinition>
21 <!-- 工具栏高 -->
22 <RowDefinition Height="48"></RowDefinition>
23 <!-- 其余内容 -->
24 <RowDefinition Height="*"></RowDefinition>
25 </Grid.RowDefinitions>
26
27 <!-- 头部定义 -->
28 <Grid Background="LightGray" x:Name="head" Grid.Row="0">
29 <StackPanel Orientation="Horizontal"
30 HorizontalAlignment="Left"
31 VerticalAlignment="Center"
32 Margin="12, 0, 0, 0">
33 <Image Source="http://img.alicdn.com/tps/i4/TB12mhwHVXXXXctXVXXAAT2HVXX-63-63.png"
34 Width="80"
35 Height="80"></Image>
36
37 <TextBlock HorizontalAlignment="Left"
38 VerticalAlignment="Center"
39 Margin="12, 0, 0, 0">这是个测试</TextBlock>
40 </StackPanel>
41 </Grid>
42
43 <!-- 工具栏定义 -->
44 <Grid Grid.Row="1" Background="DarkGray">
45 <StackPanel Orientation="Horizontal">
46 <HyperlinkButton Margin="18, 0, 0, 0">按钮1</HyperlinkButton>
47 <HyperlinkButton Margin="12,0, 0, 0">按钮2</HyperlinkButton>
48 </StackPanel>
49 </Grid>
50
51 <!-- 内容部分 -->
52 <ScrollViewer x:Name="scroller" Grid.Row="2" ViewChanged="scroller_ViewChanged">
53 <ListView x:Name="list" ScrollViewer.HorizontalScrollMode="Disabled"
54 ScrollViewer.VerticalScrollMode="Disabled">
55 <ListView.ItemTemplate>
56 <DataTemplate>
57 <Grid Height="120" Background="LightGoldenrodYellow">
58 <TextBlock Text="{Binding}"></TextBlock>
59 </Grid>
60 </DataTemplate>
61 </ListView.ItemTemplate>
62 </ListView>
63 </ScrollViewer>
64 </Grid>
65 </Page>View Code
效果如下图:
接下来我们需要在ScrollViewer.ViewChanged事件中处理滚动事件,将页面头部分慢慢的隐藏,知道高度为0,进而模拟出工具栏吸顶的状态,同学们可以在脑子里想象一下这个场景(●’’●)。但是由于在XAML中我们使用了RowDefinition定义的头部高度,所以就不能直接修改控件高度了,而是要动态修改这个RowDefinition的高度(我之前也不知道这货也是可以动态修改的,也是看了其他同学的code学到的)。
1 public sealed partial class MainPage : Page
2 {
3 public List<string> Items { get; set; } = Enumerable.Repeat("this is a test item", 30).ToList;
4
5 public MainPage
6 {
7 this.InitializeComponent;
8
9 this.Loaded += MainPage_Loaded;
10 }
11
12 private void MainPage_Loaded(object sender, RoutedEventArgs e)
13 {
14 this.list.ItemsSource = this.Items;
15 }
16
17 private void scroller_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
18 {
19 //得到scrollerview的滚动高度
20 var verticalOffset = scroller.VerticalOffset;
21
22 //计算当前header应该显示的高度
23 var delta = _headerDefaultHeight - verticalOffset;
24
25 //高一定大于0!!!
26 headerRow.Height = new GridLength(delta < 0 ? 0 : delta);
27 }
28
29 // 为了方便这里hard code,也可以从行定义中取得
30 double _headerDefaultHeight = 120D;
31 }View Code
一个简单的头部隐藏,工具栏吸顶的小功能就实现了。
但是如果你仔细观察,头部在消失的过程中,布局有变化,强迫症表示好难受啊。
为了解决这个问题,首先让我们分析原因,其实很简单,因为在代码中一直在动态的修改头的高度,而头部控件中都是动态的布局。为了怎么解决这个问题呢?简单一点就是修改margin,既然是因为高度变化了导致布局变化,那么我们就让可显示区域变化相应的高度,这样布局就没有影响了!
修改后代码如下(只贴出修改部分):
1 private void scroller_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
2 {
3 //得到scrollerview的滚动高度
4 var verticalOffset = scroller.VerticalOffset;
5
6 //计算当前header应该显示的高度
7 var delta = _headerDefaultHeight - verticalOffset;
8
9 //高一定大于0!!!
10 headerRow.Height = new GridLength(delta < 0 ? 0 : delta);
11
12 // 将head的位置提升,改变可显示区域
13 head.Margin = new Thickness(0, -verticalOffset, 0, 0);
14 }View Code
这样再来看看滚动的效果!
到这里你以为完了么?不,这里面还有一个小坑在前面等着你去踩!如果你在一些比较慢的机器上(比如一些低端的win phone)运行这个页面,快速滚动的话你会有碰到一个很迷的异常,“Layout cycle detected.”,而且根本定位不到异常的具体位置。。。相信有些同学也遇到过这个问题,通常这是因为更新UI太频繁,简单粗暴的方法是加上Task.Delay,时间可以短一些,比如1ms。同时上面的代码其实还有很大的优化空间,比如我们的ViewChanged事件里其实不是必须每次都要更新UI的,通过优化更新逻辑也会降低这个异常出现的概率。
相关推荐
- MySQL进阶五之自动读写分离mysql-proxy
-
自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...
- 3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?
-
引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...
- 一文由浅入深带你完全掌握MySQL的锁机制原理与应用
-
本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...
- 验证Mysql中联合索引的最左匹配原则
-
后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...
- MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)
-
目录1.索引基础...
- 你会看 MySQL 的执行计划(EXPLAIN)吗?
-
SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...
- MySQL 从入门到精通(四)之索引结构
-
索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...
- mysql总结——面试中最常问到的知识点
-
mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...
- mysql总结——面试中最常问到的知识点(2)
-
首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...
- MySQL基础全知全解!超详细无废话!轻松上手~
-
本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...
- 深入剖析 MySQL 中的锁机制原理_mysql 锁详解
-
在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...
- Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析
-
引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...
- MySQL基础篇:DQL数据查询操作_mysql 查
-
一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...
- MySql:索引的基本使用_mysql索引的使用和原理
-
一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
如何将AI助手接入微信(打开ai手机助手)
-
Java面试必考问题:什么是乐观锁与悲观锁
-
SparkSQL——DataFrame的创建与使用
-
redission YYDS spring boot redission 使用
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)
