View的基本绘制

View的基本绘制

翻译自:点击链接

综述


让我们来构建一个用户自定义的View,它允许用户用他们按下的手指在屏幕上进行绘图。这片文章将会阐述怎样去构建一个用户自定义的组件,怎样在View上去绘制几何图形和路径,以及怎样处理用户的触摸交互。

生成我们的View


创建一个叫SimpleDrawingView简单的class,并且让它继承自View:

public class SimpleDrawingView extends View {
    public SimpleDrawingView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }
}

把下面这个加入到activity的xml布局文件中,这样我们定义的View才会被植入:

<RelativeLayout     xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <com.codepath.example.simpledrawapp.SimpleDrawingView
        android:id="@+id/simpleDrawingView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true" />

</RelativeLayout>

使用Canvas进行简单绘制

让我们试试在在屏幕上画几个圆这需要我们定义一个Paint对象,用来控制绘画的风格和颜色让我们开始使用它吧:

public class SimpleDrawingView extends View {
  // setup initial color
  private final int paintColor = Color.BLACK;
  // defines paint and canvas
  private Paint drawPaint;

  public SimpleDrawingView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setFocusable(true);
    setFocusableInTouchMode(true);
    setupPaint();
  }

  // Setup paint with color and stroke styles
  private void setupPaint() {
    drawPaint = new Paint();
    drawPaint.setColor(paintColor);
    drawPaint.setAntiAlias(true);//设置抗锯齿
    drawPaint.setStrokeWidth(5);//设置空心线宽
    drawPaint.setStyle(Paint.Style.STROKE);//设置成空心
    drawPaint.setStrokeJoin(Paint.Join.ROUND);//设置结合处为圆弧
    drawPaint.setStrokeCap(Paint.Cap.ROUND);//笔尖是圆形
  }
}

现在我们设置一个黑色的空心风格的Paint,我们试试用不同颜色画一些圆圈。所有这些绘制的过程将写在onDraw方法里面,这个方法会在View渲染之后进行回调:

public class SimpleDrawingView extends View {
    // ...variables and setting up paint... 
    // Let's draw three circles
    @Override
    protected void onDraw(Canvas canvas) {
      canvas.drawCircle(50, 50, 20, drawPaint);
      drawPaint.setColor(Color.GREEN);
      canvas.drawCircle(50, 150, 20, drawPaint);
      drawPaint.setColor(Color.BLUE);
      canvas.drawCircle(50, 250, 20, drawPaint);
    }
}

注意onDraw方法传入了一个canvas对象,我们用这个进行绘制,并且还要借助我们之前定义的Paint,drawCircle方法需要x,y坐标和radius半径,以下是绘制效果:
这里写图片描述

处理触摸事件

假设我们想要任何时候,当我们的用户在屏幕上按下时,都能画一个圈。我们需要一系列的点给我们的圆圈,并且每次触摸都将追加新的点,这里的点使用Point这个对象,它提供了一个点应有的要素,和操作它的一些方法,它维护x,和y这两个值:

public class SimpleDrawingView extends View {

  private final int paintColor = Color.BLACK;

  private Paint drawPaint;

  private List<Point> circlePoints;// 装Point

  public SimpleDrawingView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setupPaint(); 
    circlePoints = new ArrayList<Point>();
  }

  // Draw each circle onto the view
  @Override
  protected void onDraw(Canvas canvas) {
  //把所有的点绘制一遍,感觉有点。。。
    for (Point p : circlePoints) {
      canvas.drawCircle(p.x, p.y, 5, drawPaint);
    }
  }

  // Append new circle each time user presses on screen
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    float touchX = event.getX();
    float touchY = event.getY();
    circlePoints.add(new Point(Math.round(touchX), Math.round(touchY)));
    postInvalidate();//重新绘制,重新调用onDraw方法
    return true;
  }

  private void setupPaint() {
    // 这里把风格改成填充,其它的跟前面一样
    drawPaint.setStyle(Paint.Style.FILL); // change to fill
    // ...
  }
}

有了这个,我们就可以在任意按下的位置画黑圆了
这里写图片描述

使用Path来绘制

