模仿半塘app的图片添加标签功能,刚开始反编译了半塘的代码,结果代码太多了,用一些三方的东西,觉的比较麻烦,这里自己写了一下实现,感觉和半塘的没啥区别(自我感觉良好,嘿嘿)

一、半塘功能实现步骤

android标签table 安卓实现标签功能_动画


android标签table 安卓实现标签功能_动画_02


android标签table 安卓实现标签功能_初始化_03


android标签table 安卓实现标签功能_ide_04

二、半塘实现分析

功能分析

  • 本地相册选择
  • 添加图片
  • 添加标签
  • 标签移动
  • 标签动画切换
  • 图片生成

实现说明

(1) 标签类型有两种 第一种是单个标签 第二种是两个标签

(2) 第一种标签的包含:原点和文字

(3) 第二步标签包含:原点、文字和横线

(4) 原点的位置是点击的位置

(5) 文字有两种样式,一种是向左的,一种是向右的(对应实现)

(6) 横线有四种样式

android标签table 安卓实现标签功能_android标签table_05

三、实现

(1)类结构图

android标签table 安卓实现标签功能_初始化_06


(2)代码结构

android标签table 安卓实现标签功能_android_07


(3)具体实现

圆点的实现

public class LabelRoundDot extends Drawable{

    private Paint mPaint;
    private Bitmap mBitmap;

    private RectF rectF;

    public LabelRoundDot(Bitmap bitmap) {
        mBitmap = bitmap;
        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setShader(bitmapShader);
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom)
    {
        super.setBounds(left, top, right, bottom);
        rectF = new RectF(left, top, right, bottom);
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.drawRoundRect(rectF, 30, 30, mPaint);
    }

    @Override
    public int getIntrinsicWidth()
    {
        return mBitmap.getWidth();
    }

    @Override
    public int getIntrinsicHeight()
    {
        return mBitmap.getHeight();
    }

    @Override
    public void setAlpha(int alpha)
    {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter cf)
    {
        mPaint.setColorFilter(cf);
    }

    @Override
    public int getOpacity()
    {
        return PixelFormat.TRANSLUCENT;
    }
}

文字的实现
初始化

public LabelTextView(String labelText, int textType){
        this.labelText = labelText;
        this.currentType = textType;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setTextSize(TEXT_SIZE);
        initLabel();
    }
  private void initLabel(){
        Rect mBounds = new Rect();
        mPaint.getTextBounds(this.labelText, 0, this.labelText.length(), mBounds);
        this.labelTextHeight = mBounds.height() + VIEW_PADDING;
        this.labelTextWidth = labelTextHeight/2 +(mBounds.width()<6? 6:mBounds.width()) + VIEW_PADDING;
        Log.i(TAG, "initLabel labelTextHeight = " + labelTextHeight + " labelTextWidth = " + labelTextWidth);
    }

画第一种类型

private void drawFrist(Canvas canvas){
        mPaint.setStrokeWidth(3);
        mPaint.setColor(Color.parseColor("#99000000"));
        //画半圆
        RectF oval = new RectF(0, 0, labelTextHeight, labelTextHeight);
        canvas.drawArc(oval, 270, -180, true, mPaint);

        Rect targetRect = new Rect(labelTextHeight/2, 0, labelTextWidth - VIEW_PADDING, labelTextHeight);
        canvas.drawRect(targetRect, mPaint);
        //画三角形
        Path path = new Path();
        path.moveTo(labelTextWidth - VIEW_PADDING, 0);// 此点为多边形的起点
        path.lineTo(labelTextWidth, labelTextHeight/2);
        path.lineTo(labelTextWidth - VIEW_PADDING, labelTextHeight);
        path.close(); // 使这些点构成封闭的多边形
        canvas.drawPath(path, mPaint);
        //写文字
        mPaint.setColor(Color.parseColor("#ffffff"));
        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
        int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
        // 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()
        mPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(labelText, targetRect.centerX(), baseline, mPaint);
    }

画第二种类型

private void drawSecond(Canvas canvas){
        Log.i(TAG, "onDrawSecond ---------");
        mPaint.setStrokeWidth(3);
        mPaint.setColor(Color.parseColor("#99000000"));
        //画半圆
        RectF oval = new RectF(labelTextWidth - labelTextHeight, 0, labelTextWidth, labelTextHeight);
        canvas.drawArc(oval,90, -180, true, mPaint);

        //画矩形
        Rect targetRect = new Rect(VIEW_PADDING, 0, labelTextWidth-labelTextHeight/2, labelTextHeight);
        canvas.drawRect(targetRect, mPaint);
        //画三角形
        Path path = new Path();
        path.moveTo(VIEW_PADDING, 0);// 此点为多边形的起点
        path.lineTo(0, labelTextHeight/2);
        path.lineTo(VIEW_PADDING, labelTextHeight);
        path.close(); // 使这些点构成封闭的多边形
        canvas.drawPath(path, mPaint);
        //写文字
        mPaint.setColor(Color.parseColor("#ffffff"));
        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
        int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
        // 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()
        mPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(labelText, targetRect.centerX(), baseline, mPaint);
    }

