Android画布(一 )

获取画布的方法

* onDraw()
* diapatchDraw()
* 通过Bitmap创建
* 通过SurfaceView的SurfaceHolder.lockCanvas()

Drawable类

  • 可以通过创建dawable对象,然后将画好的drawable对象画在画布上,也是创建Bitmap的一种方式
ShapeDrawable
shape标签与GradientDrawable
  • 首先shap标签所对应的类是GradientDrawable而不是ShapeDrawable,但是GradienntDrawable并不能完成shape标签的所有功能,GradientDrawable的构造函数如下
GradientDrawable(GradientDrawable.Orientation orientation, int[] colors)
  • 构造函数可以看出GradientDrawable对应的是gradient标签的功能,并不能完成shape标签的构造矩形、椭圆等功能,但是获得shape的时候需要将其强制转换为GradientDrawable
获取shape标签的实例

eg: 获取shape对象并设置为圆角:

public class AndroidHuaBuActivity extends AppCompatActivity {


    private Button button;
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_hua_bu);
        button = (Button)findViewById(R.id.add_shape_corner);
        textView = (TextView)findViewById(R.id.shape_tv);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //通过textView的getBackground获得shape标签
                GradientDrawable gradientDrawable = (GradientDrawable)textView.getBackground();
                gradientDrawable.setCornerRadius(20);
            }
        });


    }
}



通过textView的背景将下面的xml引入:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ff0000"/>
    <stroke android:width="2dp"
        android:color="#00ff00"
        android:dashGap="5dp"
        android:dashWidth="5dp"/>
</shape>
ShapeDrawable的构造函数:
ShapeDrawable()
ShapeDrawable(Shape shape)
  • 注意:
    • ShapeDrawable对象是需要与Shape对象进行关联的,所以如果使用第一个构造函数,必须使用ShapeDrawable.setShape(Shape shape)函数来设置 Shape对象,一般使用第二个构造函数
    • Shape类是一个基类,draw()函数是一个虚函数,每个子类可以根据不同的需求来绘出不同的图形,所以构造ShapeDrawable并不能直接传递shape类型的队形,因为shape中没有实现draw(),而是需要传入已经实现了draw()函数的Shape类的派生类
Shape的派生类:
派生类 意义
RectShape 构造一个矩形Shape
ArcShape 构造一个扇形Shape
OcalShape 构造一个椭圆的Shape
RoundRectShape 构造一个圆角矩形Shape, 可带有镂空矩形效果
PathShape 构造一个可根据路径绘制的Shape
RectShape
public class ShapeView extends View {
    private ShapeDrawable shapeDrawable;


    public ShapeView(Context context) {
        super(context);
        init();
    }
    public ShapeView(Context context, AttributeSet attributeSet){
        super(context, attributeSet);
        init();
    }


    public ShapeView(Context context, AttributeSet attributeSet, int defStyle){
        super(context, attributeSet, defStyle);
        init();
    }


    private void init() {
        setLayerType(LAYER_TYPE_SOFTWARE,null);
        //生成一个ShapeDrawable,并将这个形状的定义为矩形
        shapeDrawable = new ShapeDrawable(new RectShape());
        //设置shapeDrawable在空间中的显示位置(在当前控件中的位置)
        shapeDrawable.setBounds(new Rect(50, 50, 200, 100));
        //获取画笔,并将整个Drawable填充为黄色
        shapeDrawable.getPaint().setColor(Color.YELLOW);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //将shape实例画在画布上
        shapeDrawable.draw(canvas);
    }
}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".AndroidHuabu.ShapeExample.ShapeActivity">


    <com.example.adminstator.myviewdesign.AndroidHuabu.ShapeExample.ShapeView
        android:layout_width="250px"
        android:layout_height="150px"
        android:layout_margin="100dp"
        android:background="#ffffff"/>
