版本号2仅仅是简单的实现了当手指按下的时候listView的Item向左移动一定的距离,并没有随着手指的左右移动而左右滚动。在这个版本号3.0中将会实现随着手指的移动而滚动的目标:当手指向左移动的时候。listView向左滚动;当手指向右移动的时候。listView向右滚动;在開始进入正题之前,先说说预备的知识和涉及到的方法。

在version2.0之前加入View的时候用的都是addView终于辗转调用了addViewInner方法,经过查询viewGroup的源代码发现有一个addViewInLayout方法。api的说明例如以下:Adds a view during layout,this is usefule if in your onLayout method.而我们这个横向listView正好是在onLayout里面加入的View,所以从如今開始加入View的时候就改为addViewInLayout了。ViewGrouup里面提供一个View的数组mChildren,在addView方法中调用了addVew(view
view,index)index默认传的是-1。表明加入到View数组mChildren最后面。

而addViewInLaout的方法的第二个參数就是这个index,假设index为负数的话就把该View方法数组的最后面(实际代码处理中就是数组的插入操作,非常easy的源代码),当index为正数的时候就把该View插入到数组中的index的位置,相应的View数组mChildren里面的元素后移。当然相应的删除View的时候也有removeViewInLayout方法;事实上getChildAt(int index)就是从数组mChildrenCount获取返回相应索引的View,所以左右滑动的核心思想就出来了:

1)当向左滚动的时候,把右边即将要滚动出来的View加入到mChildren数组的最后面。在页面上的显示为在屏幕的右边的View,同一时候提供一个rightIndex来表示adapter.getView的position。即addViewInlayout(view,-1,view.getLayoutParams,true),

2)当向右滚动的时候。把左边即将滚动出来的View加入的mChildren数组的最前面数组0的位置,在页面上的显示为在屏幕左边的View,同事提供一个left来表示adapter.getView 的position參数。

即addViewInlayout(view,0,view.getLayoutParams,true)。用代码体现例如以下

  1. private void addRightChildViews(int dianceX) {
  2.         // 2.让屏幕尽可能的显示Item。注意刚開始的时候是没有
  3.         View rightChildView = getChildAt(getChildCount() - 1);
  4.         // 获取此childView右边框距离parentView左边框的距离
  5.         int rightEdge = rightChildView != null ? rightChildView.getRight() : 0;
  6.         while (rightEdge + dianceX < getWidth()
  7.                 && rightIndex < listAdapter.getCount()) {
  8.             View child = listAdapter.getView(rightIndex, null, null);
  9.             child = measureChild(child);
  10.             addViewInLayout(child, -1, child.getLayoutParams(), true);
  11.             rightEdge += child.getMeasuredWidth();
  12.             rightIndex++;
  13.         }
  14.     }
  15.  
  16.     private void addLeftChildViews(int dianceX) {
  17.         View leftChildView = getChildAt(0);
  18.         int leftEdge = leftChildView != null ?
  19.  
  20. leftChildView.getLeft() : 0;
  21.         while (leftEdge + dianceX > 0 && leftIndex >= 0) {
  22.  
  23.             View child = listAdapter.getView(leftIndex, null, null);
  24.             child = measureChild(child);
  25.             addViewInLayout(child, 0, child.getLayoutParams(), true);
  26.             leftEdge -= child.getMeasuredWidth();
  27.             leftIndex--;
  28.            //此处省略了一个重要的代码
  29.         }
  30.     }

该篇博客就是环绕着这两个核心思路进行拓展和改动进而实现左右滚动的。

