您的位置:首页 > 产品设计 > UI/UE

Android UI控件之RecyclerView的简单应用

2016-11-05 11:11 543 查看

概述

RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好。

RecyclerView是ViewGroup的子类,每一个列表项都是作为一个View子对象显示的。这些View子对象既可以是复杂的View对象,也可以是简单的View对象,这取决于我们对列表显示复杂度的需要。

假设我们有100个View对象要显示,我们并不需要准备100个视图。RecyclerView只创建刚好充满屏幕的11~12个视图,用户滑动屏幕切换视图时,上一个视图会被回收利用。RecyclerView 所做的就是回收再利用,循环往复。

创建Info类

public class Info {
private UUID mId;
private String mTtitle;

private Date mDate;

public Info(){
//为每个Info对象生成唯一标识符
this(UUID.randomUUID());
}

public Info(UUID uuid) {
mId = uuid;
//记录当前日期
mDate = new Date();
}

public UUID getmId() {
return mId;
}

public void setmId(UUID mId) {
this.mId = mId;
}

public String getmTtitle() {
return mTtitle;
}

public void setmTtitle(String mTtitle) {
this.mTtitle = mTtitle;
}

public Date getmDate() {
return mDate;
}

public void setmDate(Date mDate) {
this.mDate = mDate;
}
}


创建单例类InfoLab.java

public class InfoLab {
private static InfoLab sInfoLab;
private Context mAppContext;
//创建可容纳Info对象的List
private ArrayList<Info> mInfos;

private InfoLab(Context appContext){
mAppContext = appContext;
mInfos = new ArrayList<Info>();
//生成100个数据,并为ArrrayList填充数据
for(int i = 0;i<100;i++){
Info info = new Info();
info.setmTtitle("info #"+i);
mInfos.add(info);
}
}
//获取InfoLab对象实例
public static InfoLab get(Context c){
if(sInfoLab==null){
sInfoLab = new InfoLab(c.getApplicationContext());
}
return sInfoLab;
}
//返回数组列表
public ArrayList<Info> getInfos(){
return mInfos;
}
//返回带有指定ID的Info对象
public Info getInfo(UUID uuid){
for(Info i:mInfos){
if(i.getmId().equals(uuid)){
return i;
}
}
return null;
}
}


定制列表项

