android中自定义View

View 的基本概念

这个类是用户接口的基础构件。 View 表示屏幕上的一块矩形区域,负责绘制这个区域和事件
处理。
View 是所有widget类的基类,Widget 类用于创建交互式UI构件(按钮,输入框等)。
View 类的ViewGroup子类是layout的基类,Layout是一个不可见的容器,它保存着
View(或ViewGroup)并定义这些View的layout属性。
可以说View类是用户接口类中最重要的一个类。

IDs: Views 有一个整数相对应,id被用于在View树中找到指定的View。可以在layout文件中
定义 一个唯一的ID, 在Activity的onCreate函数中调用findViewById来查找这个View。
在整个树内, id可以不是唯一的,但再指定的范围内查找时我们可以确信它是唯一的。
View是一个矩形区域, 使用左&上的坐标以及长和宽可以表示一个View。我们可以使用方
法 getLeft() , getTop() , getRight() , getBottom() , getWidth() , getHeight() 等函数来
获取其位置信息

View创建概述
在API中对View的回调流程有以个详细的描述:
1. Creation:创建流程
2. 1.调用构造器
3. public View(Context context) //使用java代码创建View时的构造方法
4. public View(Context context, AttributeSet attrs)//在XML中配置时的构造方法,attrs存储xml中设置的属性
5. 2.onFinishInflate() //当View和他的所有子View从XML中解析完成后调用。
6. Layout:布局流程
7. 1.onMeasure(int, int) //确定View和它所有的子View要求
的尺寸时调用
8. 2.onLayout(boolean, int, int,int, int) //当这个View为其所有的子View指
派一个尺寸和位置时调用
9. 3.onSizeChanged(int, int, int,int) //当这个View的尺寸改变后调用
10. Drawing:绘制过程
11. 1.onDraw(Canvas) //当View给定其内容时调用
12. Event:事件流程
13. 1.onKeyDown(int, KeyEvent) //当一个新的键按下时
14. 2.onKeyUp(int, KeyEvent) //当一个键弹起时
15. 3.onTrackballEvent(MotionEvent) //当滚迹球事件发生时
16. 4.onTouchEvent(MotionEvent) //当一个触摸屏事件发生时
17. Focus:焦点流程
18. 1.onFocusChanged(boolean, int,Rect) //当View得到和失去焦点时调用
19. 2.onWindowFocusChanged(boolean) //当Window包含的View得到或失去焦点时调用。

定制View

为了实现一个定制View, 需要重写一些View的标准方法。
framework会调用这些方法, 并且认为这些方法应该是所有的view都有实现。
这些方法不必全部重写, 事实上,可以只重写onDraw 函数就可以了
自定义View的步骤:
扩展View或者View的子类。
必须实现其中一个构造方法
重写onDraw(canvas)方法进行绘制
如需调整大小,重写onMesure,默认是全屏的。
如需样式在xml中布置,自定义属性。

测量布局MeasureSpec

在View系统中,指定宽和高,以及指定布局的属性,是由MeasureSpec来封装的。下面是各
个模式的标志位表示。

private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
/**
 * Measure specification mode: The parent has not imposed any
constraint
* on the child. It can be whatever size it wants.
*/
 public static final int UNSPECIFIED = 0 << MODE_SHIFT;
/**
 * Measure specification mode: The parent has determined an
exact size
 * for the child. The child is going to be given those bounds
regardless
 * of how big it wants to be.
 */
 public static final int EXACTLY = 1 << MODE_SHIFT;
 /**
 * Measure specification mode: The child can be as large as it
wants up
 * to the specified size.
. */
 public static final int AT_MOST = 2 << MODE_SHIFT;

在这个解析系统中是通过移位来存放更多的数据,现在每个数据标志位都向左移动了30位。这
样表示一个View大小是很方便的,我们来看下面的方法:

 public static int makeMeasureSpec(int size, int mode) {
 return size + mode;
 }

通过这个方法就可以制作一个含有两个参数的int值,这个参数包含一个mode标志和一个宽或
高的表示。我们通过如下方法来获取到mode:

public static int getMode(int measureSpec) {
 return (measureSpec & MODE_MASK);
 }

我们也可以用下面方法来获取高或宽的数据表示:

 public static int getSize(int measureSpec) {
 return (measureSpec & ~MODE_MASK);
}

测量这个View的高和宽。通过调用这个方法来设置View的测量后的高和宽,其最终调用的方
法是:

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
 mMeasuredWidth = measuredWidth;
 mMeasuredHeight = measuredHeight;
 mPrivateFlags |= MEASURED_DIMENSION_SET;
 }

可见其最终是将高和宽保存在 mMeasuredWidth 、 mMeasuredHeight 这两个参数中。

View

构造方法 只有两个
1. View(Context context) 在使用java代码new出来的时候
2. View(Context context,AttributeSet att) 在xml中设置的,系统帮助我们去构建的
onMeasure 测量布局
onLayout 放在布局中的位置
onDraw 绘制视图 只在最初始的时候加载一次。
如果需要改变,必须刷新视图
主线程
invalidate();
子线程
postInvalidate();

定制View

继承View,重写构造方法
重写onDraw来绘制View
重写onMeasure来给定一个默认大小
MeasureSpec 2+30模式 2:mode 30:size(32位即4个字节,其中两位自己代表模式,其余代表大小)

示例:下面diy一个view

这里写图片描述

MainActivity 类

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

MyView 类

public class MyView extends View {

    //画笔
    Paint paint = new Paint();

    Point point = new Point();


