完整代码,请参考我的博客园客户端,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. hdu 4496(并查集)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4496. 思路:简单并查集应用,从后往前算就可以了. #include<iostream> ...

  2. A mail sent to Google chromium.org Groups for Help

    Hi, I've ported Chromium M39 to 4.4 using WebView. The main modifications are: I changed AwContents: ...

  3. Spring_day02--Spring的aop操作

    Spring的aop操作 1 在spring里面进行aop操作,使用aspectj实现 (1)aspectj不是spring一部分,和spring一起使用进行aop操作 (2)Spring2.0以后新 ...

  4. 一道money计算题引发的思考

    网友提出一个问题如下 是小学和中学时候学到了增长折线问题,有点像数学问题,不过这个要求用编程来实现,恐怕还是有些逻辑要处理的,话不多说看代码吧 我给出的代码如下 代码清单: <?php func ...

  5. U盘重装Windows系统

    1.制作一个U盘老毛桃或者大白菜 2.进入BIOS 3.Secure Boot-Disabled,作用是关闭微软的Secure BOOT,这个功能开启会导致不能识别U盘启动系统的安装 4.Lauch ...

  6. NSUserDefaults设置bool值重启后bool只设置丢失问题

    本文转载至 http://blog.csdn.net/cerastes/article/details/38036875   NSUserDefaultsbool同步synchronize无效 今天使 ...

  7. IOS7开发~新UI学起(一)

    本文转载至:http://blog.csdn.net/lizhongfu2013/article/details/9124893 IOS7在UI方面发生了很大改变,所以感觉有必要重新审视的学习一下(新 ...

  8. 《jquery权威指南2》学习笔记------基础函数

    Math.floor(Math.random() * 7 + 1); Math.random() 生成0和1之间的随机小数Math.random() * 7 生成0和7之间的随机小数Math.rand ...

  9. Servlet------>servletDemo 及细节注意

    原理图: 前提:我用的命令行都是mac系统下用的,非win jsp实质是一个servlet,所以要先了解servlet,如上页面是一个servletdemo,下面是尝试的步骤 1.先写好Demo.ja ...

  10. 如何在ubuntu上搭建服务器并且可以使用ftp上传

    参考: 配置ftp:  http://jingyan.baidu.com/article/67508eb4d6c4fd9ccb1ce470.html 配置ftp这个中把 新建 allowed_user ...