赞
踩
绘图基础类涉及 Canvas(画布)、Paint(画笔)、Path(多条直线任意图形)。
@1 Canvas类解读
Android绘图方式是继承View组件,并重写它的onDraw()方法来实现绘制。Canvas的绘制方法有:
API详细内容可以参照文档:Android Canvas 各种drawXXX绘制方法
除了绘制drawXXX方法,还有rotate(旋转)、scale(缩放)、skew(倾斜)、translate(平移)来对其进行坐标变换。
关于Canvas画布类,更多解读可查看官方文档:Android Canvas类详细解读
@2 Paint类解读
Paint表示Canvas画布上的画笔,主要用于设置颜色、粗细、绘制风格和填充风格等。Paint的设置方式有:
API详细内容可以参照文档:Android Paint 各种setXXX方法解读
关于Paint画笔类,更多解读可查看官方文档:Android Paint类详细解读
@3 Path类解读
Path可以预先在View上将多个点连接成一条“路径”,然后调用Canvas的drawPath方法沿着路径绘制任意图形,灵活度很高。Path中的方法有:
API详细内容可以参照官方文档:Android Path类详细解读
@4 基础类实战(基于Canvas、Paint、Path绘制图形训练)
绘制内容及程序最终运行结果如下所示:
关于该程序,自定义View的关键代码如下所示:
- public class MyView extends View {
- private static final String TAG = "MyView";
- private Paint paint =new Paint();
- private Path path1 = new Path();
- private Path path2 = new Path();
- private Path path3 = new Path();
-
- public MyView(Context context) {
- super(context);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.WHITE);
- paint.setAntiAlias(true);//抗锯齿
- paint.setColor(Color.RED);//画笔颜色
- paint.setStyle(Paint.Style.STROKE);//描边模式
- paint.setStrokeWidth(4f);//设置画笔粗细度
- paint.setTextSize(52f);
- drawsth(canvas,paint,path1,0f);
- paint.setStyle(Paint.Style.FILL);//填充模式
- paint.setColor(Color.BLUE);//画笔颜色
- drawsth(canvas,paint,path2,220f);
- paint.setColor(Color.MAGENTA);
- paint.setStyle(Paint.Style.STROKE);//描边模式
- drawTextOnCurve(canvas,paint,path3);
- }
- //绘制曲线和曲线上的文字
- private void drawTextOnCurve(Canvas canvas, Paint paint,Path path){
- path.moveTo(440f,10f);
- path.lineTo(460f,80f);
- path.lineTo(490f,150f);
- path.lineTo(550f,250f);
- path.lineTo(700f,400f);
- canvas.drawPath(path,paint);
- canvas.drawTextOnPath("曲线上的文字",path,10f,10f,paint);
- }
-
- //绘制基本图形、Path不规则图形和文字
- private void drawsth(Canvas canvas, Paint paint,Path path,float offset){
-
- //paint绘制圆形,参数:圆心坐标(x,y)半径r,画笔paint
- canvas.drawCircle(100f+offset,100f,90f,paint);
- //paint绘制矩形,参数:左上角坐标(x,y),右下角坐标(x,y),画笔paint
- canvas.drawRect(10f+offset,200f,210f+offset,400f,paint);
- //paint绘制圆角矩形,参数:左上角坐标(x,y),右下角坐标(x,y),x方向上的圆角半径,y方向上的圆角半径,画笔paint
- canvas.drawRoundRect(10f+offset,410f,210f+offset,610f,40f,40f,paint);
- //paint绘制椭圆,参数:左上角坐标(x,y),右下角坐标(x,y)[参数表示矩形内切的椭圆],画笔paint
- canvas.drawOval(10f+offset,620f,210f+offset,720f,paint);
-
- //Path绘制三角形,moveto表示第1个坐标,lineto分别表示第2、3个坐标。以此类推。
- path.moveTo(110f+offset,730f);
- path.lineTo(10f+offset,930f);
- path.lineTo(210f+offset,930f);
- path.close();
- canvas.drawPath(path,paint);
- //Path绘制不规则多点多边形
- path.moveTo(10f+offset,950f);
- path.lineTo(50f+offset,1000f);
- path.lineTo(200f+offset,970f);
- path.lineTo(150f+offset,1070f);
- path.lineTo(10f+offset,1110f);
- path.lineTo(210f+offset,1130f);
- path.close();
- canvas.drawPath(path,paint);
-
- //注意:这个坐标(x,y)并不是文字的左上角,而是一个与左下角比较接近的位置。
- canvas.drawText("测试文字",10f+offset,1190f,paint);
- }
- }
在MainActivity中实现代码为:
- public class MainActivity extends AppCompatActivity {
- private static String TAG = "MainActivity";
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(new MyView(this));
- }
- }
说明:该程序利用关键类Canvas、Paint、Path类来绘制基本图形、文字和沿曲线绘制文字。
@1 PathEffect类解读
PathEffect派生类主要有:
ComposePathEffect:Android ComposePathEffect类详细解读,组合2种效果。
CornerPathEffect:Android CornerPathEffect类详细解读,平滑过渡。
DashPathEffect:Android DashPathEffect类详细解读,线段虚线化。
DiscretePathEffect:Android DiscretePathEffect类详细解读,打散线段。
PathDashPathEffect:Android PathDashPathEffect类详细解读,使用Path图形来填充当前的路径。
SumPathEffect:Android SumPathEffect类详细解读,叠加2种效果。
@2 PathEffect类实战
分别展示以上几种PathEffect派生类的效果,最终呈现 如下所示:
关于该程序,自定义View的关键代码如下所示:
- public class MyView extends View {
- private static final String TAG = "MyView";
- private Paint paint =new Paint();
- private PathEffect[] effects = new PathEffect[7];
-
- private int[] colors = new int[]{Color.BLACK,Color.BLUE,
- Color.RED,Color.GREEN,Color.CYAN,Color.MAGENTA,Color.GRAY};
-
- private String[] effect = new String[]
- {"null","CornerPathEffect","DiscretePathEffect","DashPathEffect",
- "PathDashPathEffect","ComposePathEffect","SumPathEffect"};
- private Path path = new Path();
-
- public MyView(Context context) {
- super(context);
- paint.setStyle(Paint.Style.STROKE);
- paint.setStrokeWidth(4f);
- paint.setTextSize(38f);
- path.moveTo(0f,0f);
- for(int i=0;i<=25;i++){
- path.lineTo(i*25f,(float)(Math.random()*90));
- }
- effects[0] = null;
- effects[1] = new CornerPathEffect(10f);
- effects[2] = new DiscretePathEffect(3.0f,5.0f);
- effects[3] = new DashPathEffect(new float[]{20f,10f,5f,10f},0f);
- Path p = new Path();
- p.addRect(0f,0f,8f,8f,Path.Direction.CCW);
- effects[4] = new PathDashPathEffect(p,12f,3f,PathDashPathEffect.Style.ROTATE);
- effects[5] = new ComposePathEffect(effects[3],effects[4]);
- effects[6] = new SumPathEffect(effects[2],effects[4]);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawColor(Color.WHITE);
- canvas.translate(10f,10f);
-
- for(int k=0;k<effects.length;k++){
- paint.setColor(colors[k]);
- canvas.drawText(k+" "+effect[k],10f,50f+k*90f,paint);
- }
- canvas.translate(400f,0f);
- for(int j=0;j<effects.length;j++){
- paint.setPathEffect(effects[j]);
- paint.setColor(colors[j]);
- canvas.drawPath(path,paint);
- canvas.translate(0f,90f);
- }
- //注意:这里如果不设置为null重绘时paint依然持有effect效果
- paint.setPathEffect(null);
- }
- }
在MainActivity中实现代码同上,依然为:
- public class MainActivity extends AppCompatActivity {
- private static String TAG = "MainActivity";
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(new MyView(this));
- }
- }
该程序主要展现几种PathEffect派生类的实际效果。
@1 动起来的View
以上只是静态图像的显示,如果要动起来,需要让View组件上的绘图不断刷新,通知View重绘的方法是:invalidate(UI线程)和postInvalidate(非UI线程)。
为了保留之前的绘制内容,采用双缓冲技术。它的主要原理是:当一些列动画争先显示时,程序在不断改变它,前面的画面还没有显示完,程序又请求重新绘制,这就回导致屏幕不停闪烁。为了避免闪烁,可以使用双缓冲技术,将要处理的图片都在内存中处理好之后,再将其显示到屏幕上。这样显示出来的总是完整的图像,不会出现闪烁现象。而在android系统中可以理解为:当程序需要在指定View上绘制时,不直接绘制,而是先绘制在内存中的Bitmap图片上,等到内存中Bitmap图片绘制好后,一次性将Bitmap图片绘制到View组件上。
@2 实战(使用双缓冲的绘图板)
实现功能为:触摸屏幕时候沿着触摸的痕迹自由绘制内容。效果如下:
关于该程序,自定义View的关键代码如下所示:
- public class MyView extends View {
- private static final String TAG = "MyView";
- private Paint paint =new Paint(Paint.DITHER_FLAG);
- private Paint bitmapPaint = new Paint();
- private Path path = new Path();
- private float preX = 0.0f;
- private float preY = 0.0f;
- private Bitmap cacheBitmap;
- private Canvas cacheCanvas = new Canvas();
-
- public MyView(Context context) {
- super(context);
- }
-
- public MyView(Context context,AttributeSet attrs){
- super(context,attrs);
- }
-
- public MyView(Context context,AttributeSet attrs,int defStyleAttr){
- super(context,attrs,defStyleAttr);
- }
-
- public void clear(){
- cacheBitmap.eraseColor(Color.WHITE);
- invalidate();
- }
-
- public void init(int w,int h){
- //双缓冲绘制设置
- cacheBitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
- cacheCanvas.setBitmap(cacheBitmap);
- paint.setColor(Color.RED);//画笔颜色
- paint.setStyle(Paint.Style.STROKE);//描边模式
- paint.setStrokeWidth(1f);//设置画笔粗细度
- paint.setAntiAlias(true);//抗锯齿
- paint.setDither(true);//防抖动
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawColor(Color.WHITE);
- //绘制过去内容
- canvas.drawBitmap(cacheBitmap,0f,0f,bitmapPaint);
- //绘制当下内容
- canvas.drawPath(path,paint);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- float x = event.getX();
- float y = event.getY();
- switch(event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- path.moveTo(x,y);
- preX = x;
- preY = y;
- break;
- case MotionEvent.ACTION_MOVE:
- path.lineTo(x,y);
- preX = x;
- preY = y;
- break;
- case MotionEvent.ACTION_UP:
- //将之前的轨迹图像记录在cacheCanvas中的cacheBitmap中
- cacheCanvas.drawPath(path,paint);
- path.reset();
- break;
- }
- invalidate();//刷新,下一帧
- return true;
- }
- }
在MainActivity中实现代码如下所示:
- public class MainActivity extends AppCompatActivity {
- private static String TAG = "MainActivity";
- private MyView myView;
- private Button btn_clear;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.sample_my_view);
- myView = findViewById(R.id.myView);
- btn_clear = findViewById(R.id.btn_clear);
-
- //获取屏幕宽度和高度
- DisplayMetrics displayMetrics = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
- myView.init(displayMetrics.widthPixels,displayMetrics.heightPixels);
-
- btn_clear.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- myView.clear();
- }
- });
- }
- }
@1 了解Matrix
Matrix在程序中可用于平移、旋转、缩放、倾斜,不仅可用于绘制中的图形,还可用于View组件中。Matrxi的常见方法也主要是针对平移、旋转、缩放、倾斜4个操作。
关于Matrix矩阵类,更多解读可查看官方文档:Android Matrix类详细解读
@2 Matrix实战
使用按键响应Matrix的常见操作,效果如下所示:
关于该程序,自定义View的关键代码如下所示:
- public class MyView extends View {
- private static final String TAG = "MyView";
- private Matrix initMatrix = new Matrix();
- private Matrix matrix = new Matrix();
- private int h,w;
- public Bitmap bitmap = null;
-
- public MyView(Context context) {
- super(context);
- init(context);
- }
-
- public MyView(Context context,AttributeSet attrs){
- super(context,attrs);
- init(context);
- }
-
- public MyView(Context context,AttributeSet attrs,int defStyleAttr){
- super(context,attrs,defStyleAttr);
- init(context);
- }
-
- public void setMatrix(Matrix matrix){
- this.matrix = matrix;
- invalidate();
- }
-
- private void init(Context context){
- initMatrix.reset();
- initMatrix.setScale(5.0f,5.0f);
- try {
- InputStream isImage = context.getAssets().open("test1.png");
- bitmap = BitmapFactory.decodeStream(isImage);
- w = bitmap.getWidth();
- h = bitmap.getHeight();
- bitmap = Bitmap.createBitmap(bitmap,0,0, w, h, initMatrix, true);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawColor(Color.WHITE);
- //居中显示
- float centerStartX = (canvas.getWidth()-bitmap.getWidth())*1.0f/2;
- float centerStartY = (canvas.getHeight()-bitmap.getHeight())*1.0f/2;
- canvas.translate(centerStartX,centerStartY);
- //bitmap绘制
- canvas.drawBitmap(bitmap, matrix, null);
- }
- }
在MainActivity中实现代码如下所示:
- public class MainActivity extends AppCompatActivity implements View.OnClickListener {
- private static String TAG = "MainActivity";
- private MyView myView;
- private Button btn_reset,btn_scale,btn_skew,btn_rotate,btn_translate;
- private Matrix matrix = new Matrix();
- private float scaleX=0.5f,scaleY=0.5f;
- private float rotate = 0.0f;
- private float skewX = 0.0f,skewY = 0.0f;
- private float translateX=0.0f,translateY=0.0f;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.sample_my_view);
- myView = findViewById(R.id.myView);
- btn_reset = findViewById(R.id.btn_reset);
- btn_scale = findViewById(R.id.btn_scale);
- btn_rotate = findViewById(R.id.btn_rotate);
- btn_translate = findViewById(R.id.btn_translate);
- btn_skew = findViewById(R.id.btn_skew);
- matrix.reset();
- }
-
- @Override
- public void onClick(View v) {
- switch (v.getId()){
- case R.id.btn_rotate://旋转:持续,参数表度数,可持续叠加
- rotate+=10.0f;
- matrix.setRotate(rotate);
- break;
- case R.id.btn_scale://缩放范围0.5f-1.5f
- scaleX+=0.1f;
- scaleY+=0.1f;
- if(scaleX>=1.5f ||scaleY>=1.5f){
- scaleX = 0.5f;
- scaleY = 0.5f;
- }
- matrix.setScale(scaleX,scaleY);
- break;
- case R.id.btn_skew://错切范围:0.0f-1.0f
- skewX+=0.1f;
- if(skewX>1.0f || skewY>1.0f){
- skewX = 0.0f;
- }
- matrix.setSkew(skewX,skewY);
- break;
- case R.id.btn_translate://平移范围:0.0f-200.f
- translateX+=10.0f;
- if(translateX>=200.0f ||translateY>=200.0f){
- translateX = 0.0f;
- }
- matrix.setTranslate(translateX,translateY);
- break;
- default:
- matrix.reset();
- break;
- }
- myView.setMatrix(matrix);
- }
- }
关于该程序,sample_my_view.xml 布局参考文件如下所示:
- <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:visibility="visible">
-
- <Button
- android:id="@+id/btn_scale"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="20"
- android:onClick="onClick"
- android:text="@string/scale" />
-
- <Button
- android:id="@+id/btn_rotate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="20"
- android:onClick="onClick"
- android:text="@string/rotate" />
-
- <Button
- android:id="@+id/btn_skew"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="20"
- android:onClick="onClick"
- android:text="@string/skew" />
-
- <Button
- android:id="@+id/btn_translate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="20"
- android:onClick="onClick"
- android:text="@string/translate" />
-
- <Button
- android:id="@+id/btn_reset"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="20"
- android:onClick="onClick"
- android:text="@string/reset" />
- </LinearLayout>
-
- <com.ags.myapplication.MyView
- android:id="@+id/myView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@mipmap/ic_launcher" />
Matrix变换是基本的图形变换,关于一些图形特效的效果,在下一节中详细讲解。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。