简单的横向ListView实现(version 2.0)

版本1.0的横向listView核心只是简单的用layout来进行横向的布局,并没有实现基本的滚动操作,整个屏幕智能显示固定的数目的Item,且Adapter中剩余的View虽然添加到了viewGroup中但是并由于没法滚动无法显示出来,这个版本的横向listView将简单的实现滚动的功能。再说滚动之前的时候需要准备的知识资料如下:

如上图,外层蓝色的矩形框为parentView,黑色的矩形框为childView.其中parentView的左上角是相对于child的坐标原点(0,0);在android里面子view在父view里面可以调用getLeft(),getRight(),getBottom()和getTop来确定子view在父View中的位置。其中getRight() == child.getWidth()+getLeft(); getBottom() == child.getTop() + child.getHeight();

这个版本version 2.0的目标也很简单,只是让listView滚动起来就算达成目标;滚动的操作也很简单,当手指按下的时候listView的Item向左移动一定的距离(在这个版本中是写死的值)。所以该版本滚动既没有用Scroller也没有用GestureDetector,只是简单的响应一下MotionEvent.ACTION_DOWN,然后对页面进行重绘就偶了,当然在version3.0将做较大改进,会让整个左右滚动都显示完全,在3.0之前还是一步步慢慢来(同样在最后会将源代码奉上,若是有什么不正确的地方欢迎批评指正)。

版本运行效果:手机点击屏幕的时候,整个listView向左移动显示其余的Item

关键思路:

1)动态加载childView:在屏幕宽度的范围内能显示多少个childView就添加多少个,adapter里面其余剩下的childView就随着滚动调用requestLayout的时候在onLayout里面动态的添加到viewGroup.这时有一个关键的问题就出来了,怎么判断手机屏幕宽度的范围中显示满了,没法显示多余的Item了呢?此时child.getRight()有了用武之地:每次取viewGroup中最后一个子childView也就是parentView.getChildAt(getChildCount()-1),调用childView.getRight(),如果childView.getRight()<parentView.getWidth()就继续addView添加下一个childView;并重复childView.getRight()<parentView.getWidth();如果不成立的话说明屏幕中显示满了。啰嗦了这么多不是很清晰,下面用代码来表示就是如下(该段代码在parentView的onLayout中调用):

//获取最右边的那个view,刚开始的时候是为null的
View rightChildView = getChildAt(getChildCount()-1);
//获取此childView右边框距离parentView左边框的距离
int rightEdge = rightChildView!=null? rightChildView.getRight():0;
//做一个循环
		while(rightEdge <getWidth()&&index<listAdapter.getCount()) {
			View child = listAdapter.getView(index, null, null);
			child = measureChild(child);
			addView(child);
			rightEdge += child.getMeasuredWidth();
			index++;
		}

每一层的while循环用画图直观分析就是如下(偷个懒,直接用手画的):

每次for循环过后,最右边的那个childView就是while变量中的rightChildView.在上图当添加到四个view的时候屏幕的宽度区域已经沾满了(此时rightChildView.getRight>parentView.getWidt(),所以此时添加到的子view的个数为4个,而不是version1.0版本中的全部的childview.

2) 滚动逻辑:该版本是向左移动的,所以移动的时候某个坐标点在滚动停止的时候是该坐标点是变小的,中间的差值或者说运动的距离是负值(假设此处用leftSrcollDistance =-500)。在这里简单的就定义为-500;关键点:滚动的时候,确切地说是向左边滚动的时候.rightChildView的getRight()是越来越小的,当getRight()+leftSrcollDistance <parentView.getWidth()的时候,说明屏幕的右边是由空余的空间的,此时就可以动态的添加并显示下一条Item了,具体的代码如下:

public class HListView extends ViewGroup{
	/**存储数据用的Adapter**/
	private ListAdapter listAdapter;
    //保存adapter中View的索引
	private int index = 0;

	public HListView(Context context) {
		super(context);
	}

	public HListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public HListView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	public ListAdapter getAdapter() {
		return listAdapter;
	}
	public void setAdapter(ListAdapter adapter) {
		this.listAdapter = adapter;
	}

