03 Anykey左分页布局RecyclerView

目录

1. 首先看一下最终的效果图

page1

本章我们只制作左分页,这是一个以卡片为单位组成的可滚动的列表,我们需要用到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”同步一下:
Sync Now!

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制作卡片模板

在卡片中包含以下属性:

  1. 头像(头像默认是由标题的拼音的首字母生成的)
  2. 标题
  3. 摘要(摘要信息=帐号+密码+备注)
  4. 功能按钮:
    1. 弹出菜单
    2. 复制帐号到剪切板
    3. 复制密码到剪切板

代码如下:

<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>

卡片预览图如下:

CardView

至此两个布局文件都写好了。下面才是本章的重点,为了方便理解,我们分为“卡片的属性及适配”和“列表的加载”两大部分来讲,其中适配器和模板属性的建立是所有业务处理的基础,也是本章的难点。

5. 卡片的属性及适配

5.1. 首先新建Card.java,制作卡牌模型

制作卡片的主要思路:

  1. 卡片有哪些属性要传入数据:头像id(本章暂时先不做),标题(cardTitle)以及摘要(cardSummary)
  2. 生成传入和传出接口(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的快捷键:

  1. 写好private String cardTitleprivate String cardSummary后点击鼠标右键
  2. 右键列表 - Generate - Getter and Setter - 弹出对话框 - 按住Ctrl选择cardTitle和cardSummary - OK
  3. 自动生成这两个变量的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行的空间
    }

}

至此左分页布局代码全部完成,不过很多功能(包括基本功能)都还没有实现,随着后续的学习我们还会不断完善这个布局,直到应用到实战中去。

下章会写右分页布局,然后很快就能联系左分页的知识模拟完整的数据加载逻辑了。


相关参考

  1. 卡片式布局:郭霖《第一行代码 第二版》p431
  2. RecyclerView:郭霖《第一行代码 第二版》p122
  3. http://blog.csdn.net/qq_31403303/article/details/51506524
  4. findViewById http://blog.sina.cn/dpool/blog/s/blog_95bc05000101bhxe.html

日志

2017年11月21日
1. 【新增】左分页制作列表界面

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值