先创建一个自定义MyView1,继承View,实现构造方法。
public class MyView1 extends View{
private Bitmap bitmap;
public MyView1(Context context, AttributeSet attrs) {
super(context, attrs);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
// TODO Auto-generated constructor stub
}
public MyView1(Context context) {
super(context);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
// TODO Auto-generated constructor stub
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
Paint paint=new Paint();//按Java的理解,可以理解为画笔
paint.setTextSize(30);//设置画笔大小
canvas.drawText("Hello", 20, 20, paint);//绘制文字
//canvas.drawRect(20, 50, 60, 100, paint);//绘制矩形方法一
RectF rect = new RectF(20, 50, 60, 100);//绘制矩形方法二
//canvas.drawRect(rect, paint);//根据需求选方法
canvas.drawRoundRect(rect, 15, 15, paint);//绘制圆角矩形
canvas.drawCircle(50, 150, 50, paint);//绘制圆形
canvas.drawBitmap(bitmap, 0, 200, paint);//绘制图片,注意要在构造方法中创建bitmap
}
}
使用View可以用代码直接加载也可以通过XMl布局文件加载,要使用自定义属性,记得添加命名空间
xmlns:nt="http://schemas.android.com/apk/res/com.example.xin.view"
<pre name="code" class="html"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:nt="http://schemas.android.com/apk/res/com.example.xin.view"
tools:context="com.example.xin.view.MainActivity" >
<com.example.xin.view.MyView1
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
<com.example.xin.view2.MyView2
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
<com.example.xin.view3.MyView3
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.example.xin.view3.MyView4
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</RelativeLayout>
代码直接加载
setContentView(new MyView(this));
为View加入逻辑线程,使其实现动态效果
创建第二个MyView2,同样继承View,绘制移动的文字,改变角度的圆,以及大小变化的圆,主要把通过改变参数大小实现,把参数大小改变交给别的线程
MyView2
public class MyView2 extends View{
private RectF rectF = new RectF(0 ,60 ,100, 160);
private int s=0,a=0;
private float rx = 0;
private Paint paint = new Paint();
private float sweepAngle = 0;
private MyThread thread;
public MyView2(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public MyView2(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
paint.setTextSize(10);
canvas.drawText("程序猿程序猿", rx, 30, paint);
canvas.drawArc(rectF, 0, sweepAngle, true, paint);//sweepAngle这个参数表示圆的角度
canvas.drawCircle(150, 210, s, paint);
if(thread == null){
thread = new MyThread();
thread.start();
}
}
class MyThread extends Thread{
Random rand = new Random();
@Override
public void run() {
while(true){
rx += 3;
if(rx > getWidth()){
rx = 0 - paint.measureText("LogicView");
}
sweepAngle ++;
if(sweepAngle > 360){
sweepAngle = 0;
}
if(a==0)
{
s++;
if(s>49)
{
a=1;
}
}else if(a==1){
s--;
if(s<1)
{
a=0;
}
}
int r = rand.nextInt(256);//RGB的值为0到255
int g = rand.nextInt(256);
int b = rand.nextInt(256);
paint.setARGB(255, r, g, b);
postInvalidate();//刷新,会调用<span style="font-family: Arial, Helvetica, sans-serif;">onDraw这个方法</span>
try {
Thread.sleep(30);//控制休眠时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
为了代码简介且重复利用,可以提取封装自己的自定义View
创建一个自定义View作为提取封装好的baseView,便于之后之类间的继承
public abstract class BaseView extends View{
private boolean running=true;
private MyThread thread;
public BaseView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public BaseView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
//提供绘画的抽象方法,之后在继承该自定义View的View中,绘画的功能就在该方法里完成
protected abstract void huizhi(Canvas canvas);
//提供编写逻辑的抽象方法,之后在继承该自定义View的View中,所以参数改变的逻辑就直接写入方法里
protected abstract void luoji();
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
if(thread==null)
{
thread=new MyThread();
thread.start();
}else{
huizhi(canvas);
}
}
//当我们销毁VIew时,会调用这个方法
protected void onDetachedFromWindow() {
running = false;
super.onDetachedFromWindow();
}
class MyThread extends Thread
{
@Override
public void run() {
// TODO Auto-generated method stub
while(running)
{
luoji();
postInvalidate();//刷新,会调用onDraw()方法
try {
Thread.sleep(30);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
为什么要提取封装?若之后程序中还有很多自定义VIew需要编写,就可不管线程代码,直接在新的View中重写抽象的逻辑与绘画方法,可以达到代码的简化以及多重利用的便捷
继承上面封装好自定义baseView的新MyView3
public class MyView3 extends BaseView{
private Paint paint=new Paint();
private float rx=0;
private int a=0,s=0;
private int sweepAngle=0;
private RectF rectF = new RectF(0 ,60 ,100, 160);
Random rand=new Random();//产生随机数对象
public MyView3(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public MyView3(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
//新的View只需要重写绘制方法和逻辑方法
protected void huizhi(Canvas canvas) {
// TODO Auto-generated method stub
paint.setTextSize(10);
canvas.drawText("程序猿程序猿", rx, 30, paint);
canvas.drawArc(rectF, 0, sweepAngle, true, paint);//sweepAngle这个参数表示圆的角度
canvas.drawCircle(150, 210, s, paint);
}
@Override
//新的View只需要重写绘制方法逻辑方法
protected void luoji() {
//颜色变换逻辑
int r = rand.nextInt(256);
int g = rand.nextInt(256);
int b = rand.nextInt(256);
paint.setARGB(255, r, g, b);
//文字逻辑
rx++;
if(rx>getWidth())
{
rx = - paint.measureText("程序猿程序猿");
}
//角度变化圆形逻辑
sweepAngle++;
if(sweepAngle>360)
{
sweepAngle=0;
}
//大小变化的圆形逻辑
if(a==0)
{
s++;
if(s>49)
{
a=1;
}
}else if(a==1){
s--;
if(s<1)
{
a=0;
}
}
}
}
<img src="https://img-blog.csdn.net/20150810185724339?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
在XML定义样式来影响显示效果,分为三步
1.定义属性,将需要在XML改变的属性定义好
2.在values中创建XML文件,定义属性样式
3.解析样式
public class View4 extends BaseView{
private int mx=0;
private int lineNum=0;
private boolean ispao=true;
private Paint paint=new Paint();
public View4(Context context, AttributeSet attrs) {
super(context, attrs);
//解析样式
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.View4);
lineNum = ta.getInt(R.styleable.Viwe4_lineNum, 1);
ispao = ta.getBoolean(R.styleable.Viwe4_ispao, false);
ta.recycle();
}
public View4(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
@Override
protected void huizhi(Canvas canvas) {
// TODO Auto-generated method stub
for(int i=0;i<lineNum;i++)//lineNum控制输出行数,也作为之后view对应的属性名
{
int textSize = 15 + i;
canvas.drawText("程序猿程序猿", mx, textSize + textSize * i, paint);
}
}
@Override
protected void luoji() {
// TODO Auto-generated method stub
if(ispao)//控制移动与否,也作为之后view对应的属性名
{
mx++;
if(mx > getWidth()){
mx = (int) -paint.measureText("程序猿程序猿");
}
}
}
}
values文件夹的样式文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="View4">
<attr name="lineNum" format="integer"/>
<attr name="ispao" format="boolean" />
</declare-styleable>
</resources>