    //保证xml中能初始化
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        DisplayMetrics dm = getResources().getDisplayMetrics();
        //抗锯齿
        paint.setAntiAlias(true);
        //防抖动
        paint.setDither(true);
        //设置颜色
        paint.setColor(Color.RED);  //0xffff0000
        paint.setAlpha(128); //透明度 0-255   ,float  0-1
        //空心
        //paint.setStyle(Paint.Style.STROKE);
        //线条加粗
        paint.setStrokeWidth(10);

        //设置渐变效果
        //Shader shader = new LinearGradient(0, 0, dm.widthPixels, dm.heightPixels, Color.RED, Color.BLUE, Shader.TileMode.MIRROR);
        //Shader shader = new RadialGradient(dm.widthPixels / 2, dm.heightPixels / 2, 400, Color.RED, Color.BLUE, Shader.TileMode.REPEAT);
        Shader shader = new SweepGradient(dm.widthPixels / 2, dm.heightPixels / 2, Color.RED, Color.BLUE);
        paint.setShader(shader);

        //圆心点
        point.set(getResources().getDisplayMetrics().widthPixels / 2, getResources().getDisplayMetrics().heightPixels / 2);

    }

    public MyView(Context context) {
        super(context);
        init();
    }


//    @Override
//    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        MeasureSpec.AT_MOST;
//        //100 100;
//        //拼接大小
//        //  int width = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
//        super.onMeasure(width, width);
//    }

    Rect rect = new Rect(300, 500, 400, 800);
    RectF rectF = new RectF(200f, 0f, 400f, 200f);
    RectF rectF2 = new RectF(20f, 400f, 200f, 700f);

    //绘制方法
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制圆
        canvas.drawCircle(point.x, point.y, 100, paint);

        //点
        canvas.drawPoint(15, 15, paint);

        canvas.drawPoints(new float[]{30, 30, 40, 50, 60, 60, 70, 80}, paint);
        //线条
        canvas.drawLine(100, 50, 300, 300, paint);
        //矩形
        canvas.drawRect(rect, paint);

        //弧线
        canvas.drawArc(rectF, 45, 270, true, paint);

        //圆角矩形
        canvas.drawRoundRect(rectF2, 10, 10, paint);
        paint.setTextSize(30);
        paint.setTextAlign(Paint.Align.CENTER);
        //绘制文本
        canvas.drawText("我是绘制的文本", 10, 200, paint);

        canvas.drawText("我是居中的文字", getResources().getDisplayMetrics().widthPixels / 2, 300, paint);

        canvas.drawText("abcdefg", 0, paint.getTextSize(), paint);
    }


}

main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.administrator.lesson12_diyview.MainActivity">

    <com.example.administrator.lesson12_diyview.MyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

示例2:diy一个progressbar

这里写图片描述

MainActivity 类

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ArcProgressBar apb = (ArcProgressBar) findViewById(R.id.apb);
        apb.setOnProgressCompleteListener(new ArcProgressBar.OnProgressCompleteListener() {
            @Override
            public void onFinish() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getBaseContext(), "执行完毕", Toast.LENGTH_LONG).show();
                    }
                });

            }
        });
        apb.start();
    }
}

ArcProgressBar 类

public class ArcProgressBar extends View {

    Paint paint;
    Paint textPaint;

    private int max = 100;

    private int progress = 0;


    int width;
    private RectF oval;

    public ArcProgressBar(Context context) {
        super(context);
        init();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //测量布局之后 才能获取宽度
        width = getWidth();
        oval = new RectF(width / 2 - 100, width / 2 - 100, width / 2 + 100, width / 2 + 100);
    }

    public ArcProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        //获取自定义属性
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ArcProgressBar);
        //取出我们的属性
        max = a.getInt(R.styleable.ArcProgressBar_max, 100);
        progress = a.getInt(R.styleable.ArcProgressBar_progress, 0);
        //一定要释放
        a.recycle();
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(3);
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setDither(true);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setColor(Color.BLUE);
        textPaint.setTextSize(30);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //灰色圆在背后
        paint.setColor(Color.GRAY);
        canvas.drawCircle(width / 2, width / 2, 100, paint);
        //绘制一个弧线 进度
        paint.setColor(Color.RED);
        //结束角度 进度/max   50/100; 0.5 *360
        canvas.drawArc(oval, -90, (int) (progress * 1f / max * 360), false, paint);
        //绘制百分比文本
        paint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText((int) (progress * 1f / max * 100f) + "%", width / 2, width / 2, textPaint);
    }

    //模拟启动
    public void start() {
        new Thread() {
            @Override
            public void run() {
                while (progress < max) {
                    progress++;
                    try {
                        Thread.sleep(80);
                        postInvalidate();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (onProgressCompleteListener != null) {
                    onProgressCompleteListener.onFinish();
                }
            }
        }.start();
        //主线程刷新
        // invalidate();
    }


    //1.创建监听
    public interface OnProgressCompleteListener {
        void onFinish();
    }

    //2.创建接口对象
    OnProgressCompleteListener onProgressCompleteListener;

    //3. set方法
    public void setOnProgressCompleteListener(OnProgressCompleteListener onProgressCompleteListener) {
        this.onProgressCompleteListener = onProgressCompleteListener;
    }
}

mian_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.administrator.lesson12_diyprogressbar.MainActivity">

    <com.example.administrator.lesson12_diyprogressbar.ArcProgressBar
        android:id="@+id/apb"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:max="500"
        app:progress="23" />
</RelativeLayout>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值