最近工作不是很忙,就随便按照自己的意愿胡乱整了个小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 右下角的位置,其图示如下:
其中right和bottom一般需要经过如下运算:right = left + view.getMeasureWidth(),bottom = top + view.getMeasureHeight();所以简单的实现横向的ListView还是很容易的;如下图:
如上图,假设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
for(int i=0;i
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
我屮艸芔茻,报错了;错误代码定位到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.");
}
........
}
//切记,此时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;
}
}
也许会有读者说把HListView方法中addView去掉直接改成如下的代码不也是解决的办法吗?改动的代码如下:
for(int i=0;i
为毛?其实看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 version1.0版就出来了;不过现在试试最最简单功能,看看页面效果都还有些图片没有显示完全,也无法滚动,点击也没有任何响应。至于功能的完善,在version2.0的时候会逐步完善。此处是源代码下载链接,version2.0的实现点击此处
项目源码