线条的实现
初始化

public LabelLineView (int type){
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStrokeWidth(3);
        mPaint.setColor(Color.parseColor("#99000000"));
        labelHeight = slashHeight;
        labelWidth = slashWidth + straightLine;
        this.currentType = type;
        Log.i(TAG, "onDrawFirst  labelHeight = " + labelHeight + " labelWidth = " + labelWidth);
    }

四种类型

/**
     *
     * @param canvas
     */
    private void drawFrist(Canvas canvas){
        //画第一条直线
        int lineSx = 0;
        int lineSy = 0;
        int lineEx = straightLine;
        int lineEy = 0;
        canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线

        lineSx = straightLine;
        lineSy = 0;
        lineEx = labelWidth;
        lineEy = labelHeight;
        canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
    }

    /**
     *
     * @param canvas
     */
    private void drawSecond(Canvas canvas){
        //画第一条直线
        int lineSx = 0;
        int lineSy = labelHeight;
        int lineEx = straightLine;
        int lineEy = labelHeight;
        canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线

        lineSx = straightLine;
        lineSy = labelHeight;
        lineEx = labelWidth;
        lineEy = 0;
        canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
    }

    private void drawThird(Canvas canvas){
        int lineSx = 0;
        int lineSy = labelHeight;
        int lineEx = slashWidth;
        int lineEy = 0;
        canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线

        lineSx = slashWidth;
        lineSy = 0;
        lineEx = labelWidth;
        lineEy = 0;
        canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
    }

    private void drawFourth(Canvas canvas){
        //画第一条直线
        int lineSx = 0;
        int lineSy = 0;
        int lineEx = slashWidth;
        int lineEy = slashHeight;
        canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线

        lineSx = slashWidth;
        lineSy = slashHeight;
        lineEx = labelWidth;
        lineEy = labelHeight;
        canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
    }

LabelViewListener标签的实现监听
包含:标签的位置、切换样式、显示和隐藏动画、更新坐标(中心)、更新内容、判断是否点击到中心等方法。

只有一个内容的标签
有两种绘制模式,包含原点和文字,记录中心位置和当前的模式
包含绘制两种模式的方法、显示和隐藏的动画、提供view的大小和位置等。
第一种模式

/**
     * 绘制第一模式
     *
     * @param canvas
     */
    private void onDrawFirst(Canvas canvas) {
        Log.i(TAG, "onDrawFirst ---------");
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextView),4,0,mPaint);
        //画原点
        int xBmp = labelViewWidth-originPic.getWidth();
        int yBmp = (labelViewHeight-originPic.getWidth())/2;
        canvas.drawBitmap(originPic, xBmp, yBmp, null);
        Log.i(TAG, "onDrawFirst xBmp = " + xBmp + " yBmp = " + yBmp);
    }

第二种模式

/**
     * 绘制第二模式
     *
     * @param canvas
     */
    private void onDrawSecond(Canvas canvas) {
        Log.i(TAG, "onDrawSecond ---------");
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextView), originPic.getWidth()-4, 0, mPaint);
        //画原点
        int xBmp = 0;
        int yBmp = (labelViewHeight-originPic.getWidth())/2;
        canvas.drawBitmap(originPic, xBmp, yBmp, null);
        Log.i(TAG, "onDrawFirst xBmp = " + xBmp + " yBmp = " + yBmp);
    }

显示动画

@Override
    public ValueAnimator getLabelShowAnim() {
        ValueAnimator anim = null;
        if (currentType == FRIST_MODEL) {
            anim = ValueAnimator.ofInt(labelViewWidth, 0);
        } else if (currentType == SECOND_MODEL) {
            anim = ValueAnimator.ofInt(0, labelViewWidth);
        }
        if (anim == null) {
            return null;
        }
        anim.setDuration(500);
        return anim;
    }

隐藏动画

@Override
    public ValueAnimator getLabelHideAnim() {
        ValueAnimator anim = null;
        if (currentType == FRIST_MODEL) {
            anim = ValueAnimator.ofInt(0, labelViewWidth);
        } else if (currentType == SECOND_MODEL) {
            anim = ValueAnimator.ofInt(labelViewWidth, 0);
        }
        if (anim == null) {
            return null;
        }
        anim.setDuration(500);
        return anim;
    }

有两个内容的标签
包含圆点、线条和文字
有四种绘制模式

第一种模式

