看这里
RecyclerView
用于在有限的窗口展示大量的数据集。可以通过设置它提供的不同 LayoutManager (控制显示的方式)、ItemDecoration (控制 Item 间的间隔,可绘制)、ItemAnimator (控制 Item 增删的动画)实现一些效果。
可以看出,相比 ListView ,RecyclerView在功能上没有什么大的不同,不同之处在于实现了高度的解耦,给予开发人员充分的自由,所以可以用这个控件实现 ListView、 GirdView,瀑布流等效果。
基础功能使用:
mRecyclerView = findView(R.id.id_recyclerview);
//设置布局管理器
mRecyclerView.setLayoutManager(layout);
//设置adapter
mRecyclerView.setAdapter(adapter)
//设置Item增加、移除动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//添加分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(
getActivity(), DividerItemDecoration.HORIZONTAL_LIST));
一个小例子
1、在 build.gradle 中导入包
compile 'com.android.support:recyclerview-v7:24.+'
2、写布局文件
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.chandelier.recyclerview.MainActivity">
<android.support.v7.widget.RecyclerView
android:id = "@+id/recyclerViewId"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/pic_id"
android:scaleType="centerCrop"
android:layout_marginTop="5dp"
android:layout_marginEnd="5dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
3、实现:
MainActivity.java:
package com.chandelier.recyclerview;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static String TAG ="MainActivity";
private RecyclerView recyclerView;
private myAdapter mAdapter;
private int lastVisibleItem ;
private LinearLayoutManager linearLayoutManager;
private okhttpClass okhttp;
private int page = 1;
private List<picRes> more;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
okhttp = new okhttpClass();
} catch (IOException e) {
e.printStackTrace();
}
more= new ArrayList<>();//用来存放要加载的 Item(在这里是图片)对象(主要是url)
initView();//初始化布局
setListener();//设置监听事件
new GetData().execute("http://gank.io/api/data/福利/10/1");//先加载一些 Item
}
private void initView(){
recyclerView = (RecyclerView)findViewById(R.id.recyclerViewId);
linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
}
private void setListener(){
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE
&& lastVisibleItem +2>=linearLayoutManager.getItemCount()) {
new GetData().execute("http://gank.io/api/data/福利/10/"+(++page));
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
}
});
}
private class GetData extends AsyncTask<String, Integer, String> {
@Override
protected String doInBackground(String... params) {
String responce = null;
try {
responce = okhttp.getOp(params[0]);
} catch (IOException e) {
e.printStackTrace();
}
return responce;
}
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(!TextUtils.isEmpty(result)){
JSONObject jsonObject;
JSONArray jsonarray = new JSONArray();
int count = 0;
try {
jsonObject = new JSONObject(result);
jsonarray = jsonObject.getJSONArray("results");
count = jsonarray.length();
} catch (JSONException e) {
e.printStackTrace();
}
String[] shujus = new String[count];
for (int x = 0; x < count; x++){
try {
shujus[x] = jsonarray.getJSONObject(x).getString("url");
Log.i(TAG,"shujus["+x+"]:"+shujus[x]);
picRes pic = new picRes(shujus[x]);
more.add(pic);
Log.i(TAG,"将pic加入到more中:"+x);
} catch (JSONException e) {
e.printStackTrace();
}
}
if(mAdapter==null){
mAdapter = new myAdapter(MainActivity.this,more);
Log.i(TAG,"new 一个 myAdapter。");
recyclerView.setAdapter(mAdapter);
Log.i(TAG,"设置好 一个 myAdapter。");
}else{
mAdapter.notifyDataSetChanged();
}
}
}
}
}
GetData:继承自 AyncTask,主要重写了两个方法。
doInBackground(String... params)
:这个里面的代码在子线程中执行,执行一些耗时操作,这里主要使用 okHttp 进行网络请求,这里我对 okhttp 进行了小小的封装,下面会写到。onPostExecute(String result)
:这个方法是当后台任务执行完毕并通过 return 返回时调用。在这个方法里我主要是对网络请求返回的数据进行解析,将图片的url 存储到 picRes 中,picRes 会在下面贴,就是用来存储 Item 信息的一个类。并且构造一个适配器,并为 recyclerView 设置该适配器。
setListener():这个方法里主要是做当用户滑动屏幕时,剩下的 item 少于两个时,自动加载下一页的工作。注意:添加了监听器不要忘记清除。
onScrolled (RecyclerView recyclerView,int dx,int dy)
:当滚动完成的时候,会调用此方法,第一个参数,表示哪一个 RecyclerView 会被调用;第二个参数表示水平滚动量;第三个参数垂直滚动量。onScrollStateChanged (RecyclerView recyclerView,int newState)
:RecyclerView 的滚动状态发生改变的时候,该方法会被回调。第二个参数表示更新的滚动状态,有三种:
SCROLL_STATE_IDLE
:RecyclerView 目前不滚动。SCROLL_STATE_DRAGGING
:RecyclerView 当前正在被外部输入拖动,例如用户触摸输入。SCROLL_STATE_SETTLING
:RecyclerView 目前正在移动到最终位置,而不再外部控制下。
这里面的 okhttpclass :
package com.chandelier.recyclerview;
import android.util.Log;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;
import java.io.IOException;
public class okhttpClass {
OkHttpClient mOkHttpClient;
String TAG = "okhttpClass";
public okhttpClass() throws IOException {
Log.i(TAG,"in okhttpClass!");
mOkHttpClient = new OkHttpClient();
}
public String getOp(String url) throws IOException{
Log.i(TAG,"in getOp!");
Request request = new Request.Builder().url(url).build();
Response response = mOkHttpClient.newCall(request).execute();
Log.i(TAG,"get responce");
String str;
if (response.isSuccessful()){
ResponseBody responceBody = response.body();
str = responceBody.string();
}else{
return "error";
}
return str;
}
}
picRes :
package com.chandelier.recyclerview;
public class picRes{
private String url;//图片地址
public picRes(String url){
this.url = url;
}
public String getUrl() {
return url;
}
}
myAdapter:
package com.chandelier.recyclerview;
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.ImageView;
import com.squareup.picasso.Picasso;
import java.util.List;
public class myAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<picRes> data;
private LayoutInflater inflater;
private Context context;
public myAdapter(Context context,List<picRes> data) {
this.context = context;
this.data = data;
inflater= (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public class myViewHolder extends RecyclerView.ViewHolder{
public ImageView iv;
public myViewHolder(View itemView) {
super(itemView);
iv= (ImageView) itemView.findViewById(R.id.pic_id);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = inflater.inflate(R.layout.item, parent, false);
return new myViewHolder(itemView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Picasso.with(context).load(data.get(position).getUrl()).into(((myViewHolder)holder).iv);
}
@Override
public int getItemCount() {
return data!=null?data.size():0;
}
}
先看看重写的方法吧
int getItemCount()
:返回适配器中数据集中的 Item 的总数。void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
:由 RecyclerView 调用来在指定的位置显示数据,这个方法应该在定的位置更新 itemView 的内容。
- 请注意,与ListView不同,如果 item 的位置在数据集中更改,则 RecyclerView 将不会再次调用这个方法,除非 item 本身无效或者新的位置无法确定。因此,我们应该只在获取此方法中的相关数据 item 时使用 position 这个参数,并且不应该保留这个参数的副本。如果我们在之后需要一个 item 的 position (比如在监听器中),应该使用
getAdapaterPosition()
方法。如果适配器可以处理有效的数据,就重写此方法。 - 所以在这个方法中,我做的操作是将图片加载到 ViewHolder 中。这里使用到了
Picasso
,picasso 是 Square 公司开源的一个 android图形缓存库,可以实现图片下载和缓存的功能,就像这个方法中,仅仅用一行代码就能实现图片的异步加载。他还解决了 android 中加载图片时需要解决的一些常见问题。后面我可能会写一篇博客详细说明,吧。
- 请注意,与ListView不同,如果 item 的位置在数据集中更改,则 RecyclerView 将不会再次调用这个方法,除非 item 本身无效或者新的位置无法确定。因此,我们应该只在获取此方法中的相关数据 item 时使用 position 这个参数,并且不应该保留这个参数的副本。如果我们在之后需要一个 item 的 position (比如在监听器中),应该使用
RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
:当 RecyclerView 一个新的 ViewHolder 代表一个指定类型的 Item 时调用。一个新的 ViewHolder 应该以一个新的View,即可以代表给定类型的 item来构建。我们可以手动创建一个新的 View, 也可以从 xml 布局文件中 inflate 他,这里我使用的就是 inflate 。创建的 ViewHolder 将用于 onBindViewHolder() 中显示适配器的 item。由于它将重新用于在数据集中显示不同的项目,所以缓存对视图子视图的引用是以一个好主意,以避免不必要的 findViewById(int) 调用。