android自定义View(六)、Android实现词云效果

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wanggang514260663/article/details/86437824

相信大家对于词云应该或多或少都有见过,没见过也没关系,这里先给大家看看网络上效果。词云使用python或者javascript实现的话,非常方便,插件也很多,但是android目前还没有见过开源实现。

网络词云效果:http://yciyun.com/

下面是我自己使用canvas实现的词云效果。

实现原理分析

我这里使用的实现原理是使用填格子的方式,下面给一张图来说明。

仔细看这张图,你会发现我画了四个不同大小的文字。四个不同的字体占用了不同大小的表格。

  • 最大的字体: 占用4x4
  • 二号字体:3x3
  • 三号字体:2x2
  • 四号字体: 1x1

看到现在你是不是已经有了思路想要动手了,主要是这个思路,接下来就是使用代码填格子,直到格子填满位置。

下面放出来我的实现代码,目前只是按照思路进行了逻辑实现,在填充处理上还是有很多问题,而且没有处理竖直方向。但是由于公司倒闭,所以提前回家了,没有做处理,代码有点糙,一天写出来的东西,没有优化,主要在思路,代码只做参考。

public class WordGroupView extends View {

    private static final String TAG = WordGroupView.class.getSimpleName();

    //文字绘制画笔
    private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    //辅助线绘制画笔
    private Paint mSublinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private List<DrawPoint> drawPointList = new ArrayList<>();
    private List<String> keyWords = new ArrayList<>();
    private int[] colors = {
            Color.parseColor("#009f9d"),
            Color.parseColor("#cd4545"),
            Color.parseColor("#7ed3b2"),
            Color.parseColor("#d195f9"),
            Color.parseColor("#222831"),
            Color.parseColor("#b55400"),
    };

    //文字展示最小可用宽高
    private int mMinTextWidth;
    private int mMinTextHeight;

    //最小可展示文字大小
    private int mMinFontSize;

    private int[][] mPoints;
    private int mMaxXPointCount;
    private int mMaxYPointCount;
    //设置词条最大长度和最小长度
    private int mMaxLength = 0;
    private int mMinLength = 0;

