转载 请注明 明桑Android
这是Material Design的第三篇,前两篇介绍了Design Support Library中引入的新的控件(导航视图,悬浮ActionBar等的用法)
拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar..)
Material Design: NavigationView FlaotingActionBar SnackBar的使用
今天主要介绍:RecyclerView 和 CardView的用法,通过RecyclerView和CardView实现新闻卡片。
本文代码地址:CardViewDemo
代码中用到的图片: news_photo.zip
最终要实现的效果:
1,支持低版本
RecyclerView 和 CardView都是在Android 5.0中引入的,所以要想在低版本中使用,需要在build.gradle中添加:
dependencies { ... compile 'com.android.support:cardview-v7:22.2.0' compile 'com.android.support:recyclerview-v7:22.2.0' }
2,CardView的使用
你可以简单的认为它是一个使用了Material Desgin风格的FrameLayout,只不过比普通的FrameLayout多了圆角背景和阴影效果。所以它常用作ListView 或者 RecyclerView等视图Item的布局容器;
我们自然可以联想到它的使用跟FrameLayout非常相似,只不多多了几个用于控制圆角、阴影等自身特有的属性:
创建:res/layout/news_item.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="5dp"> <android.support.v7.widget.CardView android:id="@+id/card_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:foreground="?android:attr/selectableItemBackground" android:clickable="true" app:cardCornerRadius="3dp" app:cardElevation="8dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <RelativeLayout android:id="@+id/news_header" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/news_photo" android:scaleType="centerCrop" android:layout_alignParentTop="true" android:layout_width="match_parent" android:layout_height="150dp"/> <TextView android:id="@+id/news_title" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:maxLines="1" android:textSize="20sp" android:padding="5dp" android:textColor="#ffffff" android:gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content"/> </RelativeLayout> <TextView android:id="@+id/news_desc" android:maxLines="2" android:layout_below="@+id/news_header" android:layout_margin="15dp" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <LinearLayout android:orientation="horizontal" android:layout_below="@+id/news_desc" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/btn_share" android:text="SHARE" android:background="#00000000" android:layout_marginLeft="10dp" android:layout_marginRight="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btn_more" android:background="#00000000" android:textColor="#7AD3E0" android:text="READ MORE" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> </RelativeLayout> </android.support.v7.widget.CardView> </RelativeLayout>
我们注意到CardView里面有三个属性:
android:clickable="true" //cardView是否可点击,默认是不可点击的 app:cardCornerRadius="3dp" //圆角 app:cardElevation="8dp" //阴影
以上的布局效果:
2,RecyclerView的使用
RecyclerView可以看做是升级版的ListView,它具有更高的可扩展性,同时可以实现水平和垂直布局;之所以说是ListView的升级版,它解决了很多以前在使用ListView中不得不面对的问题:包括ListView的优化,动态删除ListView Item等,它都对此都做了很好的封装;
相比ListView,RecyclerView主要有以下特点:
适配器中需要提供ViewHolder类:ListView中者不是必须的,但在RecyclerView是必须的
灵活的自定义Item布局:ListView只能实现垂直的线性列布局,但RecyclerView可以通过RecyclerView.LayoutManager实现Item任何复杂的布局
容易的Item动画:RecyclerView.ItemAnimator用来处理Item动画
数据源:ListView可以通过ArrayAdapter CursorAdapter等直接提供数据源,RecyclerView需要自定义数据源
Item项:ListView可以通过android:divider=”“等属性控制Item项的显示,RecyclerView可以通过RecyclerView.ItemDecoration来设置
Item CLick Listener:ListView提供了AdapterView.OnItemClickListener来实现Item事件的监听,RecyclerView没有直接提供类似方法,需要自己实现;
关于二者的更多比较,参见这篇文章:Android RecyclerView vs ListView | Comparison
2.1,使用RecyclerView布局
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView>
在Activity中:
RecyclerView recyclerView= (RecyclerView) findViewById(R.id.recyclerView);
2.2,使用LayoutManager
不同于ListView,RecyclerView需要通过LayoutManager来管理和回收Item View,你可以通过继承RecyclerView.LayoutManager实现自己的LayoutManager,也可以使用现有的LayoutManager:LinearLayoutManager GridLayoutManager StaggeredGridLayoutManager;
以LinearLayoutManager为例
LinearLayoutManager llm = new LinearLayoutManager(context); recyclerView.setLayoutManager(llm);
2.3,定义数据类
这里我们建立一个新闻类:我们这里implements Serialzable是为了在Intent中能够直接传递News对象;
News.java
public class News implements Serializable{ //新闻标题,内容,图片 private String title; private String desc; private int photoId; /** * Constructs a new instance of {@code Object}. */ public News(String name, String age, int photoId) { this.title=name; this.desc=age; this.photoId=photoId; } public void setDesc(String desc) { this.desc = desc; } public void setTitle(String title) { this.title = title; } public void setPhotoId(int photoId) { this.photoId = photoId; } public String getDesc() { return desc; } public int getPhotoId() { return photoId; } public String getTitle() { return title; } }
2.4,自定义Adappter
这部分内容比较重要,如果对ListView自定义Adapter比较熟悉的话,也很容易看懂:相比而言RecyclerView Adapter中必须要实现ViewHolder类,然后需要覆写几个方法,唯一复杂的就是我在onBingViewHolder方法中为两个按钮和CardView实现了单击事件,跳转到新闻详细页面(NewsActivity)或者弹出分享;
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.NewsViewHolder>{ private List<News> newses; private Context context; public RecyclerViewAdapter(List<News> newses,Context context) { this.newses = newses; this.context=context; } //自定义ViewHolder类 static class NewsViewHolder extends RecyclerView.ViewHolder{ CardView cardView; ImageView news_photo; TextView news_title; TextView news_desc; Button share; Button readMore; public NewsViewHolder(final View itemView) { super(itemView); cardView= (CardView) itemView.findViewById(R.id.card_view); news_photo= (ImageView) itemView.findViewById(R.id.news_photo); news_title= (TextView) itemView.findViewById(R.id.news_title); news_desc= (TextView) itemView.findViewById(R.id.news_desc); share= (Button) itemView.findViewById(R.id.btn_share); readMore= (Button) itemView.findViewById(R.id.btn_more); //设置TextView背景为半透明 news_title.setBackgroundColor(Color.argb(20, 0, 0, 0)); } } @Override public RecyclerViewAdapter.NewsViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View v= LayoutInflater.from(context).inflate(R.layout.news_item,viewGroup,false); NewsViewHolder nvh=new NewsViewHolder(v); return nvh; } @Override public void onBindViewHolder(RecyclerViewAdapter.NewsViewHolder personViewHolder, int i) { final int j=i; personViewHolder.news_photo.setImageResource(newses.get(i).getPhotoId()); personViewHolder.news_title.setText(newses.get(i).getTitle()); personViewHolder.news_desc.setText(newses.get(i).getDesc()); //为btn_share btn_readMore cardView设置点击事件 personViewHolder.cardView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(context,NewsActivity.class); intent.putExtra("News",newses.get(j)); context.startActivity(intent); } }); personViewHolder.share.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_SUBJECT, "分享"); intent.putExtra(Intent.EXTRA_TEXT, newses.get(j).getDesc()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(Intent.createChooser(intent, newses.get(j).getTitle())); } }); personViewHolder.readMore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(context,NewsActivity.class); intent.putExtra("News",newses.get(j)); context.startActivity(intent); } }); } @Override public int getItemCount() { return newses.size(); } }
2.5,完成我们的Demo
关于RecyclerView和CardView的部分已经完成了,剩下的就是修修补补了,我们还需要创建NewsActivity用来显示新闻详细内容,布局也使用CardView,
res/layout/activity_news.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="20dp"> <android.support.v7.widget.CardView android:id="@+id/card_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:foreground="?android:attr/selectableItemBackground" android:clickable="true" app:cardCornerRadius="3dp" app:cardElevation="8dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <RelativeLayout android:id="@+id/news_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true"> <ImageView android:id="@+id/news_info_photo" android:scaleType="centerCrop" android:layout_alignParentTop="true" android:layout_width="match_parent" android:layout_height="180dp"/> <TextView android:id="@+id/news_info_title" android:layout_alignParentLeft="true" android:layout_below="@+id/news_info_photo" android:textSize="20sp" android:padding="5dp" android:layout_width="match_parent" android:layout_height="wrap_content"/> </RelativeLayout> <TextView android:id="@+id/news_info_desc" android:lineSpacingExtra="5dp" android:layout_below="@+id/news_header" android:layout_margin="15dp" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout> </android.support.v7.widget.CardView> </RelativeLayout>
布局效果:
NewsActivity.java
public class NewsActivity extends AppCompatActivity { private ImageView newsPhoto; private TextView newsTitle; private TextView newsDesc; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_news); newsPhoto= (ImageView) findViewById(R.id.news_info_photo); newsTitle= (TextView) findViewById(R.id.news_info_title); newsDesc= (TextView) findViewById(R.id.news_info_desc); Intent intent=getIntent(); News item= (News) intent.getSerializableExtra("News"); newsPhoto.setImageResource(item.getPhotoId()); newsTitle.setText(item.getTitle()); newsDesc.setText(item.getDesc()); } }
MainActivity.java
public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private List<News> newsList; private RecyclerViewAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LinearLayoutManager layoutManager=new LinearLayoutManager(this); recyclerView= (RecyclerView) findViewById(R.id.recyclerView); initPersonData(); adapter=new RecyclerViewAdapter(newsList,MainActivity.this); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(adapter); } private void initPersonData() { newsList =new ArrayList<>(); //添加新闻 newsList.add(new News(getString(R.string.news_one_title),getString(R.string.news_one_desc),R.mipmap.news_one)); newsList.add(new News(getString(R.string.news_two_title),getString(R.string.news_two_desc),R.mipmap.news_two)); newsList.add(new News(getString(R.string.news_three_title),getString(R.string.news_three_desc),R.mipmap.news_three)); newsList.add(new News(getString(R.string.news_four_title),getString(R.string.news_four_desc),R.mipmap.news_four)); } }
注意到上面的新闻内容实在太长,所以我们使用R.string:
res/values/strings.xml
<resources> <string name="app_name">CardViewDemo</string> <string name="news_one_title">Dallas police HQ attack: Suspect believed killed during standoff</string> <string name="news_one_desc">and ows the dark van ramming the nose of a police car before retreating in reverse</string> <string name="news_two_title">Hugh Jackman says coffee can change the world</string> <string name="news_two_desc">(CNN)-Hugh Jackman may be finished playing Wolverine in Marvels X-Men films,but the actior still uses his superhuman powers to impact global poverty,Humanitarianism is deeply ingrained in Jackman upbringing. His father, Christopher Jackman, instilled the value of giving through his work as a volunteer accountant for charities serving the developing world. I suppose he was leading by example, Jackman said. Areas of poverty and extreme poverty, I learned, are not a natural state. It is man-made and solvable.</string> <string name="news_three_title">Australia in charge of second Test against West Indies in Jamaica</string> <string name="news_three_desc">Australia in charge of second Test against West Indies in Jamaica</string> <string name="news_four_title">Sweden royal wedding</string> <string name="news_four_desc">嘿~如果觉得这篇文章对您有帮助,希望你能点个赞,你可以通过订阅微信公众号:ITBird,或新浪微博:明桑Anroid 找到我,记得关注博客哦</string> </resources>
Manifest.xml记得将NewsActivity添加进去:
<activity android:name=".NewsActivity" android:parentActivityName=".MainActivity"/>
总结:
限于篇幅,除了CardView和RecyclerView,其余的部分讲解的不太详细,这部分不太熟悉的可以留言讨论哈。RecyclerView理论上是可以完成ListView所有可以完成的,但代码比较复杂,所以二者的使用根据自己的具体情况而定,不过在Material Design下,数据集比较复杂时建议使用RecyclerView!
参考资料:
Getting Started With RecyclerView and CardView on Android
Using the RecyclerView
Using the CardView
微博: @明桑Android
邮箱: <13141459344@163.com>
个人主页: Android修行记
微信公众号: ITBird