Android入门 在ListView中如何进行精确的位置设定


在android的开发中,经常会遇到需要主动去设定某条ListItem的位置的需求。设置位置的函数有

ListView.setSelection(int position)

ListView.setSelectionFromTop(int position, int y);

其中

position指的是指定的item的在ListView中的索引,注意如果有Header存在的情况下,索引是从Header就开始算的。

y指的是到ListView可见范围内最上边边缘的距离。

函数有了,现在就是根据自身需求来进行设置。


这次遇到的需求,ListView要求是从下往上展示的,并且当Cursor更新时,要保持住原先的最上方的item(不包括header)的位置不变,然后新的历史数据在原先那条item上方继续向上展示。如图:


ListView从下往上展示,也就是

[java] 
android:stackFromBottom="true" 
但是发现这一属性的设置不会影响索引的排序顺序,也就是item的索引都是从上往下递增的,不会变成从下往上递增。索引为0的item,都是在ListView的最上方的item(或header).
那么当Cursor更新时,原先第一条的索引便会发生变化。要想保持住它(图中的 R)的位置。步骤如下:

(1)获取这一条在新Cursor中的位置(posiition)

(2)获取这一条在更换Cursor后ListView中的位置。

(4)由于ListView的可滚动的属性,我们需要记录更换Cursor前可视的第一条item的索引(ListView.getFirstVisiblePosition())
(3)区分FirstVisiblePosition是0和大于0的情况。由于header,也就是图中的Loading那一条在新数据出来后是会消失的。
(4)当FirstVisiblePosition为0时实际指向的是header,我们要保持位置不变的是header下面第一条(R)的位置。那么此时要设置FirstVisiblePosition为1

(5)当FirstVisiblePosition大于0时实际指向的就是item,但是我们需要设置FirstVisiblePosition为0。*

(6)我们根据FirstVisiblePosition用ListView.getChildAt(int position)函数获取对应的item的View,再根据View.getTop()函数获取到ListView顶部的距离Y。

这样ListView.setSelectionFromTop(int position, int y)所需的两个参数 position 和 y就都有了。

*注解:ListView.getChildAt(int position), 这个position指的是在可视的item中的索引,跟cursor里的位置是大不一样的。可以看看ListView.getChildCount()函数得到个数是小于或等于Cursor里的个数的(不考虑header的话)。虽然一共可能有20条数据,但是界面只能看到8条,那么这个ChildCount大约就是8了。另一方面, FirstVisiblePosition取出的是在总的条数中的索引,再将会消失的header考虑进来,所以就是 FirstVisiblePosition为0时要设为1,大于0时又要设为0。

下面上代码:

调用的代码:

[java] 
int headerCount = mListContainer.getListView().getHeaderViewsCount(); 
int firstVisiblePos = mListContainer.getListView().getFirstVisiblePosition(); 
int newCursorPosition = getPositionInNewCursor(cursor.getCount(), firstVisiblePos); 
int offsetY = getOffsetY(cursor, firstVisiblePos, newCursorPosition); 
 
mAdapter.changeCursor(cursor); 
 
mUpRefreshLayout.setVisibility(View.GONE); 
       
mListContainer.getListView().setSelectionFromTop(newCursorPosition + headerCount, offsetY); 
getPositionInNewCursor函数:
[java] 
private int getPositionInNewCursor(int newCursorCount, int firstVisiblePos){ 
    if(firstVisiblePos == 0){ 
        firstVisiblePos += 1; 
    } 
 
    int headerCount = mListContainer.getListView().getHeaderViewsCount(); 
    int newCursorPos = newCursorCount - mAdapter.getCount() + firstVisiblePos - headerCount; 
     
    return newCursorPos; 

getOffsetY函数:
[java] 
private int getOffsetY(Cursor cursor, int firstVisiblePos, int newCursorPosition){ 
        int y; 
         
        View firstVisibleItem = null; 
        if(firstVisiblePos == 0){ 
            firstVisibleItem = mListContainer.getListView().getChildAt(1); 
        }else{ 
            firstVisibleItem = mListContainer.getListView().getChildAt(0); 
        } 
        y = firstVisibleItem.getTop(); 
 
        View timeView = firstVisibleItem.findViewById(R.id.time_text_view); 
        if(timeView != null && timeView.getVisibility() == View.VISIBLE){ 
 
            Cursor curItem = (Cursor)mAdapter.getItem(newCursorPosition); 
            Cursor preItem = (Cursor)mAdapter.getItem(newCursorPosition - 1); 
            if(curItem != null || preItem != null){ 
                long curTimeStamp = curItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX); 
                long preTimeStamp = preItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX); 
                 
                if(Math.abs(curTimeStamp - preTimeStamp) <= SHOW_TIME_STAMP_TEN_MINS){ 
                    LayoutParams param = (LinearLayout.LayoutParams)mTimeView.getLayoutParams(); 
                    y += mTimeView.getHeight() + param.topMargin + param.bottomMargin; 
                } 
            } 
        } 
         
        return y; 
    } 

getOffsetY中有一段计算图中TimeStamp的高度的代码,不关心的可以自己跳过一下。因为查询出历史数据后可能会造成原先有TimeStamp的那一条在刷新后不再显示TimeStamp(与上一条合并到一个时间段了),所以要把它的高度也计算进去。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值