写在前面
最近手感不错,老想写点轮子。正好周末外卖点得多,就仿一仿“饿了么”好了。先上图吧,这样的订单页面是不是很眼熟:
右边的listview分好组以后,在左边的Tab页建立索引。可以直接导航,是不是很方便。关键在于右边滑动,左边也会跟着滑;而点击左边呢,也能定位右边的项。它们存在这样一种特殊的交互。像这种联动的效果,还有些常见的例子呢,比如知乎采用了常见的tabLayout+viewPager的联动,只不过是上下布局:
再看看点评,它的城市选择页面也有这种联动的影子,只是稍微弱一点。侧边栏可以对listview进行索引,这最早是在微信好友列表里出现的把:
趁着周末,我也撸一个。就拓展性而言,应该可以适配以上所有情况吧。我称其为LinkedLayout,看下效果图:
我把右边按5个一组,可以看到,左边的索引 = 右边/5
特点
- 右边滑动,左边跟着动
- 左边滑动到边界,右边跟着动
- 点击左边tab项,右边滑动定位到相应的group
源码
github 传送门: https://github.com/fashare2015/LinkedScrollDemo
知识点
做之前先罗列一下知识点,或者说我们能从这个demo里收获到什么。
- 面向抽象/接口编程
- 自定义 view
- 代理模式
- UML类图
- 复习 listview && recyclerview 的细节
感觉做完以后收获最大的还是第一点,面向接口编程。事实上,完成功能的时间只占了一半,后边的时间一直在抽象和重构;哎,一步到位太难了,还是老老实实写具体类,再抽取基类把。
构思
UI部分
LinkedLayout
要做的呢是两个相互关联的列表,在左边的作为tab
页,右边的作为content
页。先不考虑交互,我们来打个界面:搞一个叫做LinkedLayout
的类,用来盛放tab
和content
:
public class LinkedLayout extends LinearLayout {
private Context mContext;
private BaseScrollableContainer mTabContainer;
private BaseScrollableContainer mContentContainer;
private SectionIndexer mSectionIndexer; // 代理
...
}
我们让它继承了LinearLayout
,同时持有两个Container
的东东,还有一个上帝对象mContext
,以及一个分组用的SectionIndexer
。
BaseScrollableContainer
先别管这些,主要看两个Container
,从名字上看一个是tab页,一个是content页,嘿嘿。因为它们都能scroll嘛,干脆搞一个BaseScrollableContainer
把。取名为Container
呢,当然是致敬Fragment
啦。我们来定义一下这个类:
初步一想,无非有一个 mContext, 一个 viewGroup, 还有一些 Listener 嘛:
public abstract class BaseScrollableContainer<VG extends ViewGroup> {
protected Context mContext;
public VG mViewGroup;
protected RealOnScrollListener mRealOnScrollListener;
private EventDispatcher mEventDispatcher;
...
}
和我们预想的差不多嘛,mContext
上下文,mViewGroup
基本就是指代我们的两个listview
了吧。当然,我之后可是要做tablayout
+viewpager
的,肯定得依赖抽象,不能直接写listview
啦。余下两个是Listener
,等我们界面搭好,写交互的时候在看把。
看来UML图还是有好处的,继承和依赖关系一目了然。
自定义View && 动态布局
好了到了自定义view地环节了。我们已经有了一个LinkedLayout
,这是我们的activity_main.xml
布局代码:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"