前言
常見的圖片裁剪有兩種,一種是圖片固定,裁剪框移動(dòng)放縮來確定裁剪區(qū)域,早期見的比較多,缺點(diǎn)在于不能直接預(yù)覽裁剪后的效果;還有一種現(xiàn)在比較普遍了,就是裁剪框固定,直接拖動(dòng)縮放圖片,便于預(yù)覽裁剪結(jié)果。
我做的這個(gè)控件屬于后者。一般來說,做圖片裁剪的思路無外乎是先監(jiān)聽手勢(shì),獲取坐標(biāo),再對(duì)圖片變形,最后確定裁剪區(qū)域的坐標(biāo)對(duì)位圖進(jìn)行裁剪,最后保存圖片到本地。我嘛還是個(gè)技術(shù)小白,一想到要監(jiān)控手勢(shì)這些就頭疼,碰巧項(xiàng)目之前為了做查看大圖而引入了大名鼎鼎的第三方圖片查看控件——PhotoView(使用步驟參考這篇文章:Android PhotoView使用步驟實(shí)例詳解)。于是轉(zhuǎn)念一想,能不能把到圖片變形為止的前幾步交給PhotoView來搞定,我只要負(fù)責(zé)確定確定裁剪區(qū)域后面這幾步呢。后來掉了好幾個(gè)坑導(dǎo)致偷懶也沒輕松多少其實(shí)ε=(´ο`*)))唉~
先簡要介紹一下設(shè)計(jì)思路,如上圖所示,主要分為兩部分,上層是遮罩(也可以理解為是裁剪框),用于預(yù)覽裁剪后的效果;下層是PhotoView,這里多包了一層改為正方形顯示。
下面是遮罩的代碼,比較簡單,這里就不贅述了。
/** * Created by MandyLu on 2018/7/14. * 圓形裁剪框 */public class CircleCropView extends View { public final int CIRCLE_MARGIN = 50; public CircleCropView(Context context) { super(context); } public CircleCropView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public CircleCropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public CircleCropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); } @RequiresApi(api = Build.VERSION_CODES.O) @Override protected void onDraw(Canvas canvas) { canvas.save(); Path path = new Path(); Rect viewDrawingRect = new Rect(); getDrawingRect(viewDrawingRect); float radius = viewDrawingRect.width() / 2 - CIRCLE_MARGIN; path.addCircle(viewDrawingRect.left + radius + CIRCLE_MARGIN, viewDrawingRect.top + radius + CIRCLE_MARGIN, radius, Path.Direction.CW); Paint outsidePaint = new Paint(); outsidePaint.setAntiAlias(true); outsidePaint.setARGB(151, 0, 0, 0); canvas.clipPath(path, Region.Op.DIFFERENCE); canvas.drawRect(viewDrawingRect, outsidePaint); canvas.restore(); }}
SquarePhotoView只是在PhotoView的基礎(chǔ)上改了長寬,重寫一下onMeasure方法即可:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); }
那么現(xiàn)在最關(guān)鍵的一步,就是從PhotoView獲取當(dāng)前圖片顯示區(qū)域的Drawable或Bitmap了。粗略看了一下PhotoView的函數(shù),并沒有找到能用的(囧)。解決第一個(gè)坑的笨辦法就是,自己動(dòng)手豐衣足食——直接拿原圖的bitmap,然后問PhotoView要當(dāng)前圖片的變形矩陣,自個(gè)兒通過矩陣一步步變形拿到對(duì)應(yīng)的位圖。
思路其實(shí)是沒問題的,然而第二個(gè)坑又出現(xiàn)了(囧)。這里的變形矩陣,我最早百度的結(jié)果是getSuppMatrix,源碼我沒有細(xì)看,但掉坑的過程中據(jù)我觀察,猜測(cè)應(yīng)該是對(duì)應(yīng)最新一次的手勢(shì)變形結(jié)果(不確定= =,也可能是其他坑綜合導(dǎo)致的錯(cuò)誤結(jié)果)。總之最后我查了一會(huì)源碼,最終確定用的是getDisplayMatrix。
緊接著是第三個(gè)坑,坑多了就習(xí)慣了。矩陣中的XY位移量,我起初以為是顯示區(qū)域中心相對(duì)于原圖中心的位移,即如果僅有縮放操作的話,位移應(yīng)該為0。但實(shí)際通過特殊位置(例如取四個(gè)頂點(diǎn))的裁剪結(jié)果來看,這里的XY位移量實(shí)際最后顯示區(qū)域左上角的點(diǎn)相對(duì)原點(diǎn)(即原圖左上角)的位移,簡單點(diǎn)說,可以把位移量作為最終顯示區(qū)域左上角的坐標(biāo)。
然后我就迎來了第四個(gè)坑(
注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到Android開發(fā)頻道。
|
新聞熱點(diǎn)
疑難解答
網(wǎng)友關(guān)注