</LinearLayout>
  • 结论:

    • 整个矩形区域水平居中显示,可以证明ShapeDrawable.setBounds()函数所设置的矩形位置是指所在控件中的位置,并非整个屏幕
  • Drawable画布问题:

    • 通过getPaint()获得画笔,但是ShapeDrawable.draw(canvas)是将shape画到控件上,并没有绘制shapeDrawable本身。shapeDrawable是自带画笔的,质押奥得到了画笔。改变 paint的任何内容,就会立刻在ShapeDrawable上重画
ArcShape
  • ArcShape是在 OvalShape所形成的椭圆的基础上,将其进行角度切割所形成的扇形,其中扇形开始的0度在椭圆的x轴正方向上
//startAngle:指的是开始的角度,扇形开始的0度在椭圆的X轴正方向上,即右中间的位置(顺时针方向)
//sweepAngle:指扇形所扫过的角度
public ArcShape(ffloat startAngle, float sweepAngle)
RoundRectShape
  • 字面上的意思是圆角矩形,它不止可以实现圆角矩形,他的本意是实现镂空的圆角矩形
  • 效果:
    • 可以实现单纯的圆角矩形
    • 可以实现中间带有镂空的圆角矩形,而且中间镂空的矩形也可以带有圆角
//outerRadii:外围矩形的各个角的角度大小,需要填充8个数字,每两个数字一组,分别对应(左上角,右上角, 右下角,左下角)4个角的角度组(x, y),每两个一组的数字构成一个椭圆。第一个数字代表椭圆的X轴半径,第二个数字代表椭圆的Y轴半径, 如果不需要指定外围矩形的各个角的角度,可以传入null

//RectF inset:表示内部矩形与 外部矩形各边的边距,RectF的4个值分别对应left,top, right, bottom, 如果不需要内部矩形的镂空效果,则可以传入null

//float[] innerRadii:表示内部矩形的各个角的角度大小,同样需要填充8个数字,其含义与outerRadii一样,如果不指定各个角的角度,则传入null即可

public RoundRectShape(float[] outerRadii, RectF inset, float[] innerRadii) 

eg:




public class ShapeView extends View {
    private ShapeDrawable shapeDrawable;


    public ShapeView(Context context) {
        super(context);
        init();
    }
    public ShapeView(Context context, AttributeSet attributeSet){
        super(context, attributeSet);
        init();
    }


    public ShapeView(Context context, AttributeSet attributeSet, int defStyle){
        super(context, attributeSet, defStyle);
        init();
    }


    private void init() {
        setLayerType(LAYER_TYPE_SOFTWARE,null);
//        //生成一个ShapeDrawable,并将这个形状的定义为矩形
//        shapeDrawable = new ShapeDrawable(new ArcShape(0, 300));
//        //设置shapeDrawable在空间中的显示位置(在当前控件中的位置)
//        shapeDrawable.setBounds(new Rect(50, 50, 200, 100));
//        //获取画笔,并将整个Drawable填充为黄色


        float[] outerRadii = new float[]{12, 12, 12, 12, 0, 0, 0, 0};
        RectF inset = new RectF(6,  6, 6, 6);
        float[] innerRadii = new float[]{50, 12, 0, 0, 12, 50, 0, 0};
        shapeDrawable = new ShapeDrawable(new RoundRectShape(outerRadii, inset, innerRadii));
        shapeDrawable.setBounds(50, 50, 200, 100);
        shapeDrawable.getPaint().setColor(Color.BLUE);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //将shape实例画在画布上
        shapeDrawable.draw(canvas);
    }
}
PathShape
//path:所要画的路径

//stdWidth:表示标注宽度,即将整个ShapeDrawable的宽度分成多少份,Path中moveTo(x,  y),lineTo(x, y)这些函数中的数值在这里起始都是以每一份的位置来计算的,当ShapeDrawable动态变大、变小时,每一份都会变小,而根据这些分的数值画出来的Path图形就是动态缩放

