分享一个自定义的View--SimpleClock

首先这是一个自定义的View,继承自android.view.View .这个是在安卓开发中十分最常用的。

上张效果图:


我们在使用安卓系统自带的View时,都是有规定好的属性,我们来赋值,今天简单介绍一下如何自己定义一些属性。

正如上图,SimpleClock有两种类型,我姑且把它分为before和after两种。

1.我们在values文件夹中创建一个attrs.xml文件。

在其中定义该属性。用以确定这两种类型。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SimpleClock"> 
        <attr name="styleMode" >
            <flag name="before" value="1"></flag>
            <flag name="after" value="2"></flag>
        </attr>        
    </declare-styleable>
</resources>


2.其次创建一个class继承自View类,取名为SimpleClock。

我的工程是放在包com.android.deskcolok.widget下。


3.在布局文件中添加这个自定义的View时,我们要在name属性中填写完整的类名,包括包的路径


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    xmlns:app="http://schemas.android.com/apk/res/com.android.deskcolok">
	<com.android.deskcolok.widget.SimpleClock 
	    android:layout_width="100dp"
	    android:layout_height="100dp"
	    app:styleMode="before"
	    android:layout_marginLeft="20dp"/>
	<com.android.deskcolok.widget.SimpleClock 
	    android:layout_width="80dp"
	    android:layout_height="80dp"
	    app:styleMode="after"
	    android:layout_marginLeft="20dp"/>
</LinearLayout>

该布局正如上图,添加了两个SimpleClock.

xmlns:app="http://schemas.android.com/apk/res/com.android.deskcolok"
这句是为你自定义的view指定一个命名空间,xmlns:后面的是前缀,res后面的是工程所在的包名,也就是该工程中AndroidManifest.xml文件中package属性的值,此处是

package="com.android.deskcolok"


4.在自定义的view中引用自己定义的属性,就用前缀(app)加attrs.xml中定义的属性名(styleMode),属性的值也是你在attrs.xml中规定好的

5.我们在该类中提取属性时使用如下的语句

TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.SimpleClock) ;
		mStyle = typedArray.getInt(R.styleable.SimpleClock_styleMode, 1) ;

6.然后通过取得的属性可以进行操作。

在这里我是根据该值给不同的画笔设置不用的颜色


	switch(mStyle){
		case STYLE_BEFORE ://1
			bgPaint.setColor(context.getResources().getColor(R.color.black)); 
			numPaint.setColor(context.getResources().getColor(R.color.white));
			secondPaint.setColor(context.getResources().getColor(R.color.red));
			minutesPaint.setColor(context.getResources().getColor(R.color.white));
			hourPaint.setColor(context.getResources().getColor(R.color.white));
 			break ;
		case STYLE_AFTER ://2
			bgPaint.setColor(context.getResources().getColor(R.color.white));
			numPaint.setColor(context.getResources().getColor(R.color.black));
			secondPaint.setColor(context.getResources().getColor(R.color.red));
			minutesPaint.setColor(context.getResources().getColor(R.color.black));
			hourPaint.setColor(context.getResources().getColor(R.color.black));
			break ;
		}

最后贴上该自定义view的完整代码:


package com.android.deskcolok.widget;

import java.util.TimeZone;

import com.android.deskcolok.R;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Handler;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.View;

public class SimpleClock extends View {
	private Time mCalendar;
	private Time mShowTime ;
	private int mStyle ;
	private int viewWidth ;
	private int viewHeight ;
	private Paint bgPaint ;
	private Paint numPaint ;
	private Paint secondPaint ;
	private Paint minutesPaint ;
	private Paint hourPaint ;
	private String[] numArray = {
			"12","1","2","3","4","5","6","7","8","9","10","11"
	} ;
	private float mHour ;
	private float mSecond ;
	private float mMinutes ;
	
	public static final int STYLE_BEFORE = 1 ;
	public static final int STYLE_AFTER = 2 ;
	
