主要包括RecyclerView基本用法、插入删除Item、Item点击事件监听
一、RecyclerView的基本用法
1.RecyclerView的介绍
RecyclerView是support-V7报下的一个控件,用来替代ListView、GridView等。下面是官方的解释:
RecyclerView is a more advanced and flexible version of ListView. This widget is a container for large sets of views that can be recycled and scrolled very efficiently. Use the RecyclerView widget when you have lists with elements that change dynamically. 其实也就是说当你需要动态展示一些item的时候建议使用RecyclerView。
2.RecyclerView的使用
- 首先需要导入这个包的引用,需要在app对应的build.gradle文件中导入引用:
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:recyclerview-v7:23.4.0'//这个是RecyclerView的依赖项
}
我用的AndroidStudio一般都是下面这个步骤来添加引用:File→Project Structure左侧选择对应的Moudle,选择右侧Dependences→add→Library Dependece然后找到对应的包选择添加。
- 布局文件activity_normal中使用:
<?xml version="1.0" encoding="utf-8"?>
<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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.zwf.recyclerviewdemo.Activity.NormalActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/insertBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="插入"
android:layout_weight="1"/>
<Button
android:id="@+id/removeBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="移除"
android:layout_weight="1"/>
</LinearLayout>
<!--这里是使用RecyclerView-->
<android.support.v7.widget.RecyclerView
android:id="@+id/id_normal_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
布局文件很简单,上面两个按钮,下面一个RecyclerView。
3.然后在代码中对RecyclerView进行使用,一般需要做以下设置:
public class NormalActivity extends AppCompatActivity implements View.OnClickListener {
private RecyclerView mRecyclerView;
private List<String> mDatas;
private HomeAdapter adapter;
private Button insertBtn,removeBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal);
insertBtn = (Button) findViewById(R.id.insertBtn);
removeBtn = (Button) findViewById(R.id.removeBtn);
insertBtn.setOnClickListener(this);
removeBtn.setOnClickListener(this);
mDatas = this.getIntent().getStringArrayListExtra("data");
//获取RecyclerView
mRecyclerView = (RecyclerView) findViewById(R.id.id_normal_recyclerview);
//为RecyclerView设置布局管理器
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//为RecyclerView设置Adapter
adapter = new HomeAdapter(this, mDatas);
mRecyclerView.setAdapter(adapter);
//为RecyclerView设置分割线(这个可以对DividerItemDecoration进行修改,自定义)
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.HORIZONTAL_LIST));
//动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.insertBtn:
adapter.addData(1);
break;
case R.id.removeBtn:
adapter.removeData(1);
break;
}
}
}
要想使用一个RecyclerView一般需要设置布局管理器、适配器、分割线、动画。
主要说一下布局管理器,一共有三种:LinearLayoutManager(线性布局)、GridLayoutManager(网格布局)、StaggeredGridLayoutManager(瀑布流布局)
然后看一下HomeAdapter的内容:
public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>{
private List<String> mDatas;
private LayoutInflater mInflater;
/**
* Adapter适配器的构造函数,主要用来初始化数据,例如:LayoutInflater、data
* @param mInflaters 这个参数也可以是Context,无非就是在哪获得LayoutInflater
* @param mDatas 这是对应的RecyclerView要显示的数据
*/
public HomeAdapter(Context context, List<String> mDatas){
this.mDatas = mDatas;
mInflater = LayoutInflater.from(context);
}
/**
* 创建一个ViewHolder并返回
* @param parent
* @param viewType 可以根据这个参数创建不同item的ViewHolder
* @return
*/
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
/**
* LayoutInflater.from(MainActivity.this) 获取到父容器的inflater,跟fragment类似
*/
MyViewHolder holder = new MyViewHolder(mInflater.inflate(R.layout.item_normal, parent, false));
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.tv.setText(mDatas.get(position));
}
/**
* 返回item的个数
*/
@Override
public int getItemCount() {
return mDatas.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView tv;
public MyViewHolder(View view){
super(view);
// 这里传进来的view是一个MyViewHolder
tv = (TextView) view.findViewById(R.id.id_num);
}
}
public void addData(int position){
mDatas.add(position, "insert");
notifyItemInserted(position);
}
public void removeData(int position){
mDatas.remove(position);
notifyItemRemoved(position);
}
}
这个Adapter需要继承自RecyclerView.Adapter,并且实现其中的抽象方法
上面还用到了这么一个类DividerItemDecoration,用来画分割线:
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
二、插入删除Item
上面的HomeAdapter中已经实现了插入删除的功能,如下:
public void addData(int position){
mDatas.add(position, "insert");
notifyItemInserted(position);
}
public void removeData(int position){
mDatas.remove(position);
notifyItemRemoved(position);
}
其实主要是对数据进行插入删除操作,然后更新界面。插入用notifyItemInserted()删除用notifyItemRemoved(),这两个方法都需要一个下标参数。
三、Item点击监听
这里其实我是用接口回调实现的,具体会在瀑布流相关的代码中体现。
- 初始化一个接口
- 在Adapter中声明一个接口属性
- onBindViewHolder()方法中为每一个Item添加点击事件或者长按事件,同时调用接口变量中对应的方法(此时可以把点击的下标也传进去)
- 在为RecyclerView设置Adapter的时候,同时给Adapter设置上面声明接口属性,此时就可以实现在Activity或者Fragment中实现对Item点击事件的监听
四、关于瀑布流的使用
这里需要把RecyclerView的布局管理器设置成StaggeredGridLayoutManager,如下:
mRecycler.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL));
//第一个参数是表示显示列(行),第二个参数是滚动方向
public class StaggeredAdapter extends RecyclerView.Adapter<MyViewHolder> {
private List<String> mDatas;
private LayoutInflater mInflaters;
private List<Integer> mHieghts;
private List<Integer> mWidths;
/**
* Adapter适配器的构造函数,主要用来初始化数据,例如:LayoutInflater、data
* @param mInflaters 这个参数也可以是Context,无非就是在哪获得LayoutInflater
* @param mDatas 这是对应的RecyclerView要显示的数据
*/
public StaggeredAdapter(LayoutInflater mInflaters, List<String> mDatas){
this.mInflaters = mInflaters;
this.mDatas = mDatas;
//init heights and width
mHieghts = new ArrayList<>();
for (int i=0; i<mDatas.size(); i++){
mHieghts.add((int) (150+Math.random()*300));
}
mWidths = new ArrayList<>();
for (int i=0; i<mDatas.size();i++){
mWidths.add((int) (320+Math.random()*300));
}
}
/**
* 创建一个ViewHolder并返回
* @param parent
* @param viewType 可以根据这个参数创建不同item的ViewHolder
* @return
*/
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(mInflaters.inflate(R.layout.item_home, parent, false));
return holder;
}
@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
//获取Layout内容
LayoutParams lp = holder.tv.getLayoutParams();
//设置Layout的高度(根据具体位置的具体要求设置不同的高度)
lp.height = mHieghts.get(position);
// lp.width = mWidths.get(position);
holder.tv.setLayoutParams(lp);
holder.tv.setText(mDatas.get(position));
if (mOnItemClickListener != null){
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int pos = holder.getLayoutPosition();
mOnItemClickListener.onItemClick(view, pos);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener(){
@Override
public boolean onLongClick(View view) {
int pos = holder.getLayoutPosition();
mOnItemClickListener.onItemLongClick(holder.itemView, pos);
removeData(pos);
return false;
}
});
}
}
@Override
public int getItemCount() {
return mDatas.size();
}
/**
* add item
* @param position
*/
/**********************add and remove*************************/
public void addData(int position){
mDatas.add(position, "insert");
mHieghts.add(position, (int) (150 + Math.random() * 300));
mWidths.add(position, (int) (320 + Math.random() * 300));
notifyItemInserted(position);
}
public void removeData(int position){
if (mDatas.size() > position)
{
mDatas.remove(position);
}else {
return;
}
mHieghts.remove(position);
mWidths.remove(position);
notifyItemRemoved(position);
}
/**********************add and remove*************************/
/**********************click listener*************************/
public interface OnItemClickListener{
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
private OnItemClickListener mOnItemClickListener;
public void setmOnItemClickListener(OnItemClickListener onItemClickListener){
mOnItemClickListener = onItemClickListener;
}
/**********************click listener*************************/
}
上面的实现是其实是使用的同一个ViewHolder,然后对这个ViewHolder的布局文件高度进行改变,来实现类似瀑布流的效果。
重点看一下onCreateViewHolder(ViewGroup parent, int viewType)这个方法,这个方法有两个参数。其中第二个参数就是View的类型,其实是getItemViewType(int position)的返回值。我们可以重写这个方法, 根据不同的数据,返回不同的值(这个值可以定义枚举类型),然后在穿件ViewHolder的时候就可以直接根据类型来创建了。
然后就是ViewHolder中常用的一个API是:getLayoutPosition(),可以获得Item的下标。
总的来说,关于RecyclerView的基本用法是弄明白了,但是表述的有点乱。这里做个总结,整理一下思路。还有像mRecyclerView.addItemDecoration(); mRecyclerView.setItemAnimator();这两个方法的用法可能在实际用的时候还需要深究。还有点击的时候长按在手指离开的时候也会触发点击事件。
网友评论