我正在尝试实现类似效果,例如 iPhone 联系人应用程序中类似 iPhone 的滑动 header (按首字母对联系人进行分组的滑动 header )。
这是我的应用程序的屏幕,我想要实现的是:
我有一个“指南标题”和三个用于对列表进行排序的“选项卡”。当用户向上滚动列表时,我希望所有内容都向上滚动(指南标题、选项卡、列表)。 However, when the tabs reach the top of the screen (and the guide header will just be gone off the screen), I want the tabs to stop and stay there (remain as "sticky header"), and only the list items scroll as在任何常规 ListView 中。
我在 ListView 上方有一个 View 组(指南标题)。
首先,我想让指南标题根据 ListView 的滚动位置调整它的位置。
第一种方法: 我的想法是将 onScrollListener 设置为 ListView ,并将指南标题的上边距更改为 ListView 中第一项的滚动位置(这将是一个负值)。
逻辑是正确的,但我面临的问题是当我在 ListView 中滚动时,指南标题 View 的重绘速度不够快。指南标题 View 仅在 ListView 结束时更新(到我更改的顶部边距值)。即使是慢速滚动也不起作用。使 guide header view 或其父 View 无效(invalidate())也无济于事,因为它只会将无效请求放入队列,但无效和重绘不会立即发生,而只会在 UI 线程空闲时发生,当用户仍将手指放在滚动 ListView 上时,这似乎不会发生。似乎抛出 ListView 会阻塞整个 UI 线程或让它自己忙。
所以主要问题是:当用户滚动 ListView 时,更改指南标题 View 的边距不会立即变得可见。我正在使用它的代码:
@Override
public void onScroll(final AbsListView view, final int firstVisibleItem,
final int visibleItemCount, final int totalItemCount) {
// Get the first list item and check it's scroll position. This will be the value (top), that we also
// use the scroll the header parallel.
View v = mainList.getChildAt(0);
final int top = (v==null)?0:v.getTop();
// This logs the current scroll position of the first list item element/view group.
Log.d("onScroll", "onScroll: " + top);
// Here we finally change the margin (setting a negative margin) to the header element.
((LinearLayout.LayoutParams)(findViewById(R.id.header_container).getLayoutParams())).setMargins(0, top, 0, 0);
// was just a test: invalidating the outer container/view group, doesn't help
// findViewById(R.id.ll_container).invalidate();
}
我确实在 logcat 中看到了我在上面的代码中插入的“onScroll:”日志输出,但是上边距的以下调整只是不可见。
我的第二种方法: 是为指南标题和选项卡使用 ScrollView 并使用它们。使用 ListView 的 onScroll 方法中的 scrollView.scrollTo(0,Math.abs(Math.abs(top)) 从代码滚动指南标题(然后是 ScrollView )确实有效,并且几乎立即显示在屏幕上,然而,当用户快速滑动 ListView 时,它不是很准确/稳定 - 这意味着它在间隔中跳跃并且看起来不平滑;它只有在缓慢滚动时才准确/稳定。
我现在的问题是:是否有任何最佳实践来实现这种滑动标题效果,更具体地说:是否有一种方法可以在用户静止不动时强制重绘指南标题 View 滚动 ListView (在我第一次提到的方法中)。
最佳答案
为此,您应该使用一些技巧(据我所知,没有现成的此类功能实现)。
例如,您可以检测 View 中的手势,并且
- 如果当前手势匹配 向下滚动,第一个列表项 是可见的,动画缩小 标题的大小为 0,选项卡 View 的 大小匹配父级。开始滚动 只有当标题不是时的列表 出现了。
- 如果当前手势匹配滚动 向上,第一个已经可见, 动画扩展标题到它的 原始大小。
因此,在标题 View 上使用动画可能是您的解决方案。
更新
另一种解决方法是扩展您的 List
(适配器的值数组):
在标题表示的顶部插入一个新的(虚拟)项目,并修改您的 ListAdapter
的 getView
方法:
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (position == 0)
{
convertView = inflater.inflate(R.layout.sliding_header, parent,
false);
return convertView;
}
//TODO: your original method body comes here
}
R.layout.sliding_header
引用的 xml 应包含列表的标题布局。
应用于 ListView
的自定义 OnScrollListener
实现会使 header 实际上是列表的一个项目变得不明显,因为它会隐藏滚动条。
您应该在 Activity 的 onCreate
方法中将此监听器添加到您的 listView
:
listView.setOnScrollListener(new MyScrollListener());
MyScrollListener
是:
/**
* Custom OnScrollListener
*/
private final class MyScrollListener implements OnScrollListener
{
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
{
if (view.getFirstVisiblePosition() == 0)
view.setVerticalScrollBarEnabled(false);
else if (!view.isVerticalScrollBarEnabled())
view.setVerticalScrollBarEnabled(true);
}
}
关于android - 类似 iPhone 的滑动表头,或 : how to force immediate redraw on top margin change?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5927738/