zoukankan      html  css  js  c++  java
  • 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] view plaincopy
     
    1. 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] view plaincopy
     
    1. int headerCount = mListContainer.getListView().getHeaderViewsCount();  
    2. int firstVisiblePos = mListContainer.getListView().getFirstVisiblePosition();  
    3. int newCursorPosition = getPositionInNewCursor(cursor.getCount(), firstVisiblePos);  
    4. int offsetY = getOffsetY(cursor, firstVisiblePos, newCursorPosition);  
    5.   
    6. mAdapter.changeCursor(cursor);  
    7.   
    8. mUpRefreshLayout.setVisibility(View.GONE);  
    9.         
    10. mListContainer.getListView().setSelectionFromTop(newCursorPosition + headerCount, offsetY);  
    getPositionInNewCursor函数:
    [java] view plaincopy
     
    1. private int getPositionInNewCursor(int newCursorCount, int firstVisiblePos){  
    2.     if(firstVisiblePos == 0){  
    3.         firstVisiblePos += 1;  
    4.     }  
    5.   
    6.     int headerCount = mListContainer.getListView().getHeaderViewsCount();  
    7.     int newCursorPos = newCursorCount - mAdapter.getCount() + firstVisiblePos - headerCount;  
    8.       
    9.     return newCursorPos;  
    10. }  
    getOffsetY函数:
    [java] view plaincopy
     
    1. private int getOffsetY(Cursor cursor, int firstVisiblePos, int newCursorPosition){  
    2.         int y;  
    3.           
    4.         View firstVisibleItem = null;  
    5.         if(firstVisiblePos == 0){  
    6.             firstVisibleItem = mListContainer.getListView().getChildAt(1);  
    7.         }else{  
    8.             firstVisibleItem = mListContainer.getListView().getChildAt(0);  
    9.         }  
    10.         y = firstVisibleItem.getTop();  
    11.   
    12.         View timeView = firstVisibleItem.findViewById(R.id.time_text_view);  
    13.         if(timeView != null && timeView.getVisibility() == View.VISIBLE){  
    14.   
    15.             Cursor curItem = (Cursor)mAdapter.getItem(newCursorPosition);  
    16.             Cursor preItem = (Cursor)mAdapter.getItem(newCursorPosition - 1);  
    17.             if(curItem != null || preItem != null){  
    18.                 long curTimeStamp = curItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX);  
    19.                 long preTimeStamp = preItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX);  
    20.                   
    21.                 if(Math.abs(curTimeStamp - preTimeStamp) <= SHOW_TIME_STAMP_TEN_MINS){  
    22.                     LayoutParams param = (LinearLayout.LayoutParams)mTimeView.getLayoutParams();  
    23.                     y += mTimeView.getHeight() + param.topMargin + param.bottomMargin;  
    24.                 }  
    25.             }  
    26.         }  
    27.           
    28.         return y;  
    29.     }  

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

     
    3
  • 相关阅读:
    VS 2010 Addin 开发 1
    事件冒泡和默认事件
    闭包试验
    查询字符串中出现频率最高的几种方法
    JS函数中的参数
    a 项目已更新,b 项目可以基于这个项目处理
    windows 生成ssh 公钥 命令
    git fetch upstream 报错 XXX Permission denied (publickey). fatal: Could not read from remote repository
    考试的那点事儿
    MySQL数据库InnoDB存储引擎多版本控制(MVCC)实现原理分析
  • 原文地址:https://www.cnblogs.com/xgjblog/p/4228782.html
Copyright © 2011-2022 走看看