首发于:http://blog.csdn.net/likianta/article/details/78618247
1. 首先看一下最终的效果图
![](https://img.haomeiwen.com/i1874022/0da472205aa16f8e.jpg)
本章我们只制作左分页,这是一个以卡片为单位组成的可滚动的列表,我们需要用到CardView(卡片布局)和RecyclerView(列表布局)。
2. 配置RecyclerView和CardView
RecyclerView和CardView需要预先在app/build.gradle中配置,代码如下:
dependencies {
...
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1' //配置recyclerview,版本与上面的appcompat保持一致,这里我的就是v7:25.3.1
compile 'com.android.support:cardview-v7:25.3.1' //配置cardview
}
写好了以后,注意在该页的右上角点击“Sync Now”同步一下:
![](https://img.haomeiwen.com/i1874022/076c2d3e33f04d45.jpg)
3. 制作RecyclerView布局
在page_item_all.xml
布局中写RecyclerView布局。这个和上章中的代码差不多是一样的,基本没什么变化,只是将背景色改了一下:
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/mycolorBackground">
<!--背景色改成了浅灰色-->
</android.support.v7.widget.RecyclerView>
这就是整体的代码,看起来很简洁。
background的值是在res/values/colors.xml
中定义的:
<resources>
...
<!--自制色卡-->
...
<color name="mycolorBackground">#dedede</color>
</resources>
接下来我们要往这个布局中动态地生成小卡片,所以我们要制作卡片的模板。
4. 新建card.xml
制作卡片模板
在卡片中包含以下属性:
- 头像(头像默认是由标题的拼音的首字母生成的)
- 标题
- 摘要(摘要信息=帐号+密码+备注)
- 功能按钮:
- 弹出菜单
- 复制帐号到剪切板
- 复制密码到剪切板
代码如下:
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_margin="5dp"
android:elevation="4dp"
app:cardCornerRadius="8dp"
>
<!--elevation表示卡片的高度,cardCornerRadius表示卡片四个角的弧度,xmlns:tools用于识别TextView中的\n换行符-->
<!--CardView本身是一个FrameLayout,显然不适合摆放控件。因此为了充分利用空间,要内建一个RelativeLayout来盛放所有子控件-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--首先思路就是把RelativeLayout分为左中右三个部分,左边贴一个头像;中间占的面积最大,用来写标题和摘要;右边则放置功能按钮-->
<!--现在写的是中间的布局LinearLayout。在LinearLayout中上半部分显示标题,下半部分显示摘要-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="80dp"
android:layout_marginRight="40dp"
android:orientation="vertical">
<!--注意设定LinearLayout的方向为vertical-->
<!--标题,深色,字号较大,高度比重为40%-->
<TextView
android:id="@+id/cardTitle"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4"
android:gravity="bottom"
android:padding="4dp"
android:text="TitleTest"
android:textSize="32sp"
android:textColor="@color/mycolorText1"/>
<!--摘要,浅色或同色,字号比正常文字还要小,高度比重为60%,限制显示三行文字-->
<TextView
android:id="@+id/cardSummary"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="6"
android:paddingTop="3dp"
android:maxLines="3"
android:lines="3"
android:ellipsize="end"
android:text="name: ______\npassword: ______\nnote: this view limited 3 lines" />
<!--maxLines表示最大行数,lines表示即使只有一行字也要占用三行字的高度空间,ellipsize表示多出的字数表示为省略号
参考:http://blog.csdn.net/qq_31403303/article/details/51506524-->
</LinearLayout>
<!--接下来写的是右边的布局,也是LinearLayout布局。从上到下依次显示三个按钮:菜单、复制name、复制password-->
<LinearLayout
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_margin="5dp"
android:orientation="vertical">
<!--菜单按钮-->
<ImageButton
android:id="@+id/cardMenu"
android:layout_width="match_parent"
android:layout_height="40dp" />
<!--两个复制按钮平分剩下的高度空间-->
<ImageButton
android:id="@+id/copyName"
android:layout_gravity="center"
android:layout_width="20dp"
android:layout_height="0dp"
android:layout_weight="1" />
<ImageButton
android:id="@+id/copyPassword"
android:layout_gravity="center"
android:layout_width="20dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
<!--不要忘了左边部分。之所以留待最后才写,是为了让头像以及分割线最后加载,这样就处于其它二者的上方了-->
<!--先做分割线,非常细,非均等分割(上窄下宽)-->
<TextView
android:layout_width="match_parent"
android:layout_height="0.4dp"
android:layout_marginTop="46dp"
android:background="#000000" />
<!--头像-->
<ImageView
android:id="@+id/cardHead"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="20dp"
android:src="@color/mycolorText2" />
</RelativeLayout>
</android.support.v7.widget.CardView>
卡片预览图如下:
![](https://img.haomeiwen.com/i1874022/a3533cce8fc1a8e6.jpg)
至此两个布局文件都写好了。下面才是本章的重点,为了方便理解,我们分为“卡片的属性及适配”和“列表的加载”两大部分来讲,其中适配器和模板属性的建立是所有业务处理的基础,也是本章的难点。
5. 卡片的属性及适配
5.1. 首先新建Card.java,制作卡牌模型
制作卡片的主要思路:
- 卡片有哪些属性要传入数据:头像id(本章暂时先不做),标题(cardTitle)以及摘要(cardSummary)
- 生成传入和传出接口(getter&setter)
代码如下:
(注:头像id、三个按钮都先不做)
public class Card {
//定义一个卡片的属性
private String cardTitle; //卡片标题
private String cardSummary; //卡片的摘要内容(摘要=用户名+密码+备注)
// 摘要是后面学习的一个重点,在完成一个卡片的填写后,这个卡片的摘要会自动生成。卡片摘要也是软件搜索功能的重要依据
//private ImageView cardHead; //头像
//private ImageButton cardMenu; //卡片菜单按钮
//private ImageButton copyName; //复制按钮,复制用户名
//private ImageButton copyPassword; //复制按钮,复制密码
//一张卡片完整的属性为:
public Card(String cardTitle, String cardSummary) {
//将外界传入的3个参数赋值给Card内部类
//this.cardHead = cardHead;
this.cardTitle = cardTitle;
this.cardSummary = cardSummary;
}
//创建卡片的Getter&Setter方法
public String getCardSummary() {
return cardSummary;
}
public void setCardSummary(String cardSummary) {
this.cardSummary = cardSummary;
}
public String getCardTitle() {
return cardTitle;
}
public void setCardTitle(String cardTitle) {
this.cardTitle = cardTitle;
}
}
getter和setter的快捷键:
- 写好
private String cardTitle
和private String cardSummary
后点击鼠标右键 - 右键列表 - Generate - Getter and Setter - 弹出对话框 - 按住Ctrl选择cardTitle和cardSummary - OK
- 自动生成这两个变量的getter和setter方法
5.2. 然后制作卡片适配器CardAdapter
适配器是连接数据(比如说json数组)和模板(比如这里的Card)之间的桥梁,用一个不太准确的说法来说,它的作用就是把数组切片并把每一片贴到一张张白卡上面,然后RecyclerView利用setAdapter()方法就能很轻松的载入这些写好的卡片了。
新建CardAdapter.java,代码如下:
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> {
/* 什么是适配器(Adapter)?
适配器为原料做适配工作。
用一种形象的说法,比如把适配器看作一座工厂,适配器给卡片们组配上了手臂(ViewHolder),更方便卡片去抓握数据了,这就是适配的好处。
*/
private Context context; //定义一个context,由于没有赋值,所以现在是null的状态
private List<Card> cardList;
public CardAdapter(List<Card> cardList) {
this.cardList = cardList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//首先传入context
if (context == null) {
context = parent.getContext(); //这里的parent就是指列表页(RecyclerView)所属的类了
}
View card = LayoutInflater.from(context).inflate(R.layout.card, parent, false);
//inflate的第三个参数表示是否连接该布局和父控件。由于系统已经插入了这个布局到父控件,所以设为false(若写true则会产生一个多余的parent)
return new ViewHolder(card); //获得了一张白卡
}
static class ViewHolder extends RecyclerView.ViewHolder {
CardView cardView;
//ImageView cardHead;
TextView cardTitle;
TextView cardSummary;
public ViewHolder(View view) {
//引入外界的item,在卡片中开始实例化
super(view);
cardView = (CardView) view;
//cardHead = (ImageView)view.findViewById(R.id.cardHead);
cardTitle = (TextView) view.findViewById(R.id.cardTitle);
cardSummary = (TextView) view.findViewById(R.id.cardSummary);
}
}
//开始给白卡绑数据
@Override
public void onBindViewHolder(ViewHolder whiteCard, int positon) {
Card card = cardList.get(positon); //通过位置参数判断用户点的是哪个卡片
//whiteCard.cardHead.set...??? //头像图片还没做,这里先记个书签,以后再说
whiteCard.cardTitle.setText(card.getCardTitle());
whiteCard.cardSummary.setText(card.getCardSummary());
}
//获取列表中卡片总数量
@Override
public int getItemCount() {
return cardList.size();
}
}
6. 列表的加载
列表的加载在MainActivity中进行,为了让思路更清晰,这里只在MainActivity中引用加载的方法,而具体的代码则新建一个PageRender.java文件放置。
6.1. 首先在MainActivity.java中的onCreate()里面新增一行代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
//为左分页加载卡片列表
new PageRender(view1); //新建一个PageRender.java写相关代码
}
...
}
注意我们传入了一个view1
参数,它是由View view1 = inflater.inflate(R.layout.page_item_all, null);
得来的,也就是左分页的布局(我们在开头也提到过,该布局只有一个RecyclerView,背景是浅灰色的)。
6.2. 然后新建PageRender.java处理列表加载业务
这是本章的最后一个重点。代码如下:
public class PageRender extends MainActivity {
//注意继承自MainActivity
private List<Card> cardList = new ArrayList<Card>();
private CardAdapter cardAdapter;
public PageRender(View page) {
//首先绑定RecyclerView
RecyclerView recyclerView = (RecyclerView) page.findViewById(R.id.recyclerView);
/* 关于findViewById是一个重点
特别注意,必须写成page.findViewById,不能把前缀page省略掉。
如果省略掉,虽然编译时不会报错,但运行时程序会崩溃。
通过观察recyclerView的值会发现,崩溃时其值为0,也就是根本不存在这个对象。这是为什么呢?
原因在于findViewById其实是“view.findViewById”的缩写,这个默认的view是什么呢?就是你当前所在的布局,也就是layout_main.xml,显然我们在layout_main.xml里是找不到RecyclerView控件的,因为RecyclerView在page_item_all.xml布局里
参考:http://blog.sina.cn/dpool/blog/s/blog_95bc05000101bhxe.html
*/
//初始化数据,将原数据按card为单位切分,并装载到数组里
initCards();
cardAdapter = new CardAdapter(cardList); //根据数据组建出一个个写好的卡片
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager); //在recyclerView里面放置一个线性布局,以使卡片呈线性排列
recyclerView.setAdapter(cardAdapter); //将卡片们装载到RecyclerView的布局中
}
//先自建一组数据来测试一下……
private void initCards() {
cardList.clear();
//遍历装载
cardList.add(new Card("Title1", "name:aaa\npassword:aaa\nnote:aaa"));
cardList.add(new Card("Title2", "name:bbb\npassword:bbb\nnote:bbb"));
cardList.add(new Card("Title3", "name:ccc\npassword:ccc\nnote:ccc"));
cardList.add(new Card("Title4", "name:ddd\npassword:ddd\nnote:ddd\nextra:ddd")); //验证是否只显示3行
cardList.add(new Card("Title5", "name:eee\npassword:eee\nnote:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")); //验证多余字符是否显示为省略号
cardList.add(new Card("Title6", "name:fff")); //验证单行字符是否可以占据3行的空间
}
}
至此左分页布局代码全部完成,不过很多功能(包括基本功能)都还没有实现,随着后续的学习我们还会不断完善这个布局,直到应用到实战中去。
下章会写右分页布局,然后很快就能联系左分页的知识模拟完整的数据加载逻辑了。
相关参考
- 卡片式布局:郭霖《第一行代码 第二版》p431
- RecyclerView:郭霖《第一行代码 第二版》p122
- http://blog.csdn.net/qq_31403303/article/details/51506524
- findViewById http://blog.sina.cn/dpool/blog/s/blog_95bc05000101bhxe.html
日志
2017年11月21日
- 【新增】左分页制作列表界面
网友评论