自定义视图

View显示流程

1、onMeasure()测量宽高
2、ViewGroup布局,onLayout()
3、draw()视图显示自身的内容

1、绘制背景
2、为显示渐变框做一些准备动作
3、调用自身的onDraw(),(ViewGroup不需要)
4、dispatchDraw()绘制子视图(调用了drawChild()通知孩子draw()自身)
5、画滚动条
View的自定义的认识

要自定义View的显示效果,可以重写其onDraw方法,然后在Canvas上绘制内容即可

public class MyView extends View {

    public MyView(Context context) {
        super(context);
        Log.e("m_tag","MyView(Context context)");
        init();
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.e("m_tag","MyView(Context context, AttributeSet attrs)");
        init();
    }

    private Paint paint;

    private void init(){
        setBackgroundColor(0xff999999);
        //创建画笔对象
        paint = new Paint();
        //设置画笔的颜色
        // paint.setColor(0xFFFF0000);
        paint.setColor(Color.RED);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画图 : 画布 画笔 颜色
        canvas.drawRect(50,100,150,200,paint);
    }
}

使用:

<com.xykj.viewdemo.MyView
android:layout_width="match_parent"
android:layout_height="300dp" />
Canvas的基本绘制
//画矩形
//前面4个参数表示矩形的左上右下,第五个参数表示画笔
canvas.drawRect(50,100,150,200,paint);
//创建一个矩形对象
Rect rect = new Rect(150,300,350,400);
canvas.drawRect(rect,paint);
//画一个圆
//(圆心x坐标,圆心y坐标,半径,画笔)
canvas.drawCircle(50,300,50,paint);

Paint的基本设置

设置颜色
paint.setColor(0xFFFF0000);
paint.setColor(Color.RED); //从Color类中获取的颜色值
//消除锯齿
paint.setAntiAlias(true);
//分开设置各个颜色参数0x00-0xff 十进制就是0-255
paint.setARGB(255,20,50,100);
paint.setAlpha(100); //设置透明度
//设置画笔样式STROKE表示空心 FILL表示实心
paint.setStyle(Paint.Style.STROKE);
//空心边框宽度
paint.setStrokeWidth(5);

文本相关

//文本大小
paint.setTextSize(20); //文本大小
paint.setFakeBoldText(true); //加粗
paint.setTextSkewX(45); //倾斜
View的刷新

如果要修改视图显示的内容,需要让视图重新onDraw,然后在画布上画上新的内容即可,要让视图重新画需要使用其刷新方法
主线程刷新:

invalidate();-->使视图重新绘制

结合触摸监听实现刷新

public class RefreshView extends View {
    public RefreshView(Context context) {
        super(context);
        init();
    }

    public RefreshView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    private Paint paint;
    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
    }
    //圆的圆心
    private int cx = 50, cy = 100;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画圆
        canvas.drawCircle(cx, cy, 50, paint);
    }
    //视图的触摸监听
    @Override
    public boolean onTouchEvent(MotionEvent event) {
    //获取触摸的行为
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                //手指触摸到视图的瞬间
                //得到触控点跟视图左上角点的坐标(相对位置)
                int x = (int) event.getX();
                // int pX = event.getRawX(); //得到触控点和屏幕左上角的位置
                int y = (int) event.getY();
                cx = x;
                cy = y;
                invalidate(); //刷新视图(视图会重新绘制)
                break;
            case MotionEvent.ACTION_MOVE:
                //按下之后离开之前的整个过程(重复执行)
                cx = (int) event.getX();
                cy = (int) event.getY();
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                //手指离开视图的瞬间
                break;
            }
        return true;
        }
    }

子线程刷新,需要使用postInvalidate方法,该方法等效于Handler+invalidate方式

