前言:Android平台的核心是C/C++,为了吸引更多App开发者来使用它,就在C/C++之上,增添一层Java框架,这框架的基类一方面提供接口(即抽像函数)来支持App开发者撰写子类。这项接口的幕后,隐藏了默认函数(Default Function)及其复杂代码,让App开发者只要撰写简单的子类,就能继承而调用这些默认函数,藉由简单的接口调用,就能轻易叫出复杂的默认行为(Default Behavior)。

    抽象类的焦点在于抽象函数,它们的实作指令部份都是从缺的。所以该抽象类的子类『必须』补足这个欠缺,才能成为一个具体类。这抽像函数是基类与子类两者相遇的地方,也就成为两者的接口,又称为基类的API。不过,在上述文章里,偏重于API的角色及其设计要点。但并未碰触到应用开发者的心理层面:为何应用开发者愿意来使用框架及其API呢? 也就是说,框架开发者必须关心如何去吸引应用开发者来使用框架及其API。其解答之一是:在基类里提供默认行为。


1.   基类接口隐藏了默认函数

   刚才提过了,抽象类里的抽象函数,其实作指令部份都是从缺的。所以该抽象类的子类『必须』补足这个欠缺,才能成为一个具体类。可知,子类开发者的负担是蛮重的,框架设计可以透过默认行吸引应用子类开发者。想一想,如果将一些默认(Default)的实作部份加到抽象类里,则其子类就可选择要不要修正这个默认实作部份,这样可以减轻子类开发者的负担。例如下述之范例:

思考Android架构(二):像Android框架,如何(How-to)吸引开发者来使用它呢?_高煥堂

其程序代码如下:

/*   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);  

}}

此程序画出两只海鸥如下图:

思考Android架构(二):像Android框架,如何(How-to)吸引开发者来使用它呢?_架構設計_02

    其中的抽象类Shape的paint()和onPaint()函数都含有默认的实作指令。具体类Bird的设计者选择如下:

  • 不覆写(即不修正)paint()函数。直接使用默认行为,所以减轻了负担。

  • 覆写(即修正)onPaint()函数。重用(Reuse)了默认行为,也减轻了部分的负担。

     此程序执行时,主程序的指令:bird.paint()呼叫子类Bird的paint(),但是Bird并没有paint()函数,于是采用基类Shape的默认paint()函数。此默认函数呼叫onPaint(),就反向呼叫了子类的onPaint()。请注意,是基类paint()呼叫子类的onPaint();并不是基类onPaint()来呼叫子类的onPaint()。反而是子类onPaint()呼叫基类的onPaint()。[歡迎光臨 高煥堂 網頁: http://www.cnblogs.com/myEIT/ ]

2.  Android框架的默认函数之例

      Android是应用框架,而在抽象类里摆上一些默认行为又是应用框架的主要技俩,所以Android里不仅含有各式各样的抽象类,而且也有各式各样的默认行为。例如在View基类(或称抽象类)里就定义了onDraw()函数。Button又从View类继承而得到onDraw()函数。最后,我们还可以撰写新类去继承Button类,仍然继承而得onDraw()函数。在这个继承体系里的每一层级皆可覆写这继承而来的onDraw()函数,形成默认行为了。例如下述的范例程序:

2.1 操作情境

  • 此程序一开始,画面出现两个按钮如下:

思考Android架构(二):像Android框架,如何(How-to)吸引开发者来使用它呢?_架構設計_03

  • 如果按下<Exit>按钮,程序就结束了。

2.2 撰写步骤

Step-1: 建立Android项目:Fx02。

思考Android架构(二):像Android框架,如何(How-to)吸引开发者来使用它呢?_高煥堂_04

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) 此程序执行时,呈现如下的画面:

思考Android架构(二):像Android框架,如何(How-to)吸引开发者来使用它呢?_架構設計_05

2) 如果按下RadioButton,画面就变换如下:

思考Android架构(二):像Android框架,如何(How-to)吸引开发者来使用它呢?_架構設計_06

3) 若按下<Exit>按钮,程序就结束了。


3.2 撰写步骤:

Step-1: 建立Android应用程序项目:Fx03。

思考Android架构(二):像Android框架,如何(How-to)吸引开发者来使用它呢?_高煥堂_07

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架构师学苑