到目前为止我们已经会使用onDraw了,并且会用基于触摸事件处理的画圆方式了。下一步,让我们用路径绘制方式代替刚刚的逐一绘制。Path这个类允许用户在屏幕上绘制。一个Path可以包含很多条线,轮廓和其他几何图形。首先我们添加个Path变量去绘制一个轨迹:

public class SimpleDrawingView extends View {
  // ...
  private Path path = new Path();
  // ...
}

下一步,当用户按下的时候,我们在屏幕上追加一些点。当用户按下,们开始增加一个路径,把这些点连接起来。为了做这些,我们需要修改onTouchEvent方法,并追加这些点进我们的Path对象:

public class SimpleDrawingView extends View {
    private Path path = new Path();

    // Get x and y and append them to the path
    public boolean onTouchEvent(MotionEvent event) {
        float pointX = event.getX();
        float pointY = event.getY();
        // Checks for the event that occurs
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // Starts a new line in the path
            path.moveTo(pointX, pointY);
            break;
        case MotionEvent.ACTION_MOVE:
            // Draws line between last point and this point
            path.lineTo(pointX, pointY);
            break;
        default:
            return false;
       }

       postInvalidate(); // Indicate view should be redrawn
       return true; // Indicate we've consumed the touch
    }

   // ...
}

把之前的onDraw方法改一下,用绘制路径的方式代替:

public class SimpleDrawingView extends View {
  // ... onTouchEvent ...

  // Draws the path created during the touch events
  @Override
  protected void onDraw(Canvas canvas) {
      canvas.drawPath(path, drawPaint);
  }

  private void setupPaint() {
    // same as before
    drawPaint.setStyle(Paint.Style.STROKE); // change back to stroke
    // ...
  }
}

有了这些,我们就有了一个最基本的绘图的app了:
这里写图片描述

用Bitmap高效率绘制


当你在canvas上面绘画的时候,你经常会想到要依靠bitmap缓存图片,就像这个stackoverflow网站上的问题一样。stackoverflow的问题

Bitmap mField = null;

public void init()
{
  mField = new Bitmap(...dimensions...);
  Canvas c = new Canvas(mField);
  c.drawRect(...);
  ...
}

public void onDraw(Canvas c)
{
  c.drawBitmap(mField);
}

这是一个常用的提高性能的模型

关于SimpleDrawingView


SimpleDrawingView的所有代码如下所示:

public class SimpleDrawingView extends View {
    // setup initial color
    private final int paintColor = Color.BLACK;
    // defines paint and canvas
    private Paint drawPaint;
    // stores next circle
    private Path path = new Path();

    public SimpleDrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setFocusable(true);
        setFocusableInTouchMode(true);
        setupPaint();
    }

    private void setupPaint() {
        // Setup paint with color and stroke styles
        drawPaint = new Paint();
        drawPaint.setColor(paintColor);
        drawPaint.setAntiAlias(true);
        drawPaint.setStrokeWidth(5);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(path, drawPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float pointX = event.getX();
        float pointY = event.getY();
        // Checks for the event that occurs
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:  
            path.moveTo(pointX, pointY);
            return true;
        case MotionEvent.ACTION_MOVE:
            path.lineTo(pointX, pointY);
            break;
        default:
            return false;
        }
        // Force a view to draw again
        postInvalidate();
        return true;
    }
}

使用单独的像素来绘制

当绘制或是正在进行动画,你往往想用整齐的独立的像素,在不同尺寸大小和密度的设备上稳健地绘制。你也许想更好的整齐的决定设备的高度或者宽度。拷贝这个DeviceDimensionsHelper.java工具类到你的工程中,只要你获得context,你就能获得设备的宽高,并且在dp和px之间任意转换:

// Get height or width of screen
int screenHeight = DeviceDimensionsHelper.getDisplayHeight(this);
int screenWidth = DeviceDimensionsHelper.getDisplayWidth(this);
// Convert dp to pixels
float px = DeviceDimensionsHelper.convertDpToPixel(25f, this);
// Convert pixels to dp
float dp = DeviceDimensionsHelper.convertPixelsToDp(25f, this);

你可以用这个更方便的绘制你定义的View了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值