	/**
	 * 手指向左移动时滚动的距离,因为向左移动,所以移动的终点坐标和起点坐标
	 * 的差值为赋值,随着移动,最右边的view个的getRight会越来越小,当小于getWidht()的时候
	 * 就可以动态添加下一个了,这就是该横向listView的核心思想
	 */
	private int leftSrcollDistance = -500;
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		int eventAction = event.getAction();
		switch (eventAction) {
		case MotionEvent.ACTION_DOWN://手指滑动的时候
			requestLayout();
			break;

		}
            //注意此处要返回true
			return true;
	}

	/**
	 * 测量每个child的宽和高
	 * @param view
	 * @return
	 */
	private View measureChild(View view) {
		LayoutParams params = view.getLayoutParams();
		if(params==null) {
			params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
			view.setLayoutParams(params);
		}

		view.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getHeight(),
				MeasureSpec.AT_MOST));
		return view;

	}
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {

		if(listAdapter==null) {
			return;
		}		

		/*for(;index<listAdapter.getCount();index++) {
			View child = listAdapter.getView(index, null, null);
			child = measureChild(child);
			addView(child);
		}*/
		//注意刚开始的时候是null
		View rightChildView = getChildAt(getChildCount()-1);
		//获取此childView右边框距离parentView左边框的距离
		int rightEdge = rightChildView!=null? rightChildView.getRight():0;
		while(rightEdge+leftSrcollDistance<getWidth()&&index<listAdapter.getCount()) {
			View child = listAdapter.getView(index, null, null);
			child = measureChild(child);
			addView(child);
			rightEdge += child.getMeasuredWidth();
			index++;
		}

		Log.e("HListView", "getChildCount=="+getChildCount());

		//把childview通过layout布局到viewGroup中
		int childLeft = 0;
		for(int i=0;i<getChildCount();i++) {
			View child = getChildAt(i);
			int childWidth = child.getMeasuredWidth();
			child.layout(childLeft, 0, childWidth+childLeft, child.getMeasuredHeight());
			//不过最好的写法是
			childLeft +=  childWidth+child.getPaddingRight();
		}

	}
}

运行一把发现此时是不会运动的,为什么?很简单,因为之前这段代码没有把最左边的View在合适的时机从ViewGroup里面删除,导致虽然动态添加了view但是并没有多余的空间让新加的view显示出来。这个合适的时机就是当最左边的chilView的getRight()<=0的时候,因为是滚动确切地说是getRight()+leftSrcollDistance()<=0的时候,于是乎修改后的onLayout(该方法最好重构一下,添加,测量等弄到子程序里面最好):

protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {

		if(listAdapter==null) {
			return;
		}		

		/*for(;index<listAdapter.getCount();index++) {
			View child = listAdapter.getView(index, null, null);
			child = measureChild(child);
			addView(child);
		}*/

		//1.先删除最左边看不见的Item
		View firtVisiableView = getChildAt(0);
		if(firtVisiableView!=null&&leftSrcollDistance+firtVisiableView.getRight()<=0) {
			removeView(firtVisiableView);
		}

		//2.让屏幕尽可能的显示Item。注意刚开始的时候是没有
		View rightChildView = getChildAt(getChildCount()-1);
		//获取此childView右边框距离parentView左边框的距离
		int rightEdge = rightChildView!=null? rightChildView.getRight():0;
		while(rightEdge+leftSrcollDistance<getWidth()&&index<listAdapter.getCount()) {
			View child = listAdapter.getView(index, null, null);
			child = measureChild(child);
			addView(child);
			rightEdge += child.getMeasuredWidth();
			index++;
		}
		//打印此时添加了多少个childView
		Log.e("HListView", "getChildCount=="+getChildCount());

		//3.把步骤2添加的view通过Layout布局到parentView中
		int childLeft = 0;
		for(int i=0;i<getChildCount();i++) {
			View child = getChildAt(i);
			int childWidth = child.getMeasuredWidth();
			child.layout(childLeft, 0, childWidth+childLeft, child.getMeasuredHeight());
			//不过最好的写法是
			childLeft +=  childWidth+child.getPaddingRight();
		}

	}

到此为止,这个仍然存在如开头所说问题的横向可滚动listView算是实现了:简单的总结一句话就是,总的核心就是计算坐标和requestLayout过程,难度不是很大。通过写这个小东西自己倒是又更多的掌握了写东西,希望对大家有帮助。在版本verson3.0的时候会对此版本进行全面的修改,未完待续吧!(此处为源代码)

时间: 2024-11-10 23:26:24

