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.Adapter

2 写构造方法 获取传入的值

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 = 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

用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

你可能感兴趣的:(android)