无意间看到京东的分类列表做的非常炫, 是那种横排列表的形式,本来是想截图上来的,但是公司没找到数据线。。。。好吧,今天我们也来实现个这种效果。 这次我选择的ListView,但是ListView默认是横向铺满屏的,怎么做到并排呢? 重写!!!
虽然京东的没法截图了,但是我自己做的效果可以在模拟器上运行,先来看看效果吧,界面有点丑,没关系,对吧? 我们主要研究的是如何实现这种框架,而不是界面本身。
恩, 界面确实有点丑,算了,还是直接看实现过程吧。
上面说了,这次我们选重写ListView, 那么我们的需求是什么呢? 很简单,重写它默认横向铺满屏的特性,让它可以wrap_content。
public class MyListView extends ListView { public MyListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int height = getMeasuredHeight(); int width = 0; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); if(widthMode == MeasureSpec.EXACTLY) { width = widthSize; }else { if(widthMode == MeasureSpec.AT_MOST) { final int childCount = getChildCount(); for(int i=0;i<childCount;i++) { View view = getChildAt(i); measureChild(view, widthMeasureSpec, heightMeasureSpec); width = Math.max(width, view.getMeasuredWidth()); } } } setMeasuredDimension(width, height); } }
还是很简单的, 只重写了onMeasure()方法, 看看具体的逻辑吧。
13行,首先调用了super.onMeasure()方法,因为这里我们不考虑height,所以让ListView自己去算吧,我们只关心width/
14行,获取了ListView自己测量的结果,我们只需要height,因为width是我们自己需要计算的。
17~18行,不说了,获取父布局给推荐的mode和size。
接下来是一个if...else..., 在我们这个需求里,这个if...else...是多余的,但为了代码规范,还是加上了,有总比没有强吧。
if里,if的条件是widthMode == MeasureSpec.EXACTLY 即为精确值时, 这时候就不需要我们自己去计算了,直接赋值width为父布局给推荐的大小就ok。
看else里,这里又一个if,这个条件widthMode == MeasureSpec.AT_MOST 即当mode为尽量大时,也就是layout_width="wrap_content",好,这里需要我们自己做工作了。
23行,我们获取了所有的子item的个数。
接着24行一个for循环,获取所有的子view。
26行,通过measureChild(view, widthMeasureSpec, heightMeasureSpec);去测量一下该view的宽高。
27行,用width保存所有子view中宽度最大的那个的宽度。
最后的最后,32行,别忘了保存一下测量结果。
ok,重写的ListView搞定了,现在它就有了wrap_content的能力,赶紧来布局中布局它吧。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:weightSum="3" > <org.loader.jdmemu.MyListView android:id="@+id/list1" android:layout_width="0dip" android:layout_weight="1" android:layout_height="wrap_content" android:gravity="center" android:background="@android:color/darker_gray" /> <org.loader.jdmemu.MyListView android:id="@+id/list2" android:layout_width="0dip" android:layout_weight="2" android:layout_height="wrap_content" /> </LinearLayout> </RelativeLayout>
可以看到,这里是两个并排的ListView,占的宽度比为1:2。前期工作都做完了,去Activity中填充数据看看效果吧。
public class MainActivity extends Activity { private static final String[] mMenus = { "常用分类", "服饰内衣", "鞋靴", "手机", "家用电器", "数码", "电脑办公", "个护化妆", "图书" }; private MyListView mListView1; private MyListView mListView2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView1 = (MyListView) findViewById(R.id.list1); mListView2 = (MyListView) findViewById(R.id.list2); mListView1.setAdapter(new ArrayAdapter<String>(this, R.layout.menu, mMenus)); mListView1.setOnItemClickListener(new ItemClick()); } private class ItemClick implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { mListView1.smoothScrollToPositionFromTop(position, 0); String[] items = new String[(position + 1) * 2]; for(int i=0;i<items.length;i++) { items[i] = mMenus[position] + "中的数据:" + i; } mListView2.setAdapter(new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, items)); } } }
恩,偷懒了,不过很好理解,就是为ListView1设置一个adapter,并通过ListView的itemClick动态改变ListView2的adapter。
重点看看
mListView1.smoothScrollToPositionFromTop(position, 0);
这句话的作用就是实现当我们点击该item时,让该ListView滑动,并到在该item到达顶部时停止,实现动画中那个自动滑动的ListView的效果。
如此简单,就可以达到文章刚开始的效果。
等等,突然想到,是不是可以把菜单做成侧滑的效果呢? 嘿嘿,应该还没有人这么干吧。 看来下篇文章可能就是“不一样的侧滑菜单了”。嘿嘿,今天下班回去研究研究。