//stdHeight:表示标准高度,即将ShapeDrawable的高度分成多少份

public PathShape(Path path, float stdWidth, float stdHeight)

eg:

public class ShapeView extends View {
    private ShapeDrawable shapeDrawable;


    public ShapeView(Context context) {
        super(context);
        init();
    }
    public ShapeView(Context context, AttributeSet attributeSet){
        super(context, attributeSet);
        init();
    }


    public ShapeView(Context context, AttributeSet attributeSet, int defStyle){
        super(context, attributeSet, defStyle);
        init();
    }


    private void init() {
        setLayerType(LAYER_TYPE_SOFTWARE,null);
        Path path = new Path();
        //注意这里此时的单位是份
        path.moveTo(0, 0);
        path.lineTo(100, 0);
        path.lineTo(100, 100);
        path.lineTo(0, 100);
        path.close();
        //首先我们要知道这里的单位是份的概念而不是px,所以为了对比,我们将shapeDrawable和画布大小设为一致
        // 开始将shape 100, 100 可以发现它填满了控件如果使用的是px当然是不可能将其填满的,
        // 将ShapeDrawable的高设置成200,则原来的只能占1/2,所以只能展示为控件的一半
        shapeDrawable = new ShapeDrawable(new PathShape(path, 100, 200));
        shapeDrawable.setBounds(new Rect(0, 0, 250, 150));
        shapeDrawable.getPaint().setColor(Color.BLUE);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //将shape实例画在画布上
        shapeDrawable.draw(canvas);
    }
}
自定义shape
  • 想要自定义shape,必须先看他的各个派生类的源码是如何实现相应的draw()

public class PathShape extends Shape {
    private final float mStdWidth;
    private final float mStdHeight;


    private Path mPath;


    private float mScaleX; // cached from onResize
    private float mScaleY; // cached from onResize


    /**
     * PathShape constructor.
     *
     * @param path a Path that defines the geometric paths for this shape
     * @param stdWidth the standard width for the shape. Any changes to the
     *                 width with resize() will result in a width scaled based
     *                 on the new width divided by this width.
     * @param stdHeight the standard height for the shape. Any changes to the
     *                  height with resize() will result in a height scaled based
     *                  on the new height divided by this height.
     */
    public PathShape(@NonNull Path path, float stdWidth, float stdHeight) {
        mPath = path;
        mStdWidth = stdWidth;
        mStdHeight = stdHeight;
    }


    @Override
    public void draw(Canvas canvas, Paint paint) {
        canvas.save();
        canvas.scale(mScaleX, mScaleY);
        canvas.drawPath(mPath, paint);
        canvas.restore();
    }


    @Override
    protected void onResize(float width, float height) {
        mScaleX = width / mStdWidth;
        mScaleY = height / mStdHeight;
    }


    @Override
    public PathShape clone() throws CloneNotSupportedException {
        final PathShape shape = (PathShape) super.clone();
        shape.mPath = new Path(mPath);
        return shape;
    }
}

  • 发现这个派生类做的就是在创建PathShape的时候将path传递进去,然后利用draw()函数将其画出来(onResize()函数是根据我们刚才发现的份的关系,进行重新的计算)
package com.example.adminstator.myviewdesign.AndroidHuabu.ShapeExample;


import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RegionIterator;
import android.graphics.drawable.shapes.Shape;


/**
* Created with Android Studio.
* Description:
*
* @author: 王拣贤
* @date: 2019/06/27
* Time: 21:32
*/
public class OwnShapeView extends Shape {
    private Region region;
    public OwnShapeView(Region region){
        if(region!=null){
            this.region = region;
        }
    }
    @Override
    public void draw(Canvas canvas, Paint paint) {
        RegionIterator iter = new RegionIterator(region);
        Rect r = new Rect();
        while (iter.next(r)){
            canvas.drawRect(r, paint);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39424143/article/details/93915695