ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# **漫画阅读(滑动翻页)** **本节说的是哪一模块?** 漫画阅读模块 **本节要讲的知识点有哪些?** 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的宽度和高度。 这样,就完美得实现了图片裁剪和切割。