private Thread changeThread;
//随机数(产生0-255之间的数字)
private Random r = new Random();

    public void startChangeColor() {
        if (null != changeThread && changeThread.isAlive()) {
            return;
        }
        changeThread = new Thread() {
            @Override
            public void run() {
                try {
                    while(true) {
                        Thread.sleep(500);
                        //改变画笔颜色
                        paint.setARGB(255,r.nextInt(256),r.nextInt(256),r.nextInt(256));
                        //刷新视图(子线程)
                        postInvalidate(); //等效于Handler+invalidate
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
            }
        }
    };
    changeThread.start();
}
Canvas其他的各种画
//圆角矩形
RectF rectF = new RectF(300,10,400,80);
//(矩形区域,圆角x方向半径,圆角y方向半径)
canvas.drawRoundRect(rectF,20,20,paint);

//画椭圆
RectF oval = new RectF(300,100,400,300);
canvas.drawOval(oval,paint);

RectF arcRectF = new RectF(450,10,550,110);
//画弧线或者扇形
//第一个参数:扇形所在的圆所在的矩形区域
//第二个参数:起始角度
//第三个参数:跨越的总度数
//第四个参数:true表示扇形,false表示弧线
//第五个参数:画笔
canvas.drawArc(arcRectF,-90,90,false,paint);

Path p = new Path();
//指定起始点
p.moveTo(450,100);
//画直线到下一个点
p.lineTo(600,200);
p.lineTo(500,400);
//画贝塞尔曲线
//前面两个表示弧度控制点,后面两个参数表示弧线的终点
p.quadTo(300,300,500,200);
//将最后一个点连接到起始点上
p.close();
//画路径
canvas.drawPath(p,paint);
子线程刷新

子线程中如果不想直接使用Handler提交到主线程刷新,那么也可以直接使用runOnUIThread方法

new Thread() {
    @Override
    public void run() {
        ...
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                progressBar.setProgress(pro);
            }
        });
    ...
    }
}.start();
视图显示流程

1、onMeasure 测量
2、ViewGroup布局,onLayout
3、View显示效果draw

onMeasure,测量

在onMeasure方法中上一级会告诉该视图宽以及高的情况,其中包括模式和尺寸

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
}

获取模式方法

int mode = MeasureSpec.getMode(measureSpc)
EXACTLY表示固定值,如math_parent 100dp
AT_MOST:父容器会给一个最大的参考尺寸,
还在可以在该参考区域内任意获取尺寸(可能按照自己的内容来)如:wrap_content

UNSPECIFIED:未指定,一般不会给开发人员来用

获取大小

int size = MeasureSpec.getSize(measureSpc);

设置测量结果(必须要有)
setMeasuredDimension(w, h);

文本绘制
//(绘制的文本,参考点x坐标,参考点y坐标,画笔)
canvas.drawText(str,10,50,paint);

文本高相关的信息的获取

Paint.FontMetrics fm = paint.getFontMetrics();
top:文本顶部作为
bottom:文本底部的位置
ascent:基准线到顶部的距离
descent:基准线到底部的距离

如果需要获取某一段文本的宽高(占据的区域),可以使用paint的getTextBound方法来获取

Rect textBound = new Rect();
//获取文本所占区域(默认参考点是0,0所以得到的top是负数)
//(字符串对象,字符起始下标,字符的结束下标,文本区域)
paint.getTextBounds(str,0,str.length(),textBound);

自定义参考线

drawTextOnPath(String text,Path path, float hOffset,
float vOffset,Paint paint)
参数:
text:表示绘制的文本
path:参考线的路径
hOffset:在水平方向上文本和参考点的偏移,正数往右,负数往左
vOffset:在垂直方向上文本和参考点的偏移,正数往下,负数往上
paint:画笔

图像变换
图像变换需要使用Matrix来处理

scale_x skew_x tran_x
skew_y scale_y tran_y
pers_0 pers_1 pers_2

初始状态

Matrix matrix = new Matrix(); //图像的默认状态(1倍 0度 偏移0,0 倾斜0度)

操作——set:

//设置一个状态
//设置旋转(旋转的角度,旋转中心点的x坐标,旋转的中心点y坐标)
matrix.setRotate(60, bWidth / 2, bHeight / 2);
//设置平移(x方向的偏移量,y方向的偏移量)
matrix.setTranslate(100,200);
//设置缩放(宽的倍数,高的倍数)
matrix.setScale(2,3);
设置会将之前的状态覆盖

post:基于之前的状态,添加新的状态(向后加)

//设置一个状态
matrix.setRotate(60, bWidth / 2, bHeight / 2);
//添加平移
matrix.postTranslate(100,200);
//添加缩放
matrix.postScale(2,3);

pre:添加状态(向前加)

区别:平移(100,200) 然后旋转30matrix.postTranslate(100,200);
matrix.postRotate(30);
matrix.setRotate(30);
matrix.preTranslate(100,200);
多点触控

多点触控的行为需要结合MotionEvent.ACTION_MASK来过滤出来

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction();
    switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            //单个触控点按下(接触视图瞬间)
            Log.e("m_tag","单个down");
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            //多个触控点按下
            Log.e("m_tag","==多个down");
            break;
        case MotionEvent.ACTION_MOVE:
            //只要有触控点触摸在视图上都会执行move
            Log.e("m_tag","move");
            break;
        case MotionEvent.ACTION_UP:
            //单个触控点离开的瞬间
            Log.e("m_tag","单个up");
            break;
        case MotionEvent.ACTION_POINTER_UP:
            //多个触控点离开
            Log.e("m_tag","==多个up");
            break;
    }
    return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值