众所周知我们的ListView、GridView都可以增加头部布局;这样在遇到复杂的头部布局就不用增加视图类型了,不管是对于代码的可维护性还是整洁性;在Adapter中写又臭又长的代码,无论是自己看还是留给后来者都是痛苦的,而且你要是把代码都堆在getView中,oh no!我要分分钟切腹自尽…
真的不是我懒,真的;传统方式我相信大家应该都会了,好吧我还是简单的说一下:(ps:这里我主要是增加了两种视图)
在RecyclerView.Adapter中复写getItemViewType方法
/**
* 重写该方法用以区分不同的视图
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
if (position == 0) {
return VIEW_HEADER;
}
return VIEW_ITEM;
}
然后在onBindViewHolder中根据positon给不同的视图填充数据
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (getItemViewType(position) == VIEW_ITEM) {
...
// 设置常规的数据数据
} else if (getItemViewType(position) == VIEW_HEADER) {
...
// 设置底部视图数据
}...
}
当然我们在创建ViewHolder的时候就要根据不同的视图来创建不同的ViewHolder,在onBindViewHolder方法返回的应该是ViewHolder而不是他们的子类,要用到相应的ViewHolder只要强制转换就好了,当然这也没什么好说的;以上方式可以应对列表中存在多种视图的情况,可以说是万精油;
到底是如何添加头部布局呢?好了我也不卖关子了;详情请看以下地址:
快速添加header https://github.com/blipinsk/RecyclerViewHeader
相信此时大家内心的OS是这样的“我去,早说是开源项目啊;”,哈哈,我要早说你们就不看了,开玩笑了啦我可没这么坏:);
看了RecyclerViewHeader的Usage相信大家也应该会用了,好了我们直接码代码了;
首先看一下项目依赖:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:cardview-v7:23.1.0'
compile 'com.bartoszlipinski.recyclerviewheader:library:1.2.0'
compile 'com.jakewharton:butterknife:7.0.1'
}
activity_main.xml
<LinearLayout 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">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top" />
<com.bartoszlipinski.recyclerviewheader.RecyclerViewHeader
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_gravity="center_horizontal|top"
android:background="@android:color/holo_green_light">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="header" />
com.bartoszlipinski.recyclerviewheader.RecyclerViewHeader>
FrameLayout>
LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Bind(R.id.header)
RecyclerViewHeader mHeader;
@Bind(R.id.recycler)
RecyclerView mRecyclerView;
MyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mAdapter = new MyAdapter(this,getList());
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter);
// 将顶部视图与RecyclerView关联即可
mHeader.attachTo(mRecyclerView, true);
}
private List getList() {
List list = new ArrayList<>();
for (int i = 0;i < 15;i++) {
list.add("Item" + i);
}
return list;
}
}
上面的视图我都是用ButterKnife自动注入的;
MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ItemViewHolder> {
private Context mContext;
private LayoutInflater mInflater;
private List mList;
public MyAdapter(Context context,List list) {
mContext = context;
mList = list;
mInflater = LayoutInflater.from(mContext);
}
@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ItemViewHolder holder = new ItemViewHolder(mInflater.inflate(R.layout.view_item,parent,false));
return holder;
}
@Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
holder.mTextView.setText(getItem(position));
}
private String getItem(int position) {
return mList.get(position);
}
@Override
public int getItemCount() {
return mList.size();
}
class ItemViewHolder extends RecyclerView.ViewHolder {
@Bind(R.id.textView)
TextView mTextView;
public ItemViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this,itemView);
}
}
}
3.运行效果图如下
如过到这里就结束的话肯定很多人会骂我,这不是把人家开源项目的例子重复了一遍吗,值得费这么多口舌吗;那么请耐心的你接着往下看了;
下拉组件也是有很多的开源组件,这里我们就用Google自带的下拉组件好了;整合到视图中,下载布局改为如下方式:
activity_main.xml
<LinearLayout 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">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top" />
<com.bartoszlipinski.recyclerviewheader.RecyclerViewHeader
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_gravity="center_horizontal|top"
android:background="@android:color/holo_green_light">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="header" />
com.bartoszlipinski.recyclerviewheader.RecyclerViewHeader>
FrameLayout>
android.support.v4.widget.SwipeRefreshLayout>
LinearLayout>
MainActivity.java增加如下代码
@Bind(R.id.swipeRefreshLayout)
SwipeRefreshLayout mRefreshLayout;
...
mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mRefreshLayout.postDelayed(new Runnable() {
@Override
public void run() {
mRefreshLayout.setRefreshing(false);
}
},2000);
}
});
由上图不难看出当我们加入下拉刷新组件时,在滑动到下面之后不能不能上滑到顶部了;原因肯定是header的触摸事件和刷新组件的冲突了;
我们知道SwipeRefreshLayout可以设置enable属性使得该组件是否可用;有了这一点我们就可根据是否滑动到顶部来做判断,并设置SwipeRefreshLayout可用性;
可以庆幸的是这个开源代码只有一个类并且没有布局文件,这样的话我们直接拷贝这个java类到我们项目;
RecyclerViewHeader.java增加如下接口
public interface OnScrollTopListener {
void onTop(boolean isTop);
}
public OnScrollTopListener mOnScrollTopListener;
public void setOnScrollTopListener(OnScrollTopListener onScrollTopListener) {
mOnScrollTopListener = onScrollTopListener;
}
我们在setupHeader方法中可以设置是否滚动到顶部的回调
@SuppressLint("NewApi")
private void setupHeader(final RecyclerView recycler) {
recycler.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mCurrentScroll += dy;
RecyclerViewHeader.this.setTranslationY(-mCurrentScroll);
// 增加时候滚动的顶部回调
if (mOnScrollTopListener != null) {
if (mCurrentScroll > 0) {
mOnScrollTopListener.onTop(false);
} else {
mOnScrollTopListener.onTop(true);
}
}
}
});
...
}
MainActivity.java中增加回调接口
mHeader.setOnScrollTopListener(new RecyclerViewHeader.OnScrollTopListener() {
@Override
public void onTop(boolean isTop) {
mRefreshLayout.setEnabled(isTop);
}
});
记得把activity_main.xml中的RecyclerViewHeader替换成我们本地的类路经;图我就不截了。。。
为什么会大费周章介绍一个顶部布局,可能也是因为自己贱吧,在项目中想尝试新的东西;可是纯列表布局真的是很少,不是要在头部加个图片就是加个按钮;这样就会导致你要在Adapter中增加一种视图类型,但是Adapter中为一个顶部增加布局是很繁琐的,而且当顶部很复杂的时候;于是就看到了Github上有这种方案,有简单又方便何不拿来用呢;
注意:该项目中还是存在一些bug就比如我文中提到的那个,需要自行修改;如果引入项目中测试没有问题方可用,如果有能力可以自行修改源码(毕竟也就一个类吗,也是一个学习的机会);如果实在遇到了问题,可以看看Github中的issuses,还是解决不了换用ListView或者使用传统方式;
RecyclerView真的要替代ListView了吗?我会在下一篇中文章中说说这段时间使用的感受;