【问题标题】:ListViewDraggingAnimation broken on Android 5 LollipopListViewDraggingAnimation 在 Android 5 Lollipop 上损坏
【发布时间】:2014-10-30 09:13:57
【问题描述】:

我使用的是 DevBytesListViewDraggingAnimation,但它在 Android Lollipop 开发者预览版 2 (LPX13D) 上似乎坏了。当我将一行拖到其他行上时,这些行将消失并且不再可点击(见下文)。我尝试为列表视图禁用硬件加速,但没有任何效果。

有人遇到过同样的问题吗?有什么提示吗?谢谢:)

【问题讨论】:

    标签: android android-listview android-5.0-lollipop


    【解决方案1】:

    我发现了问题。它来自这面旗帜。 StableArrayAdapter.hasStableId.

    它从 Lollipop 的这个视图中解决了所有问题。

    @Override
    public boolean hasStableIds()
    {
        return android.os.Build.VERSION.SDK_INT < 20;
    }
    

    【讨论】:

    • 你应该使用常量,像这样:Build.VERSION.SDK_INT
    • 你是救生员。 47deg.com 的滑动列表也有类似的问题。
    • 你是个天才,杰瑞米!你能分享一下你是如何解决这个问题的吗,还是太久以前不记得了?!
    • 这在技术上是错误的。 ID 是稳定的,它们必须是稳定的,否则系统将无法工作。
    • 此修复在 Android 5.1 上对我不起作用,而 Tatarize 解决方案确实有效。答案开始于:“正确的解决方案是重新打开该视图的可见性......”
    【解决方案2】:

    说实话,我不知道是什么导致了问题,但是这个修复在任何版本上都不会再出现视觉错误。而且因为所有改变的只是视图的可见性,我相信它不应该产生任何新的功能问题。


    在类DynamicListView中的函数handleCellSwitch()中替换此代码:

    mobileView.setVisibility(View.VISIBLE);
    switchView.setVisibility(View.INVISIBLE);
    

    if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.KITKAT){
        mobileView.setVisibility(View.VISIBLE);
        switchView.setVisibility(View.INVISIBLE);
    } else{
        mobileView.setVisibility(View.INVISIBLE);
        switchView.setVisibility(View.VISIBLE);
    }
    

    我尝试修改代码,最后我找到了一种让它像以前一样显示的方法。但修复 Lollipop 的问题后,同样的问题出现在 KitKat 和以前的版本上。所以我只对高于 KitKat 的 android 版本应用了修复。

    我的猜测是,由于某种原因,这两种观点在新 Lollipop 版本的过程中被交换了。实际上,其中一个视图始终显示,而另一个视图始终隐藏。很高兴知道棒棒糖android代码在哪里以及发生了什么变化......

    【讨论】:

    • 感谢您的修复,它几乎可以正常工作,但仍然存在故障。当您将项目拖过列表底部时,列表开始自动向下滚动。如果您现在向上拖动同一个项目而不释放它,列表开始向上滚动,但会出现一个空项目。
    • 对我来说,如果我完全删除 else 部分,Lollipop 在两个方向上都可以正常工作。换句话说,对于 Lollipop,不要更改 mobileView 或 switchView 的可见性。
    • 在 Lollipop 中,所做的更改是为了尽可能多地为相同的 StableID 返回相同的视图。这意味着当您因为 notifyDataSetChanged() 调用关闭而回收视图时,它会在存在时为相同的 stableID 提供相同的视图。通常,如果您有五个视图,它们将填写 0、1、2、3、4 并保持该顺序。但是,具有稳定的 ID。如果视图 2 用于特定的 stableID,它会在内容更改时返回该视图。因为它可以跟踪 stableID,如果该对象位于第 5 位,它仍会被交给该视图以进行回收。
    • 此修复在 Android 5.1 上对我不起作用,而 Tatarize 解决方案确实有效。答案开始于:“正确的解决方案是重新打开该视图的可见性......”
    【解决方案3】:

    正确的解决方案是在该视图被回收之前重新打开它的可见性,然后在它被绘制之前将其关闭。

           ((BaseAdapter) getAdapter()).notifyDataSetChanged();
    
            mDownY = mLastEventY;
    
            final int switchViewStartTop = switchView.getTop();
    
            mobileView.setVisibility(View.VISIBLE);
            switchView.setVisibility(View.INVISIBLE);
    
            updateNeighborViewsForID(mMobileItemId);
    
            final ViewTreeObserver observer = getViewTreeObserver();
            observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                public boolean onPreDraw() {
                    observer.removeOnPreDrawListener(this);
    

    替换为,

            mobileView.setVisibility(VISIBLE);
            ((BaseAdapter) getAdapter()).notifyDataSetChanged();
    
    
            mDownY = mLastEventY;
    
            final int switchViewStartTop = switchView.getTop();
    
            updateNeighborViewsForID(mMobileItemId);
    
            final ViewTreeObserver observer = getViewTreeObserver();
            observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                public boolean onPreDraw() {
                    observer.removeOnPreDrawListener(this);
    
                    View mobileView = getViewForID(mMobileItemId);
                    if (mobileView != null) mobileView.setVisibility(INVISIBLE);
    

    stableid 解决方案是错误的。 ID 是稳定的并告诉它它们不是错误的。它只是使系统以旧方式回收视图。这实际上是一种不好的做法,并且在棒棒糖中得到了修复。这是一个更正确的解决方案。而不是预测 .notifyDataSetChanged 将如何影响视图并根据您使用的任何版本打开和关闭适当的元素。这将在所有情况下打开可见性。然后在预绘制侦听器中再次找到视图并将其变为不可见,在它被绘制之前和它处于正确状态之后。

    不管该视图是否相同,或者未来版本是否具有不同的执行方式,或者实际上没有回收视图的坏适配器等。稳定的 Ids 或不稳定的 ids(标记为不稳定,它们仍然必须实际上是稳定的,但这很容易修复),这是正确执行此操作和面向未来的方法。

    这里的问题是,您将获得哪个回收视图实际上并不清楚(毕竟它们是半垃圾回收视图),而不是您可以安全存储属性的东西。在 Lollipop 中更改了行为以保留如果可以,稳定视图实际上是稳定的,您可以签入适配器功能,并且回收的视图可能已经拥有您的数据,因为它会最大限度地再次为您提供相同的视图。这就是你想要的东西,这就是 Lollipop 这样做的原因。

    这里的问题是代码说 mobileView 应该是可见的(你正在移动的那个),而 switchView 应该是不可见的(你正在切换的那个)。但是,这些将被回收,因此它们在技术上是模糊的,您将取回哪个,并且完全取决于系统如何回收视图,并且完全允许将这种行为改变得更好,并且确实做到了。

    附言。我费心检查 null,因为我个人在中途交换了视图,如果你恰到好处,它有时最终会为 null。

        int deltaYTotal = (mHoverCellOriginalBounds.bottom + mHoverCellOriginalBounds.top) / 2
                + mTotalOffset + deltaY;
    
    ...
    
            boolean isBelow = (belowView != null) && (deltaYTotal > belowView.getTop());
            boolean isAbove = (aboveView != null) && (deltaYTotal < aboveView.getBottom());
    

    【讨论】:

    【解决方案4】:

    您需要在适配器通知之前看到 mobileView 和 switchView。 这对我有用。

     mobileView.setVisibility(View.VISIBLE);
     switchView.setVisibility(View.INVISIBLE);
    
     ((BaseAdapter) getAdapter()).notifyDataSetChanged();
    
        mDownY = mLastEventY;
    
        final int switchViewStartTop = switchView.getTop();
    
        updateNeighborViewsForID(mMobileItemId);
    

    【讨论】:

    • 如果有人在 Lollypop 之前的版本中对此进行测试,他们会发现这个答案会导致所有 pre-L 版本失败。原始代码是错误的,您可以在回收时使用视图来存储信息。您必须正确地将它们都设置为可见,然后添加一个预绘制侦听器以在绘制之前将正确的设置为不可见。
    • 问题是如果你在 K 中尝试,一切都会变得很糟糕。我在 L.L+ 之前说过不会有事的。
    • 我不认为它会发生,因为我在我的应用程序中使用了这个,它支持高达 4+ 版本并且它在所有版本中都可以正常工作..
    • 在 KitKat 的实现上,你的代码会变得很糟糕。 AVD 是 Kitkat 的一个版本,你会说他们疯了。至少假设您的适配器没有说稳定 ID 是错误的。 Lollypop 中更改了回收视图的实现。如果可能的话,它开始始终如一地将 StableID 视图返回到正确的索引。这就是为什么将 StableID 设置为 false 也倾向于修复它的原因,它会导致 Lollypop 恢复为旧代码。但是,你总是会在这里引起问题。您无法始终如一地确定 mobileView 或 switchView 是否正确。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-16
    相关资源
    最近更新 更多