/**
     * 绘制第一模式
     *
     * @param canvas
     */
    private void onDrawFirst(Canvas canvas) {
        Log.i(TAG, "onDrawFirst ---------");

        //上面的文字
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewFirst),
                labelViewWidth-originPic.getWidth()-labelLineViewFirst.getIntrinsicWidth()-labelTextViewFirst.getIntrinsicWidth(),
                0,mPaint);
        //上面的线条
        canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewFirst),
                labelViewWidth-originPic.getWidth()-labelLineViewFirst.getIntrinsicWidth(),
                labelTextViewFirst.getIntrinsicHeight()/2,
                mPaint);
        //下面的文字
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewSecond),
                labelViewWidth - originPic.getWidth() - labelLineViewSecond.getIntrinsicWidth() - labelTextViewSecond.getIntrinsicWidth(),
                labelViewHeight - labelTextViewSecond.getIntrinsicHeight(),
                mPaint);
        //下面的线条
        canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewSecond),
                labelViewWidth - originPic.getWidth() - labelLineViewSecond.getIntrinsicWidth(),
                labelTextViewFirst.getIntrinsicHeight() / 2 + labelLineViewFirst.getIntrinsicHeight(),
                mPaint);
        //画原点
        int xBmp = labelViewWidth - originPic.getWidth();
        int yBmp = (labelViewHeight - originPic.getWidth()) / 2;
        canvas.drawBitmap(originPic, xBmp, yBmp, null);
        Log.i(TAG, "onDrawFirst xBmp = " + xBmp + " yBmp = " + yBmp);
    }

第二种模式

/**
     * 绘制第二模式
     *
     * @param canvas
     */
    private void onDrawSecond(Canvas canvas) {
        Log.i(TAG, "onDrawSecond ---------");

        //上面条线
        canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewFirst),
                originPic.getWidth(),
                labelTextViewFirst.getIntrinsicHeight()/2,
                mPaint);
        //上面文字
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewFirst),
               originPic.getWidth()+labelLineViewFirst.getIntrinsicWidth(),
                0,
                mPaint);
        //下面线条
        canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewSecond),
                originPic.getWidth(),
                labelTextViewFirst.getIntrinsicHeight()/2+labelLineViewFirst.getIntrinsicHeight(),
                mPaint);
        //下面文字
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewSecond),
                originPic.getWidth()+labelLineViewSecond.getIntrinsicWidth(),
                labelViewHeight - labelTextViewSecond.getIntrinsicHeight(),
                mPaint);

        //画原点
        int xBmp = 0;
        int yBmp = (labelViewHeight-originPic.getWidth())/2;
        canvas.drawBitmap(originPic, xBmp, yBmp, null);
        Log.i(TAG, "onDrawSecond xBmp = " + xBmp + " yBmp = " + yBmp);
    }

第二种模式

/**
     * 第三种绘制
     */
    private void onDrawThird(Canvas canvas){
        Log.i(TAG, "onDrawSecond ---------");

        //左边的文字
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewFirst),
                0,
                0,
                mPaint);

        //上面条线
        canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewFirst),
                labelTextViewFirst.getIntrinsicWidth(),
                labelTextViewFirst.getIntrinsicHeight()/2,
                mPaint);

        //画原点
        int xBmp = labelTextViewFirst.getIntrinsicWidth()+labelLineViewFirst.getIntrinsicWidth();
        int yBmp = (labelViewHeight-originPic.getHeight())/2;
        canvas.drawBitmap(originPic, xBmp, yBmp, null);
        Log.i(TAG, "onDrawSecond xBmp = " + xBmp + " yBmp = " + yBmp);

        //下面线条
        canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewSecond),
                labelTextViewFirst.getIntrinsicWidth()+labelLineViewFirst.getIntrinsicWidth()+originPic.getWidth(),
                labelViewHeight/2,
                mPaint);
        //下面文字
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewSecond),
                labelViewWidth - labelTextViewSecond.getIntrinsicWidth(),
                labelViewHeight - labelTextViewSecond.getIntrinsicHeight(),
                mPaint);


    }

第四种模式

/**
     * 第四种绘制
     */
    private void onDrawFourth(Canvas canvas){
        Log.i(TAG, "onDrawFourth ---------");
        //左边文字
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewFirst),
                0,
                labelViewHeight-labelTextViewFirst.getIntrinsicHeight(),
                mPaint);

        //左边条线
        canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewFirst),
                labelTextViewFirst.getIntrinsicWidth(),
                labelViewHeight-labelTextViewFirst.getIntrinsicHeight()/2-labelLineViewFirst.getIntrinsicHeight(),
                mPaint);
        //画原点
        int xBmp = labelTextViewFirst.getIntrinsicWidth()+labelLineViewFirst.getIntrinsicWidth();
        int yBmp = (labelViewHeight-originPic.getWidth())/2;
        canvas.drawBitmap(originPic, xBmp, yBmp, null);
        Log.i(TAG, "onDrawSecond xBmp = " + xBmp + " yBmp = " + yBmp);

        //右边线条
        canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewSecond),
                labelTextViewFirst.getIntrinsicWidth()+labelLineViewFirst.getIntrinsicWidth()+originPic.getWidth(),
                labelTextViewSecond.getIntrinsicHeight()/2,
                mPaint);
        //右边文字
        canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewSecond),
                labelViewWidth-labelTextViewSecond.getIntrinsicWidth(),
                0,
                mPaint);


    }

以上大致说了一下思路
这是代码链接
https://github.com/gyh/GCustomView