注:本文3个自定义view布局共用使用一个xml文件,
1.自定义矩形TextView的view
package com.bwie.CustomView.view; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.widget.TextView; import com.bwie.CustomView.R; /**
自定义矩形TextView的view
public class CustomTextView extends TextView{ private String text; private int color; private Paint paint; private int size; private Rect rect; public CustomTextView(Context context) { super(context); } public CustomTextView(Context context, AttributeSet attrs) { super(context, attrs); //通过context.obtainStyledAttributes()方法读取自定义view的属性 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView); //获取自定义view的属性的数量 int count = typedArray.getIndexCount(); //通过下标获取自定义view的属性 for (int i=0;iint indexAttr = typedArray.getIndex(i); switch (indexAttr){ //获取自定义view的text属性 case R.styleable.CustomTextView_text: text = typedArray.getString(indexAttr); break; //获取自定义view的color属性,先设置一个默认颜色 case R.styleable.CustomTextView_textColor: color = typedArray.getInt(indexAttr, Color.YELLOW); break; //获取自定义view的size属性 case R.styleable.CustomTextView_textSize: //通过下面前2行代码的方法转换获取的size属性数据 DisplayMetrics metrics = getResources().getDisplayMetrics(); float dimension = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 18, metrics); size = typedArray.getDimensionPixelSize(indexAttr, (int) dimension); break; } } //数据不用时,回收以下 typedArray.recycle(); //设置画笔的size paint = new Paint(); paint.setTextSize(size); //设置一个矩形。对text内容进行测量 rect = new Rect(); paint.getTextBounds(text,0,text.length(),rect); } /** * onMeasure方法:计算子控件的尺寸和模式,以及设置自己的宽和高,测量View大小 widthMeasureSpec:当前父容器的widthMeasureSpec heightMeasureSpec:当前父容器的heightMeasureSpec 使用的是onMeasure函数,关于三种测量模式mode说法: (1)EXACTLY:一般是设置了明确的值(100dp)或者是MATCH_PARENT; (2)AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT; (3)UNSPECIFIED:表示子布局想要多大就多大,很少使用; */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //计算当前父容器下view的模式 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); //计算当前父容器下view的size int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); System.out.println("width = " + width); System.out.println("height = " + height); } //onDraw方法:绘图方法(canvas画布) @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //getWidth(): View在设定好布局后,整个View的宽度 getHeight(): View在设定好布局后,整个View的高度 int widht = getWidth(); int height = getHeight() ; //获取矩形的宽高 int rectWidth = rect.width(); int rectHeight = rect.height(); //设置画笔的颜色 paint.setColor(Color.YELLOW); //drawRect 就是使用画笔绘制一个矩形 前面两个参数代表起始坐标, 也就是左上角 后面两个参数可以标识你想画的长度和宽度 也可以理解为右下角的坐标点。 canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint); //设置画笔画出的内容颜色,设置画出内容的所在位置坐标 paint.setColor(color); /** * drawText方法:绘制文字。参数: * text:要绘制的文字 * x:绘制原点x坐标 * y:绘制原点y坐标 * paint:用来做画的画笔 */ canvas.drawText(text,(getWidth()-rect.width())/2,(getHeight()+rect.height())/2,paint); System.out.println("getWidth() = " + getWidth()); System.out.println("getMeasuredWidth() = " + getMeasuredWidth()); } /** * * @param w view改变后当前的宽 * @param h view改变后当前的高 * @param oldw view改变前的宽 * @param oldh view改变前的高 */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); System.out.println("w = " + w + " " + h + " " + oldw + " " + oldh); } public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
※ 在values文件夹下的自定义矩形TextView使用的自定义属性xml文件
xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomTextView"> <attr name="text" format="string"/> <attr name="textSize" format="dimension"/> <attr name="textColor" format="color"/> declare-styleable> resources>
2. 自定义圆形进度条view
package com.bwie.CustomView.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; /** * 自定义显示圆形进度条的view */ public class CustomProgressView extends View{ private boolean runing = true; private int progress = 0; private Paint paint; public CustomProgressView(Context context) { super(context); } public CustomProgressView(Context context, AttributeSet attrs) { super(context, attrs); //创建一个画笔 paint = new Paint(); //设置画笔的颜色 paint.setColor(Color.BLUE); //设置画笔的style:内容是填充的空心圆 paint.setStyle(Paint.Style.STROKE); //执行进度条是耗时操作,需放在子线程中执行 new Thread(new Runnable() { @Override public void run() { while (runing){ if (progress >= 360){ runing = false; return; } progress += 10; //子线程刷新方法:postInvalidate(),此事系统会调用onDraw()方法 postInvalidate(); try { Thread.sleep(188); }catch (Exception e){ e.printStackTrace(); } } } }).start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //(1)获取当前自定义view的宽高度 int x = getWidth() / 2; int y = getHeight() / 2; //(2)设置圆半径的大小 int radius = 300; //(3)设置画笔的粗细 paint.setStrokeWidth(18); //(4)定义一个矩形区域:RectF对象持有一个矩形的四个float坐标值 RectF rectF = new RectF(x - radius, y - radius, x + radius, y + radius); /** * 画一个圆弧:drawArc方法:绘制圆弧,该方法用于在画布上绘制圆弧,通过指定圆弧所在的椭圆对象、起始角度、终止角度来实现。 * oval:圆弧所在的椭圆对象。 startAngle:圆弧的起始角度。 sweepAngle:圆弧的角度。 useCenter:是否显示半径连线,true表示显示圆弧与圆心的半径连线,false表示不显示。 paint:绘制时所使用的画笔。 */ canvas.drawArc(rectF,-90,progress,false,paint); //(5)把progress转换为int值 int intProgress = (int) ((float)progress/360 * 100); //(6) measureText 测量字符串的宽度 float textWidth = paint.measureText(intProgress + "%"); //定义一个矩形区域:Rect对象持有一个矩形的四个integer坐标值 Rect rect = new Rect(); //测量矩形中内容 paint.getTextBounds(intProgress+"%",0,(intProgress+"%").length(),rect); //设置画笔写出内容的size和画笔的粗细西 paint.setTextSize(90); paint.setStrokeWidth(1); //画出文字 rect.height() 获取字符串的高度 /** * drawText方法参数: * text:要绘制的文字 * x:绘制原点x坐标 * y:绘制原点y坐标 * paint:用来做画的画笔 */ canvas.drawText(intProgress+"%",x-textWidth/2,y+rect.height()/2,paint); } public CustomProgressView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
3. 自定义圆环view
package com.bwie.CustomView.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * 自定义圆环view */ public class CustomCircleView extends View{ private Paint paint; private int xCircle = 200; //圆心的x坐标 private int yCircle = 200; //圆心的y坐标 public CustomCircleView(Context context) { super(context); } //CustomCircleView方法:设置自定义圆形view的视图 public CustomCircleView(Context context, AttributeSet attrs) { super(context, attrs); //设置画笔的颜色,style,setAntiAlias 抗锯齿形式(true为去除锯齿,false则不去),setStrokeWidth方法:设置空心线宽 paint = new Paint(); paint.setColor(Color.BLUE); paint.setStyle(Paint.Style.STROKE); paint.setAntiAlias(true); paint.setStrokeWidth(80); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //getRawX()和getRawY()获得的是相对屏幕的位置 System.out.println("按下时: x "+event.getRawX()+" y "+event.getRawY()); break; case MotionEvent.ACTION_MOVE: //getRawX()和getRawY()获得的是相对屏幕的位置 System.out.println("移动时: x "+event.getRawX()+" y "+event.getRawY()); //获取鼠标移动时的坐标,为X轴和Y轴坐标重新赋值:getX()和getY()获得的永远是view的触摸位置坐标 xCircle = (int) event.getX(); yCircle = (int)event.getY(); /** * Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型: 1. Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。 invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。 2.Android 程序默认情况下也只有一个进程,但一个进程下却可以有许多个线程。在这么多线程当中,把主要是负责控 制UI界面的显示、更新和控件交互的线程称为UI线程,由于onCreate()方法是由UI线程执行的,所以也可以把UI线程理解 为主线程。其余的线程可以理解为工作者线程。invalidate()得在UI线程中被调动,在工作者线程中可以通过Handler来通 知UI线程进行界面更新。而postInvalidate()在工作者线程中被调用。 */ invalidate(); break; case MotionEvent.ACTION_UP: //getRawX()和getRawY()获得的是相对屏幕的位置 System.out.println("抬起时: x "+event.getRawX()+" y "+event.getRawY()); break; } return true; } /** * onMeasure方法:计算子控件的尺寸和模式,以及设置自己的宽和高 widthMeasureSpec:当前父容器的widthMeasureSpec heightMeasureSpec:当前父容器的heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } //onDraw方法:绘图方法(canvas画布) @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /** * canvas.drawCircle()方法:该方法用于在画布上绘制圆形,通过指定圆形圆心的坐标和半径来实现。该方法是绘制圆形的主要方法,同时也可以通过设置画笔的空心效果来绘制空心的圆形。 * cx:圆心的x坐标。 cy:圆心的y坐标。 radius:圆的半径。 paint:绘制时所使用的画笔。 */ canvas.drawCircle(xCircle,yCircle,200,paint); } public CustomCircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
4. 3个自定义view共用的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" android:layout_width="match_parent" android:layout_height="match_parent" > <com.bwie.CustomView.view.CustomTextView android:layout_centerInParent="true" android:layout_marginTop="28dp" android:layout_width="188dp" android:layout_height="188dp" app:textSize="18sp" app:text="自定义TextView" app:textColor="@color/colorAccent" /> RelativeLayout>