Android自定义控件——时钟、进度条

由于Android提供的空间有限,不能满足程序的需求,所以才有了自定义控件

时钟

首先要写一个类继承自View

package com.example.administrator.myselfview.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;

import java.util.Calendar;

/**
 * Created by Administrator on 2015/9/16.
 */
public class MyAlarmview extends View {
    private int width;
    private int height;
    private Paint mPaintLine;
    private Paint mPaintSecondLine;
    private Paint mPaintCricle;
    private Paint mPaintText;
    private Calendar mCalendar;
    public static final int UPDATE_TIME=0X22;
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case UPDATE_TIME:
                    mCalendar=Calendar.getInstance();
                    invalidate();
                    handler.sendEmptyMessageDelayed(UPDATE_TIME,1000);
                    break;
            }
        }
    };
    public MyAlarmview(Context context) {
        super(context);
    }

    public MyAlarmview(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaintLine=new Paint();
        mPaintLine.setColor(Color.BLACK);//设置颜色
        mPaintLine.setStrokeWidth(10);//画笔宽度

        mPaintSecondLine=new Paint();
        mPaintSecondLine.setColor(Color.GRAY);
        mPaintSecondLine.setStrokeWidth(5);

        mPaintCricle=new Paint();
        mPaintCricle.setColor(Color.BLACK);
        mPaintCricle.setStrokeWidth(10);
        mPaintCricle.setStyle(Paint.Style.STROKE);//圆形画笔空心

        mPaintText=new Paint();
        mPaintText.setColor(Color.BLACK);
        mPaintText.setTextSize(30);//字体画笔设置字体大小
        mPaintText.setTextAlign(Paint.Align.CENTER);//让字体居中显示

        mCalendar=Calendar.getInstance();

        handler.sendEmptyMessage(UPDATE_TIME);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width=getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        height=getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //先画一个圆,调用canvas的drawCircle方法,第一个参数为圆心的横座标,第二个参数为圆心纵座标,第三个参数为半径,第四个参数为画笔
        canvas.drawCircle(width/2,height/2,300,mPaintCricle);
        //划出12个表示小时的线
        for (int i=1;i<=12;i++){
            canvas.rotate(30,width/2,height/2);//画布旋转30度
            canvas.drawLine(width/2,height/2-300,width/2,height/2-280,mPaintLine);
            canvas.drawText(""+i,width/2,height/2-250,mPaintText);
        }
        //划出表示分钟的60个点
        for (int i=0;i<60;i++){
            canvas.rotate(6,width/2,height/2);
            canvas.drawLine(width/2,height/2-300,width/2,height/2-290,mPaintLine);
        }
        //用Calendar类得到当前的小时分钟秒
        int minute=mCalendar.get(Calendar.MINUTE);
        int hour=mCalendar.get(Calendar.HOUR);
        int second=mCalendar.get(Calendar.SECOND);
        //然后得到当前的时间时针分针秒针需要旋转的角度
        float minDegree=minute/60f*360;
        float hourDegree=(hour*60+minute)/12f/60*360;
        float secondDegree=second/60f*360;

        canvas.save();//保存当前状态
        canvas.rotate(hourDegree,width/2,height/2);
        canvas.drawLine(width/2,height/2-150,width/2,height/2+10,mPaintLine);
        canvas.restore();//返回之前保存的状态

        canvas.save();
        canvas.rotate(minDegree,width/2,height/2);
        canvas.drawLine(width/2,height/2-200,width/2,height/2+15,mPaintLine);
        canvas.restore();

        canvas.save();
        canvas.rotate(secondDegree,width/2,height/2);
        canvas.drawLine(width/2,height/2-230,width/2,height/2+20,mPaintSecondLine);
        canvas.restore();
    }
}

在XML文件中定义一下

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

    <com.example.administrator.myselfview.view.MyAlarmview
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

由于是自定义控件,所以要写全名
然后MainActivity中不需要多写什么,只需加入这个界面就行。
继承自View之后需要重写其构造器,View有四个构造器,这里重写其前两个构造器,并在第二个构造器中定义自定义的画笔。然后重写onMeasure()和onDraw()方法,在onMeasure()方法中定义画布的宽和高,这里使用的是系统定义的画布的大小;然后在onDraw()方法中进行对画布的操作,即作图。最后利用Handler类来对信息进行处理,使用 invalidate()对画布进行重画刷新操作。