每个列表项的视图布局应包含三项内容:标题、创建日期,以及图片(list_item_info.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/list_item_info_image_view"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:src="@mipmap/ic_launcher"/>

<TextView
android:id="@+id/list_item_info_title_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/list_item_info_image_view"
android:textStyle="bold"
android:padding="4dp"
android:text="Info Title"/>

<TextView
android:id="@+id/list_item_info_date_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/list_item_info_title_text_view"
android:textStyle="bold"
android:padding="4dp"
android:text="Info Date"/>

</RelativeLayout>


添加RecyclerView视图

先找到并选择recyclerview-v7支持库,完成依赖库添加。(info_list_activity.xml

<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/info_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>


为InfoListActivity配置视图(InfoListActivity.java

public class InfoListActivity extends AppCompatActivity {
private RecyclerView mInfoRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.info_list_activity);
mInfoRecyclerView = (RecyclerView)this.findViewById(R.id.info_recycler_view);
mInfoRecyclerView.setLayoutManager(new LinearLayoutManager(this));
}

...
}


实现 Adapter 和 ViewHolder

RecyclerView的任务仅限于回收和定位屏幕上的UI控件,比如这里的TextView和ImageView。 UI能够显示数据还离不开另外两个类的支持:

Adapter子类和ViewHolder子类。

(1)ViewHolder

ViewHolder要做的事很少,我们首先讨论它。顾名思义,ViewHolder只做一件事:容纳View视图。

ViewHolder为itemView而生:它引用着我们传给super(view)的整个View视图

RecyclerView自身不会创建视图,它创建的是ViewHolder,而ViewHolder引用着一个个itemView,如图所示。



如果处理的是简单视图, ViewHolder的工作也会相对简单。对于复杂视图, ViewHolder就得处理不同部分的itemView,以简单高效地展示View。

下面就是ViewHolder的基本用法:

首先在InfoListActivity类中定义ViewHolder内部类,(InfoListActivity.java

public class InfoListActivity extends AppCompatActivity {
...
private class InfoHolder extends RecyclerView.ViewHolder{
public TextView mTitleTextView;
private TextView mDateTextView;
private ImageView mImageView;

private Info mInfo;

public InfoHolder(View itemView) {
super(itemView);
mTitleTextView = (TextView)itemView.findViewById(R.id.list_item_info_title_text_view);
mDateTextView = (TextView)itemView.findViewById(R.id.list_item_info_date_text_view);
mImageView = (ImageView)itemView.findViewById(R.id.list_item_info_image_view);
}
//为UI控件填充数据
private void bindView(Info info){
mInfo = info;
mTitleTextView.setText(info.getmTtitle());
mDateTextView.setText(info.getmDate().toString());
mImageView.setImageResource(R.mipmap.litten);
}
}
...
}


(2)adapter

但是RecyclerView自己并不会创建ViewHolder。这个任务实际是由adapter来完成的。

adapter是个控制器对象,从模型层获取数据,然后提供给RecyclerView显示,起到了沟通的桥梁作用。

adapter负责:

 创建必要的ViewHolder;

 绑定ViewHolder至模型层数据。

要创建adapter,首先要定义RecyclerView.Adapter子类。然后由它封装从InfoLab获取的info。

RecyclerView需要显示视图对象时,就会去找它的adapter。下图展示了一个RecyclerView可能发起的会话。



首先,通过调用adapter的getItemCount()方法, RecyclerView询问数组列表中包含多少个对象。

接 着 , RecyclerView 调 用 adapter 的 createViewHolder(ViewGroup, int) 方 法 创 建 ViewHolder以及ViewHolder要显示的视图。

最后,RecyclerView会传入ViewHolder及其位置,调用onBindViewHolder(ViewHolder, int)方法。

adapter会找到目标位置的数据并绑定到ViewHolder的视图上。所谓绑定,就是使用 模型数据填充视图

整个过程执行完毕, RecyclerView就能在屏幕上显示info列表项了。

注意:

相对于onBindViewHolder(ViewHolder, int)方法,createViewHolder(ViewGroup, int)方法的调用并不频繁。一旦创建了够用的ViewHolder, RecyclerView就会停止调用createViewHolder(…)方法。然后,通过回收利用旧的ViewHolder节约时间和内存。

下面就是adapter的基本创建:

定义完ViewHolder,接下来的任务是创建adapter,(InfoListActivity.java

public class InfoListActivity extends AppCompatActivity{
...
private class InfoAdapter extends RecyclerView.Adapter<InfoHolder>{
private List<Info> mInfos;

public InfoAdapter(List<Info> list){
mInfos = list;
}

@Override
public InfoHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(InfoListActivity.this);
View view = inflater.inflate(R.layout.list_item_info,parent,false);
return new InfoHolder(view);
}

@Override
public void onBindViewHolder(InfoHolder holder, int position) {
Info info = mInfos.get(position);
//holder.mTitleTextView.setText(info.getmTtitle());
holder.bindView(info);
}

@Override
public int getItemCount() {
return mInfos.size();
}
}
...
}


RecyclerView需要新的View视图来显示列表项时,会调用onCreateViewHolder方法。在这个方法内部,我们创建View视图,然后封装到ViewHolder中。此时, RecyclerView并不要求

封装视图装载数据。

onBindViewHolder方法会把ViewHolderView视图模型层数据*绑定起来。收到ViewHolder和列表项在数据集中的索引位置后,我们通过索引位置找到要显示的数据进行绑定*。绑定完毕,刷新显示View视图。

索引位置,实际上就是数组中info的位置。取出目标数据后,通过发送**info相关的数据给**ViewHolder的响应的视图,我们就完成了Info数据View视图绑定

设置Adapter(InfoListActivity)

搞 定 了 Adapter , 最 后 要 做 的 就 是 将 它 和 RecyclerView 关 联 起 来 。 实 现 一 个 设 置InfoListActivity用 户 界 面的updateUI 方 法 ,该 方法 创建InfoAdapter , 然 后设置 给RecyclerView:

public class InfoListActivity extends AppCompatActivity {
private RecyclerView mInfoRecyclerView;
private InfoAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.info_list_activity);
mInfoRecyclerView = (RecyclerView)this.findViewById(R.id.info_recycler_view);
mInfoRecyclerView.setLayoutManager(new LinearLayoutManager(this));
updateUI();
}

private void updateUI() {
InfoLab infoLab = InfoLab.get(this);
List<Info> infos = infoLab.getInfos();
mAdapter = new InfoAdapter(infos);
mInfoRecyclerView.setAdapter(mAdapter);
mInfoRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
}
...
}


