SimpleDraweeView實(shí)現(xiàn)聊天圖片消息的尖嘴效果

項(xiàng)目中聊天模塊要實(shí)現(xiàn)圖片消息和視頻消息帶尖嘴的效果。

老的項(xiàng)目中實(shí)現(xiàn)方式是 重寫ImageView的onDraw方法,通過BitMapShaper和Path路徑實(shí)現(xiàn)圓角和尖角的繪制。

新的項(xiàng)目中大量的使用了fresco的simpleDraweeView,故需要一種新的實(shí)現(xiàn)方式。

image

實(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í)的組合效果:


image

在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)效果:


image

github地址:
https://github.com/feifei-123/ArrowSimpleDraweeView-master

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容