	private final Handler mHandler = new Handler();
	//handle the second changed,every second to draw a new view
	private final Handler secondHandler = new Handler();
	private Runnable secondRunning = new Runnable() {
		
		@Override
		public void run() {
			onTimeChanged() ;
			invalidate(); 
			secondHandler.postDelayed(secondRunning, 1000) ;
		}
	};
	
	private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
                String tz = intent.getStringExtra("time-zone");
                mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
            }

            onTimeChanged();
            
            invalidate();
        }
    	};
	
	public SimpleClock(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context, attrs) ;
	}

	public SimpleClock(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context, attrs) ;
	}

	private void init(Context context, AttributeSet attrs) {
		mCalendar = new Time(); 
		TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.SimpleClock) ;
		mStyle = typedArray.getInt(R.styleable.SimpleClock_styleMode, 1) ;
		typedArray.recycle(); 
		bgPaint = new Paint() ;
		bgPaint.setAntiAlias(true); 
		numPaint = new Paint() ;
		numPaint.setTextSize(16); 
		secondPaint = new Paint() ;
		secondPaint.setAntiAlias(true); 
		minutesPaint = new Paint() ;
		minutesPaint.setAntiAlias(true); 
		minutesPaint.setStrokeWidth(3);
		hourPaint = new Paint() ;
		hourPaint.setAntiAlias(true); 
		hourPaint.setStrokeWidth(5);
		switch(mStyle){
		case STYLE_BEFORE :
			bgPaint.setColor(context.getResources().getColor(R.color.black)); 
			numPaint.setColor(context.getResources().getColor(R.color.white));
			secondPaint.setColor(context.getResources().getColor(R.color.red));
			minutesPaint.setColor(context.getResources().getColor(R.color.white));
			hourPaint.setColor(context.getResources().getColor(R.color.white));
 			break ;
		case STYLE_AFTER :
			bgPaint.setColor(context.getResources().getColor(R.color.white));
			numPaint.setColor(context.getResources().getColor(R.color.black));
			secondPaint.setColor(context.getResources().getColor(R.color.red));
			minutesPaint.setColor(context.getResources().getColor(R.color.black));
			hourPaint.setColor(context.getResources().getColor(R.color.black));
			break ;
		}
		onTimeChanged() ;
	}

	@Override
	protected void onAttachedToWindow() {
		// TODO Auto-generated method stub
		super.onAttachedToWindow();
		IntentFilter filter = new IntentFilter();

        filter.addAction(Intent.ACTION_TIME_TICK);
        filter.addAction(Intent.ACTION_TIME_CHANGED);
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);

        getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
        mCalendar = new Time();
        secondHandler.postDelayed(secondRunning, 1000) ;
	}
	
	 @Override
	    protected void onDetachedFromWindow() {
	        super.onDetachedFromWindow();
	            getContext().unregisterReceiver(mIntentReceiver);
	            secondHandler.removeCallbacks(secondRunning); 
	    }
	
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		viewWidth = getWidth() ;
		viewHeight = getHeight() ;
		drawFrame(canvas);
		canvas.save() ;
		canvas.rotate(mHour / 12.0f * 360.0f,viewWidth/2, viewHeight/2);
		canvas.drawLine(viewWidth/2, viewHeight/2, viewWidth/2, viewHeight/4, hourPaint);
		canvas.restore(); 
		canvas.save() ;
		canvas.rotate(mMinutes / 60.0f * 360.0f,viewWidth/2, viewHeight/2);
		canvas.drawLine(viewWidth/2, viewHeight/2, viewWidth/2, 1*viewHeight/6, minutesPaint);
		canvas.restore(); 
		canvas.save() ;
		canvas.rotate(mSecond / 60.0f * 360.0f,viewWidth/2, viewHeight/2);
		canvas.drawLine(viewWidth/2, viewHeight/2, viewWidth/2, 1*viewHeight/8, secondPaint);
		canvas.restore(); 
	}

	private void onTimeChanged() {
		if(mShowTime == null){
			mCalendar.setToNow(); 
			int hour = mCalendar.hour;
	        int minute = mCalendar.minute;
	        int second = mCalendar.second;
	        mMinutes = minute + second / 60.0f;
	        mHour = hour + mMinutes / 60.0f;
	        mSecond = second ;
		}
	}

	private void drawFrame(Canvas canvas) {
		int circleRadio = viewWidth < viewHeight ?viewWidth/2 :viewHeight/2 ;
		int radio = circleRadio-16 ;
		
		canvas.drawCircle( viewWidth/2, viewHeight/2,circleRadio, bgPaint);
		int distance = 4;
		for (int i = 0; i < numArray.length; i++) {
			//这里是对刻度盘上的数字位置做微处理,使之更加美观
			distance = 4;
			switch(i){
			case 0 : distance = 8 ;
				break ;
			case 1 : distance = 6 ;
				break ;
			case 11 :distance = 7 ;
				break ;
			case 10 :distance = 6 ;
				break ;
			}
			canvas.drawText(numArray[i], (int)(viewWidth/2-distance+radio*(-Math.sin(Math.PI/6*i+Math.PI))),
					(int)(viewHeight/2+6+radio*(Math.cos(Math.PI/6*i+Math.PI))), numPaint);
		}
		canvas.drawCircle(viewWidth/2, viewHeight/2, 5, numPaint);
	}

}

