AssemblyAdapter是Android上的一个Adapter扩展库,有了它你就不用再写Adapter了。其支持组合式使用多Item、支持添加header和footer并且还自带加载更多功能
Item一处定义处处使用
. 你只需为每一个item layout写一个ItemFactory,然后使用ItemFactory即可便捷的组合式多Item
. 可以使用多个ItemFactory,每个ItemFactory就代表一种itemType支持header和footer
. 使用AssemblyAdapter可以让ExpandableListView、GridView、RecyclerView、ViewPager等也支持header和footer自带加载更多功能
. 自带滑动到列表底部触发加载更多功能,你只需定义一个专门用于加载更多的ItemFactory即可支持常用Adapter
. 支持BaseAdapter、RecyclerView.Adapter、BaseExpandableListAdapter、PagerAdapter、 FragmentPagerAdapter和FragmentStatePagerAdapter,涵盖了Android开发中常用的大部分Adapter无性能损耗
. 没有使用任何反射相关的技术,因此无须担心性能问题
dependencies{
compile 'me.xiaopan:assemblyadapter:lastVersionName'
}
最低兼容API 7
共有6种Adapter:
Adapter | 父类 | 适用于 | 支持功能 |
---|---|---|---|
AssemblyAdapter | BaseAdapter | ListView、GridView、Spinner、Gallery | 多Item、header和footer、加载更多 |
AssemblyRecyclerViewAdapter | RecyclerView.Adapter | RecyclerView | 多Item、header和footer、加载更多 |
AssemblyExpandableAdapter | BaseExpandableListAdapter | ExpandableListView | 多Item、header和footer、加载更多 |
AssemblyPagerAdapter | PagerAdapter | ViewPager + View | 多Item、header和footer |
AssemblyFragmentPagerAdapter | FragmentPagerFragment | ViewPager + Fragment | 多Item、header和footer |
AssemblyFragmentStatePagerAdapter | FragmentStatePagerFragment | ViewPager + Fragment | 多Item、header和footer |
接下来以AssemblyAdapter为例讲解具体的用法,其它Adapter你只需照葫芦画瓢,然后ItemFactory和Item继承各自专属的类即可,详情请参考sample源码
AssemblyAdapter分为三部分:
- Adapter:负责维护数据、itemType以及加载更多的状态
- ItemFactory:负责匹配数据和创建Item
- Item:负责itemView的一切,包括创建itemView、设置数据、设置并处理事件
AssemblyAdapter与其它万能Adapter最根本的不同就是其把item相关的处理全部定义在了一个ItemFactory类里面,在使用的时候只需通过Adapter的addItemFactory(AssemblyItemFactory)方法将ItemFactory加到Adapter中即可。
这样的好处就是真正做到了一处定义处处使用,并且可以方便的在一个页面通过多次调用addItemFactory(AssemblyItemFactory)方法使用多个ItemFactory(每个ItemFactory就代表一种ItemType),这正体现了AssemblyAdapter名字中Assembly所表达的意思
另外由于支持多Item,一个Adapter又只有一个数据列表,所以数据列表的数据类型就得是Object
在使用AssemblyAdapter之前得先创建ItemFactory和Item,如下:
public class UserItemFactory extends AssemblyItemFactory<UserItemFactory.UserItem> {
@Override
public boolean isTarget(Object itemObject) {
return itemObject instanceof User;
}
@Override
public UserListItem createAssemblyItem(ViewGroup parent) {
return new UserListItem(R.layout.list_item_user, parent);
}
public class UserItem extends AssemblyItem<User> {
private ImageView headImageView;
private TextView nameTextView;
private TextView sexTextView;
private TextView ageTextView;
private TextView jobTextView;
public UserListItem(int itemLayoutId, ViewGroup parent) {
super(itemLayoutId, parent);
}
@Override
protected void onFindViews(View itemView) {
headImageView = (ImageView) findViewById(R.id.image_userListItem_head);
nameTextView = (TextView) findViewById(R.id.text_userListItem_name);
sexTextView = (TextView) findViewById(R.id.text_userListItem_sex);
ageTextView = (TextView) findViewById(R.id.text_userListItem_age);
jobTextView = (TextView) findViewById(R.id.text_userListItem_job);
}
@Override
protected void onConfigViews(Context context) {
getItemView().setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Toast.makeText(v.getConext(), "第" + (getPosition() + 1) + "条数据", Toast.LENGTH_LONG).show();
}
});
}
@Override
protected void onSetData(int position, User user) {
headImageView.setImageResource(user.headResId);
nameTextView.setText(user.name);
sexTextView.setText(user.sex);
ageTextView.setText(user.age);
jobTextView.setText(user.job);
}
}
}
详解:
ItemFactory的泛型
是为了限定其createAssemblyItem(ViewGroup)方法返回的类型- ItemFactory的
isTarget()
方法是用来匹配数据列表中的数据的,Adapter从数据列表中拿到当前位置的数据后会依次调用其所有的ItemFactory的isTarget(Object)方法,谁返回true就用谁处理当前这条数据- ItemFactory的
createAssemblyItem(ViewGroup)
方法用来创建Item,返回的类型必须跟你在ItemFactory上配置的泛型一样Item的泛型
是用来指定对应的数据类型,会在onSetData和getData()方法中用到- Item的
onFindViews(View)
和onConfigViews(Context)
方法分别用来初始化View和配置View,只会在创建Item的时候调用一次
,另外在onFindViews方法中你可以直接使用findViewById(int)
法获取View- Item的
onSetData()
方法是用来设置数据的,在每次getView()的时候都会调用
- 你可以通过Item的
getPosition()
和getData()
方法可直接获取当前所对应的位置和数据,因此你在处理click的时候不再需要通过setTag()来绑定位置和数据了,直接获取即可- 你还可以通过过Item的
getItemView()
方法获取当前的itemView
另外你还可以通过ContentSetter简化你的代码,上述例子使用ContentSetter简化后如下:
public class UserItemFactory extends AssemblyItemFactory<UserListItemFactory.UserListItem> {
@Override
public boolean isTarget(Object itemObject) {
return itemObject instanceof User;
}
@Override
public UserListItem createAssemblyItem(ViewGroup parent) {
return new UserListItem(R.layout.list_item_user, parent);
}
public class UserItem extends AssemblyItem<User> {
public UserListItem(int itemLayoutId, ViewGroup parent) {
super(itemLayoutId, parent);
}
@Override
protected void onFindViews(View itemView) { }
@Override
protected void onConfigViews(Context context) {
getItemView().setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Toast.makeText(v.getConext(), "第" + (getPosition() + 1) + "条数据", Toast.LENGTH_LONG).show();
}
});
}
@Override
protected void onSetData(int position, User user) {
getSetter()
.setImageResource(R.id.image_userListItem_head, user.headResId)
.setText(R.id.text_userListItem_name, user.name)
.setText(R.id.text_userListItem_sex, user.sex)
.setText(R.id.text_userListItem_age, user.age)
.setText(R.id.text_userListItem_job, user.job);
}
}
}
首先你要准备好数据并new一个AssemblyAdapter,然后通过Adapter的addItemFactory(AssemblyItemFactory)
方法添加ItemFactory即可,如下:
ListView listView = ...;
List<Object> dataList = new ArrayList<Object>;
dataList.add(new User("隔离老王"));
dataList.add(new User("隔壁老李"));
AssemblyAdapter adapter = new AssemblyAdapter(dataList);
adapter.addItemFactory(new UserItemFactory());
listView.setAdapter(adapter);
你还可以一次使用多个ItemFactory,如下:
ListView listView = ...;
List<Object> dataList = new ArrayList<Object>;
dataList.add(new User("隔离老王"));
dataList.add(new Game("英雄联盟"));
dataList.add(new User("隔壁老李"));
dataList.add(new Game("守望先锋"));
AssemblyAdapter adapter = new AssemblyAdapter(dataList);
adapter.addItemFactory(new UserItemFactory());
adapter.addItemFactory(new GameItemFactory());
listView.setAdapter(adapter);
AssemblyAdapter支持添加header和footer,可以方便的固定显示内容在列表的顶部或尾部。 Adapter支持header和footer的重要性在于可以让GridView、RecyclerView等也支持header和footer
首先定义好一个用于header或footer的ItemFactory
然后调用addHeaderItem(AssemblyItemFactory, Object)
或addFooterItem(AssemblyItemFactory, Object)
方法添加即可,如下:
AssemblyAdapter adapter = new AssemblyAdapter(objects);
adapter.addHeaderItem(new HeaderItemFactory(), "我是小额头呀!");
...
adapter.addFooterItem(new HeaderItemFactory(), "我是小尾巴呀!");
addHeaderItem(AssemblyItemFactory, Object)和addFooterItem(AssemblyItemFactory, Object)的第二个参数是Item需要的数据,直接传进去即可
当你需要删除header或footer的时候,只需调用removeHeaderItem(FixedItemInfo)
或removeFooterItem(FixedItemInfo)
删除即可,删除时需要用到addHeaderItem(AssemblyItemFactory, Object)或addFooterItem(AssemblyItemFactory, Object)返回的FixedItemInfo,如下:
AssemblyAdapter adapter = new AssemblyAdapter(objects);
FixedItemInfo userFixedItemInfo = adapter.addHeaderItem(new HeaderItemFactory(), "我是小额头呀!");
...
adapter.removeHeaderItem(userFixedItemInfo);
由于有了header和footer那么Item.getPosition()方法得到的位置就是Item在Adapter中的位置,要想得到其在所属部分的真实位置可通过Adapter的getPositionInPart(int)
获取
首先你需要创建一个继承自AssemblyLoadMoreItemFactory的ItemFactory,AssemblyLoadMoreItemFactory已经将加载更多相关逻辑部分的代码写好了,你只需关心界面即可,如下:
public class LoadMoreItemFactory extends AssemblyLoadMoreItemFactory {
public LoadMoreListItemFactory(OnLoadMoreListener eventListener) {
super(eventListener);
}
@Override
public AssemblyLoadMoreItem createAssemblyItem(ViewGroup parent) {
return new LoadMoreListItem(R.layout.list_item_load_more, parent);
}
public class LoadMoreItem extends AssemblyLoadMoreItem {
private View loadingView;
private View errorView;
private View endView;
public LoadMoreListItem(int itemLayoutId, ViewGroup parent) {
super(itemLayoutId, parent);
}
@Override
protected void onFindViews(View itemView) {
loadingView = findViewById(R.id.text_loadMoreListItem_loading);
errorView = findViewById(R.id.text_loadMoreListItem_error);
endView = findViewById(R.id.text_loadMoreListItem_end);
}
@Override
public View getErrorRetryView() {
return errorView;
}
@Override
public void showLoading() {
loadingView.setVisibility(View.VISIBLE);
errorView.setVisibility(View.INVISIBLE);
endView.setVisibility(View.INVISIBLE);
}
@Override
public void showErrorRetry() {
loadingView.setVisibility(View.INVISIBLE);
errorView.setVisibility(View.VISIBLE);
endView.setVisibility(View.INVISIBLE);
}
@Override
public void showEnd() {
loadingView.setVisibility(View.INVISIBLE);
errorView.setVisibility(View.INVISIBLE);
endView.setVisibility(View.VISIBLE);
}
}
}
然后调用Adapter的setLoadMoreItem(AssemblyLoadMoreItemFactory)
方法设置加载更多ItemFactory即可,如下:
AssemblyAdapter adapter = ...;
adapter.setLoadMoreItem(new LoadMoreItemFactory(new OnLoadMoreListener(){
@Override
public void onLoadMore(AssemblyAdapter adapter) {
// 访问网络加载数据
...
boolean loadSuccess = ...;
if (loadSuccess) {
// 加载成功时判断是否已经全部加载完毕,然后调用Adapter的setLoadMoreEnd(boolean)方法设置加载更多是否结束
boolean loadMoreEnd = ...;
adapter.setLoadMoreEnd(loadMoreEnd);
} else {
// 加载失败时调用Adapter的loadMoreFailed()方法显示加载失败提示,用户点击失败提示则会重新触发加载更多
adapter.loadMoreFailed();
}
}
}));
你还可以通过setDisableLoadMore(boolean)
方法替代setLoadMoreEnd(boolean)来控制是否禁用加载更多功能,两者的区别在于setLoadMoreEnd(boolean)为true时会在列表尾部显示end提示,而setDisableLoadMore(boolean)则是完全不显示加载更多尾巴
Copyright (C) 2015 Peng fei Pan <sky@xiaopan.me>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.