简单的横向ListView实现(version 2.0)的相关文章

简单的横向ListView实现(version 3.0)

版本2只是简单的实现了当手指按下的时候listView的Item向左移动一定的距离,并没有随着手指的左右移动而左右滚动,在这个版本3.0中将会实现随着手指的移动而滚动的目标:当手指向左移动的时候,listView向左滚动:当手指向右移动的时候,listView向右滚动:在开始进入正题之前,先说说预备的知识和涉及到的方法. 在version2.0之前添加View的时候用的都是addView最终辗转调用了addViewInner方法,经过查询viewGroup的源码发现有一个addViewInLay

简单的横向ListView实现(version 1.0)

最近工作不是很忙,就随便按照自己的意愿胡乱整了个小demo,简单的实现横向的ListView:   功能随着版本的增加而完善,包括左右滚动.itemClick等等listView的基本功能: 虽然说demo超级简单,但是真正的写起来倒是废了不小的功夫.废话少说,开始吧. ListView肯定需要一个Adapter对象,这个小demo的核心原理就是循环遍历Adapter里面的子View,并调用子view的layout方法使其按计算的位置横向的排列在ViewGroup上面,就构成了一个横向的一排Vi

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

这个版本的博客写起来颇费口舌,有些代码自己语言组织能力有限,感觉描述起来很费劲,前前后后改了五六遍稿子还是不尽人意 ,不过我还是坚持写出来自己当初的思路,如果看得不明白的地方我在文章最后仍然会上传源代码,可以直接运行看效果,看过运行的效果后对文中有些别扭的语言估计会能直观的了解.在版本3.0的虽然实现了随着手指的左右移动listView中的item也随着滚动,但是会出现如下的情况: 当左边已经是第一个的时候,会出现如下的情况(仍然可以向右移动): 当右边是adapter最后一个item的时候,会

一个简单的loading动画,version 1.0

一个简单的loading动画:如图 点我查看

Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载)

Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载) POSTED ON 2014年6月27日 BY 天边的星星 本文内容: 1.横向ListView的所有实现思路; 2.其中一个最通用的思路HorizontalListView,并基于横向ListView开发一个简单的相册: 3.实现的横向ListView在点击.浏览时item背景会变色,并解决了listview里setSelected造成item的选择状态混乱的问题.

Android-搭建简单服务端+ListView异步加载数据

Android-搭建简单服务端+ListView异步加载数据 2014年5月6日 本篇博文带给大家的是教大家如何在MyEclipse中搭建一个服务端,并通过手机端与其通信,异步加载数据. 笔者使用的是MyEclipse,各位也可以直接用Eclipse创建Java Web项目,谷歌提供的ADT Bundle是不能创建Web项目,读者可以下载Eclipse For JaveEE Developer这个IDE. 下面来介绍如何在MyEclipse创建一个Web项目,并部署到Tomcat当中,关于Tom

项目总结--Version 1.0(三)

可扩展性决定了项目能走多远,可复用行决定了项目走的是否轻快. 本文主要讨论1.0版本的项目在进行设计时对可复用性和可扩展性的思考,涉及了整个项目分层的所有层(想查阅分层相关部分的可以点这:项目总结--Version 1.0(一)和项目总结--Version 1.0(二)). 由于经验有限,做过多的扩展容易误导其他人,所以本文所做的一些讨论总结只限在本项目范围内进行,对这个项目在进行架构设计的时候遇到的问题和自己的一些想法在此做一些梳理和总结. 同样的,先从最底层-数据持久层开始说起.先看一下数据

PopupWindow弹出产品属性+横向ListView HorizontialList实现产品属性

先来张效果图吧~ 先说下思路吧: 这是个商品详情页,然后商品页面里面使用layoutInflater获取出要弹出框框的view,当然了,这里面参数的加载数据也就写在这个popwindow里面啦. 开始贴代码了 商品弹出框布局:(下面的购物车和购买偷懒直接设定了宽度) activity_product_attribute.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:an

必须添加对程序集“EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”的引用。

我在使用mvc时候.遇到的一个问题...  必须添加对程序集"EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"的引用. 下面是解决方法 关于我们在使用ASP.NET  MVC架构时候...在Model中创建了实体框架EF...如图 我们需要在DAL中使用Entity来操作数据库,执行一些查询等crud操作的时候... 我们在dal中获的entity... 然后我们再创建