# **漫畫閱讀(滑動翻頁)**
**本節說的是哪一模塊?**
漫畫閱讀模塊
**本節要講的知識點有哪些?**
1.滑動翻頁特效——左滑動,右滑動。
2.圖片切割裁剪——適應手機屏幕。
**標題索引:**
翻頁特效的實現原理
recyclerview的簡單介紹
recyclerview能實現哪些效果
recyclerview的簡單用法
圖片切割裁剪原理
ImageVIew準備工作
裁剪圖片的原理
快速裁剪圖片——使用框架
## **翻頁特效**
首先,滑動翻頁這種功能挺基礎的,通常有多種做法,ViewPager、LIstVIew、RecyclerView,GridView等等,即使用Linerlayout也可以。
我們不要糾結使用哪一種方式,去實現翻頁特效,
當前,最重要的是,根據我以及諸位很多同行的經驗來看,使用RecyclerVIew是大勢所趨,也是最方便快捷的。
優點不展開講了。在此處,只需要知道,Android有很多方式,都可以實現滑動翻頁效果;并且,使用RecyclerVIew去實現滑動特效,是比較優秀的做法。
首先,我們來看,RecyclerView能實現哪些常見的UI布局:
**特效1:上下滑動特效(經典上下list列表)**

**特效2:左右滑動特效(經典的手指觸摸水平滑動列表)**

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

顯示的結果是:

圖很丑陋,但是言簡意賅。是的,只有藍色方框里的內容,會被顯示到ImageView上,其他部分,只是因為“ImageVIew放不下了”,而沒有顯示出來——
這就造成了圖片被切割裁剪的視覺效果,也是第一種實現方式,通過控制ImageView的屬性(動態設置其寬高),來實現切割、裁剪效果。
ImageView有很多屬性和函數,都可以動態的實現切割、裁剪效果:比如matrix矩陣,設置圖片Bitmap縮放的比例;比如通過setWidth和setHeight,設置Imageview的寬和高度,匹配圖片的寬度和高度;
既然ImageView可以縮放、匹配圖片Bitmap的寬度和高度,那么是否轉換思路,縮放圖片源Bitmap試試呢?
答案是肯定的,當然可以,以及推薦這樣做
直接裁剪圖片Bitmap,可以實現這樣的效果:

可以看到,圖片Bitmap,默認寬高大于ImageView,直接顯示至ImageVIew會被裁剪,以至于ImageViw只能顯示部分圖片內容;
而在我們對Bitmap對象處理之后,圖片恰好顯示至手機屏幕上——當然,左右留白是允許的,也是等比縮放圖片Bitmap不得不面對的結果,這也是最好裁剪縮放效果了。
換一張圖,我們試試

總結,為了使得圖片完美的裁剪,匹配手機屏幕的寬高,我們必須采用等比裁剪的方式:
保持圖片的長邊與手機屏幕一致性,同比例縮放圖片源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的寬度和高度。
這樣,就完美得實現了圖片裁剪和切割。