Skip to content

Commit

Permalink
给出了viewpager懒加载和正常加载的例子,对databinding做的viewdata进行了自动绑定的。
Browse files Browse the repository at this point in the history
  • Loading branch information
Kale committed Jan 29, 2016
1 parent a28d500 commit 35e226c
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 119 deletions.
125 changes: 76 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,55 @@

[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-CommonAdapter-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/1861)

通过封装BaseAdapter和RecyclerView.Adapter得到的通用的,简易的Adapter对象。
通过封装BaseAdapter和RecyclerView.Adapter得到的通用、简易的Adapter对象。

### 添加依赖

### 添加依赖
1.在项目外层的build.gradle中添加JitPack仓库

```
```
repositories {
maven {
url "https://jitpack.io"
}
}
```
```

2.在用到的项目中添加依赖

```
```
dependencies {
compile 'com.github.tianzhijiexian:CommonAdapter:1.1.5'
}
```
### 示例

### 已解决的问题

- [x] 提升item的独立性,完美支持item被多处复用
- [x] item会根据type来做自动复用
- [x] 支持多种类型的item
- [x] 仅仅会在item创建完毕后调用一次配置item的操作,不会避免重复建立监听器
- [x] 一个item仅会触发一次绑定视图的操作
- [x] ​支持dataBinding和其他第三方注入框架
- [x] 提供了getView()方法来代替findViewById
- [x] 支持通过item的构造方法来传入Activity对象
- [x] 支持通过item的构造方法来传入item中事件的回调
- [x] 提供了getConvertedData(data, type)方法来对item传入的数据做转换,方便拆包和提升item的复用性
- [x] 支持viewpager的正常加载模式和懒加载
- [x] 支持快速将listview的适配器切换为recyclerView的适配器
- [ ] 支持recyclerView的添加头部和底部(下个版本支持)

### 示例

![](./demo/demo.png)

### ListView+GridView的通用适配器——CommonAdapter
----

**1. Adapter中的Item实现`AdapterItem`这个接口**
### 零、重要接口

接口的源码如下
adapter的item必须实现此接口,接口源码如下

```java
/**
* Adapter的所有item必须实现的接口.<br>
*
* 通过{@link #getLayoutResId()}初始化view;<br>
* 在{@link #bindViews(View)}中就初始化item的内部视图<br>
* 在{@link #handleData(Object, int)}中处理每一行的数据<p>
*
* @author Jack Tony
* @date 2015/5/15
*/
```java
public interface AdapterItem<T> {

/**
Expand Down Expand Up @@ -69,11 +78,11 @@ public interface AdapterItem<T> {
void handleData(T model, int position);

}
```
```

例子:

```java
```java
public class TextItem implements AdapterItem<DemoModel> {

@Override
Expand All @@ -95,61 +104,79 @@ public class TextItem implements AdapterItem<DemoModel> {
public void handleData(DemoModel model, int position) {
textView.setText(model.content);
}

}
```
```

**2. 通过继承`CommonAdapter`来实现适配器**
现在所需要做的只剩下继承CommonAdapter实现自己的适配器了,下面是一个简单的例子:
### 一、ListView+GridView的通用适配器——CommonAdapter

只需继承CommonAdapter便可实现适配器:
```java
listView.setAdapter(new CommonAdapter<DemoModel>(data) {
@Override
public AdapterItem<DemoModel> createItem(Object type) {
return new TextItem();
}
});
```

### RecyclerView的通用适配器——CommonRcvAdapter
1. Adapter中的每个Item需要实现`AdapterItem`这个接口(同上)
2. 通过继承`CommonRcvAdapter`来实现适配器
### 二、RecyclerView的通用适配器——CommonRcvAdapter
通过继承`CommonRcvAdapter`来实现适配器:

```java
mAdapter = new CommonRcvAdapter<DemoModel>(data) {
public AdapterItem createItem(Object type) {
return new TextItem();
}
};
```

### 三、ViewPager的通用适配器——CommonPagerAdapter
通过继承`CommonPagerAdapter`来实现适配器:

```java
viewPager.setAdapter(new CommonPagerAdapter<DemoModel>() {
public AdapterItem createItem(Object type) {
return new TextItem();
}
});
```


### 设计思路
其实现在的效果和原本的adapter差不多,只是做了点小的重构,这种重构最终保持了和原本一样的可扩展性。下面我来分析下具体的细节:
### 设计思路

**1. Adapter**
因为adapter原始的代码很多,所以如果你把adapter作为activity的内部类的话很别扭,而且如果adapter中如果有多个类型的Item,你就必须在getView()中写很多if-else语句,而且里面都是一些设置view的方法,很乱,你要更换Item的话还需要去删减代码,而现在我让adapter的代码量减少到一个方法,如果你需要更新item或者添加一个新的item你直接在initItem中返回即可,实现了可插拔化。最关键的是item现在作为一个独立的对象,内部view的设置完全可以和adapter独立出来。

因为adapter原始的代码很多,所以如果你把adapter作为activity的内部类的话很别扭,而且如果adapter中如果有多个类型的Item,你就必须在getView()中写很多if-else语句,而且里面都是一些设置view的方法,很乱。你要更换Item的话还需要去删减代码,而现在我让adapter的代码量减少到一个方法,如果你需要更新item或者添加一个新的item你直接在initItem中返回即可,实现了可插拔化。最关键的是item现在作为一个独立的对象,item内部的设置完全可以和adapter独立出来。

**2. AdapterItem**
和原来方式最为不同的就是我把adapter的item作为了一个实体,这种方式借鉴了RecyclerView的ViewHolder的设计。把Item作为实体的好处有很多,就不细说了。

**3. One more thing**
如果你是一个倾向于MVP的开发者,你完全可以把原本项目中独立的adapter变成activity的内部类,这样做增加了adapter和activity的聚合性,同时减少了项目中的众多adapter类。这样的坏处是什么呢?activity现在和adapter的聚合度高了,而现在adapter中仅仅有view,这样activity和view的聚合度也会很高。如果你认为activity是一个controler,那么请千万不要用我的做法,因为这样会让你的项目层次出现混乱。但如果你认为activity就是一个view管理对象,逻辑是写在presenter中的,那么你可以放心的用这种方式。欢迎大家来继续讨论。
和原来方式最为不同的就是我把adapter的item作为了一个实体,这种方式借鉴了RecyclerView的ViewHolder的设计。把Item作为实体的好处有很多,比如复用啊,封装啊,其余的就不细说了。

**3. 分层**

在使用过程中,我发现如果adapter放在view层,那就会影响view层的独立性。因为adapter中有很多数据处理,比如通过type选择item,数据的拆包、转换等操作。于是我还是推荐把adapter放在mvp的p层,或者是mvvm的vm层。通过在实际的项目中使用来看,放在vm或p层的效果较好,view的复用也比较好做。


## 开发者

![](https://avatars3.githubusercontent.com/u/9552155?v=3&s=460)

Jack Tony: <developer_kale@foxmail.com>


## License

Copyright 2015 Jack Tony
```
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
Copyright 2015 Jack Tony
http://www.apache.org/licenses/LICENSE-2.0
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
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.
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.
```
48 changes: 25 additions & 23 deletions adapter/src/main/java/kale/adapter/BasePagerAdapter.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package kale.adapter;

import android.support.annotation.NonNull;
import android.support.v4.util.ArrayMap;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
Expand All @@ -19,11 +20,9 @@
*/
public abstract class BasePagerAdapter<T> extends PagerAdapter {

public static final String TAG = "BasePagerAdapter";

private int mChildCount = 0;

protected T currentItem;
protected T currentItem = null;

/**
* 这的cache的最大大小是:type * pageSize
Expand All @@ -39,16 +38,17 @@ public BasePagerAdapter() {
*/
@Override
public boolean isViewFromObject(View view, Object obj) {
return view == getViewFromItem((T) obj);
return view == getViewFromItem((T) obj, 0);
}

@Override
public T instantiateItem(ViewGroup container, int position) {
T item = mCache.getItem(getItemType(position));
T item = mCache.getItem(getItemType(position)); // get item from type
if (item == null) {
item = createItem((ViewPager) container, position);
}
View view = getWillBeAddedView(item, position);
// 通过item得到将要被add到viewpager中的view
View view = getViewFromItem(item, position);
if (view.getParent() != null) {
((ViewGroup) view.getParent()).removeView(view);
}
Expand All @@ -69,7 +69,10 @@ public void setPrimaryItem(ViewGroup container, int position, Object object) {
public void destroyItem(ViewGroup container, int position, Object object) {
T item = (T) object;
Object type = getItemType(position);
container.removeView(getWillBeDestroyedView(item, position));

// 缓存的大小不够时,会移出最早的item。
// 现在通过item拿到其中的view,然后remove掉
container.removeView(getViewFromItem(item, position));
mCache.putItem(type, item);
}

Expand Down Expand Up @@ -101,25 +104,25 @@ protected PagerCache<T> getCache() {
return mCache;
}

/**
* @return obj中的view对象
*/
protected abstract View getViewFromItem(T item);

/**
* 得到初始化后的item中的view
*/
protected abstract View getWillBeAddedView(T item, int position);
///////////////////////////////////////////////////////////////////////////
// 交给子类的实现
///////////////////////////////////////////////////////////////////////////

/**
* 当{@link ViewPager#getOffscreenPageLimit()}缓存的大小不够时,会移出最早显示的item
* 这里要实现一个从item拿到view的规则
*
* @return 被移除的item中view的对象(如果item是view那么直接返回即可)
* @param item 包含view的item对象
* @param position item所处的位置
* @return item中的view对象
*/
protected abstract View getWillBeDestroyedView(T item, int position);
protected abstract
@NonNull
View getViewFromItem(T item, int position);

/**
* 当缓存中无法得到所需item时才会调用,这返回需要放入容器的view。
* 当缓存中无法得到所需item时才会调用
*
* @return 需要放入容器的view
*/
protected abstract T createItem(ViewPager viewPager, int position);

Expand All @@ -137,7 +140,7 @@ public PagerCache() {

/**
* @param type item type
* @return
* @return cache中的item,如果拿不到就返回null
*/
public T getItem(Object type) {
Queue<T> queue;
Expand All @@ -149,8 +152,7 @@ public T getItem(Object type) {
}

/**
* @param type item type
* @param item item
* @param type item's type
*/
public void putItem(Object type, T item) {
Queue<T> queue;
Expand Down
37 changes: 25 additions & 12 deletions adapter/src/main/java/kale/adapter/CommonPagerAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,48 @@ public abstract class CommonPagerAdapter<T> extends BasePagerAdapter<View> imple

private List<T> mDataList;

LayoutInflater mInflater;
private LayoutInflater mInflater;

private boolean mIsLazy = false;

public CommonPagerAdapter(@Nullable List<T> data) {
this(data, false);
}

public CommonPagerAdapter(@Nullable List<T> data, boolean isLazy) {
if (data == null) {
data = new ArrayList<>();
}
mDataList = data;
mIsLazy = isLazy;
}

@Override
public int getCount() {
return mDataList.size();
}

@NonNull
@Override
protected View getViewFromItem(View item) {
return item;
}

@Override
protected View getWillBeAddedView(View item, int position) {
protected View getViewFromItem(View item, int pos) {
return item;
}

@Override
protected View getWillBeDestroyedView(View item, int position) {
return item;
public View instantiateItem(ViewGroup container, int position) {
View view = super.instantiateItem(container, position);
if (!mIsLazy) {
AdapterItem item = (AdapterItem) view.getTag(R.id.tag_item);
// 如果不是懒加载,那么在初始化时就做数据的处理
item.handleData(getConvertedData(mDataList.get(position), getItemType(position)), position);
}
return view;
}

@Override
public void setPrimaryItem(ViewGroup container, int position, @NonNull Object object) {
// 这里应该放置数据更新的操作
if (object != currentItem) {
if (mIsLazy && object != currentItem) {
// 如果是懒加载,那么这里应该放置数据更新的操作
AdapterItem item = (AdapterItem) ((View) object).getTag(R.id.tag_item);
item.handleData(getConvertedData(mDataList.get(position), getItemType(position)), position);
}
Expand All @@ -67,12 +76,16 @@ protected View createItem(ViewPager viewPager, int position) {
}
AdapterItem item = createItem(getItemType(position));
View view = mInflater.inflate(item.getLayoutResId(), null);
view.setTag(R.id.tag_item, item); // 万一你要用到这个item可以通过这个tag拿到
view.setTag(R.id.tag_item, item);
item.bindViews(view);
item.setViews();
return view;
}

public void setIsLazy(boolean isLazy) {
mIsLazy = isLazy;
}

@NonNull
@Override
public Object getConvertedData(T data, Object type) {
Expand Down
Loading

0 comments on commit 35e226c

Please sign in to comment.