android系统本身提供了相当丰富的组件如: Button
, TextView
, EditText
, ListView
, CheckBox
, RadioButton
, Gallery
, Spinner 等
如果系统中提供的组件不能满足你的需求,你可以自己定义一个组件,如果你只是想对现有的widget做一些小的调整,你之需要继承这些子类然后重载其中的一些方法即可。
你也可以完全定义自己的组件,基本步骤如下:
1、你定义的组件类(CustomClass)继承系统的View类
2、你需提供相应的构造函数,来解析XML中的属性和参数
3、你可以定义自己的监听事件
4、重载OnDraw()和OnMeasure()方法
5、可以根据需要重载基类中的其他的以on...开头的方法
Custom View Example:
在android的源码中有一个很好的例子,我们可以参考一下,就是模拟时钟的源码:
- /*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package android.widget;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.content.BroadcastReceiver;
- import android.content.res.Resources;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.drawable.Drawable;
- import android.os.Handler;
- import android.text.format.Time;
- import android.util.AttributeSet;
- import android.view.View;
- import android.widget.RemoteViews.RemoteView;
- import java.util.TimeZone;
- /**
- * This widget display an analogic clock with two hands for hours and
- * minutes.
- */
- @RemoteView
- public class AnalogClock extends View { //继承自View
- private Time mCalendar;
- private Drawable mHourHand;
- private Drawable mMinuteHand;
- private Drawable mDial;
- private int mDialWidth;
- private int mDialHeight;
- private boolean mAttached;
- private final Handler mHandler = new Handler();
- private float mMinutes;
- private float mHour;
- private boolean mChanged;
- //提供相应的构造函数
- public AnalogClock(Context context) {
- this(context, null);
- }
- public AnalogClock(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- //在这个构造函数中加载了一些资源
- public AnalogClock(Context context, AttributeSet attrs,
- int defStyle) {
- super(context, attrs, defStyle);
- Resources r = mContext.getResources();
- TypedArray a =
- context.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.AnalogClock, defStyle, 0);
- mDial = a.getDrawable(com.android.internal.R.styleable.AnalogClock_dial);
- if (mDial == null) {
- mDial = r.getDrawable(com.android.internal.R.drawable.clock_dial);
- }
- mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour);
- if (mHourHand == null) {
- mHourHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_hour);
- }
- mMinuteHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_minute);
- if (mMinuteHand == null) {
- mMinuteHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_minute);
- }
- mCalendar = new Time();
- mDialmDialWidth = mDial.getIntrinsicWidth();
- mDialmDialHeight = mDial.getIntrinsicHeight();
- }
- //重载某些方法
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (!mAttached) {
- mAttached = true;
- 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);
- }
- // NOTE: It's safe to do these after registering the receiver since the receiver always runs
- // in the main thread, therefore the receiver can't run before this method returns.
- // The time zone may have changed while the receiver wasn't registered, so update the Time
- mCalendar = new Time();
- // Make sure we update to the current time
- onTimeChanged();
- }
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAttached) {
- getContext().unregisterReceiver(mIntentReceiver);
- mAttached = false;
- }
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- float hScale = 1.0f;
- float vScale = 1.0f;
- if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {
- hScale = (float) widthSize / (float) mDialWidth;
- }
- if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) {
- vScale = (float )heightSize / (float) mDialHeight;
- }
- float scale = Math.min(hScale, vScale);
- setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec),
- resolveSize((int) (mDialHeight * scale), heightMeasureSpec));
- }
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mChanged = true;
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- boolean changed = mChanged;
- if (changed) {
- mChanged = false;
- }
- int availableWidth = mRight - mLeft;
- int availableHeight = mBottom - mTop;
- int x = availableWidth / 2;
- int y = availableHeight / 2;
- final Drawable dial = mDial;
- int w = dial.getIntrinsicWidth();
- int h = dial.getIntrinsicHeight();
- boolean scaled = false;
- if (availableWidth < w || availableHeight < h) {
- scaled = true;
- float scale = Math.min((float) availableWidth / (float) w,
- (float) availableHeight / (float) h);
- canvas.save();
- canvas.scale(scale, scale, x, y);
- }
- if (changed) {
- dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
- }
- dial.draw(canvas);
- canvas.save();
- canvas.rotate(mHour / 12.0f * 360.0f, x, y);
- final Drawable hourHand = mHourHand;
- if (changed) {
- w = hourHand.getIntrinsicWidth();
- h = hourHand.getIntrinsicHeight();
- hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
- }
- hourHand.draw(canvas);
- canvas.restore();
- canvas.save();
- canvas.rotate(mMinutes / 60.0f * 360.0f, x, y);
- final Drawable minuteHand = mMinuteHand;
- if (changed) {
- w = minuteHand.getIntrinsicWidth();
- h = minuteHand.getIntrinsicHeight();
- minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
- }
- minuteHand.draw(canvas);
- canvas.restore();
- if (scaled) {
- canvas.restore();
- }
- }
- private void onTimeChanged() {
- mCalendar.setToNow();
- int hour = mCalendar.hour;
- int minute = mCalendar.minute;
- int second = mCalendar.second;
- mMinutes = minute + second / 60.0f;
- mHour = hour + mMinutes / 60.0f;
- mChanged = true;
- }
- 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();
- }
- };
- }
使用自定义的组件,在XML文件中像使用系统预定义的组件一样使用
- <com.android.example.CustomView
- android:id="@+id/customView" android:layout_height="fill_parent"
- android:layout_width="fill_parent" />