前言:Android平台的核心是C/C++,为了吸引更多App开发者来使用它,就在C/C++之上,增添一层Java框架,这框架的基类一方面提供接口(即抽像函数)来支持App开发者撰写子类。这项接口的幕后,隐藏了默认函数(Default Function)及其复杂代码,让App开发者只要撰写简单的子类,就能继承而调用这些默认函数,藉由简单的接口调用,就能轻易叫出复杂的默认行为(Default Behavior)。
抽象类的焦点在于抽象函数,它们的实作指令部份都是从缺的。所以该抽象类的子类『必须』补足这个欠缺,才能成为一个具体类。这抽像函数是基类与子类两者相遇的地方,也就成为两者的接口,又称为基类的API。不过,在上述文章里,偏重于API的角色及其设计要点。但并未碰触到应用开发者的心理层面:为何应用开发者愿意来使用框架及其API呢? 也就是说,框架开发者必须关心如何去吸引应用开发者来使用框架及其API。其解答之一是:在基类里提供默认行为。
1. 基类接口隐藏了默认函数
刚才提过了,抽象类里的抽象函数,其实作指令部份都是从缺的。所以该抽象类的子类『必须』补足这个欠缺,才能成为一个具体类。可知,子类开发者的负担是蛮重的,框架设计可以透过默认行吸引应用子类开发者。想一想,如果将一些默认(Default)的实作部份加到抽象类里,则其子类就可选择要不要修正这个默认实作部份,这样可以减轻子类开发者的负担。例如下述之范例:
其程序代码如下:
/* Shape.java */
import java.awt.Color;
import java.awt.Graphics;
public abstract class Shape {
Graphics m_gr;
public Shape(Graphics gr) { m_gr = gr; }
public void onPaint(){
// drawing background
m_gr.setColor(Color.black);
m_gr.fillRect(10,30, 200,100);
}
public void paint() {
onPaint();
}}
/* Bird.java */
import java.awt.Color;
import java.awt.Graphics;
public class Bird extends Shape {
Graphics m_gr;
public Bird(Graphics gr) {
super(gr);
m_gr = gr;
}
public void onPaint(){
super.onPaint();
// drawing a bird
m_gr.setColor(Color.cyan);
m_gr.drawArc(30,80,90,110,40,100);
m_gr.drawArc(88,93,90,100,40,80);
m_gr.setColor(Color.white);
m_gr.drawArc(30,55,90,150,35,75);
m_gr.drawArc(90,80,90,90,40,80);
}}
/* JMain.java */
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
class JP extends JPanel {
public void paintComponent(Graphics gr){
super.paintComponents(gr);
Shape bird = new Bird(gr);
bird.paint();
}}
public class JMain extends JFrame{
public JMain(){
setTitle("");
setSize(350, 250);
}
public static void main(String[] args) {
JMain frm = new JMain();
JP panel = new JP();
frm.add(panel);
frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frm.setVisible(true);
}}
此程序画出两只海鸥如下图:
其中的抽象类Shape的paint()和onPaint()函数都含有默认的实作指令。具体类Bird的设计者选择如下:
不覆写(即不修正)paint()函数。直接使用默认行为,所以减轻了负担。
覆写(即修正)onPaint()函数。重用(Reuse)了默认行为,也减轻了部分的负担。
此程序执行时,主程序的指令:bird.paint()呼叫子类Bird的paint(),但是Bird并没有paint()函数,于是采用基类Shape的默认paint()函数。此默认函数呼叫onPaint(),就反向呼叫了子类的onPaint()。请注意,是基类paint()呼叫子类的onPaint();并不是基类onPaint()来呼叫子类的onPaint()。反而是子类onPaint()呼叫基类的onPaint()。[�g迎光�R 高�ㄌ� �W�: http://www.cnblogs.com/myEIT/ ]
2. Android框架的默认函数之例
Android是应用框架,而在抽象类里摆上一些默认行为又是应用框架的主要技俩,所以Android里不仅含有各式各样的抽象类,而且也有各式各样的默认行为。例如在View基类(或称抽象类)里就定义了onDraw()函数。Button又从View类继承而得到onDraw()函数。最后,我们还可以撰写新类去继承Button类,仍然继承而得onDraw()函数。在这个继承体系里的每一层级皆可覆写这继承而来的onDraw()函数,形成默认行为了。例如下述的范例程序:
2.1 操作情境
此程序一开始,画面出现两个按钮如下:
如果按下<Exit>按钮,程序就结束了。
2.2 撰写步骤
Step-1: 建立Android项目:Fx02。
Step-2: 撰写Activity的子类:ac01,其程序代码如下:
/* ac01.java */
package com.misoo.pkaz;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
public class ac01 extends Activity implements OnClickListener {
private okButton ok_btn;
private exitButton exit_btn;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
ok_btn = new okButton(this);
ok_btn.setOnClickListener(this);
LinearLayout.LayoutParams param =
new LinearLayout.LayoutParams(ok_btn.get_width(),
ok_btn.get_height());
layout.addView(ok_btn, param);
exit_btn = new exitButton(this); exit_btn.setOnClickListener(this);
exit_btn.setText("Exit"); exit_btn.setTextColor(Color.BLUE);
exit_btn.setTextSize(25); exit_btn.setBackgroundResource(R.drawable.icon2);
LinearLayout.LayoutParams param2 =
new LinearLayout.LayoutParams(80, 55);
param2.topMargin = 5; param2.leftMargin = 5;
layout.addView(exit_btn, param2);
setContentView(layout);
}
public void onClick(View v) {
if(v == ok_btn) setTitle("OK");
else if(v == exit_btn) finish();
}}
Step-3: 撰写Button的子类:okButton,其程序代码如下:
/* okButton.java */
package com.misoo.pkaz;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.widget.Button;
public class okButton extends Button{
public okButton(Context ctx){
super(ctx); super.setText(" K");
super.setTextSize(30); super.setTextColor(Color.RED);
}
@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint pa = new Paint();
pa.setColor(Color.YELLOW);
canvas.drawCircle(30, 25, 17, pa);
pa.setColor(Color.RED);
canvas.drawCircle(30, 25, 15, pa);
pa.setColor(Color.YELLOW);
canvas.drawCircle(30, 25, 12, pa);
}
public int get_width(){ return 90; }
public int get_height(){ return 60; }
}
Step-4: 撰写Button的子类:exitButton,其程序代码如下:
/* exitButton.java */
package com.misoo.pkaz;
import android.content.Context;
import android.graphics.Canvas;
import android.widget.Button;
public class exitButton extends Button {
public exitButton(Context ctx){ super(ctx); }
@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}}
2.3 说明:
1) 我们在撰写okButton类时,面对Button的默认行为,可重复利用(Reuse)它,也可以不用它。
2) 因之,我们有三种选择:
并不覆写onDraw()函数。这表示onDraw()函数的默认行为适合于okButton类,直接继承Button基类的onDraw()函数就行了。
完全覆盖掉基类的默认行为。这表示onDraw()函数的默认行为不适合于okButton类,而且不想去重复利用(Reuse)它。
重复利用(Reuse)它,并且修正它。呼叫基类的默认行为(如画出图画的背景),在增添新的行为,达到修正默认行为之目的。
3) 例如,okButton类采取上述的第(三)种做法。既呼叫默认的行为绘出一个白色按钮,而且自己画出彩色的图案。
4) 再如,exitButton类采取上述的第(一)种做法。虽然表面上有覆写的形式,但也只是呼叫基类的默认行为而已。所以,exitButton的onDraw()函数是多余的、可删去之。
3. Android的<IoC + 默认函数>
Android是应用框架,不但在抽象类里摆上一些默认行为,而且能进行反向呼叫具体类的覆写函数。抽象类、默认行为和反向呼叫是应用框架的主要元素,所以Android里不仅含有各式各样的抽象类,而且也有各式各样的默认行为,还能顺畅地反向呼叫。例如在View类体系里各类皆能覆写onDraw()函数,让View类继承的基类能顺利反向呼叫子类的onDraw()函数。例如下述的范例程序:
3.1 操作情境
1) 此程序执行时,呈现如下的画面:
2) 如果按下RadioButton,画面就变换如下:
3) 若按下<Exit>按钮,程序就结束了。
3.2 撰写步骤:
Step-1: 建立Android应用程序项目:Fx03。
Step-2: 撰写Activity的子类ac01,其程序代码如下:
/* ac01.java */
package com.misoo.ppxx;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RadioButton;
public class ac01 extends Activity implements OnClickListener {
private final int WC = ViewGroup.LayoutParams.WRAP_CONTENT;
private MyView mv;
private RadioButton ra;
private Button btn;
@Override public void onCreate(Bundle icicle) {
super.onCreate(icicle);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams param =
new LinearLayout.LayoutParams(115, 250);
param.leftMargin = 1;
mv = new MyView(this);
layout.addView(mv, param);
ra = new RadioButton(this); ra.setOnClickListener(this);
param = new LinearLayout.LayoutParams(WC, WC);
param.topMargin = 40;
layout.addView(ra, param);
btn = new Button(this); btn.setText("Exit");
btn.setOnClickListener(this); btn.setBackgroundResource(R.drawable.gray);
param.leftMargin = 30;
layout.addView(btn, param);
setContentView(layout);
}
public void onClick(View v) {
if( v == ra) mv.ReDraw();
else if(v == btn) finish();
}}
Step-3: 撰写View的子类MyView,其程序代码如下:
/* MyView.java */
package com.misoo.ppxx;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class MyView extends View {
private Paint pa = new Paint();
private boolean yn = false;
public MyView(Context context) { super(context); }
public void ReDraw(){
this.invalidate();
}
@Override protected void onDraw(Canvas canvas) {
yn = !yn;
pa.setColor(Color.WHITE);
canvas.drawRect(10, 10, 100, 100, pa);
pa.setColor(Color.YELLOW);
if(yn){ pa.setColor(Color.BLUE);
canvas.drawCircle(55, 55, 15, pa); }
else { pa.setColor(Color.RED);
canvas.drawRect(40, 40, 70, 70, pa);
}
}}
Step-4: 执行之。
3.3 说明:
1) 这MyView子类覆写了onDraw()函数,创造了反向呼叫的机会。
2) 当我们按下RadioButton时,就呼叫到MyView类的ReDraw()函数,进而呼叫到View的invalidate()函数。
3) 接着,就反向呼叫MyView子类的onDraw()函数,在UI画面上绘出图形来。这就是典型的「反向沟通」了。
4) 如果你没有定义onDraw()函数的话,会执行View基类默认的onDraw()函数,而依据框架默认之惯例而行了。◆
ee ee
【思考Android技术】
��⒖迹� Android从程序员到架构师之路课程(在线视频学习)
请进入:ADT架构师学苑