    public WordGroupView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        mMinFontSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10,
                context.getResources().getDisplayMetrics());

        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize(mMinFontSize);

        Rect textBound = new Rect();
        mTextPaint.getTextBounds("正", 0, 1, textBound);

        mMinTextWidth = mMinTextHeight = textBound.width();

        Log.e(TAG, mMinTextWidth + "-" + mMinTextHeight);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mMaxXPointCount = w / mMinTextWidth;
        mMaxYPointCount = h / mMinTextHeight;

        mPoints = new int[mMaxXPointCount][mMaxYPointCount];
        for (int i = 0; i < mPoints.length; i++) {
            for (int j = 0; j < mPoints[i].length; j++) {
                mPoints[i][j] = 0;
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawColor(Color.parseColor("#F2F2F2"));

        /**
         * 绘制辅助线
         */
//        for (int i = 0; i <= mMaxXPointCount; i++) {
//            canvas.drawLine(i * mMinTextWidth, 0, i * mMinTextWidth, mMinTextHeight * mMaxYPointCount, mSublinePaint);
//        }
//
//        for (int i = 0; i <= mMaxXPointCount; i++) {
//            canvas.drawLine(0, i * mMinTextHeight, mMinTextWidth * mMaxXPointCount, i * mMinTextHeight, mSublinePaint);
//        }

        //widget=4表示单个文字占用空间为4x4,其他类似
        for (int i = 4; i >= 1; i--) {
            buildPoints(mPoints, getFullContent(i), i, 0);
        }

        /**
         * 绘制真实的效果
         */
        for (DrawPoint point : drawPointList) {
            point.draw(canvas);
        }
    }

    private String[] getFullContent(int widget) {

        int maxCount = mMaxXPointCount * mMaxYPointCount * widget * widget;
        int currMaxItemCount = (int) (Math.random() * maxCount);
        String[] maxItems = new String[currMaxItemCount];
        for (int i = 0; i < maxItems.length; i++) {
            maxItems[i] = keyWords.get((int) (Math.random() * keyWords.size()));
        }
        if (maxItems.length == 0) {
            maxItems = new String[]{
                    keyWords.get(0)
            };
        }
        return maxItems;
    }

    /**
     * 不断检索并构建指定widget大小的数据,直到不能容纳或者绘制完成
     */
    private void buildPoints(int[][] points, String[] maxItems, int widget, int index) {

        if ((index > maxItems.length - 1) || capacityOut(points, widget, maxItems[index])) {
            return;
        }

        final int randomPointX = (int) (Math.random() * mMaxXPointCount);
        final int randomPointY = (int) (Math.random() * mMaxYPointCount);

        boolean isBuildSuc = false;
        if (isCanFull(points, randomPointX, randomPointY)
                && isCanDraw(points, randomPointX, randomPointY, maxItems[index], widget)) {
            for (int i = randomPointX; i < randomPointX + maxItems[index].length() * widget; i++) {
                for (int j = randomPointY; j < randomPointY + widget; j++) {
                    points[i][j] = 1;
                }
            }
            drawPointList.add(new DrawPoint(randomPointX, randomPointY, maxItems[index], widget));
            isBuildSuc = true;
        } else {
            buildPoints(points, maxItems, widget, index);
        }

        if (isBuildSuc) {
            buildPoints(points, maxItems, widget, index + 1);
        }
    }

    /**
     * 超出可容纳范围
     */
    private boolean capacityOut(int[][] points, int widget, String maxItem) {
        boolean isCapacityOut = true;
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
                if (isCanDraw(points, i, j, maxItem, widget)) {
                    isCapacityOut = false;
                    break;
                }
            }
        }
        return isCapacityOut;
    }

    /**
     * 判断是否可以绘制
     */
    private boolean isCanDraw(int[][] points, int randomPointX, int randomPointY, String maxItem, int widget) {
        boolean isCanFull = false;
        if (isOutHorizontal(randomPointX + maxItem.length() * widget)
                && isOutVertical(randomPointY + widget)) {
            isCanFull = true;
            for (int i = randomPointX; i < randomPointX + maxItem.length() * widget; i++) {
                for (int j = randomPointY; j < randomPointY + widget; j++) {
                    if (!isCanFull(points, i, j)) {
                        isCanFull = false;
                        break;
                    }
                }
            }
        }
        return isCanFull;
    }

    /**
     * 判断水平方向是否越界
     */
    private boolean isOutHorizontal(int pointX) {
        return pointX <= mMaxXPointCount;
    }

    /**
     * 判断竖直方向是否越界
     */
    private boolean isOutVertical(int pointY) {
        return pointY <= mMaxYPointCount;
    }

    /**
     * 是否可以填充
     */
    private boolean isCanFull(int[][] point, int pointX, int pointY) {
        if (pointX < mMaxXPointCount && pointY < mMaxYPointCount) {
            return point[pointX][pointY] == 0;
        }
        return false;
    }

    public void random() {
        for (int i = 0; i < mPoints.length; i++) {
            for (int j = 0; j < mPoints[i].length; j++) {
                mPoints[i][j] = 0;
            }
        }
        drawPointList.clear();
        invalidate();
    }

    public class DrawPoint {
        private int pointX;
        private int pointY;
        public String label;
        public int widget;

        DrawPoint(int pointX, int pointY, String label, int widget) {
            this.pointX = pointX;
            this.pointY = pointY;
            this.label = label;
            this.widget = widget;
        }

        public void draw(Canvas canvas) {
            mTextPaint.setTextSize(mMinFontSize * widget - 8 * widget);
            mTextPaint.setColor(colors[(int) (Math.random() * colors.length)]);
            Log.e(TAG, widget + "-" + label + "-" + pointX + "-" + pointY);
            canvas.drawText(label, pointX * mMinTextWidth + 4 * widget, (pointY + widget) * mMinTextHeight - 4 * widget, mTextPaint);

            mTextPaint.setStyle(Paint.Style.FILL);
        }
    }

    public void setWords(List<String> words) {
        this.keyWords = words;
        for (int i = 0; i < keyWords.size(); i++) {
            if (i == 0) {
                mMaxLength = keyWords.get(i).length();
                mMinLength = keyWords.get(i).length();
                continue;
            }
            if (keyWords.get(i).length() > mMaxLength) {
                mMaxLength = keyWords.get(i).length();
            }
            if (keyWords.get(i).length() < mMinLength) {
                mMinLength = keyWords.get(i).length();
            }
        }
        drawPointList.clear();
        invalidate();
    }
}

猜你喜欢

转载自blog.csdn.net/wanggang514260663/article/details/86437824