在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从下往上展示,也就是

  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。

下面上代码:

调用的代码:

  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. mAdapter.changeCursor(cursor);
  6. mUpRefreshLayout.setVisibility(View.GONE);
  7. mListContainer.getListView().setSelectionFromTop(newCursorPosition + headerCount, offsetY);

getPositionInNewCursor函数:

  1. private int getPositionInNewCursor(int newCursorCount, int firstVisiblePos){
  2. if(firstVisiblePos == 0){
  3. firstVisiblePos += 1;
  4. }
  5. int headerCount = mListContainer.getListView().getHeaderViewsCount();
  6. int newCursorPos = newCursorCount - mAdapter.getCount() + firstVisiblePos - headerCount;
  7. return newCursorPos;
  8. }

getOffsetY函数:

  1. private int getOffsetY(Cursor cursor, int firstVisiblePos, int newCursorPosition){
  2. int y;
  3. View firstVisibleItem = null;
  4. if(firstVisiblePos == 0){
  5. firstVisibleItem = mListContainer.getListView().getChildAt(1);
  6. }else{
  7. firstVisibleItem = mListContainer.getListView().getChildAt(0);
  8. }
  9. y = firstVisibleItem.getTop();
  10. View timeView = firstVisibleItem.findViewById(R.id.time_text_view);
  11. if(timeView != null && timeView.getVisibility() == View.VISIBLE){
  12. Cursor curItem = (Cursor)mAdapter.getItem(newCursorPosition);
  13. Cursor preItem = (Cursor)mAdapter.getItem(newCursorPosition - 1);
  14. if(curItem != null || preItem != null){
  15. long curTimeStamp = curItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX);
  16. long preTimeStamp = preItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX);
  17. if(Math.abs(curTimeStamp - preTimeStamp) <= SHOW_TIME_STAMP_TEN_MINS){
  18. LayoutParams param = (LinearLayout.LayoutParams)mTimeView.getLayoutParams();
  19. y += mTimeView.getHeight() + param.topMargin + param.bottomMargin;
  20. }
  21. }
  22. }
  23. return y;
  24. }

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

 
3

Android入门 在ListView中如何进行精确的定位的更多相关文章

  1. Android 如何在 ListView 中更新 ProgressBar 进度

    =======================ListView原理============================== Android 的 ListView 的原理打个简单的比喻就是: 演员演 ...

  2. Android如何在ListView中嵌套ListView

    前几天因为项目的需要,要在一个ListView中放入另一个ListView,也即在一个ListView的每个ListItem中放入另外一个ListView.但刚开始的时候,会发现放入的小ListVie ...

  3. [Android学习笔记]ListView中含有Button导致无法响应onItemClick回调的解决办法

    转自:http://www.cnblogs.com/eyu8874521/archive/2012/10/17/2727882.html 问题描述: 当ListView的Item中的控件只是一些展示类 ...

  4. Android入门:MVC模式(中)

    MVC 模式的最基本概念是分层设计,把我们的代码基于 View(视图).Model(模型).Controller(控制器)进行分类封装,这样做的目的是为了清晰结构,使代码更易维护和扩展. 在上一篇文章 ...

  5. android开发之 listview中的item去掉分割线 隐藏分割线

    有三种方法: 1> 设置android:divider="@null" 2> android:divider="#00000000" #000000 ...

  6. Android单选中listview中的一项

    public class LipsListAdapter extends BaseAdapter { private Context context; private List<Lips> ...

  7. ListView 中的ImageView Button

    1.ListView 中的ImageView  Button的点击事件会屏蔽掉ItemView的点击事件 原因是ImageView Button在回执过程中会抢夺item的焦点 解决办法:在xml中添 ...

  8. Android入门(五)UI-单位与尺寸、ListView

    原文链接:http://www.orlion.ga/453/ 一.单位与尺寸 布局文件中一共有以下单位供选择:px,pt,dp,sp px:是像素,屏幕中可见的最小元素单位. pt:是磅,1磅等于1/ ...

  9. Android,LIstView中的OnItemClick点击无效的解决办法

    在List_Item布局文件中的根节点加上如下背景标黄的这一行 <?xml version="1.0" encoding="utf-8"?> < ...

随机推荐

  1. Spring与Struts2的整合

    一.复制jar文件. 把struts2-spring-plugin-..*.jar和spring.jar复制到Web工程的WEB-INF/lib目录下,并且还需要复制commons-logging.j ...

  2. 我为什么喜欢Go语言

    从2000年至今,也写了11年代码了,期间用过VB.Delphi.C#.C++.Ruby.Python,一直在寻找一门符合自己心意和理念的 语言.我很在意写代码时的手感和执行的效率,所以在Go出现之前 ...

  3. Openstack celi

    http://www.51testing.com/html/76/n-3720076.html

  4. Usage of API documented as @since 1.6+

    报错:即方法是Java1.6才开始有的 File ->Project Structure->Project Settings -> Modules -> Language Le ...

  5. 用了GradientDrawable后,当点击控件时,控件大小发生变化

    android新手:发现一个很奇怪的问题,用了GradientDrawable后,当点击控件时,程序自动使我的一些控件大小保持一致,为什么呢,我就是不想它们保持一致啊 改了好久好久:GradientD ...

  6. 探究堆喷射(heap spray)

    博客园的自动保存系统真心不咋地,写的差不多的文章蓝屏之后就没有了,醉了! 浏览器是互联网世界最主要的软件之一,从IE6到IE11安全攻防在不断升级,防御措施的实施促使堆喷射技巧不断变化.写这篇博文想好 ...

  7. 【bzoj3173】【Tjoi2013】【最长上升子序列】treap+dp二分优化

    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=61560361 向大(hei)佬(e)实力学(di ...

  8. 二分图变种之最小路径覆盖、最小点覆盖集【poj3041】【poj2060】

    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=54859604 向大(hei)佬(e)势力学(di ...

  9. [POI2014]FarmCraft

    题目大意: 一个$n(n\le5\times10^5)$个点的树,每个点有一个权值$c_i$,从$1$出发进行欧拉遍历,每个单位时间移动一条边,记每个点$i$被访问到的时间是$t_i$,问最后$\ma ...

  10. vue中自定义指令vue.direvtive,自定义过滤器vue.filter(),vue过渡transition

    自定义指令 默认设置的核心指令( v-model,v-bind,v-for,v-if,v-on等 ),Vue 也允许注册自定义指令.注意,在 Vue2.0 里面,代码复用的主要形式和抽象是组件——然而 ...