如何使用BitmapShader而不重复

时间:2015-08-30 16:52:31

标签: java android canvas graphics

我正在制作一个绘图应用程序,我想用手指触摸移动屏幕绘制心形。所以我将BitmapShader设置为以下代码

QString username = ui->lineEdit->text();
QString password = ui->lineEdit_2->text();
QMessageBox Failed;
Failed.setWindowFlags(Qt::FramelessWindowHint);

if(username == "Jon" && password == "12345")
{
    Failed.setText("Login failed. Try again.");
    Failed.exec();
} else {
    Failed.setText(password);
    Failed.exec();
}

然后我指定着色器绘画对象。

//Initialize the bitmap object by loading an image from the resources folder
Bitmap fillBMP = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart);
fillBMP = Bitmap.createScaledBitmap(fillBMP, 20, 20, false);                
//Initialize the BitmapShader with the Bitmap object and set the texture tile mode
shader= new BitmapShader(fillBMP, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);

我还设置了触控监听器来跟踪用户手指。在用户触摸时,我在canvas对象上绘制为。

paint.setShader(preset.shader);

这给我这个

enter image description here

某些心形被歪曲,并且这些也是重复的。我想要的不是这些都是重复的,永远不会像这样发作。

enter image description here

提前致谢。

1 个答案:

答案 0 :(得分:7)

上传到Git时遇到了一些麻烦,所以现在我会在这里发布解决方案。

这是我写的一个帮助类,用于创建你想要的效果。我在线添加评论来解释我在做什么。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;

// Use this class with a Canvas to create the effect you want.
public class CurvedBitmapDrawer
{
    private Context mContext;
    private Paint mPaint;
    private int mResourceId;
    private Bitmap mBitmap;
    private Path mPath;
    private int mBitmapMargin;

    // Create with a context so that this class can use resource ids.
    public CurvedBitmapDrawer(Context context) {
        mContext = context;
        mPath = new Path();
    }

    // getters setters for paint.
    // this paint will be used to draw the bitmaps, and the strokeWidth value in the paint
    // will be used to set the thickness of the curve / line that is drawn.
    public Paint getPaint() {
        return mPaint;
    }

    public void setPaint(Paint paint) {
        mPaint = paint;
    }

    // getters setters for the space between the bitmaps.
    public int getBitmapMargin()
    {
        return mBitmapMargin;
    }

    public void setBitmapMargin(int bitmapMargin)
    {
        mBitmapMargin = bitmapMargin;
    }

    // getters setters for res id
    public int getResourceId() {
        return mResourceId;
    }

    public void setResourceId(int resourceId)
    {
        mResourceId = resourceId;
        mBitmap = null;
    }

    // alternative optional getters setters for bitmap.
    public Bitmap getBitmap()
    {
        return mBitmap;
    }

    public void setBitmap(Bitmap bitmap)
    {
        mBitmap = bitmap;
        mResourceId = 0;
    }

    // I decided to only use a local path here, but feel free to change it.
    // call getPath to perform actions on the path that is drawn by this class.
    public Path getPath()
    {
        return mPath;
    }

    // draw method. comments inline.
    public void draw(Canvas canvas)
    {
        // grab a bitmap in the desired size.
        final Bitmap scaledBitmap = getScaledBitmap();

        // find the center of the bitmap.
        final float centerX = scaledBitmap.getWidth() / 2;
        final float centerY = scaledBitmap.getHeight() / 2;

        // wrap the path with a measurement tool for paths - PathMeasure
        final PathMeasure pathMeasure = new PathMeasure(mPath, false);

        // initialize the distance to the center of the bitmap.
        float distance = scaledBitmap.getWidth() / 2;

        // initialize position and slope buffers.
        float[] position = new float[2];
        float[] slope = new float[2];

        float slopeDegree;

        // draw so long as the distance traveled on the path isn't longer than
        // the total distance of the path.
        while (distance < pathMeasure.getLength())
        {
            // grab the position & slope (tangent) on a particular distance along the path.
            pathMeasure.getPosTan(distance, position, slope);

            // convert the vector to a degree.
            slopeDegree = (float)((Math.atan2(slope[1], slope[0]) * 180f) / Math.PI);

            // preserve the current state of the canvas
            canvas.save();

            // translate the canvas to the position on the path.
            canvas.translate(position[0] - centerX, position[1] - centerY);

            // rotate the canvas around the center of the bitmap the amount of degrees needed.
            canvas.rotate(slopeDegree, centerX, centerY);

            // draw the bitmap
            canvas.drawBitmap(scaledBitmap, 0, 0, mPaint);

            // revert the bitmap to the previous state
            canvas.restore();

            // increase the distance by the bitmap's width + the desired margin.
            distance += scaledBitmap.getWidth() + mBitmapMargin;
        }

    }

    // returns a scaled bitmap from the asset specified.
    private Bitmap getScaledBitmap()
    {
        // no bitmap or resId, return null (no special handing of this! add if you like).
        if (mBitmap == null && mResourceId == 0)
            return null;

        // if no bitmap is specified, create one from the resource id. 
        // Optimization: be sure to clear the bitmap once done.
        if (mBitmap == null)
            mBitmap = BitmapFactory.decodeResource(mContext.getResources(), mResourceId);

        // width / height of the bitmap[
        float width = mBitmap.getWidth();
        float height = mBitmap.getHeight();

        // ratio of the bitmap
        float ratio = width / height;

        // set the height of the bitmap to the width of the path (from the paint object).
        float scaledHeight = mPaint.getStrokeWidth();

        // to maintain aspect ratio of the bitmap, use the height * ratio for the width.
        float scaledWidth = scaledHeight * ratio;

        // return the generated bitmap, scaled to the correct size.
        return Bitmap.createScaledBitmap(mBitmap, (int)scaledWidth, (int)scaledHeight, true);
    }
}

这是一个用法示例:

        ImageView image = (ImageView)findViewById(R.id.img);

        CurvedBitmapDrawer drawer = new CurvedBitmapDrawer(this);

        Paint paint = new Paint();

        paint.setStrokeWidth(50);

        drawer.setPaint(paint);
        drawer.setResourceId(R.drawable.heart_icon);
        drawer.setBitmapMargin(10);

        Path path = drawer.getPath();

        path.moveTo(80, 90);
        path.cubicTo(160, 470, 750, 290, 440, 880);

        Bitmap finalBitmap = Bitmap.createBitmap(800, 1000, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(finalBitmap);

        drawer.draw(canvas);

        image.setImageBitmap(finalBitmap);

我已经在这里进行了测试,它似乎运行良好,可能会减去一些边缘情况。这是使用给定用法示例的样子:

Hearts Along A Curve

请记住在您提供的绘画对象上设置笔触宽度,否则将不会绘制任何内容(并且可能会导致当前代码出现异常)。

希望这会对你有所帮助。