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

最近工作不是很忙,就随便按照自己的意愿胡乱整了个小demo,简单的实现横向的ListView;   功能随着版本的增加而完善,包括左右滚动、itemClick等等listView的基本功能;

虽然说demo超级简单,但是真正的写起来倒是废了不小的功夫。废话少说,开始吧。

ListView肯定需要一个Adapter对象,这个小demo的核心原理就是循环遍历Adapter里面的子View,并调用子view的layout方法使其按计算的位置横向的排列在ViewGroup上面,就构成了一个横向的一排View;很简单吧,看到这如果觉得对你们没什么帮助的话,浏览器上本页面上右边的那个x,看到没,点击一下就ok了。

layout(int left,int top,int right,int bottom)方法是View的一个方法,该方法的四个参数决定了childView在parentView中的位置,其中(left,top)决定了childView在parentView中左上角的坐标,而(right,bottom)则决定了childView在parentView 右下角的位置,其图示如下:简单的横向ListView实现(version 1.0)_第1张图片

其中right和bottom一般需要经过如下运算:right = left + view.getMeasureWidth(),bottom = top + view.getMeasureHeight();所以简单的实现横向的ListView还是很容易的;如下图:

简单的横向ListView实现(version 1.0)_第2张图片


如上图,假设item的宽度和高度为width和height,那么在不考虑padding的情况下就有如下的关系:

A.layout(0,0,width,height),

B..layout(width,0,2*width,height)

C.layout(2*width,0,3*width,heigth);

用代码体现出来就是:

	int childLeft = 0;
		for(int i=0;i

当然layout之前需要通过addView把Adapter.getView生成的View添加的viewGroup中去的,代码如下:

for(int i=0;i

所以简单的横向ListView的代码可以出炉了:

public class HListView extends ViewGroup{
	/**存储数据用的Adapter**/
	private ListAdapter listAdapter;

	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;
	}

	/**
	 * 测量每个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;
		}
         //把listView中的子view添加到viewGroup中
		for(int i=0;i

运行一把,看看效果吧

简单的横向ListView实现(version 1.0)_第3张图片

我屮艸芔茻,报错了;错误代码定位到addView上面,追踪源代码addView的方法如下:

public void addView(View child) {
        addView(child, -1);
    }

 public void addView(View child, int index) {
        ......
        addView(child, index, params);
    }

	
	
	 public void addView(View child, int index, LayoutParams params) {
         ....
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }

	private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {

         .......
        if (child.getParent() != null) {
            throw new IllegalStateException("The specified child already has a parent. " +
                    "You must call removeView() on the child's parent first.");
        }

        ........
    }

通过源代码的追踪可以发现,最终addView会调用addViewInner方法,而在此方法中如果chid.getParent()!=null,就会抛出异常。话说回来了,为什么getParent()不会空呢?其实很简单,因为在Adapter的getView(position,convertView,parentView)的第三个参数我们传了this,在inflate方法中逐步追踪会发现(相关可参考 此博文:

//切记,此时root为null,attachToRoot在源码中为root!=null,所以此处为false
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {  
        synchronized (mConstructorArgs) {  
            ......
            View result = root;  //根View ,为null
            try {  
                 ....
                final String name = parser.getName();  //节点名,如果是自定义View的话 就是全限定名
               //该if语句暂不用看
               if (TAG_MERGE.equals(name)) { // 处理标签  
                   ...
                } else {  
                    // Temp is the root view that was found in the xml  
                    //这个是xml文件对应的那个根View,在item.xml文件中就是RelativeLayout  
                    View temp = createViewFromTag(name, attrs);   
  
                    ViewGroup.LayoutParams params = null;  
                    //因为root==null,if条件不成立
                    if (root != null) {  
                        // Create layout params that match root, if supplied  
                        //根据AttributeSet属性获得一个LayoutParams实例,记住调用者为root。  
                        params = root.generateLayoutParams(attrs);   
                        if (!attachToRoot) { //重新设置temp的LayoutParams  
                            // Set the layout params for temp if we are not  
                            // attaching. (If we are, we use addView, below)  
                            temp.setLayoutParams(params);  
                        }  
                    }  
                    // Inflate all children under temp  
                    //遍历temp下所有的子节点,也就是xml文件跟文件中的所有字View 
                    rInflate(parser, temp, attrs);  
                      
                    //把xml文件的根节点以及根节点中的子节点的view都添加到root中,当然此时root等于null的情况下此处是不执行的
                    if (root != null && attachToRoot) {  
                        root.addView(temp, params);  
                    }  
                    //如果根节点为null,就直接返回xml中根节点以及根节点的子节点组成的View
                    if (root == null || !attachToRoot) {  
                        result = temp;  
                    }  
                }  
            }   
            ...  
            return result;  
        }  
    }  

如果(root!=null)会调用root.addView(temp,params)而这个root正是我们传的this,所以在inflate解析xml文件的时候会把convertView 通过调用addView而添加到parentview中,所以getParent()不为null.那这样改起来就简单了,只要把getView(position,convetView,parentView)把parentView传一个null就可以了。

也许会有读者说把HListView方法中addView去掉直接改成如下的代码不也是解决的办法吗?改动的代码如下:

for(int i=0;i

运行一把试试看,结果是手机屏幕上什么都没有,只有HlistView显示的backgroud设置的颜色:

简单的横向ListView实现(version 1.0)_第4张图片

为毛?其实看infate方法的话,你就可以发现如果parentView传的值不为null的话,getView方法返回的这个View就是parentView,通过打印listAdapter.getView(i,null,this) == this,可以看到返回的是true;所以我们本来要的是childView,你返回的是parentView是几个意思,问题查到了,解决这个问题很简单,就是在getView返回的时候,把return convertView,改成 return convertView.findViewById(R.id.item);item为xml配置文件的根节点的id



@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		HolderView holderView = null;
		if(convertView == null ){
			holderView = new HolderView();
			convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item, parent);
			
			holderView.imageView =(ImageView) convertView.findViewById(R.id.imageView);
			holderView.textView = (TextView) convertView.findViewById(R.id.textView);
			
			convertView.setTag(holderView);
		}else{
			holderView = (HolderView) convertView.getTag();
		}

		holderView.imageView.setImageResource((Integer) mList.get(position).get("img"));
		holderView.textView.setText((String) mList.get(position).get("index"));
		
		return convertView.findViewById(R.id.item);
		
	}

最终的运行效果如下:

简单的横向ListView实现(version 1.0)_第5张图片

简单横向listVIew version1.0版就出来了;不过现在试试最最简单功能,看看页面效果都还有些图片没有显示完全,也无法滚动,点击也没有任何响应。至于功能的完善,在version2.0的时候会逐步完善。此处是源代码下载链接,version2.0的实现点击此处

项目源码




你可能感兴趣的:(ListView系列)