【问题标题】:How to get location (on screen) of row in listview如何在列表视图中获取行的位置(在屏幕上)
【发布时间】:2010-01-26 13:43:22
【问题描述】:

我已经实现了一些手势检测代码,以便我可以检测我的列表视图(位于 FrameLayout 中)中的一行何时被滑动。

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
        int itemId = MainActivity.this.getListView().pointToPosition((int) e1.getX(),(int) e1.getY());
        Offer order = (Offer)MainActivity.this.getListView().getAdapter().getItem(itemId);
        View v = MainActivity.this.getListView().getChildAt(itemId);
    }
}

我想在滑动行的顶部显示一个视图,其中包含该行的一组上下文相关选项。

我的问题在于以下方法:

v.getTop()
v.getBottom()

仅在视图滚动之前返回正确的值。我可能可以做一些计算来计算使用滚动位置等的偏移量,但我也注意到,当我开始滑动屏幕上可见的一行时,我才会得到值。如果我向下滚动列表,然后滑动一行(最初不是在屏幕外),那么这些方法会返回空值。

下面的方法似乎遇到了同样的问题...

v.getLocationOnScreen(loc)
v.getLocationInWindow(loc)

最终,我希望找到可见 listView 的顶部与已刷过的行之间的距离。然后,我将使用此距离向具有适当高度填充的父 FrameLayout 添加一个视图(以便它覆盖滑动的行)。

任何帮助将不胜感激!

【问题讨论】:

    标签: android


    【解决方案1】:

    有两种不同的方式来索引 ListView 项,按适配器中的位置,或按屏幕上的可见位置。一个项目将始终位于适配器内的相同位置(除非您更改列表)。 view.getChildAt() 的索引将根据屏幕上可见的内容而有所不同。

    pointToPosition 返回的位置是适配器内的位置。如果要获取物理视图,则需要考虑视图是否滚动。试试这个:

    int adapterIndex = MainActivity.this.getListView().pointToPosition((int) e1.getX(),(int) e1.getY());
    int firstViewItemIndex = MainActivity.this.getListView().getFirstVisiblePosition();
    int viewIndex = adapterIndex - firstViewItemIndex;
    View v = MainActivity.this.getListView().getChildAt(viewIndex);
    

    【讨论】:

    • 感谢您的回复。实际上我没有跟踪视图对象。在运行时,我在滑动时动态地获取该行。然后我进行上述调用以尝试找出它在屏幕上的位置。我并没有改变行本身,我只是想在行的顶部显示一个视图。我有一个滚动侦听器,一旦再次滚动列表,它就会删除新视图。可以在 iPhone 的 tweetie 2 演示中看到该功能的演示:youtube.com/watch?v=Vxjj3F5MCpQ#t=1m12s
    • 您能否详细说明如何在运行时动态获取该行,当它被滑动时?
    • 在 ListView 上使用 GestureDetector 和 GestureListener。我已经更新了上面的代码 sn-p 以包含当用户在列表上滑动手指时调用的 onFling 方法(和参数)。使用一些简单的数学来检查用户是否从左向右滑动,然后使用 pointToPosition 来查找滑动对应的行。
    • 谢谢。好的... PointToRowId 和 PointToPosition 都为被刷过的行返回正确的 id(列表中的序列号)。但是,我随后调用“查看 v = MainActivity.this.getListView().getChildAt(itemId);”当引用一个在屏幕外开始(或在我的 Nexus One 的 800 像素高度之外)的 itemId 时返回 null
    • 滚动后是否检查了 RowId 与 Position 的值?他们真的不应该是同一件事......
    【解决方案2】:

    我已经解决了这个问题,但感觉有点像 hack,可能不是最有效的实现。肯定有更简洁的方法可以使用 Android API 做到这一点吗?

    在下面的代码中,我在我的 ArrayAdaptor 中添加了一个 HashMap 来跟踪屏幕上的哪些视图保存了哪些行(因为这些在您滚动列表时会被回收)

    我对原始适配器所做的所有更改都已在下面进行了评论。

     class RestaurantAdapter extends ArrayAdapter<Offer> {
                Map hash = null;  //HashMap to keep track of rowId and on screen View
    
                RestaurantAdapter() {
                    super(MainActivity.this, android.R.layout.simple_list_item_1, model);
                    hash = new HashMap(); //instantiate HashMap in constructor
                }
    
                 //method to return correct View on screen for row id
                public View getViewOnScreen(int rowId) {
                    return (View)hash.get(rowId);
                }
    
                public View getView(int position, View convertView,
                                                        ViewGroup parent) {
                    View row=convertView;
                    hash.put(position, convertView); // add/update view for row position as it is recycled 
    
                    RestaurantWrapper wrapper=null;
    
                    if (row==null) {                                                    
                        LayoutInflater inflater=getLayoutInflater();
    
                        row=inflater.inflate(R.layout.row, parent, false);
                        wrapper=new RestaurantWrapper(row);
                        row.setTag(wrapper);
                    }
                    else {
                        wrapper=(RestaurantWrapper)row.getTag();
                    }
    
                    wrapper.populateFrom(model.get(position));
    
                    return(row);
                }
    
    
            }
    

    【讨论】:

    • 我尝试了这个解决方案,但它对我不起作用。我无法修改返回的视图。无论如何,在我的情况下,使用 getChildAt 在列表中确实有效。我不知道为什么我不能使用这个方法修改返回的视图。
    • 实际上 getChildAt 对我不起作用,原因与它对您不起作用的原因相同,所以我最终使用了此页面上的其他答案并且有效。
    【解决方案3】:

    这是我发现检测哪一行被触摸的最佳方法。

                Rect rect = new Rect();
                int childCount = mListView.getChildCount();
                int[] listViewCoords = new int[2];
                mListView.getLocationOnScreen(listViewCoords);
                int listX = (int) event.getRawX() - listViewCoords[0];
                int listY = (int) event.getRawY() - listViewCoords[1];
    
                View child;
                for (int i = 0; i < childCount; i++) {
                    child = mListView.getChildAt(i);
                    child.getHitRect(rect);
                    if (rect.contains(listX, listY)) {
                        mDownView = child;
                        break;
                    }
                }
    

    您可以在 ACTION_DOWN 事件的 onTouch 方法中使用它。问候。

    【讨论】:

    • 谢谢。但是,getChildAt() 确实返回了第一个 visible 孩子(而不是列表中的第一个孩子)。要使其工作,您可以使用 for(int i = 0 ; i
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-15
    • 1970-01-01
    • 2020-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多