响应点击

受益于RecyclerView的内置特性,列表项能够响应用户的点击。用户点击列表项时,实现弹出一个Toast消息。

设置OnClickListener监听器。既然列表项视图都关联有ViewHolder,就可以让ViewHolder为它监听用户触摸事件。

修改InfoHolder类来处理用户点击事件:

private class InfoHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView mTitleTextView;
private TextView mDateTextView;
private ImageView mImageView;

private Info mInfo;

public InfoHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
mTitleTextView = (TextView)itemView.findViewById(R.id.list_item_info_title_text_view);
mDateTextView = (TextView)itemView.findViewById(R.id.list_item_info_date_text_view);
mImageView = (ImageView)itemView.findViewById(R.id.list_item_info_image_view);
}

private void bindView(Info info){
mInfo = info;
mTitleTextView.setText(info.getmTtitle());
mDateTextView.setText(info.getmDate().toString());
mImageView.setImageResource(R.mipmap.litten);
}

@Override
public void onClick(View v) {
Toast.makeText(InfoListActivity.this,
mInfo.getmTtitle() + " clicked!", Toast.LENGTH_SHORT)
.show();
}
}


在以上代码中, InfoHolder类实现了OnClickListener接口;而对于itemView来说,InfoHolder承担了接收用户点击事件的任务。

通过RecyclerView显示的列表是没有分割线的,如果想让每项View对象之间有分割线可以参考鸿洋大神

最后效果如下:



接下来,点击列表中的某一项,跳转到详细页面。

创建详细页面布局视图(info_detail_activity.xml

,如下图所示:



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
style="?android:listSeparatorTextViewStyle"
android:text="Title"/>

<EditText
android:id="@+id/info_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:hint="Enter something you want.">
</EditText>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"
android:text="Date"
style="?android:listSeparatorTextViewStyle"/>

<Button
android:id="@+id/info_date_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"/>

</LinearLayout>


创建InfoDetailActivity类(InfoDetailActivity.java)

public class InfoDetailActivity extends AppCompatActivity {
private EditText mTitleEdittext;
private Button mDateButton;
private Info mInfo;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.info_detail_activity);

mInfo = new Info();
mTitleEdittext = (EditText)this.findViewById(R.id.info_edit_text);
mTitleEdittext.setText(mInfo.getmTtitle());
mTitleEdittext.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
mInfo.setmTtitle(s.toString());
}

@Override
public void afterTextChanged(Editable s) {

}
});

mDateButton = (Button)this.findViewById(R.id.info_date_button);
mDateButton.setText(mInfo.getmDate().toString());
mDateButton.setEnabled(false);

}
}


从InfoListActivity中启动InfoDetailActivity(InfoListActivity.java

用启动InfoDetailActivity实例的代码,替换Toast

消息处理代码:

public void onClick(View v) {
Intent intent = new Intent(InfoListActivity.this,InfoDetailActivity.class);
intent.putExtra(EXTRA_INFO_ID,mInfo.getmId());
startActivity(intent);
}


启动InfoDetailActivity时,传递附加到Intent extra上的Info ID, InfoDetailActivity就能知道该显示哪个Info。

获取Extra信息(InfoDetailActivity.java)

public class InfoDetailActivity extends AppCompatActivity {
...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.info_detail_activity);
UUID uuid = (UUID) getIntent().getSerializableExtra(InfoListActivity.EXTRA_INFO_ID);
mInfo = InfoLab.get(this).getInfo(uuid);
//mInfo = new Info();
...
}
...
}


这样便完成了所有事件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: