完整代码,请参考我的博客园客户端,git地址:http://git.oschina.net/yso/CNBlogs

在写博客园客户端的时候,突然想到,弄个知乎日报风格的简单清爽多好!不需要那么多繁杂的信息干扰视野。

先贴上效果图,左边是知乎日报的,右边是本方案的

本文所使用的ide是androidStudio

首先我们需要在项目中,引入RecyclerView、CardView

在build.gradle的 dependencies 添加两条引用语句,如

dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.android.support:cardview-v7:22.0.0'
compile 'com.android.support:recyclerview-v7:22.0.0'
compile 'com.android.support:support-v4:22.0.0'
}

先来简单说下这两个控件

RecyclerView

support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,但是直接把viewholder的实现封装起来,用户只要实现自己的viewholder就可以了,该组件会自动帮你回收复用每一个item。它不但变得更精简,也变得更加容易使用,而且更容易组合设计出自己需要的滑动布局。

我们先来看下,在调试里面,开启布局边界选项,知乎日报是什么样子的

看到日期了没,如果某条新闻比前面新闻要早一天,头上就会出现日期。类似于按照日期分组的功能。

那么在ListView里,我们要实现这个功能,可能每个新闻里面都要添加个文本控件,如果早一天,则显示该控件。缺点不多说了,很丑陋

RecyclerView的强大之处在于,他将新闻要引用哪个布局的主动权,交给了新闻本身(而不是RecyclerView)。

也就是说,我可以有两个布局文件,

新闻带日期的

新闻不带日期的

具体使用哪个布局,子类自己决定。

CardView

这个控件倒没什么特别要说的,布局类似FrameLayout,但是加了很多特效:卡片的边框、阴影,duang,很酷、很炫。。

一点题外话:RecyclerView我之前接触了好几次,很想学,但是和ListView比起来,在很多功能上会有不同(这都是更先进的做法,吸取了ListView很多不足,不然谷歌也不会推出这个包),因此,如果你觉得熟练使用ListView就可以了,不需要了解RecyclerView的话,请相信我,花费的时间绝对值得!你可能会觉得使用ListView+CardView也能实现知乎日报的布局,但是抛开性能不说,CardView在ListView里面是没法使用布局属性的!也就是说layout_margin是无效的。你的卡片就只能挤在一起了!

好,介绍完了,咱们就开始探索,如何实现知乎日报的风格吧!

前台布局工作

1:在界面上使用RecyclerView控件

    <android.support.v7.widget.RecyclerView
android:id="@+id/base_swipe_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

2:准备不带标题的CardView (fragment_base_swipe_list.xml)

里面包了一个线性布局,左边文字,右边图片

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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="120dp"
android:layout_marginBottom="2dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="2dp"
app:cardCornerRadius="5dp"
app:elevation="1dp"> <LinearLayout
android:id="@+id/base_swipe_item_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?selectorListItem"
android:orientation="horizontal"
android:padding="5dp"> <TextView
android:id="@+id/base_swipe_item_title"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="1"
android:gravity="left|center_vertical"
android:textColor="?titleColor"
android:textSize="21sp" /> <ImageView
android:id="@+id/base_swipe_item_icon"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_gravity="center_vertical|right" />
</LinearLayout> </android.support.v7.widget.CardView>

3:带标题的CardView (fragment_base_swipe_group_item.xml)

我们使用include引入第二步不带标题的CardView(复用),在此基础上,添加了个TextView显示日期

<?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="150dp"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:orientation="vertical"> <TextView
android:id="@+id/base_swipe_group_item_time"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginLeft="10dp"
android:gravity="center_vertical"
android:textColor="?textColor"
android:textSize="17sp" /> <include layout="@layout/fragment_base_swipe_item"></include> </LinearLayout>

后台绑定工作

下面我们开始逐步介绍关键的后台绑定类NewsListAdapter.java

ListView有一堆各式各样的Adapter可以绑定。那么对应RecyclerView,它的Adpter很简单,注意泛型参数必须继承自ViewHolder。

public class NewsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

什么是ViewHolder,官方是这样解释的:

对于RecyclerView里面的某个元素,ViewHolder持有了该元素的布局和数据信息。我们实现ViewHolder时,最好可以添加一些属性,来缓存一些需要花费资源处理的结果。

是不是很熟悉?ListView里面,我们也有ViewHolder的,那套网上广泛流传的提高ListView性能的法则里面,就有一个使用自定义ViewHolder来缓存元素的布局信息以提高性能。

不记得啦?看看下面的代码吧

RecyclerView的强大之处就在于,它本身就提供了ViewHolder,我们只要继承自该ViewHolder就可以了,至于ViewHolder怎么存储,系统会自动帮我们搞定。

OK,解释清楚了什么是ViewHolder,接下来是咱自己实现的新闻、带标题的新闻ViewHolder

 /**
* 新闻标题
*/
public class NormalItemHolder extends RecyclerView.ViewHolder {
TextView newsTitle;
ImageView newsIcon; public NormalItemHolder(View itemView) {
super(itemView);
newsTitle = (TextView) itemView.findViewById(R.id.base_swipe_item_title);
newsIcon = (ImageView) itemView.findViewById(R.id.base_swipe_item_icon);
itemView.findViewById(R.id.base_swipe_item_container).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showNewsDetail(getPosition());
}
});
}
} /**
* 带日期新闻标题
*/
public class GroupItemHolder extends NormalItemHolder {
TextView newsTime; public GroupItemHolder(View itemView) {
super(itemView);
newsTime = (TextView) itemView.findViewById(R.id.base_swipe_group_item_time);
}
}

ViewHolder声明好了,那么Adapter如何知道该使用哪种呢?

我们重写父类RecyclerView.Adapter的两个方法就好了。

1:生成一个标志

 /**
* 决定元素的布局使用哪种类型
* @param position 数据源List的下标
* @return 一个int型标志,传递给onCreateViewHolder的第二个参数
*/
@Override
public int getItemViewType(int position) {

2:根据第一步的标志调用对应的ViewHolder

 /**
* 渲染具体的ViewHolder
* @param viewGroup ViewHolder的容器
* @param i 一个标志,我们根据该标志可以实现渲染不同类型的ViewHolder
* @return
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

绑定ViewHolder的数据

重写父类的绑定方法就好了。

/**
* 绑定ViewHolder的数据。
* @param viewHolder
* @param i 数据源list的下标
*/
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i)

上面所述关键的三个父类方法,重写后就可以达到我们要求了,下面贴上完整的代码,构造方法有两个参数:activity,数据源List

供各位参考

package zhexian.app.zoschina.news;

import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView; import java.util.List; import zhexian.app.zoschina.R;
import zhexian.app.zoschina.base.BaseActionBarActivity;
import zhexian.app.zoschina.lib.ZImage;
import zhexian.app.zoschina.util.ConfigConstant; public class NewsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int NORMAL_ITEM = 0;
private static final int GROUP_ITEM = 1; private BaseActionBarActivity mContext;
private List<NewsListEntity> mDataList;
private LayoutInflater mLayoutInflater; public NewsListAdapter(BaseActionBarActivity mContext, List<NewsListEntity> mDataList) {
this.mContext = mContext;
this.mDataList = mDataList;
mLayoutInflater = LayoutInflater.from(mContext);
} /**
* 渲染具体的ViewHolder
* @param viewGroup ViewHolder的容器
* @param i 一个标志,我们根据该标志可以实现渲染不同类型的ViewHolder
* @return
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { if (i == NORMAL_ITEM) {
return new NormalItemHolder(mLayoutInflater.inflate(R.layout.fragment_base_swipe_item, viewGroup, false));
} else {
return new GroupItemHolder(mLayoutInflater.inflate(R.layout.fragment_base_swipe_group_item, viewGroup, false));
}
} /**
* 绑定ViewHolder的数据。
* @param viewHolder
* @param i 数据源list的下标
*/
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
NewsListEntity entity = mDataList.get(i); if (null == entity)
return; if (viewHolder instanceof GroupItemHolder) {
bindGroupItem(entity, (GroupItemHolder) viewHolder);
} else {
NormalItemHolder holder = (NormalItemHolder) viewHolder;
bindNormalItem(entity, holder.newsTitle, holder.newsIcon);
}
} @Override
public int getItemCount() {
return mDataList.size();
} /**
* 决定元素的布局使用哪种类型
* @param position 数据源List的下标
* @return 一个int型标志,传递给onCreateViewHolder的第二个参数
*/
@Override
public int getItemViewType(int position) {
//第一个要显示时间
if (position == 0)
return GROUP_ITEM; String currentDate = mDataList.get(position).getPublishDate();
int prevIndex = position - 1;
boolean isDifferent = !mDataList.get(prevIndex).getPublishDate().equals(currentDate);
return isDifferent ? GROUP_ITEM : NORMAL_ITEM;
} @Override
public long getItemId(int position) {
return mDataList.get(position).getNewsID();
} void bindNormalItem(NewsListEntity entity, TextView newsTitle, ImageView newsIcon) {
if (entity.getIconUrl().isEmpty()) { if (newsIcon.getVisibility() != View.GONE)
newsIcon.setVisibility(View.GONE);
} else {
ZImage.getInstance().load(entity.getIconUrl(), newsIcon,
ConfigConstant.LIST_ITEM_IMAGE_SIZE_DP, ConfigConstant.LIST_ITEM_IMAGE_SIZE_DP, true, mContext.getMyApplication().canRequestImage()); if (newsIcon.getVisibility() != View.VISIBLE)
newsIcon.setVisibility(View.VISIBLE);
}
newsTitle.setText(Html.fromHtml(entity.getTitle()));
} void bindGroupItem(NewsListEntity entity, GroupItemHolder holder) {
bindNormalItem(entity, holder.newsTitle, holder.newsIcon);
holder.newsTime.setText(entity.getPublishDate());
} void showNewsDetail(int pos) {
NewsListEntity entity = mDataList.get(pos);
NewsDetailActivity.actionStart(mContext, entity.getNewsID(), entity.getRecommendAmount(), entity.getCommentAmount());
} /**
* 新闻标题
*/
public class NormalItemHolder extends RecyclerView.ViewHolder {
TextView newsTitle;
ImageView newsIcon; public NormalItemHolder(View itemView) {
super(itemView);
newsTitle = (TextView) itemView.findViewById(R.id.base_swipe_item_title);
newsIcon = (ImageView) itemView.findViewById(R.id.base_swipe_item_icon);
itemView.findViewById(R.id.base_swipe_item_container).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showNewsDetail(getPosition());
}
});
}
} /**
* 带日期新闻标题
*/
public class GroupItemHolder extends NormalItemHolder {
TextView newsTime; public GroupItemHolder(View itemView) {
super(itemView);
newsTime = (TextView) itemView.findViewById(R.id.base_swipe_group_item_time);
}
}
}

ps,咱在代码里面,将日期格式统一转换成友好格式(今日、昨日、6月6日星期6),在数据绑定的时候,和前面一条对比,如果不一样,则使用带日期的格式。就这样。

奉上日期友好格式生成代码

 public static int daysOfTwo(Date originalDate, Date compareDateDate) {
Calendar aCalendar = Calendar.getInstance();
aCalendar.setTime(originalDate);
int originalDay = aCalendar.get(Calendar.DAY_OF_YEAR);
aCalendar.setTime(compareDateDate);
int compareDay = aCalendar.get(Calendar.DAY_OF_YEAR); return originalDay - compareDay;
} public static String FriendlyDate(Date compareDate) {
Date nowDate = new Date();
int dayDiff = daysOfTwo(nowDate, compareDate); if (dayDiff <= 0)
return "今日";
else if (dayDiff == 1)
return "昨日";
else if (dayDiff == 2)
return "前日";
else
return new SimpleDateFormat("M月d日 E").format(compareDate);
}

【android】使用RecyclerView和CardView,实现知乎日报精致布局的更多相关文章

  1. Android之 RecyclerView,CardView 详解和相对应的上拉刷新下拉加载

    随着 Google 推出了全新的设计语言 Material Design,还迎来了新的 Android 支持库 v7,其中就包含了 Material Design 设计语言中关于 Card 卡片概念的 ...

  2. android翻译应用、地图轨迹、视频广告、React Native知乎日报、网络请求框架等源码

    Android精选源码 android实现高德地图轨迹效果源码 使用React Native(Android和iOS)实现的 知乎日报效果源码 一款整合百度翻译api跟有道翻译api的翻译君 RxEa ...

  3. Android L 之 RecyclerView 、CardView 、Palette

    转: http://blog.csdn.net/xyz_lmn/article/details/38735117 <Material Design>提到,Android L版本中新增了Re ...

  4. Android L中间RecyclerView 、CardView 、Palette使用

    RecyclerView CardView Palette <Material Design>提到,Android L版本号中新增了RecyclerView.CardView .Palet ...

  5. Android L中的RecyclerView 、CardView 、Palette的使用

    <Material Design>提到,Android L版本中新增了RecyclerView.CardView .Palette.RecyclerView.CardView为用于显示复杂 ...

  6. Android RecyclerView And CardView

    Google I/O 2014大会公布Android L系统,还有Material Design全新的设计风格.而Material Design卡片式的设计.Google Play应用商店和G+ AP ...

  7. ANDROID L——RecyclerView,CardView进口和使用(Demo)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 简单介绍: 这篇文章是ANDROID L--Material Design具体解释(UI控 ...

  8. RecyclerView 结合 CardView 使用

    准备工作:导入 1.activity_mian.xml <android.support.v7.widget.RecyclerView android:id="@+id/recycle ...

  9. Android应用开发:CardView的使用及兼容

    引言 在Google I/O 2014上,Google公布了Android L Preview版本,此版本的UI有了非常大的改变,很炫很给力!同时,Google也给出了两个可以向下兼容的控件放到了V7 ...

随机推荐

  1. xdebug常用配置

    ;指定xdebug文件 zend_extension = "F:\tools\develop_tools\php\php_xdebug-2.2.2-5.4-vc9.dll" ;xd ...

  2. VC++中CEdit控件实现回车换行

    1.通过回车Enter换行: 这里要有两个设置 <1>.将控件的属性设置为Mutilines->true; <2>.将控件的另一个属性设置为Want return-> ...

  3. 简单脱壳教程笔记(8)---手脱EZIP壳

    本笔记是针对ximo早期发的脱壳基础视频教程,整理的笔记.本笔记用到的工具下载地址: http://download.csdn.net/detail/obuyiseng/9466056 EZIP壳 : ...

  4. 在visual studio中运行C++心得

    1.在visual studio中建立C++项目 (1)新建->项目->空项目 C++ (2)右击项目->添加->新建项->C++文件(.app) (3编写C++文件   ...

  5. Maven手动添加dependency(以Oracle JDBC为例)

    由于Oracle授权问题,Maven不提供Oracle JDBC driver,为了在Maven项目中应用Oracle JDBC driver,必须手动添加到本地仓库.首先需要到Oracle官网上下载 ...

  6. 利用脚手架vue cli搭建vue项目

    vue.js https://vuejs.org/ 基础: http://cn.vuejs.org/v2/guide/installation.html 1.安装需要利用npm包管理器,所以首先安装n ...

  7. C# 控件,MenuStrip,statusStrip,contextMenuStrip,ImageList, Listview,MonthCalendar、DataGridView,combobox,textbox,DateTimePicker,treeview,picturebox、toolStrip,radioButton,TableLayoutPanel

    一.菜单栏 1)MenuStrip 菜单栏 选择工具栏控件:menuStrip C# Menustrip控件的常用属性用法详解 C#WinForm应用程序——添加菜单栏MenuStrip] 1.通过右 ...

  8. 2017 Multi-University Training Contest - Team 1—HDU6033&&HDU6034

    HDU6033  Add More Zero 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6033 题目意思:给一个m,求一个数k使得10^k最接近2 ...

  9. Elasticsearch 监控插件安装(elasticsearch-head与Kibana)

    摘要 安装Elasticsearch插件Head与Kibana 版本 elasticsearch版本: elasticsearch-2.3.4 elasticsearch-head版本: 2.x(支持 ...

  10. paintschainer项目

    github:https://github.com/pfnet/PaintsChainer tensorflow实现:https://github.com/mizti/tensor_paint 在线测 ...