android - 类似 iPhone 的滑动表头,或 : how to force immediate redraw on top margin change?

标签 android android-layout android-ui

我正在尝试实现类似效果,例如 iPhone 联系人应用程序中类似 iPhone 的滑动 header (按首字母对联系人进行分组的滑动 header )。

这是我的应用程序的屏幕,我想要实现的是:

enter image description here

我有一个“指南标题”和三个用于对列表进行排序的“选项卡”。当用户向上滚动列表时,我希望所有内容都向上滚动(指南标题、选项卡、列表)。 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(适配器的值数组):
在标题表示的顶部插入一个新的(虚拟)项目,并修改您的 ListAdaptergetView 方法:

@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/

相关文章:

android - 为什么我的发送按钮总是与我的编辑文本重叠?

android - 键盘上方有黑线

java - 在非 UI 线程上实例化 View

java - Android MediaPlayer 重置卡住 UI

c# - Android 支持库 v7 appcompat 不完整

android - 使用云中的VM构建Android自定义ROM

java - kotlin com.google.gson.JsonSyntaxException : java. lang.IllegalStateException:预期为 BEGIN_OBJECT,但在第 1 行第 2 列路径 $ 处为 BEGIN_ARRAY

android - Circle ProgressBar Android改变厚度等

android - 如何在Android上正确配置Window/SurfaceView/Opengl-surface的像素格式

java - 为什么创建从工作线程实现可运行的类的对象会抛出异常?