RecyclerView使用(一)(仿微博首页UI设置)

RecyclerView的使用(仿微博首页UI设置) 1 导入包
包一定要导入,并且版本要适配 否则第二步的XML配置中,RecyclerView会提示找不到

compile 'com.android.support:recyclerview-v7:26.1.0'

2 MAinActivity的XML中

3 MainActivity.java中
private RecyclerView mRecyclerView; mRecyclerView = findViewById(R.id.recyclerView); initData(); initView();

4.1 initData() 实现
private RecyclerView.LayoutManager mLayoutManager; private RecyclerView.Adapter mAdapter; private void initData() { mAdapter = new ItemAdapter(getData()); }

数据载入见第6点先介绍布局
5.1 initView() 实现
设置布局管理器 进行布局的分隔线样式设置 设置适配器
private void initView() { //设置纵向滚动的 即item是横向分布的 mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); //设置布局管理器 mRecyclerView.setLayoutManager(mLayoutManager); //如果可以确定每个item的高度是固定的,设置这个选项可以提高性能 mRecyclerView.setHasFixedSize(true); //设置adapter mRecyclerView.setAdapter(mAdapter); //添加分割线 mRecyclerView.addItemDecoration(new MyDividerItemDecoration( this, DividerItemDecoration.VERTICAL)); }

5.2分隔线样式设计
5.2.1使用系统默认的分隔线样式:
package com.scnu.sihao.sinaweibodemo; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v4.view.ViewCompat; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; public class MyDividerItemDecoration 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 MyDividerItemDecoration(Context context, int orientation) { // 获取默认主题的属性 final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); }@Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { // 绘制间隔 if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } }@Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } }private void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; }/** * 绘制间隔 */ private 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++) {// 若是不需要底部的分隔线 则这里的childCount-1 final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child)); final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } }/** * 绘制间隔 */ private 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++) {// 若是不需要底部的分隔线 则这里的childCount-1 final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child)); final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } }

RecyclerView在绘制的时候,去会绘制decorator,即调用该类的onDraw和onDrawOver方法,
onDraw方法先于drawChildren
onDrawOver在drawChildren之后,一般我们选择复写其中一个即可。
getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator
//若是不需要底部的分隔线 则childCount-1
5.2.2 添加自定义的分隔线样式设计
5.2.2.1 在styles.xml中
加一句
@drawable/divider_bg

使用自定义的xml文件设置样式
5.2.2.2 在drawable中 创建一个divider_bg.xml文件

注意: ListView设置divider后一定要重新设置高度 不然高度会被置回-1
listView.setDivider(DrawableXXX) listView.setDividerHeight(1)

6 单个数据载入
6.1 数据传输
private ArrayList getData() { ArrayList screenName = new ArrayList<>(); for(int i = 0; i < RetrofitUtil.COUNT; i++) { userName[i]=retrofitUtil2.screen_name[i]; screenName.add(userName[i]); Log.i("GetUserName",userName[i]); } return screenName; }

6.2数据接收处理
代码分析:
1 创建一个自定义的Adapter类 继承 RecyclerView.Adapter2 写构造方法 获取传入的值3重写该方法用于绑定item的layout文件 可以根据viewType去让item绑定不同的layout文件 @Override public RecyclerView.ViewHolder onCreateViewHolder (ViewGroup parent,int viewType){ View v = LayoutInflater.from(parent.getContext()).inflate(R.id.XXX,parent,false); // 实例化viewholder return new ViewHolder(v); }4 创建ViewHolder(可以起其他名字)类继承RecyclerView.ViewHolder 该方法用于绑定组件 以及 设置点击事件 class ViewHolder extends RecyclerView.ViewHolder { TextView textView0; ViewHolder(View itemView, MyItemClickListener myItemClickListener) { textView0 = findViewById(R.id.xxx); } }5 重写该方法,用于对控件做操作 如setText 设置点击事件, 设置是否可见等等 @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { ((ViewHolder) holder).textView0.setText("测试"); // 这里的ViewHolder即上面定义的class ViewHolder } 6 重写该方法 用于获取所有item的数量 @Override public void getItemCount(){ return arrayList0.size()?0:arrayList0.size(); }

package com.scnu.sihao.sinaweibodemo; import android.net.Uri; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.VideoView; import java.util.ArrayList; // 创建ItemAdapter public class **ItemAdapter** extends RecyclerView.Adapter<**ItemAdapter**.ViewHolder>{ private ArrayList mData; // 构造方法 传入数组接收上面的单个数据传入 public **ItemAdapter**(ArrayList data) { this.mData = https://www.it610.com/article/data; }public void updateData(ArrayList data) { this.mData = data; notifyDataSetChanged(); }@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // 实例化展示的view这里连接的是Item的XML View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.##item_layout##, parent, false); // 实例化viewholder ViewHolder viewHolder = new ViewHolder(v); return viewHolder; }@Override public void onBindViewHolder(ViewHolder holder, int position) {// 绑定screenName数据 holder.userName.setText(mData.get(position)); }@Override public int getItemCount() { // 获取item的数量 return mData == null ? 0 : mData.size(); }public static class ViewHolder extends RecyclerView.ViewHolder { TextView userName; TextView bodyText; public ViewHolder(View itemView) { super(itemView); // 获取组件这里是Item的XML中的组件 bodyText =itemView.findViewById(R.id.##body_text##); userName=itemView.findViewById(R.id.##user_name##); } } }

加星号的地方替换自己定义的Adpter类名
加#号的地方替换自己的item的xml布局
**要传入多个参数 **
在ItemAdapter的构造方法中 设置多个参数获取不同的参数数据
private ArrayList mTitle = new ArrayList<>(); private ArrayList mContent = new ArrayList<>(); private ArrayList mPubdate = new ArrayList<>(); // 构造方法 传入数组 public SystemNewsAdapter(ArrayList title,ArrayList content,ArrayList pubdate) { this.mTitle = title; this.mContent = content; this.mPubdate = pubdate; // 上面的initdata() private void initData() { mAdapter = new SystemNewsAdapter(title,content,pubdate); }

Item的布局XML:
根据具体项目需要设置你的Item的布局内容
每个Item的高度与Item的XML布局设置的高度是相关,比如:你的Item_layout布局设置的高度为matchParent 则每个Item都会占据一整个显示屏的位置
高度不能设置为父布局为WrapContent 而子布局/控件的高度为百分比布局
并且RecyclerView会获取当前界面显示的Item位置(position,值从0开始)去显示Item的内容,若没有显示的item,里面的内容是暂时不会加载的
onBindViewHolder方法会根据Position的位置去设置内容
实例
7新浪微博数据传入:
MAinActivity.java
package com.scnu.sihao.sinaweibodemo; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.widget.VideoView; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private RecyclerView.Adapter mAdapter; private RecyclerView.LayoutManager mLayoutManager; private String[] userName = new String[RetrofitUtil.COUNT]; private static RetrofitUtil retrofitUtil2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //requestWindowFeature(Window.FEATURE_NO_TITLE); // 不显示程序的标题栏 继承Activity才可以生效 setContentView(R.layout.activity_main); mRecyclerView = findViewById(R.id.recyclerView); initData(); initView(); }// 获取对象 public static void getObject(RetrofitUtil retrofitUtil) { retrofitUtil2 = retrofitUtil; }private void initData() { mAdapter = new ItemAdapter( getScreenName(), getCreatAt(), getSource(), getRepostsCount(), getCommentsCount(), getAttitudesCount(), getText(), getProfileImageUrl(),getPicUrls()); }private void initView() { mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); //设置布局管理器 mRecyclerView.setLayoutManager(mLayoutManager); //如果可以确定每个item的高度是固定的,设置这个选项可以提高性能 mRecyclerView.setHasFixedSize(true); //设置adapter mRecyclerView.setAdapter(mAdapter); //添加分割线 mRecyclerView.addItemDecoration(new MyDividerItemDecoration( this, DividerItemDecoration.VERTICAL)); }private ArrayList getScreenName() { ArrayList screenName = new ArrayList<>(); for (int i = 0; i < RetrofitUtil.COUNT; i++) { userName[i] = retrofitUtil2.screen_name[i]; screenName.add(userName[i]); Log.i("123123123", userName[i]); } return screenName; }private ArrayList getCreatAt() { ArrayList CreatAt = new ArrayList<>(); for (int i = 0; i < RetrofitUtil.COUNT; i++) { CreatAt.add(retrofitUtil2.created_at[i]); Log.i("123123123", retrofitUtil2.created_at[i]); } return CreatAt; }private ArrayList getSource() { ArrayList Source = new ArrayList<>(); for (int i = 0; i < RetrofitUtil.COUNT; i++) { Source.add(retrofitUtil2.source[i]); Log.i("123123123", retrofitUtil2.source[i]); } return Source; }private ArrayList getRepostsCount() { ArrayList RepostsCount = new ArrayList<>(); for (int i = 0; i < RetrofitUtil.COUNT; i++) {RepostsCount.add(retrofitUtil2.reposts_count[i]); Log.i("123123123", retrofitUtil2.reposts_count[i]); } return RepostsCount; }private ArrayList getCommentsCount() { ArrayList CommentsCount = new ArrayList<>(); for (int i = 0; i < RetrofitUtil.COUNT; i++) { CommentsCount.add(retrofitUtil2.comments_count[i]); Log.i("123123123", retrofitUtil2.comments_count[i]); } return CommentsCount; }private ArrayList getAttitudesCount() { ArrayList AttitudesCount = new ArrayList<>(); for (int i = 0; i < RetrofitUtil.COUNT; i++) { AttitudesCount.add(retrofitUtil2.attitudes_count[i]); Log.i("123123123", retrofitUtil2.attitudes_count[i]); } return AttitudesCount; }private ArrayList getText() { ArrayList Text = new ArrayList<>(); for (int i = 0; i < RetrofitUtil.COUNT; i++) {Text.add(retrofitUtil2.text[i]); Log.i("123123123", retrofitUtil2.text[i]); } return Text; }private ArrayList getProfileImageUrl() { ArrayList ProfileImageUrl = new ArrayList<>(); for (int i = 0; i < RetrofitUtil.COUNT; i++) {ProfileImageUrl.add(retrofitUtil2.profile_image_url[i]); Log.i("123123123", retrofitUtil2.profile_image_url[i]); } return ProfileImageUrl; }private String[][] getPicUrls() { String[][] PicUrls = new String[RetrofitUtil.COUNT][9]; for (int i = 0; i < RetrofitUtil.COUNT; i++) { for (int j = 0; j < 9; j++) { if(retrofitUtil2.pic_urls[i][j]!=null) { PicUrls[i][j] = retrofitUtil2.pic_urls[i][j]; Log.i("123123123", retrofitUtil2.pic_urls[i][j]); } } } return PicUrls; }}

ItemAdapter.java
package com.scnu.sihao.sinaweibodemo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Message; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.widget.VideoView; import java.net.URL; import java.util.ArrayList; // 创建ItemAdapter public class ItemAdapter extends RecyclerView.Adapter{ private ArrayList mScreenName; private ArrayList mCreatAt; private ArrayList mSource; private ArrayList mRepostsCount; private ArrayList mCommentsCount; private ArrayList mAttitudesCount; private ArrayList mText; private ArrayList mProfileImageUrl; private String[][] mPicUrls= new String[RetrofitUtil.COUNT][9]; private boolean flag=false; // 构造方法 传入数组 public ItemAdapter(ArrayList screenName,ArrayList creatAt,ArrayList source, ArrayList repostsCount,ArrayList commentsCount, ArrayList attitudesCount, ArrayList text, ArrayList profileImageUrl,String[][] PicUrls) { this.mScreenName = screenName; this.mCreatAt = creatAt; this.mSource = source; this.mRepostsCount = repostsCount; this.mCommentsCount = commentsCount; this.mAttitudesCount = attitudesCount; this.mText = text; this.mProfileImageUrl = profileImageUrl; this.mPicUrls=PicUrls; }/*public void updateData(ArrayList screenName) { this.mScreenName = screenName; notifyDataSetChanged(); }*/@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // 实例化展示的view 非Activity与XML连接 View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false); // 实例化viewholder ViewHolder viewHolder = new ViewHolder(v); return viewHolder; }@Override public void onBindViewHolder(final ViewHolder holder, final int position) { Log.i("position", String.valueOf(position)); // 传过来的ArrayList 根据position绑定值position为当前界面显示的item条目从0开始 若有多个则调用多个 但是也是一个个按先后顺序的 holder.userName.setText(mScreenName.get(position)); holder.Time.setText(mCreatAt.get(position)); holder.From.setText(mSource.get(position)); holder.Transmit.setText(mRepostsCount.get(position)); holder.Comment.setText(mCommentsCount.get(position)); holder.Good.setText(mAttitudesCount.get(position)); holder.bodyText.setText(mText.get(position)); // 图片加载要用子线程!!! new Thread(new Runnable() { @Override public void run() { try { URL url = new URL(mProfileImageUrl.get(position)); Log.i("url", String.valueOf(url)); holder.UserImage.setImageBitmap(BitmapFactory.decodeStream(url.openStream())); } catch (Exception e) { e.printStackTrace(); } } }).start(); // 图片加载要用子线程!!! for (int i = 0; i < 9; i++) { if (mPicUrls[position][i] != null) { flag = true; //提前结束了 break; } } if (flag) { new Thread(new Runnable() { @Override public void run() { try { for(int j = 0; j < 9; j++) { URL url = new URL(mPicUrls[position][j]); Log.i("url111222", String.valueOf(url)); holder.image[j].setImageBitmap(BitmapFactory.decodeStream(url.openStream())); } //flag为true之后 记得将他设置回false!!! flag=false; } catch (Exception e) { e.printStackTrace(); } } }).start(); } } // 重写该方法可以实现设置item显示数量 不用调用 重写了就自动执行 @Override public int getItemCount() { // 获取item的数量 return mScreenName == null ? 0 : mScreenName.size(); }public static class ViewHolder extends RecyclerView.ViewHolder { TextView userName; TextView Time,From; Button Transmit,Comment,Good; TextView bodyText; ImageView UserImage; //图片数组 ImageView []image = new ImageView[9]; public ViewHolder(View itemView) { super(itemView); // 获取组件 userName=itemView.findViewById(R.id.user_name); Time=itemView.findViewById(R.id.time); From=itemView.findViewById(R.id.from); Transmit=itemView.findViewById(R.id.transmit); Comment=itemView.findViewById(R.id.comment); Good=itemView.findViewById(R.id.good); bodyText =itemView.findViewById(R.id.body_text); UserImage=itemView.findViewById(R.id.user_image); //图片 image[0] = itemView.findViewById(R.id.image_1); image[1] = itemView.findViewById(R.id.image_2); image[2] = itemView.findViewById(R.id.image_3); image[3] = itemView.findViewById(R.id.image_4); image[4] = itemView.findViewById(R.id.image_5); image[5] = itemView.findViewById(R.id.image_6); image[6] = itemView.findViewById(R.id.image_7); image[7] = itemView.findViewById(R.id.image_8); image[8] = itemView.findViewById(R.id.image_9); } } }

RecyclerView会根据item的真实呈现,去调用onBindViewHolder() 方法进行数据的绑定
若一个界面有多个item呈现, 则按顺序调用每个item的onBindViewHolder 根据onBindViewHolder(position)里的参数position (下标从0开始)去绑定数据
添加滚动条
XML中
android:scrollbars="vertical"// 垂直 android:scrollbars="horizontal"// 水平 android:scrollbars="none"// 无滚动条

问题1
这个是界面显示就自动加载数据,加载界面,这样recyclerView就会有adpater 可以加载
但是若加载界面时先不加载数据,点击了某个按钮再加载数据,加载recyclerView的界面,但是这样初始化界面recyclerView没有adapter会skipping
解决:可以先在加载的时候让他View.GONE 等要用了再让他visible
问题2
RecyclerView中的item图片要刷到item可见变为不可见,才能把图片加载出来
使用Glide框架进行图片加载
问题3:
getPosition()’ is deprecated
【RecyclerView使用(一)(仿微博首页UI设置)】用getLayoutPosition()
具体区别就是adapter和layout的位置会有时间差(<16ms), 如果你改变了Adapter的数据然后刷新视图, layout需要过一段时间才会更新视图, 在这段时间里面, 这两个方法返回的position会不一样.
在notifyDataSetChanged之后并不能马上获取Adapter中的position, 要等布局结束之后才能获取到.
而对于Layout的position, 在notifyItemInserted之后, Layout不能马上获取到新的position, 因为布局还没更新(需要<16ms的时间刷新视图), 所以只能获取到旧的, 但是Adapter中的position就可以马上获取到最新的position
具体使用根据具体的需求。
参考:
http://blog.csdn.net/weixin_37577039/article/details/78698865

    推荐阅读