本篇blog参照网上大神demo实现,效果一样http://frank-zhu.github.io/android/2015/02/26/android-recyclerview-part-3/。
关键点:
- CardView的使用,CardView继承的是FrameLayout,所以摆放内部控件的时候需要注意一下;
- RecyclerView的简单使用,关键理解RecyclerView实现的思想,对比ListView学习;
- array配置文件读取
一、首先,引入依赖包,重新编译
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.android.support:recyclerview-v7:21.0.3'
compile 'com.android.support:cardview-v7:21.0.3'
CardView
先来简单介绍下CardView,其实我们可以在很多App中已经看到过这样的效果,就是把list的一个item做成卡片样式的,之前很多人通过自定义view可以实现卡片的效果,不过现在有了现成的,Google亲生。CardView继承的是FrameLayout,所以摆放内部控件的时候需要注意一下啦。
简单总结下CardView的参数:
<resources>
<declare-styleable name="CardView">
<!-- Background color for CardView. -->
<!-- 背景色 -->
<attr name="cardBackgroundColor" format="color" />
<!-- Corner radius for CardView. -->
<!-- 边缘弧度数 -->
<attr name="cardCornerRadius" format="dimension" />
<!-- Elevation for CardView. -->
<!-- 高度 -->
<attr name="cardElevation" format="dimension" />
<!-- Maximum Elevation for CardView. -->
<!-- 最大高度 -->
<attr name="cardMaxElevation" format="dimension" />
<!-- Add padding in API v21+ as well to have the same measurements with previous versions. -->
<!-- 设置内边距,v21+的版本和之前的版本仍旧具有一样的计算方式 -->
<attr name="cardUseCompatPadding" format="boolean" />
<!-- Add padding to CardView on v20 and before to prevent intersections between the Card content and rounded corners. -->
<!-- 在v20和之前的版本中添加内边距,这个属性是为了防止卡片内容和边角的重叠 -->
<attr name="cardPreventCornerOverlap" format="boolean" />
<!-- 下面是卡片边界距离内部的距离-->
<!-- Inner padding between the edges of the Card and children of the CardView. -->
<attr name="contentPadding" format="dimension" />
<!-- Inner padding between the left edge of the Card and children of the CardView. -->
<attr name="contentPaddingLeft" format="dimension" />
<!-- Inner padding between the right edge of the Card and children of the CardView. -->
<attr name="contentPaddingRight" format="dimension" />
<!-- Inner padding between the top edge of the Card and children of the CardView. -->
<attr name="contentPaddingTop" format="dimension" />
<!-- Inner padding between the bottom edge of the Card and children of the CardView. -->
<attr name="contentPaddingBottom" format="dimension" />
</declare-styleable>
</resources>
RecyclerView
先来说说RecycleView的优点就是,他可以通过设置LayoutManager来快速实现listview、gridview、瀑布流的效果,而且还可以设置横向和纵向显示,添加动画效果也非常简单(自带了ItemAnimation,可以设置加载和移除时的动画,方便做出各种动态浏览的效果),也是官方推荐使用的。
关键是RecyclerView将view的操作交给了Adapter,而不在自身处理view。
OK,简单了解了CardView、RecyclerView,接下来实现我们的效果!
简单效果的实现:
很简单的List列表样式,外层RecycleView,item用CardView实现。
activity_main布局文件:
<RelativeLayout 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"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/m_rv"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
关键是我们的NormalRecyclerViewAdapter
package com.wj.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.wj.R;
/** * Created by ${wj} , * on 2015/7/15 0015. */
public class NormalRecyclerViewAdapter extends RecyclerView.Adapter<NormalRecyclerViewAdapter.NormalTextViewHolder>{
/** * 视图加载器 */
private LayoutInflater mInflater;
/** * 内容提供者 */
private Context mContext;
/** * Item展示的内容,这里只是一个简单的text. */
private String[] mTitles;
/** * 构造器,正常应该传入我们Item所要展示的对象List,这里只是简单实现了文字展示 * @param context */
public NormalRecyclerViewAdapter(Context context) {
this.mContext = context;
mInflater=LayoutInflater.from(mContext);
mTitles=mContext.getResources().getStringArray(R.array.titles);
}
/** * 创建RecyclerView的VIewHolder * @param viewGroup * @param i * @return */
@Override
public NormalTextViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
//加载布局文件
View view=mInflater.inflate(R.layout.item_text,viewGroup,false);
//创建布局文件对应的ViewHolder
return new NormalTextViewHolder(view);
}
/** * 实例化viewHolder中各控件内容 * @param normalTextViewHolder * @param i */
@Override
public void onBindViewHolder(NormalTextViewHolder normalTextViewHolder, int i) {
normalTextViewHolder.itemText.setText(mTitles[i]);
}
/** * 返回Item个数 * @return */
@Override
public int getItemCount() {
return mTitles==null ? 0 : mTitles.length;
}
/** * RecyclerView关键类,ViewHolder.这个就是RecyclerView优于ListView的关键点之一,封装了ViewHolder,可以更好的实现view的重载 * 这里需要做的操作 * 1.声明我们的Item里面的所有控件 * 2.在构造函数中初始化我们声明的控件 * 3.可以设置Item的点击事件,不再像ListView一样,交给外层来处理 */
public class NormalTextViewHolder extends RecyclerView.ViewHolder{
private TextView itemText;
public NormalTextViewHolder(View itemView) {
super(itemView);
itemText= (TextView) itemView.findViewById(R.id.text_view);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext,mTitles[getPosition()],Toast.LENGTH_SHORT).show();
}
});
}
}
}
为了方便理解,我加了详细的注释
MainActivity中很简单
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView= (RecyclerView) findViewById(R.id.m_rv);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));
mAdapter=new NormalRecyclerViewAdapter(this);
mRecyclerView.setAdapter(mAdapter);
// mAdapter1=new MultipleItemAdapter(this);
// mRecyclerView.setAdapter(mAdapter1);
}
关键注意:设置Layoutmanger方法,可以看到上面代码我们设置了linearLayoutManger和GridLayoutManger,就可以分别实现上面的两种效果。
接下来再来看一下Adapter的复杂用法,可以看到下面效果Item的展示形式不一样,对的,这里其实我们用了两个Item的布局,一个是图片+文字的,一个是单有文字的(红色背景)。感觉这样的效果蛮赞的,那么如何实现呢,其实很简单。
关键还是在Adapter中实现,MultileItemAdapter,这里我们写了两个ViewHoder
/** * 只显示文本的item */
public class TextViewHolder extends RecyclerView.ViewHolder{
private TextView itemText;
public TextViewHolder(View itemView) {
super(itemView);
itemText= (TextView) itemView.findViewById(R.id.text_view);
//Item的点击事件
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, mTitles[getPosition()], Toast.LENGTH_SHORT).show();
}
});
}
}
/** * 显示图像和文本的viewhoder */
public class ImageViewHolder extends RecyclerView.ViewHolder{
private ImageView imageView;
private TextView textView;
public ImageViewHolder(View itemView) {
super(itemView);
imageView= (ImageView) itemView.findViewById(R.id.item_image_iv);
textView= (TextView) itemView.findViewById(R.id.item_image_tv);
//Item的点击事件
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, mTitles[getPosition()], Toast.LENGTH_SHORT).show();
}
});
}
}
那么我们如何来区分加载这两个view呢,很简单
public class MultipleItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
/** * 枚举类型,两种显示方式 */
public static enum ITEM_TYPE{
ITEM_TYPE_IMAGE,
ITEM_TYPE_TEXT
}
/** * 不注释了,应该明白 */
private LayoutInflater mInflater;
private Context mContext;
private String[] mTitles;
/** * 同上 * @param context */
public MultipleItemAdapter(Context context) {
this.mContext = context;
mInflater=LayoutInflater.from(mContext);
//从values的array文件里读取数据
mTitles=mContext.getResources().getStringArray(R.array.titles);
}
/** * 关键在这里实现,onCreateViewHolder里面带了一个叫做viewType的参数,这个参数就是标识view的类型的 * @param parent * @param viewType * @return */
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//java.lang.Enum.ordinal()方法返回此枚举常量的序数(其枚举声明中的位置,其中初始常量分配的序数为零)。
if(viewType==ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal()){
View view=mInflater.inflate(R.layout.item_image,parent,false);
return new ImageViewHolder(view);
}else {
View view=mInflater.inflate(R.layout.item_text,parent,false);
return new TextViewHolder(view);
}
}
/** * 这里判断holder是属于哪一个,返回对应的viewhoder * @param holder * @param position */
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(holder instanceof ImageViewHolder){
((ImageViewHolder)holder).imageView.setImageResource(R.drawable.test);
((ImageViewHolder)holder).textView.setText(mTitles[position]);
}else if(holder instanceof TextViewHolder){
((TextViewHolder)holder).itemText.setText(mTitles[position]);
}
}
@Override
public int getItemCount() {
return mTitles==null ? 0 : mTitles.length;
}
/** * 关键方法,对应上面onCreateViewHoder里面的newsType * 重写Recycler.Adapter的getItemViewType方法 * 这里我们设置了单双数区别 * @param position * @return */
@Override
public int getItemViewType(int position) {
return position%2==0 ? ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal() : ITEM_TYPE.ITEM_TYPE_TEXT.ordinal();
}
OK,基本用法介绍到这里Over!
欢迎留言指导交流!