系统的View也都是用这种方法定义属性的,为了让大家理解,贴上ProgressBar的属性定义

<declare-styleable name="ProgressBar">
        <!-- Defines the maximum value the progress can take. -->
        <attr name="max" format="integer" />
        <!-- Defines the default progress value, between 0 and max. -->
        <attr name="progress" format="integer" />
        <!-- Defines the secondary progress value, between 0 and max. This progress is drawn between
             the primary progress and the background.  It can be ideal for media scenarios such as
             showing the buffering progress while the default progress shows the play progress. -->
        <attr name="secondaryProgress" format="integer" />
        <!-- Allows to enable the indeterminate mode. In this mode the progress
         bar plays an infinite looping animation. -->
        <attr name="indeterminate" format="boolean" />
        <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->
        <attr name="indeterminateOnly" format="boolean" />
        <!-- Drawable used for the indeterminate mode. -->
        <attr name="indeterminateDrawable" format="reference" />
        <!-- Drawable used for the progress mode. -->
        <attr name="progressDrawable" format="reference" />
        <!-- Duration of the indeterminate animation. -->
        <attr name="indeterminateDuration" format="integer" min="1" />
        <!-- Defines how the indeterminate mode should behave when the progress
        reaches max. -->
        <attr name="indeterminateBehavior">
            <!-- Progress starts over from 0. -->
            <enum name="repeat" value="1" />
            <!-- Progress keeps the current value and goes back to 0. -->
            <enum name="cycle" value="2" />
        </attr>
        <attr name="minWidth" format="dimension" />
        <attr name="maxWidth" />
        <attr name="minHeight" format="dimension" />
        <attr name="maxHeight" />
        <attr name="interpolator" format="reference" />
        <!-- Timeout between frames of animation in milliseconds
             {@deprecated Not used by the framework.} -->
        <attr name="animationResolution" format="integer" />
    </declare-styleable>

每一个属性都有一个name,即属性名,后面的format是指定了属性值得格式,如integer表示整形数,boolean表示是一个布尔值,只能取false和true.

dimension表示只一个尺寸值,如18dp,15sp等,另外还有color表示颜色值,string表示字符串。用的比较多的还有一个reference表示参考一个属性值,我们

经常见到@string/hello,@+id/text_one,诸如此类,都是参考类型的。如果可以是两种或以上格式,可以用“与”运算连接,如<attr name="textColor" format="color|reference" />

希望通过我的介绍,更多的人能够写出更好的属于自己创造的view,有好的也希望大家能够分享给我。


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值