項(xiàng)目中聊天模塊要實(shí)現(xiàn)圖片消息和視頻消息帶尖嘴的效果。
老的項(xiàng)目中實(shí)現(xiàn)方式是 重寫ImageView的onDraw方法,通過BitMapShaper和Path路徑實(shí)現(xiàn)圓角和尖角的繪制。
新的項(xiàng)目中大量的使用了fresco的simpleDraweeView,故需要一種新的實(shí)現(xiàn)方式。
實(shí)現(xiàn)圓角或尖角的原理:
欲實(shí)現(xiàn)圓角和尖嘴有兩種實(shí)現(xiàn)技術(shù):BitMapShaper 和PorterDuffXfermode。本文使用PorterDuffXfermode ,不過多介紹BitMapShaper。
PorterDuffXfermode 圖形混合模式
該類有且只有一個(gè)含參的構(gòu)造方法PorterDuffXfermode(PorterDuff.Mode mode),通過PorterDuff.Mode 可以實(shí)現(xiàn)兩個(gè)圖形的各種組合效果。
下圖是PorterDuff.Mode取不同值時(shí)的組合效果:
在API中Android為我們提供了18種(比上圖多了兩種ADD和OVERLAY)模式:
ADD:飽和相加,對圖像飽和度進(jìn)行相加,不常用
CLEAR:清除圖像
DARKEN:變暗,較深的顏色覆蓋較淺的顏色,若兩者深淺程度相同則混合
DST:只顯示目標(biāo)圖像
DST_ATOP:在源圖像和目標(biāo)圖像相交的地方繪制【目標(biāo)圖像】,在不相交的地方繪制【源圖像】,相交處的效果受到源圖像和目標(biāo)圖像alpha的影響
DST_IN:只在源圖像和目標(biāo)圖像相交的地方繪制【目標(biāo)圖像】,繪制效果受到源圖像對應(yīng)地方透明度影響
DST_OUT:只在源圖像和目標(biāo)圖像不相交的地方繪制【目標(biāo)圖像】,在相交的地方根據(jù)源圖像的alpha進(jìn)行過濾,源圖像完全不透明則完全過濾,完全透明則不過濾
DST_OVER:將目標(biāo)圖像放在源圖像上方
LIGHTEN:變亮,與DARKEN相反,DARKEN和LIGHTEN生成的圖像結(jié)果與Android對顏色值深淺的定義有關(guān)
MULTIPLY:正片疊底,源圖像素顏色值乘以目標(biāo)圖像素顏色值除以255得到混合后圖像像素顏色值
OVERLAY:疊加
SCREEN:濾色,色調(diào)均和,保留兩個(gè)圖層中較白的部分,較暗的部分被遮蓋
SRC:只顯示源圖像
SRC_ATOP:在源圖像和目標(biāo)圖像相交的地方繪制【源圖像】,在不相交的地方繪制【目標(biāo)圖像】,相交處的效果受到源圖像和目標(biāo)圖像alpha的影響
SRC_IN:只在源圖像和目標(biāo)圖像相交的地方繪制【源圖像】
SRC_OUT:只在源圖像和目標(biāo)圖像不相交的地方繪制【源圖像】,相交的地方根據(jù)目標(biāo)圖像的對應(yīng)地方的alpha進(jìn)行過濾,目標(biāo)圖像完全不透明則完全過濾,完全透明則不過濾
SRC_OVER:將源圖像放在目標(biāo)圖像上方
XOR:在源圖像和目標(biāo)圖像相交的地方之外繪制它們,在相交的地方受到對應(yīng)alpha和色值影響,如果完全不透明則相交處完全不繪制。
此處應(yīng)該使用SRC_IN,取兩個(gè)圖形的交集
PorterDuffXfermode 實(shí)現(xiàn)圓形圖片
/**
* 根據(jù)原圖和邊場繪制圓形圖片
*
* @param source
* @param min
* @return
*/
private Bitmap createCircleImage(Bitmap source, int min)
{
final Paint paint = new Paint();
paint.setAntiAlias(true);
Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888);
/**
* 產(chǎn)生一個(gè)同樣大小的畫布
*/
Canvas canvas = new Canvas(target);
/**
* (1)首先繪制圓形 - 第一次繪制
*/
canvas.drawCircle(min / 2, min / 2, min / 2, paint);
/**
* (2)paint使用SRC_IN,取兩次繪制的交集
*/
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
/**
* (3)繪制圖片 -第二次繪制
*/
canvas.drawBitmap(source, 0, 0, paint);
return target;
}
首先創(chuàng)建一個(gè)Canvas,然后就是三步走:
- 第一步 在canva上繪制一個(gè)圓形。
- 第二步 paint.setXfermode() 指定PorterDuff.Mode.SRC_IN,取兩次繪制的交集。
- 第三步 將原bitmap繪制在Canvas上,進(jìn)行第二次繪制。
最終 原始的圖片成了一個(gè)圖形圖片。
PorterDuffXfermode 實(shí)現(xiàn)尖嘴圖片
原理同“圓形圖片”,只不過將canvas.drawCircle 繪制圓形改成canvas.drawPath(path,paint) 繪制特定路徑,其中path實(shí)現(xiàn)一個(gè)尖嘴路徑。
繪制尖角路徑的方法如下:
/**
* 繪制尖嘴在左側(cè)的path
*
* A
* * * * * * * * * * * * * *
* * * *
* * * *
* * F * B *
* * * *
* * mArrowTop * * * * * * **
* * *
* * *
* * *
* * *
* * * C
* * *
* * *
* * E *
* * *
* * *
* * *
* * *
* * *
* * *
* * * * * * * * * * * * * * *
* D
*
* @param rect
* @param path 如上圖所示 從A點(diǎn)開始 順時(shí)針繪制path路徑.
*/
public void leftPath(RectF rect, Path path) {
path.moveTo(mCornerRadius + mArrowWidth, rect.top);//移動到A點(diǎn)
path.lineTo(rect.width(), rect.top);//頂部橫線
path.arcTo(new RectF(rect.right - mCornerRadius * 2, rect.top, rect.right,
mCornerRadius * 2 + rect.top), 270, 90);//繪制 右上角的90度的圓弧. (B對應(yīng)的區(qū)域)
path.lineTo(rect.right, rect.top);//繪制 右側(cè)直線
path.arcTo(new RectF(rect.right - mCornerRadius * 2, rect.bottom - mCornerRadius * 2,
rect.right, rect.bottom), 0, 90);//右下角圓弧
path.lineTo(rect.left + mArrowWidth, rect.bottom);//底部橫線 (D對應(yīng)的橫線)
path.arcTo(new RectF(rect.left + mArrowWidth, rect.bottom - mCornerRadius * 2,
mCornerRadius * 2 + rect.left + mArrowWidth, rect.bottom), 90, 90);//左下角圓弧
path.lineTo(rect.left + mArrowWidth, mArrowTop + mArrowHeight);//左側(cè)偏下部豎線(E所示豎線)
path.lineTo(rect.left, mArrowTop - mArrowOffset);//左側(cè)凸起尖角 下半部分斜線
path.lineTo(rect.left + mArrowWidth, mArrowTop); //左側(cè)凸起尖角 上半部分斜線
path.lineTo(rect.left + mArrowWidth, rect.top);//左側(cè)片上部豎線(F所示豎線)
path.arcTo(new RectF(rect.left + mArrowWidth, rect.top, mCornerRadius * 2
+ rect.left + mArrowWidth, mCornerRadius * 2 + rect.top), 180, 90);//左上角圓弧
path.close();
}
PorterDuffXfermode 與SimpleDraweeView結(jié)合
我們知道fresco的SimpleDraweeView 中包含了多個(gè)圖層,以顯示placeholder、loading、加載失敗等狀態(tài)。所以不能簡單的繼承SimpleDraweeView重寫onDraw方法。需要尋找新的方法。
查詢fresco官網(wǎng) 發(fā)現(xiàn)fresco提供了一種叫做后處理器BasePostprocessor的工具,允許在bitmap下載完成后,對原始bitmap進(jìn)行一些處理。
- 嘗試一 :利用BasePostprocessor,對下載完成的圖片利用PorterDuffXfermode 生成尖嘴 再返回處理后的圖片。
public void showImage(final Uri uri, final int imageSuitableWidth, final int imageSuitableHeight){
Postprocessor redMeshPostPorcessor = new BasePostprocessor() {
@Override
public void process(Bitmap destBitmap, Bitmap sourceBitmap) {
Canvas canvas = new Canvas(destBitmap);
int color = 0xff424242;// int color = 0xff424242;
Paint paint = new Paint();
paint.setColor(color);
// 防止鋸齒
paint.setAntiAlias(true);
Rect rect = new Rect(0,0,sourceBitmap.getWidth(),sourceBitmap.getHeight());
RectF rectF = new RectF(rect);
Path path = new Path();
if(mArrowLocation == LOCATION_LEFT){
leftPath(rectF,path);
}else {
rightPath(rectF,path);
}
canvas.drawARGB(0,0,0,0);
canvas.drawPath(path,paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(sourceBitmap,rect,rect,paint);
LogUtils.d(TAG,"test size - redMeshPostPorcessor.width:"+sourceBitmap.getWidth()+",height:"+sourceBitmap.getHeight()+",destBitmap.width:"+destBitmap.getWidth()+",destBitMap.height:"+destBitmap.getHeight()+",imageSuitableWidth:"+imageSuitableWidth+",imageSuitableHeight:"+imageSuitableHeight+",mArrowWidth:"+mArrowWidth+",location:"+mArrowLocation+",uri:"+uri);
}
};
//LogUtils.d(TAG,"test size - imageSuitableWidth:"+imageSuitableWidth+",imageSuitableHeight:"+imageSuitableHeight);
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
.setResizeOptions(new ResizeOptions(imageSuitableWidth, imageSuitableHeight))
.setPostprocessor(redMeshPostPorcessor)
.build();
ViewGroup.LayoutParams params = getLayoutParams();
params.width = imageSuitableWidth;
params.height = imageSuitableHeight;
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setOldController(getController())
.setImageRequest(request)
.build();
setController(controller);
setLayoutParams(params);
}
經(jīng)測試基本實(shí)現(xiàn)了尖角效果,但是卻存在一個(gè)問題:尖角效果是在原始Bitmap上重新繪制實(shí)現(xiàn)的,原始的bitmap 在SimpleDrawView上顯示時(shí),經(jīng)常要經(jīng)過一個(gè)放縮的處理,這個(gè)過程中繪制的尖角會被等比例的放大和縮小。導(dǎo)致的結(jié)果是 不同大小的圖片 經(jīng)過放縮處理后,在聊天列表頁面顯示時(shí) 尖嘴的大小也大小不一。
如何解決這個(gè)問題呢?查閱文檔后發(fā)現(xiàn)fresco 后處理器 還有另一個(gè)方法,允許改變bitmap的大小
public CloseableReference<Bitmap> process(
Bitmap sourceBitmap,
PlatformBitmapFactory bitmapFactory) {
}
- 嘗試二 重寫B(tài)asePostprocessor,處理原始圖片時(shí) 首先把原始圖片放縮到 最終要顯示的大小,然后再添加尖角效果。問題解決。
public void showImage(final Uri uri, final int imageSuitableWidth, final int imageSuitableHeight){
Postprocessor redMeshPostPorcessor = new BasePostprocessor() {
@Override
public CloseableReference<Bitmap> process(
Bitmap sourceBitmap,
PlatformBitmapFactory bitmapFactory) {
//創(chuàng)建一個(gè)安全的 新的bitmap
CloseableReference<Bitmap> bitmapRef = bitmapFactory.createBitmap(
imageSuitableWidth,
imageSuitableHeight);
try {
Bitmap destBitmap = bitmapRef.get();
Canvas canvas = new Canvas(destBitmap);
int color = 0xff424242;// int color = 0xff424242;
Paint paint = new Paint();
paint.setColor(color);
// 防止鋸齒
paint.setAntiAlias(true);
Rect rect = new Rect(0,0,imageSuitableWidth,imageSuitableHeight);
RectF rectF = new RectF(rect);
Path path = new Path();
if(mArrowLocation == LOCATION_LEFT){
leftPath(rectF,path);
}else {
rightPath(rectF,path);
}
canvas.drawARGB(0,0,0,0);
canvas.drawPath(path,paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
float scale = 1.0f;
float scaleWidth = (float) (imageSuitableWidth*1.0/sourceBitmap.getWidth());
float scaleHeight = (float) (imageSuitableHeight*1.0/sourceBitmap.getHeight());
scale = Math.max(scaleWidth,scaleHeight);
// canvas.drawBitmap(sourceBitmap,rect,rect,paint);
Matrix matrix = new Matrix();
matrix.postScale(scale,scale);
LogUtils.d(TAG,"scaleWidth:"+scaleWidth+",scaleHeight:"+scaleHeight+",scale:"+scale);
LogUtils.d(TAG,"test size - redMeshPostPorcessor.width:"+sourceBitmap.getWidth()+",height:"+sourceBitmap.getHeight()+",destBitmap.width:"+destBitmap.getWidth()+",destBitMap.height:"+destBitmap.getHeight()+",imageSuitableWidth:"+imageSuitableWidth+",imageSuitableHeight:"+imageSuitableHeight+",mArrowWidth:"+mArrowWidth+",location:"+mArrowLocation+",uri:"+uri);
canvas.drawBitmap(sourceBitmap,matrix,paint);
return CloseableReference.cloneOrNull(bitmapRef);
} finally {
CloseableReference.closeSafely(bitmapRef);
}
}
};
//LogUtils.d(TAG,"test size - imageSuitableWidth:"+imageSuitableWidth+",imageSuitableHeight:"+imageSuitableHeight);
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
.setResizeOptions(new ResizeOptions(imageSuitableWidth, imageSuitableHeight))
.setPostprocessor(redMeshPostPorcessor)
.build();
ViewGroup.LayoutParams params = getLayoutParams();
params.width = imageSuitableWidth;
params.height = imageSuitableHeight;
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setOldController(getController())
.setImageRequest(request)
.build();
setController(controller);
setLayoutParams(params);
}
完整代碼:
重寫SimpleDraweView實(shí)現(xiàn)自定義ArrowSimpleDraweeView
package com.sogou.arrowsdview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.ViewGroup;
import com.facebook.common.references.CloseableReference;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.request.BasePostprocessor;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.facebook.imagepipeline.request.Postprocessor;
/**
* Created by baixuefei on 18/2/11.
*/
public class ArrowSimpleDraweeView extends SimpleDraweeView {
private static final String TAG = ArrowSimpleDraweeView.class.getSimpleName();
private float mCornerRadius ;
private float mArrowTop ;//尖角縱向頂部到矩形登錄的距離.
private float mArrowWidth ;//尖角的橫向?qū)挾?
private float mArrowHeight ;//尖角的縱向高度
private float mArrowOffset ;
private int mArrowLocation = 0;
private static final int LOCATION_LEFT = 0;
private static final int LOCATION_RIGHT = 1;
private Context mContext;
public ArrowSimpleDraweeView(Context context) {
super(context);
mContext = context;
intiView(null);
}
public ArrowSimpleDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
intiView(attrs);
}
public ArrowSimpleDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
intiView(attrs);
}
public void intiView(AttributeSet attrs){
mCornerRadius = DensityUtils.dip2px(mContext,10);
mArrowTop =DensityUtils.dip2px(mContext,40);//尖角縱向頂部到矩形登錄的距離.
mArrowWidth = DensityUtils.dip2px(mContext,10);//尖角的橫向?qū)挾?
mArrowHeight = DensityUtils.dip2px(mContext,20);//尖角的縱向高度
mArrowOffset = -DensityUtils.dip2px(mContext,10);
if(attrs != null){
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ArrowSimpleDraweeView);
mCornerRadius = a.getDimension(R.styleable.ArrowSimpleDraweeView_aCornerRadius,mCornerRadius);
mArrowTop = a.getDimension(R.styleable.ArrowSimpleDraweeView_arrowTop,mArrowTop);
float tmp = a.getDimension(R.styleable.ArrowSimpleDraweeView_arrowWidth,mArrowWidth);
mArrowWidth = tmp;
//LogUtils.d(TAG,"mArrowWidth:"+tmp+",DensityUtils.dip2px(10):"+DensityUtils.dip2px(10));
mArrowHeight = a.getDimension(R.styleable.ArrowSimpleDraweeView_arrowHeihgt,mArrowHeight);
mArrowOffset = a.getDimension(R.styleable.ArrowSimpleDraweeView_arrowOffsety,mArrowOffset);
mArrowLocation = a.getInt(R.styleable.ArrowSimpleDraweeView_arrowLocation,mArrowLocation);
a.recycle();
}
}
public void showImage(final Uri uri, final int imageSuitableWidth, final int imageSuitableHeight){
// Postprocessor redMeshPostPorcessor = new BasePostprocessor() {
// @Override
// public void process(Bitmap destBitmap, Bitmap sourceBitmap) {
//
// Canvas canvas = new Canvas(destBitmap);
// int color = 0xff424242;// int color = 0xff424242;
// Paint paint = new Paint();
// paint.setColor(color);
// // 防止鋸齒
// paint.setAntiAlias(true);
//
// Rect rect = new Rect(0,0,sourceBitmap.getWidth(),sourceBitmap.getHeight());
//
// RectF rectF = new RectF(rect);
//
// Path path = new Path();
// if(mArrowLocation == LOCATION_LEFT){
// leftPath(rectF,path);
// }else {
// rightPath(rectF,path);
// }
// canvas.drawARGB(0,0,0,0);
// canvas.drawPath(path,paint);
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// canvas.drawBitmap(sourceBitmap,rect,rect,paint);
//
// LogUtils.d(TAG,"test size - redMeshPostPorcessor.width:"+sourceBitmap.getWidth()+",height:"+sourceBitmap.getHeight()+",destBitmap.width:"+destBitmap.getWidth()+",destBitMap.height:"+destBitmap.getHeight()+",imageSuitableWidth:"+imageSuitableWidth+",imageSuitableHeight:"+imageSuitableHeight+",mArrowWidth:"+mArrowWidth+",location:"+mArrowLocation+",uri:"+uri);
// }
// };
Postprocessor redMeshPostPorcessor = new BasePostprocessor() {
@Override
public CloseableReference<Bitmap> process(
Bitmap sourceBitmap,
PlatformBitmapFactory bitmapFactory) {
//創(chuàng)建一個(gè)安全的 新的bitmap
CloseableReference<Bitmap> bitmapRef = bitmapFactory.createBitmap(
imageSuitableWidth,
imageSuitableHeight);
try {
Bitmap destBitmap = bitmapRef.get();
Canvas canvas = new Canvas(destBitmap);
int color = 0xff424242;// int color = 0xff424242;
Paint paint = new Paint();
paint.setColor(color);
// 防止鋸齒
paint.setAntiAlias(true);
Rect rect = new Rect(0,0,imageSuitableWidth,imageSuitableHeight);
RectF rectF = new RectF(rect);
Path path = new Path();
if(mArrowLocation == LOCATION_LEFT){
leftPath(rectF,path);
}else {
rightPath(rectF,path);
}
canvas.drawARGB(0,0,0,0);
canvas.drawPath(path,paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
float scale = 1.0f;
float scaleWidth = (float) (imageSuitableWidth*1.0/sourceBitmap.getWidth());
float scaleHeight = (float) (imageSuitableHeight*1.0/sourceBitmap.getHeight());
scale = Math.max(scaleWidth,scaleHeight);
// canvas.drawBitmap(sourceBitmap,rect,rect,paint);
Matrix matrix = new Matrix();
matrix.postScale(scale,scale);
//LogUtils.d(TAG,"scaleWidth:"+scaleWidth+",scaleHeight:"+scaleHeight+",scale:"+scale);
//LogUtils.d(TAG,"test size - redMeshPostPorcessor.width:"+sourceBitmap.getWidth()+",height:"+sourceBitmap.getHeight()+",destBitmap.width:"+destBitmap.getWidth()+",destBitMap.height:"+destBitmap.getHeight()+",imageSuitableWidth:"+imageSuitableWidth+",imageSuitableHeight:"+imageSuitableHeight+",mArrowWidth:"+mArrowWidth+",location:"+mArrowLocation+",uri:"+uri);
canvas.drawBitmap(sourceBitmap,matrix,paint);
return CloseableReference.cloneOrNull(bitmapRef);
} finally {
CloseableReference.closeSafely(bitmapRef);
}
}
};
//LogUtils.d(TAG,"test size - imageSuitableWidth:"+imageSuitableWidth+",imageSuitableHeight:"+imageSuitableHeight);
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
.setResizeOptions(new ResizeOptions(imageSuitableWidth, imageSuitableHeight))
.setPostprocessor(redMeshPostPorcessor)
.build();
ViewGroup.LayoutParams params = getLayoutParams();
params.width = imageSuitableWidth;
params.height = imageSuitableHeight;
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setOldController(getController())
.setImageRequest(request)
.build();
setController(controller);
setLayoutParams(params);
}
/**
* 繪制尖角在左側(cè)的path
*
* A
* * * * * * * * * * * * * *
* * * *
* * * *
* * F * B *
* * * *
* * mArrowTop * * * * * * **
* * *
* * *
* * *
* * *
* * * C
* * *
* * *
* * E *
* * *
* * *
* * *
* * *
* * *
* * *
* * * * * * * * * * * * * * *
* D
*
* @param rect
* @param path 如上圖所示 從A點(diǎn)開始 順時(shí)針繪制path路徑.
*/
public void leftPath(RectF rect, Path path) {
path.moveTo(mCornerRadius + mArrowWidth, rect.top);//移動到A點(diǎn)
path.lineTo(rect.width(), rect.top);//頂部橫線
path.arcTo(new RectF(rect.right - mCornerRadius * 2, rect.top, rect.right,
mCornerRadius * 2 + rect.top), 270, 90);//繪制 右上角的90度的圓弧. (B對應(yīng)的區(qū)域)
path.lineTo(rect.right, rect.top);//繪制 右側(cè)直線
path.arcTo(new RectF(rect.right - mCornerRadius * 2, rect.bottom - mCornerRadius * 2,
rect.right, rect.bottom), 0, 90);//右下角圓弧
path.lineTo(rect.left + mArrowWidth, rect.bottom);//底部橫線 (D對應(yīng)的橫線)
path.arcTo(new RectF(rect.left + mArrowWidth, rect.bottom - mCornerRadius * 2,
mCornerRadius * 2 + rect.left + mArrowWidth, rect.bottom), 90, 90);//左下角圓弧
path.lineTo(rect.left + mArrowWidth, mArrowTop + mArrowHeight);//左側(cè)偏下部豎線(E所示豎線)
path.lineTo(rect.left, mArrowTop - mArrowOffset);//左側(cè)凸起尖角 下半部分斜線
path.lineTo(rect.left + mArrowWidth, mArrowTop); //左側(cè)凸起尖角 上半部分斜線
path.lineTo(rect.left + mArrowWidth, rect.top);//左側(cè)片上部豎線(F所示豎線)
path.arcTo(new RectF(rect.left + mArrowWidth, rect.top, mCornerRadius * 2
+ rect.left + mArrowWidth, mCornerRadius * 2 + rect.top), 180, 90);//左上角圓弧
path.close();
}
/**
* 繪制 尖角在右側(cè) 的path
* @param rect
* @param path
*/
public void rightPath(RectF rect, Path path) {
path.moveTo(mCornerRadius, rect.top);
path.lineTo(rect.width(), rect.top);
path.arcTo(new RectF(rect.right - mCornerRadius * 2 - mArrowWidth, rect.top,
rect.right - mArrowWidth, mCornerRadius * 2 + rect.top), 270, 90);
path.lineTo(rect.right - mArrowWidth, mArrowTop);
path.lineTo(rect.right, mArrowTop - mArrowOffset);
path.lineTo(rect.right - mArrowWidth, mArrowTop + mArrowHeight);
path.lineTo(rect.right - mArrowWidth, rect.height() - mCornerRadius);
path.arcTo(new RectF(rect.right - mCornerRadius * 2 - mArrowWidth, rect.bottom
- mCornerRadius * 2, rect.right - mArrowWidth, rect.bottom), 0, 90);
path.lineTo(rect.left, rect.bottom);
path.arcTo(new RectF(rect.left, rect.bottom - mCornerRadius * 2, mCornerRadius * 2
+ rect.left, rect.bottom), 90, 90);
path.lineTo(rect.left, rect.top);
path.arcTo(new RectF(rect.left, rect.top, mCornerRadius * 2 + rect.left,
mCornerRadius * 2 + rect.top), 180, 90);
path.close();
}
public float getmCornerRadius() {
return mCornerRadius;
}
public void setmCornerRadius(float mCornerRadius) {
this.mCornerRadius = mCornerRadius;
}
public float getmArrowTop() {
return mArrowTop;
}
public void setmArrowTop(float mArrowTop) {
this.mArrowTop = mArrowTop;
}
public float getmArrowWidth() {
return mArrowWidth;
}
public void setmArrowWidth(float mArrowWidth) {
this.mArrowWidth = mArrowWidth;
}
public float getmArrowHeight() {
return mArrowHeight;
}
public void setmArrowHeight(float mArrowHeight) {
this.mArrowHeight = mArrowHeight;
}
public float getmArrowOffset() {
return mArrowOffset;
}
public void setmArrowOffset(float mArrowOffset) {
this.mArrowOffset = mArrowOffset;
}
public int getmArrowLocation() {
return mArrowLocation;
}
public void setmArrowLocation(int mArrowLocation) {
this.mArrowLocation = mArrowLocation;
}
}
res->values->style.xml中 定義ArrowSimpleDraweeView 自定義屬性
<!-- 帶尖角的simpleDrawView的屬性 -->
<declare-styleable name="ArrowSimpleDraweeView">
<attr name="aCornerRadius" format="dimension"/>
<attr name="arrowTop" format="dimension"/>
<attr name="arrowWidth" format="dimension"/>
<attr name="arrowHeihgt" format="dimension"/>
<attr name="arrowOffsety" format="dimension"/>
<attr name="arrowLocation" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>
布局文件中聲明 ArrowSimpleDraweeView
<com.sogou.arrowsdview.ArrowSimpleDraweeView
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/image_left"
android:layout_width="200dp"
android:layout_height="200dp"
fresco:placeholderImage="@drawable/default_photo"
fresco:roundedCornerRadius="5dp"
fresco:arrowHeihgt="10dp"
fresco:arrowTop="10dp"
fresco:arrowOffsety="-5dp"
fresco:arrowWidth="5dp"
fresco:aCornerRadius="5dp"
fresco:arrowLocation="left"
android:layout_marginLeft="6dp"
android:layout_marginRight="115dp"/>
利用ArrowSimpleDraweeView加載尖嘴圖片
vh.leftImage.showImage(uri,imageSuitableWidth,imageSuitableHeight);
最終實(shí)現(xiàn)效果:
github地址:
https://github.com/feifei-123/ArrowSimpleDraweeView-master