# **漫画阅读(滑动翻页)**
**本节说的是哪一模块?**
漫画阅读模块
**本节要讲的知识点有哪些?**
1.滑动翻页特效——左滑动,右滑动。
2.图片切割裁剪——适应手机屏幕。
**标题索引:**
翻页特效的实现原理
recyclerview的简单介绍
recyclerview能实现哪些效果
recyclerview的简单用法
图片切割裁剪原理
ImageVIew准备工作
裁剪图片的原理
快速裁剪图片——使用框架
## **翻页特效**
首先,滑动翻页这种功能挺基础的,通常有多种做法,ViewPager、LIstVIew、RecyclerView,GridView等等,即使用Linerlayout也可以。
我们不要纠结使用哪一种方式,去实现翻页特效,
当前,最重要的是,根据我以及诸位很多同行的经验来看,使用RecyclerVIew是大势所趋,也是最方便快捷的。
优点不展开讲了。在此处,只需要知道,Android有很多方式,都可以实现滑动翻页效果;并且,使用RecyclerVIew去实现滑动特效,是比较优秀的做法。
首先,我们来看,RecyclerView能实现哪些常见的UI布局:
**特效1:上下滑动特效(经典上下list列表)**
![](https://box.kancloud.cn/a2344be3ff367005c33ee87dc4479c49_273x488.png)
**特效2:左右滑动特效(经典的手指触摸水平滑动列表)**
![](https://box.kancloud.cn/ad2bec5898a464d701cb3e93a11ccf0f_527x519.png)
ok,上述两点只是RecyclerView最普通的用法,但是已经足够理解我们今天探讨的话题了——快点想想,我们本节讨论的核心内容是啥?
左右滑动特效!
> 注:在此次研究的漫画APP中,阅读模块对应的文件是ReaderActivity,在该文件当中也创建、使用了RecyclerVIew。
我们从漫画APP的阅读模块当中,学习本节的核心内容:
## RecyclerView的简单使用
以漫画APP的阅读模块ReaderActivity为例,我们抓住RecyclerVIew出现的身影,仔细探究滑动翻页的实现过程:
~~~
public abstract class ReaderActivity extends BaseActivity implements OnTapGestureListener,
OnProgressChangeListener, OnLazyLoadListener, ReaderView {
.....
@BindView(R.id.reader_recycler_view) RecyclerView mRecyclerView;
......
protected PreCacheLayoutManager mLayoutManager;
protected ReaderAdapter mReaderAdapter;
@Override
protected void initView() {
.....
initLayoutManager();
initReaderAdapter();
mRecyclerView.setItemAnimator(null);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mReaderAdapter);
mRecyclerView.setItemViewCacheSize(2);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
mLastDx = dx;
mLastDy = dy;
}
});
....
}
....
private void initLayoutManager() {
mLayoutManager = new PreCacheLayoutManager(this);
mLayoutManager.setOrientation(turn == PreferenceManager.READER_TURN_ATB ? LinearLayoutManager.VERTICAL : LinearLayoutManager.HORIZONTAL);
mLayoutManager.setReverseLayout(turn == PreferenceManager.READER_TURN_RTL);
mLayoutManager.setExtraSpace(2);
}
.....
private void initReaderAdapter() {
....
mReaderAdapter = new ReaderAdapter(this, new LinkedList<ImageUrl>());
.....
}
}
~~~
1. 1.@BindView(R.id.reader_recycler_view) RecyclerView mRecyclerView;是RecyclerView初始化的过程,前面@BindView是高级框架的用法,本质结果,等同于findViewById。在这里,我们只需要知道,RecyclerView第一次出现,就已经从实例化对象了。
2. 2.mRecyclerView.setAdapter(mReaderAdapter); RecyclerView通过setAdapter()设置了每一个子元素的“显示器”(把RecyclerView的每一个list子元素,当成一个小的显示器,Adapter的作用,就是存储、绑定、将这些数据显示到RecyclerVIew),漫画图片,就是在此时,被附着到RecyclerView上的。
3. 3.mRecyclerView.setLayoutManager(mLayoutManager);RecyclerView通过setLayoutManager()函数,可以设置列表显示的方向:即水平左右滑动;竖直上下滑动。
4. 4.具体的,阅读页面滑动的方向,是由LayoutManager对象决定的,我们可以在此处看到:mLayoutManager.setOrientation(turn == PreferenceManager.READER_TURN_ATB ? LinearLayoutManager.VERTICAL : LinearLayoutManager.HORIZONTAL);
> 新手可能看不到此处的含义,但至少VERTICAL和HORIZONTAL查查字典还是可以理解的:水平和竖直。
水平显示RecyclerView每一个子元素,产生的效果,就是左右滑动的翻页特效;
竖直显示,产生的效果,将是普通新闻列表页那样,上下滑动翻页。
至此,我们抓住了左右滑动的两个核心:RecyclerView和LayoutManager,前者提供了多个子元素**滑动**的能力,后者提供了控制布局**方向**的能力。
所以说,要实现阅读滑动翻页特效,两者缺一不可。
而回到阅读模块最主要的功能:显示漫画图片上。
在显示漫画图片这的实现过程中,功劳最大的,当属于第二步,**传递给RecyclerView的Adapter**,是它,控制了view显示的每一个子元素,也是它,设置了子元素与屏幕宽高同样大小,每一个子元素占满屏幕,左右滑动,看上去,就等同于翻页浏览了。
接着,我们来聊图片切割裁剪技术
## **图片切割裁剪**
要讨论图片切割,必须先讨论图片的容器——ImageView
ImageView,首先并没有包含任何图片相关的属性,换言之,如果没有一张jpg图片,ImageView不可能自己显示出一些图像的。(当然还有Canvas,先不细谈)
先说ImageView默认的切割方式
假设ImageVIew宽和高分别为360和720,图片文件对应的Bitmap对象宽和高为480和920.
ImageView直接setbitmap()
![](https://box.kancloud.cn/a96f5fe550fea0524264b0d4a9090d32_745x565.png)
显示的结果是:
![](https://box.kancloud.cn/674dfcdece828fdd9001b2c0f6b56909_368x554.png)
图很丑陋,但是言简意赅。是的,只有蓝色方框里的内容,会被显示到ImageView上,其他部分,只是因为“ImageVIew放不下了”,而没有显示出来——
这就造成了图片被切割裁剪的视觉效果,也是第一种实现方式,通过控制ImageView的属性(动态设置其宽高),来实现切割、裁剪效果。
ImageView有很多属性和函数,都可以动态的实现切割、裁剪效果:比如matrix矩阵,设置图片Bitmap缩放的比例;比如通过setWidth和setHeight,设置Imageview的宽和高度,匹配图片的宽度和高度;
既然ImageView可以缩放、匹配图片Bitmap的宽度和高度,那么是否转换思路,缩放图片源Bitmap试试呢?
答案是肯定的,当然可以,以及推荐这样做
直接裁剪图片Bitmap,可以实现这样的效果:
![](https://box.kancloud.cn/420af098429d408eecad784fe93e8c2d_756x420.png)
可以看到,图片Bitmap,默认宽高大于ImageView,直接显示至ImageVIew会被裁剪,以至于ImageViw只能显示部分图片内容;
而在我们对Bitmap对象处理之后,图片恰好显示至手机屏幕上——当然,左右留白是允许的,也是等比缩放图片Bitmap不得不面对的结果,这也是最好裁剪缩放效果了。
换一张图,我们试试
![](https://box.kancloud.cn/872abe7e46e63e1d3b5a0e5a4fa13837_747x336.png)
总结,为了使得图片完美的裁剪,匹配手机屏幕的宽高,我们必须采用等比裁剪的方式:
保持图片的长边与手机屏幕一致性,同比例缩放图片源Bitmap的宽高。
ok
那么我们如何通过缩放Bitmap,达到裁剪、切割的效果呢?
假设我们读取一张res/drawable/目录下的图片文件
~~~
/**
* 通过资源id转化成Bitmap
*
* @param context
* @param resId
* @return
*/
public static Bitmap ReadBitmapById(Context context, int resId)
{
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
~~~
通过ReadBitmapById,我们获取到了图片对象BItmap
献上一段代码,我们直接压缩图片的宽度和高度
``
/**
* 按照一定的宽高比例裁剪图片
*
* @param bitmap
* @param num1
* 长边的比例
* @param num2
* 短边的比例
* @return
*/
public static Bitmap ImageCrop(Bitmap bitmap, int num1, int num2,
boolean isRecycled)
{
if (bitmap == null)
{
return null;
}
int w = bitmap.getWidth(); // 得到图片的宽,高
int h = bitmap.getHeight();
int retX, retY;
int nw, nh;
if (w > h)
{
if (h > w * num2 / num1)
{
nw = w;
nh = w * num2 / num1;
retX = 0;
retY = (h - nh) / 2;
} else
{
nw = h * num1 / num2;
nh = h;
retX = (w - nw) / 2;
retY = 0;
}
} else
{
if (w > h * num2 / num1)
{
nh = h;
nw = h * num2 / num1;
retY = 0;
retX = (w - nw) / 2;
} else
{
nh = w * num1 / num2;
nw = w;
retY = (h - nh) / 2;
retX = 0;
}
}
Bitmap bmp = Bitmap.createBitmap(bitmap, retX, retY, nw, nh, null,
false);
if (isRecycled && bitmap != null && !bitmap.equals(bmp)
&& !bitmap.isRecycled())
{
bitmap.recycle();
bitmap = null;
}
return bmp;// Bitmap.createBitmap(bitmap, retX, retY, nw, nh, null,
// false);
}
``
通过ImageCrop()我们可以裁剪切割图片Bitmap的宽度和高度。
这样,就完美得实现了图片裁剪和切割。