进度条

这里做了三个简单的进度条,方法都一样,为圆形扩散型进度条,矩形填充进度条,弧形旋转进度条

圆形进度条

同样首先需要新建一个自定义控件的类并继承自View,并重写其两个构造器和两个方法

package com.example.administrator.myselfview.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * Created by Administrator on 2015/9/16.
 */
public class MyProgressBallView extends View{
    private int width;
    private int height;
    private Paint mPaintBack;
    private Paint mPaintCurrent;
    private Paint mPaintText;
    private int maxProgress=100;
    private int currentProgress;


    public void setCurrentProgress(int currentProgress) {
        this.currentProgress = currentProgress;

    }

    public MyProgressBallView(Context context) {
        super(context);
    }

    public MyProgressBallView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaintBack=new Paint();
        mPaintBack.setColor(Color.GRAY);
        mPaintBack.setAntiAlias(true);

        mPaintCurrent=new Paint();
        mPaintCurrent.setColor(Color.GREEN);
        mPaintCurrent.setAntiAlias(true);

        mPaintText=new Paint();
        mPaintText.setColor(Color.BLUE);
        mPaintText.setTextSize(80);
        mPaintText.setTextAlign(Paint.Align.CENTER);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width=getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        height=getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        //setMeasuredDimension(width,height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        invalidate();//重新绘制
        canvas.drawCircle(width/2,height/2,300,mPaintBack);//最底下作为背景的圆
        canvas.drawCircle(width/2,height/2,currentProgress*300f/maxProgress,mPaintCurrent);
        //根据进度实时更新画出的表示进度的圆
        canvas.drawText(currentProgress*100f/maxProgress+"%",width/2,height/2,mPaintText);
    }//文本画笔实时更新表示下载进度
}

在自定义View中千万不要忘记每次都要重新绘制,即invalidate();要不然界面是不会动的。
然后在Activity中简单的模仿一个下载进度,并把当前进度传递到自定义View类中来,好实时地重绘到屏幕上。

package com.example.administrator.myselfview.subactivity;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.example.administrator.myselfview.R;
import com.example.administrator.myselfview.view.MyProgressBallView;

/**
 * Created by Administrator on 2015/9/16.
 */
public class ProgressBallActivity extends Activity {
    private MyProgressBallView myProgressBallView;
    private Button mButton_start;
    private int count=0;
    private static final int PROGRESSBALL=121;
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case PROGRESSBALL:
                    count++;
                    if (count<=100){
                        myProgressBallView.setCurrentProgress(count);
                        handler.sendEmptyMessageDelayed(PROGRESSBALL, 150);

                    }
                    break;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_progressball);
        mButton_start= (Button) findViewById(R.id.button_progress_ball_start);
        myProgressBallView= (MyProgressBallView) findViewById(R.id.progress_ball);
        mButton_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                handler.sendEmptyMessage(PROGRESSBALL);
            }
        });
    }
}

利用Handler类通过子线程控制UI,设置当前进度。
然后在XML文件中声明

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/button_progress_ball_start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始下载"/>
    <com.example.administrator.myselfview.view.MyProgressBallView
        android:id="@+id/progress_ball"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

这里定义自定义控件的时候,切记一定要写全名

弧形进度条

弧形进度条跟圆形是一样,只不过是绘制图形的时候用到的方法不一样而已,其他完全相同,这里只写绘制时候的代码

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        invalidate();
        canvas.drawArc(new RectF(width/2-300,height/2-300,width/2+300,height/2+300),90f,360f,false,mPaintBack);
        canvas.drawArc(new RectF(width/2-300,height/2-300,width/2+300,height/2+300),-90f,currentProgress*360f/maxProgress,false,mPaintCurrent);
        canvas.drawText(currentProgress*100f/maxProgress+"%",width/2,height/2,mPaintText);
    }

矩形进度条

同样,这里只是举出矩形进度条绘制时候的方法

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        invalidate();
        canvas.drawRect(width/2-120,height/2-300,width/2+120,height/2+300,mPaintBack);
        canvas.drawRect(width/2-120,height/2+300-currentProgress*600f/maxProgress,width/2+120,height/2+300,mPaintCurrent);
        canvas.drawText(currentProgress*100f/maxProgress+"%",width/2,height/2,mPaintText);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章