我们将举例说明怎么自定义一个组件,怎么样在一个视图上画各种图形和路径还有怎么处理用户的触摸交互。
步骤
1. 创建一个继承View的自定义view类
2. 在布局XML文件中加入我们新建的view
3. 在类中定义画布和线条的各种属性,其中最后的实现是用的是onDraw()
方法
//为了绘制新建一个view类
public class SimpleDrawingView extends View {
//建立首个颜色
private final int paintColor = Color.BLACK;
//定义油漆和画布
private Paint drawPaint;
public SimpleDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusable(true);
setFocusableInTouchMode(true);
setupPaint();
}
//设置笔画的颜色和风格
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);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawCircle(50, 50, 20 ,drawPaint);//参数中心的x,y坐标以及圆和使用的线条
drawPaint.setColor(Color.GREEN);
canvas.drawCircle(50, 150, 20, drawPaint);
drawPaint.setColor(Color.BLUE);
canvas.drawCircle(50, 250, 20 ,drawPaint);
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
处理触摸交互
如果我们想要用户每次触摸在绘画视图上的时候都可以画出一个点,这要求我们跟踪一系列的点,当onTouch
事件触发的时候获得那个点,然后绘制出来
要点:新建一个点对象的集合合存储相应触摸的点的坐标
在onDraw()
方法中绘制出集合中的点
//每当用户触摸屏幕时会触发我们之前存储的圆
private List<Point> circlePoints;
public SimpleDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusable(true);
setFocusableInTouchMode(true);
setupPaint();
circlePoints = new ArrayList<Point>();
}
//设置笔画的颜色和风格
private void setupPaint() {
drawPaint = new Paint();
drawPaint.setColor(paintColor);//设置为黑色
drawPaint.setAntiAlias(true);//设置抗锯齿
drawPaint.setStrokeWidth(5);//设置笔画粗细
drawPaint.setStyle(Paint.Style.FILL);//绘画风格为填充
drawPaint.setStrokeJoin(Paint.Join.ROUND);//笔画形状
drawPaint.setStrokeCap(Paint.Cap.ROUND);
}
//在view上绘制集合中的点
@Override
protected void onDraw(Canvas canvas) {
for (Point p : circlePoints) {
canvas.drawCircle(p.x, p.y, 5, drawPaint);
}
}
//设置点击事件
@Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();//获得点击点的x坐标
float touchY = event.getY();//获得点击点的Y坐标
//将点的坐标存储到集合中
circlePoints.add(new Point(Math.round(touchX), Math.round(touchY)));
//重新绘制view,表明无效
postInvalidate();
return true;
}
路径绘制
Path
类是一个允许用户在屏幕上绘制的理想的类,一个路径可以包括很多的线条图像轮廓甚至形状,首先让我们创建一个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
// ...
}
}
使用bitmap的缓存高效绘制
当在帆布上绘制时,我们可以通过将图像缓存为bitmap来改善渲染时间
是一种常用的模式
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);
}
使用DIP绘制
当我们进行绘制或可以帮我呢keyidad者动画时候,为了支持更多的屏幕我们需要使用dp,使用这个工具类可以帮我们达成测量屏幕宽高和将dp转换为px的目的
自定义一个DeviceDimensionsHelper.java
类:
public class DeviceDimensionsHelper {
// DeviceDimensionsHelper.getDisplayWidth(context) => (用像素表示宽度)
public static int getDisplayWidth(Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return displayMetrics.widthPixels;
}
// DeviceDimensionsHelper.getDisplayHeight(context) => (像素表示高度)
public static int getDisplayHeight(Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return displayMetrics.heightPixels;
}
// DeviceDimensionsHelper.convertDpToPixel(25f, context) => (25dp converted to pixels)
public static float convertDpToPixel(float dp, Context context){
Resources r = context.getResources();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
}
// DeviceDimensionsHelper.convertPixelsToDp(25f, context) => (25px converted to dp)
public static float convertPixelsToDp(float px, Context context){
Resources r = context.getResources();
DisplayMetrics metrics = r.getDisplayMetrics();
float dp = px / (metrics.densityDpi / 160f);
return dp;
}
使用:
// 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);