• 【RecyclerView与Glide】实现一个Android电子书阅读APP


    http://www.cnblogs.com/xfangs/

    欢迎在本文下方评论,小方很需要鼓励支持!!!

    本系列教程仅供学习交流

    小说阅读器最终实现效果见 一篇博文


    前言

    在上一篇文章中,我们实现了ViewPager的基本功能,按照计划,制作咱们的电子书阅读app需要使用ViewPager插入两页视图,一个用来显示当前书架,一个用来展示不同的分类。这一节,我们将在被标记为find的页面上实现分类选项。

    涉及组件或框架:RecyclerView、Glide


    首先·布局

    同样的,在这里,小方因为水平有限只能简单介绍RecyclerView的基本使用方法,涉及到更深奥的操作部分,就无能为力了。

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:orientation="vertical" android:layout_width="match_parent"
     4     android:layout_height="match_parent">
     5 
     6     <android.support.v7.widget.RecyclerView
     7         android:id="@+id/recyler_view_find_book"
     8         android:layout_margin="8dp"
     9         android:layout_width="match_parent"
    10         android:layout_height="match_parent"/>
    11 
    12 </LinearLayout>

    没有复杂的步骤,我们只需要把RecyclerView加入到之前ViewPager的两个布局之一中,就完成了整个列表布局。

    当然,你也许会遇到一些意外。

    这是说明我们还没有引入RecyclerView这个库,进入Design界面,从左边的组件中找到RecyclerView,单击,将会弹出选择框。

    在加入了库之后,我们就能看到RecyclerView正确无误的显示在界面上了。

    适配器

    这次我们工作的主战场在上一节提到的Fragment,也就是ViewPager的两个页面之一。

    回顾一下代码。

     1     public static class FindBooksFragment extends Fragment {
     2 
     3         public FindBooksFragment() {
     4         }
     5 
     6         @Override
     7         public View onCreateView(final LayoutInflater inflater, ViewGroup container,
     8                                  Bundle savedInstanceState) {
     9 
    10             View rootView = inflater.inflate(R.layout.pager_book_find, container, false);
    11 
    12             return rootView;
    13         }
    14 
    15     }

    之前说了,ViewPager会在创建这个页面的时候调用onCreateView这个函数,所以我们在这里进行初始化操作。

    在这之前,我们需要先完成RecyclerView的适配器,同样的,这里适配器起到将数据和页面结合到一起的作用,具体地说,假设一个列表中的项目可以分为三类,我们就为这三类元素分别设计布局,然后将每一项的数据传给适配器,适配器可以根据数据选择对应的布局,然后把每一项显示出来。

    首先,新建一个类。

    关于RecyclerView的适配器,网络上已经有很多博客描述了, 随意找一篇看的过去的文章,先大体了解一下。

    。。。搜索时间。。。

    了解过后,我们知道需要为列表项写布局,这在前面也间接提到了。

    那么,新建一个布局文件

    book_find_item.xml

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
     3     xmlns:tools="http://schemas.android.com/tools"
     4     android:id="@+id/bookFind_cardview"
     5     android:layout_width="match_parent"
     6     android:layout_height="wrap_content"
     7     android:layout_margin="4dp"
     8     android:foreground="?android:attr/selectableItemBackground"
     9     android:clickable="true">
    10 
    11     <ImageView
    12         android:id="@+id/bookFind_image"
    13         android:layout_width="match_parent"
    14         android:layout_height="150dp"
    15         android:scaleType="centerCrop"
    16         tools:src="@color/cardview_dark_background"/>
    17 
    18     <TextView
    19         android:id="@+id/bookFind_class"
    20         android:layout_width="match_parent"
    21         android:layout_height="match_parent"
    22         android:background="#00000000"
    23         android:textColor="#FFFFFF"
    24         android:textStyle="normal|bold"
    25         android:textSize="14sp"
    26         android:gravity="center"
    27         tools:text="123"/>
    28 
    29 </android.support.v7.widget.CardView>

     在这里,我们又使用了一个新的组件,CardView,它体现了安卓最新设计风格,恰到好处的圆角、逼真的阴影、点击特效、等等,有多种属性可供调整。

    尤其要说的是上面代码中加粗的字体,一个新的命名空间 tools ,在使用它之前,我们首先要在最外层的部件上声明。

    只要打出前面几个字母,android studio 就会自动补全好。

    tools 命名空间提供了测试的效果,以他为名号的属性在程序运行期间是被忽略的,只供测试预览使用,使得开发更加方便了。下图就是我们预览时得到的效果,

    当程序运行起来时, 还会是如图所示的样子吗?

    (必然不是)

    制作好了布局文件,我们就可以开始对适配器进行编写了。

    首先制作一个接口,用来获取点击事件。

     1     private OnItemClickListener listener;
     2 
     3     public void setOnItemClickListener(OnItemClickListener listener) {
     4         this.listener = listener;
     5     }
     6 
     7     public interface OnItemClickListener {
     8         void onItemClick(View view, int position);
     9         void onItemLongClick(View view, int position);
    10     }

    然后在适配器的类中再新建一个类。

     1     static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
     2         View tocView;
     3         private TextView textview_bookFindClass;
     4         private ImageView imageView;
     5         private OnItemClickListener listener;
     6 
     7         public ViewHolder(View itemView, OnItemClickListener l) {
     8             super(itemView);
     9             tocView = itemView;
    10             textview_bookFindClass = (TextView) itemView.findViewById(R.id.bookFind_class);
    11             imageView = (ImageView) itemView.findViewById(R.id.bookFind_image);
    12             listener = l;
    13             itemView.setOnClickListener(this);
    14             itemView.setOnLongClickListener(this);
    15         }
    16 
    17 
    18         @Override
    19         public void onClick(View v) {
    20             if (listener != null) {
    21                 listener.onItemClick(v, getAdapterPosition());
    22             }
    23         }
    24 
    25         @Override
    26         public boolean onLongClick(View v) {
    27             if (listener != null) {
    28                 listener.onItemLongClick(v, getAdapterPosition());
    29             }
    30             return false;
    31         }
    32     }

    可能略显复杂,这个类的作用是缓存列表项,具体比较低层的东西小方就不太清楚了,上网搜了搜,就不现学现卖了,欢迎有兴趣的同学一起讨论。

    ViewHolder 类继承自 RecyclerView.ViewHolder,为了实现点击监听,还要接入两个接口,一个是单击的接口,一个是长按的接口。

    从构造函数开始一点一点理解,构造函数接收两个参数,一个是每一项的View,一个是每一项的点击监听器。初始化组件之后,设置监听器,按照之前设置的接口的设定,将View和位置传给借口。

    如果还不是很清楚,那么先将代码复制过去,使用次数多了,自然就理解了。

    然后,在AdapterBookFind这个类中,完成它的构造函数,这里是根据需要自行设定的,在这里我们需要将分类名称传给适配器,显示在每一项上,所以传送了一个字符串列表。你也可以根据自己的需要传送想要传送的数据。

    1     private ArrayList<String> myCategory;
    2 
    3     public AdapterBookFind(ArrayList<String> category) {
    4         this.myCategory = category;
    5     }

    下面我们需要重写一个函数,用来获取一个ViewHolder的实例。

    1     private Context context;
    2     @Override
    3     public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
    4         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.book_find_item, parent, false);
    5         context = parent.getContext();
    6         final ViewHolder holder = new ViewHolder(view, listener);
    7 
    8         return holder;
    9     }

    在这里,我们又声明了一个叫做context的变量,并初始化,具体作用后面再表。

    1     @Override
    2     public int getItemCount() {
    3         return myCategory.size();
    4     }

    类似ViewPager的适配器一样,我们同样需要重写取数目的函数,根据数据返回一个值。

    马上就到最后一步了,我们将要对列表的每一项设置不同的内容。

     1     @Override
     2     public void onBindViewHolder(final ViewHolder holder, int position) {
     3         holder.textview_bookFindClass.setText(myCategory.get(position));
     4 
     5         switch (position) {
     6             case 0:
     7                 Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imageView);
     8                 break;
     9             case 1:
    10                 Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imageView);
    11                 break;
    12             case 2:
    13                 Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imageView);
    14                 break;
    15             case 3:
    16                 Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imageView);
    17                 break;
    18             case 4:
    19                 Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imageView);
    20                 break;
    21             case 5:
    22                 Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imageView);
    23                 break;
    24             case 6:
    25                 Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imageView);
    26                 break;
    27             case 7:
    28                 Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imageView);
    29                 break;
    30             default:
    31 
    32                 break;
    33         }
    34         holder.imageView.setColorFilter(Color.parseColor("#55555555"));
    35 
    36     }

    因为我们的布局文件中只有两个组件能体现数据的内容,TextView和ImageView。

    第三行是根据不同的位置选择不同的字符串进行设置,没有难度。

    在说switch之前,我们先来看最后一行 

    holder.imageView.setColorFilter(Color.parseColor("#55555555"));
    

     给imageView设置滤色,因为我想要在图片之上显示一个白色的文字,适当的使图片变暗,能凸显文字。

    switch当然也能看得懂,但是出现了一个陌生的东西 Glide。

    实际不难理解,这个switch的作用还是根据不同的位置选择不同的文件进行显示,我们当然可以使用ImageView自带的方法来设置图片,但是ImageView只能设置本地或者APK之中的图片资源,对于网络图片或者诸多不支持的图片类型(如GIF)就毫无办法,不仅如此,当本地图片较大时,还会不可避免的出现卡顿现象。

    这时候Glide出现了,它能快速加载各种图片,根据显示大小自动对图片进行压缩,同样一条语句,如果传入图片链接还能加载网络图片,并缓存。

    Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imageView);
    

    如上面代码所示的一句话,就能加载APK资源中的一张图片,这里我们只有图标一个文件,所以将图标文件传给了imageView。

    不过如果你只是把刚刚的代码复制粘贴到你的工程,你的程序应该会报错,显示没有找到Glide这个东西,这是因为Glide不是官方提供的,我们需要手动引入。

    找到上面那个文件。

    加入最后一行,选择同步。

    这样,我们就引入了Glide。


    整合

     1     public static class FindBooksFragment extends Fragment {
     2 
     3         public FindBooksFragment() {
     4         }
     5 
     6         private RecyclerView recyclerView;
     7         private StaggeredGridLayoutManager staggeredGridLayoutManager;
     8         private AdapterBookFind bookAdapterBookFind;
     9 
    10         @Override
    11         public View onCreateView(final LayoutInflater inflater, ViewGroup container,
    12                                  Bundle savedInstanceState) {
    13 
    14             final ArrayList<String> bookClass = new ArrayList<>();
    15             bookClass.add("//玄幻");
    16             bookClass.add("//武侠");
    17             bookClass.add("//都市");
    18             bookClass.add("//历史");
    19             bookClass.add("//游戏");
    20             bookClass.add("//科幻");
    21             bookClass.add("//女生");
    22             bookClass.add("//所有");
    23 
    24             View rootView = inflater.inflate(R.layout.pager_book_find, container, false);
    25             recyclerView = (RecyclerView) rootView.findViewById(R.id.recyler_view_find_book);
    26 
    27             staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
    28             recyclerView.setLayoutManager(staggeredGridLayoutManager);
    29             bookAdapterBookFind = new AdapterBookFind(bookClass);
    30             recyclerView.setAdapter(bookAdapterBookFind);
    31 
    32             bookAdapterBookFind.setOnItemClickListener(new AdapterBookFind.OnItemClickListener() {
    33                 @Override
    34                 public void onItemClick(View view, int position) {
    35                     Log.e(TAG, "onItemClick: 111" );
    36                 }
    37 
    38                 @Override
    39                 public void onItemLongClick(View view, int position) {
    40                     Log.e(TAG, "onItemLongClick: 222" );
    41                 }
    42             });
    43 
    44             return rootView;
    45         }
    46 
    47         private static final String TAG = "FindBooksFragment";
    48 
    49     }

    首先初始化我们的字符串列表。StaggeredGridLayoutManager是我们列表的布局管理器,安卓提供了三种布局管理器,用来实现不同的列表效果,多行列表,瀑布流列表,不同的滑动方向都可以进行设置,大家可以百度其他的布局管理器。

    所以在27-30行,我们分别为RecyclerView设置了列表管理器和适配器,32行开始,为适配器设置点击监听器,咱们暂时不设置功能,使用log工具将其输出在Android Monitor(log语句有快捷键)。


    效果

    运行程序,你将见到

    我们又完成了新的一节,现在可以尝试更换不同的图片,尝试使用网络图片(不要忘记加入网络访问权限)。

    未完待续...下一篇文章讲述使用 litePal 完成书架,敬请期待!!!

  • 相关阅读:
    07 JavaWeb
    06 XML编程(CRUD)
    05 XML
    04 DOM一窥
    03 Javascript初识
    02 CSS/javaScript
    01 HTML基础
    09_IO流
    08_集合概述
    07_基本数据类型和包装类
  • 原文地址:https://www.cnblogs.com/xfangs/p/6537524.html
Copyright © 2020-2023  润新知