在版本号2.0中向左移动的时候须要在合适的时机移除左边符合某些条件的View(详细见简单的横向ListView实现(version 2.0) 相同,在想右滚动的时候也须要把超出右边屏幕的View从Viewgroup里面删除出去(当child.getLeft()+移动偏移量大于parent.getWidth()的时候进行删除):简而言之就是向左滚动时。须要删除滚动过后左边的看不见View。当向右滚动时,须要删除滚动过后右边的看不见的View。用代码表演示样例如以下:

  1.     
  2. /* 删除看不见的view
  3.      */
  4.     private void removeAnvisiableViews(int dianceX) {
  5.         // 移除左边看不到的view
  6.         View firtVisiableView = getChildAt(0);
  7.         if (firtVisiableView != null
  8.                 && dianceX + firtVisiableView.getRight() <= 0) {
  9.             removeViewInLayout(firtVisiableView);
  10.             leftIndex++;
  11. //此处省略了一行重要的代码
  12.         }
  13.  
  14.         // 移除右边看不到的view
  15.         View lastVisialbeView = getChildAt(getChildCount() - 1);
  16.         if (lastVisialbeView != null
  17.                 && lastVisialbeView.getLeft() + dianceX >= getWidth()) {
  18.             removeViewInLayout(lastVisialbeView);
  19.             rightIndex--;
  20.         }
  21.  
  22.         
  23.     }

到此为止在考虑左右滚动的条件下什么时候加入左边的View、什么时候加入右边的View以及在什么时候删除左右两边看不见的View的思路以及代码都实现了。之前的版本号的滚动值都是写死的。如今这个版本号将用GestureDetector来处理手指的移动,至于GestureDetector的使用方法在这里不再赘述。网上好多资料能够查阅在.SimpleOnGestureListener()里面有一个 onScroll(MotionEvent e1, MotionEvent e2,  float distanceX, float
distanceY)方法。由于是水平滚动。所以第三个參数distanceX使我们所须要的,该參数指的是距离上次调用onScroll方法的时候x轴滚动的距离。所以在代码中我用变量distanceX来记录下了:

  1. public boolean onScroll(MotionEvent e1, MotionEvent e2,
  2. float distanceX, float distanceY) {
  3. //记录滚动的距离
  4.  HListView.this.distanceX= (int) distanceX;
  5. // 仍然须要重绘
  6. requestLayout();
  7. return true;
  8.  
  9. };

这里有个须要注意的小地方,手指向左的时候distanceX>0,而手指向右的时候distanceX<0.所以在用的时候我们要取反,所以在onLayout里面这么调用(较版本号2.0之前对这种方法进行了重构处理):

  1. @Override
  2. protected void onLayout(boolean changed, int left, int top, int right,
  3. int bottom) {
  4.  
  5. if (listAdapter == null) {
  6. return;
  7. }
  8. removeAnvisiableViews(-distanceX);
  9. addRightChildViews(-distanceX);
  10. addLeftChildViews(-distanceX);
  11. layoutChildViews(-distanceX);
  12.  
  13. }

到此为止基本上该说的都说了,执行一把试试,我屮艸芔茻,发现动不了。怎么回事?各种抓耳挠腮。终于吃饭的时候灵光一闪知道问题出如今哪儿了!

我们知道横向listView滚动的逻辑就是反复addView和removeView的操作,可是最重要的一点就是终于都要调用layoutChildViews方法进行处理,而在version2.0版本号的代码中layoutChildViews代码是这么样的:

  1. int childLeft = 0;
  2. for(int i=0;i<getChildCount();i++) {
  3. View child = getChildAt(i);
  4. int childWidth = child.getMeasuredWidth();
  5. child.layout(childLeft, 0, childWidth+childLeft, child.getMeasuredHeight());
  6. //只是最好的写法是
  7. childLeft += childWidth+child.getPaddingRight();
  8. }

layout方法的第一个三处我们总是让它从0開始。!

!!问题就出在这儿。。!

简单的想象一下,当向右滚动的时候左边会出现半个Item的情况(或者不完整的Item)。怎么可能是从0開始呢。肯定会出现childLeft<0的情况。所以当处理左边第一个能够看见的子View的时候,layout第一个參数是随着滚动而发生变化的。并非固定为0的死的值!事实上上面的代码中我有写到:此处省略了一行重要的代码,事实上这行代码就是解决问题的!

我在类里面定义了个leftOffset=0的变量来进行控制表示下一个即将加入的子View的起始位置(注意要加上distanceX),(这个变量的作用想了半天不好用语言描写叙述,郁闷).当调用addleftChildView方法的时候每加入一个View时对该变量进行leftOffset -= child.getMeasuredWidth();。

对应的当删除左边的一个View的时候leftOffset += child.getMeasuredWidth();所以layoutchildView的代码改动为:

  1. private void layoutChildViews(int distanceX) {
  2. if(getChildCount()==0) {
  3. return;
  4. }
  5. leftOffset += distanceX;
  6. int childLeft = leftOffset;
  7. for (int i = 0; i < getChildCount(); i++) {
  8. View child = getChildAt(i);
  9. int childWidth = child.getMeasuredWidth();
  10. child.layout(childLeft, 0, childWidth + childLeft,
  11. child.getMeasuredHeight());
  12. // 只是最好的写法是
  13. childLeft += childWidth + child.getPaddingRight();
  14. }
  15.  
  16. }

执行一把。OK,爽歪歪!version3.0到此位置完毕了。只是功能仍然不够完好:比方当左边已经是第一个Item的时候仍然能够想右边滚动。同理当右边是最后一个View的时候仍然能够向左滚动、相同的当手指离开的时候没有惯性滚动,比方每次getView的时候都须要对xml文件进行解析,没有非常好的重用已经解析过的xml等,这些丰富的功能将在下一个版本号中实现。(此处为源码

简单的横向ListView实现(version 3.0)的更多相关文章

  1. 简单的横向ListView实现(version 4.0)

    这个版本号的博客写起来颇费口舌.有些代码自己语言组织能力有限,感觉描写叙述起来非常费劲,前前后后改了五六遍稿子还是不尽人意 ,只是我还是坚持写出来自己当初的思路,假设看得不明确的地方我在文章最后仍然会 ...

  2. gitlab 源码安装=》rpm安装横向迁移(version 9.0)

    准备: 下载版本地址: https://packages.gitlab.com/gitlab/gitlab-ce 迁移环境: 源码安装的gitlab9.0.13 目标迁移至9.0.13 RPM安装的环 ...

  3. Java简单双向链表实现 @version 1.0

    package com.list; /** * 数据结构和算法Java表示 双向链表 * * @version 1.0 * @author 小明 * */ public class MyDoublel ...

  4. Java单链表简单实现* @version 1.0

    package com.list; /** * 数据结构与算法Java表示 * @version 1.0 * @author 小明 * */ public class MyLinkedList { p ...

  5. ExpandableListView简单应用及listview模拟ExpandableListView

    首先我们还是来看一些案例,还是拿搜狐新闻客户端,因为我天天上下班没事爱看这个东东,上班又没时间看新闻,上下班路途之余浏览下新闻打发时间嘛.           看这个效果挺棒吧,其实实现起来也不难,我 ...

  6. 张高兴的 UWP 开发笔记:横向 ListView

    ListView 默认的排列方向是纵向 ( Orientation="Vertical" ) ,但如果我们需要横向显示的 ListView 怎么办? Blend for Visua ...

  7. eclipse 和 android studio 编译时报 Unsupported major.minor version 52.0错解决办法

    version 52.0 是java8的环境.当gradle tools 升级到2.2.1时候,可能编译时候会报该错误. 很多网友说更改java version,但是很多时候无效.下面是我遇到时候的解 ...

  8. (class file version 53.0), Java Runtime versions up to 52.0错误的解决方法

    遇到这个错误是在Apache Tomcat上部署应用程序的时候遇到的,具体的错误描述是: java.lang.UnsupportedClassVersionError: HelloWorld has ...

  9. Unsupported major.minor version 52.0解决办法

    一.错误现象:当改变了jdk版本时,在编译java时,会遇到Unsupported major.minor version错误.jdk版本和stanford parser对应关系 JDK版本和Java ...

随机推荐

  1. Android 查看CPU及内存

    借助getprop.dumpsys来了解一些系统相关信息. 一.getprop adb shell cat /system/build.prop 文件中存放的是用于启动系统时需要的配置文件,通常可以通 ...

  2. Apache CXF实战之二 集成Sping与Web容器

    本文链接:http://blog.csdn.net/kongxx/article/details/7525481 Apache CXF实战之一 Hello World Web Service 书接上文 ...

  3. 一 Balancer regionCountCost 权重。

    原文如下地址 : http://www.cnblogs.com/cenyuhai/p/3650943.html 看源码很久了,终于开始动手写博客了,为什么是先写负载均衡呢,因为一个室友入职新公司了,然 ...

  4. Ural 1303 Minimal Coverage(贪心)

    题目地址:Ural 1303 先按每一个线段的左端点排序,然后设置一个起点s.每次都从起点小于等于s的线段中找到一个右端点最大的. 并将该右端点作为新的起点s,然后继续找. 从左到右扫描一遍就可以. ...

  5. Hive Cilent数据操作

    Hive运行命令方式有cli,jdbc.hwi.beeline.而我们经常使用的往往是cli shell 操作. cli shell hive -help hive --help 注:命令脚本必须在集 ...

  6. js如何生成[n,m]的随机数(整理总结)

    js如何生成[n,m]的随机数(整理总结) 一.总结 一句话总结: // max - 期望的最大值 // min - 期望的最小值 parseInt(Math.random()*(max-min+1) ...

  7. shrio 加密/编码

    在涉及到密码存储问题上,应该加密/生成密码摘要存储,而不是存储明文密码.比如之前的600w csdn账号泄露对用户可能造成很大损失,因此应加密/生成不可逆的摘要方式存储. 5.1 编码/解码 Shir ...

  8. mybatis+springmvc+sqlite一个累心的问题:不在纠结

    1 java.sql.SQLException: NYI 2 org.sqlite.RS.getColumnClassName(RS.java:269) 在配置mybatis+springmvc+sq ...

  9. Coderfroces 864 D. Make a Permutation!

    D. Make a Permutation! Ivan has an array consisting of n elements. Each of the elements is an intege ...

  10. Microsoft office2016(专业增强版) 安装错误,报CRT(KB2999226)

    对着这个错误的出现,网上有解释,这里不多说(实际是我没有找到比较靠谱的说法..),跟Window Update这个服务有关. 首先打开”Windows人为管理器”->"服务" ...