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
推荐阅读
- 一个人的旅行,三亚
- 一个小故事,我的思考。
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 开学第一天(下)
- 一个人的碎碎念
- 2018年11月19日|2018年11月19日 星期一 亲子日记第144篇
- 遇到一哭二闹三打滚的孩子,怎么办┃山伯教育
- 第326天
- Y房东的后半生14
- 奔向你的城市