摘要: 转自:http://blog.csdn.net/iefreer/archive/2009/09/23/4586351.aspx处理用户界面事件Handling UI Events在Android上,不止一个途径来侦听用户和应用程序之间交互的事件。对于用户界面里的事件,侦听方法就是从与用户交互的特定视图对象截获这些事件。视图类提供了相应的手段。在各种用来组建布局的视图类里面,你可能会注意到一些公共的...  阅读全文
posted @ 2010-04-03 00:10 seedshopezhong 阅读(460) | 评论 (0)编辑 收藏

创建对话框Creating Dialogs

对话框通常是一个显示在当前活动前面的小窗口。下面的活动失去焦点而由对话框接受所有的用户交互。对话框通常被用来当做通知或者运行中的应用程序相关的短暂活动。

Android API支持下面的对话框对象类型:

警告对话框AlertDialog

这个对话框管理0,1,2,或3个按钮,和/或一个可包含复选框和单选按钮的可选项列表。这个警告对话框能够组建大多数用户界面而且是推荐使用的对话框类型。请查看下面的创建一个警告对话框Creating an AlertDialog。

进度对话框ProgressDialog

用来显示一个进度轮或进度条。因此它是警告对话框的扩展,它也支持按钮。请查看下面的Creating a ProgressDialog 。

日期选择对话框DatePickerDialog

一个允许用户选择日期的对话框。请查看Hello DatePicker 指南。

时间选择对话框TimePickerDialog

一个允许用户选择时间的对话框。请查看Hello TimePicker 指南.

如果你想定制你自己的对话框,你可以在基础对话框对象或任何上面列举的子类对话框上进行扩展并定义一个新的布局。请查看下面的创建自定义对话框Creating a Custom Dialog章节。

显示对话框Showing a Dialog

对话框经常作为活动Activity的一部分来创建和显示。你通常应该从活动的onCreateDialog(int) 回调方法里创建对话框。当你使用这个回调函数时,Android系统会有效的设置这个活动为每个对话框的所有者,从而自动管理每个对话框的状态并挂靠到活动上。这样,每个对话框继承这个活动的特定属性。比如,当一个对话框打开时,菜单键显示为这个活动定义的选项菜单,音量键修改活动使用的音频流。

注意: 如果你决定在onCreateDialog()方法之外创建一个对话框,它将不会被附着到活动上。不过,你可以通过setOwnerActivity(Activity)把它附着到一个活动上。

当你想要显示一个对话框时,调用showDialog(int) 方法并传递一个唯一标识这个对话框的整数。

当对话框第一次被请求时,Android从你的活动中调用onCreateDialog(int),你应该在这里初始化这个对话框Dialog。这个回调方法被传以和showDialog(int)相同的ID。当你创建这个对话框后,在方法的最后返回这个对象。

在对话框被显示之前,Android还调用了可选的回调函数onPrepareDialog(int, Dialog). 如果你想在每一次对话框被打开时改变它的任何属性,你可以定义这个方法。这个方法在每次打开对话框时被调用,而onCreateDialog(int) 仅在对话框第一次打开时被调用。如果你不定义onPrepareDialog(),那么这个对话框将保持和上次打开时一样。这个方法也被传递以对话框的ID,和在onCreateDialog()中创建的对话框对象。

定义onCreateDialog(int) 和 onPrepareDialog(int, Dialog) 回调函数的最佳方法是使用一个switch 语句来检查传递进来的id 参数。每个case 应该检查一个唯一的对话框ID然后创建和定义相应的对话框。比如,想象一下一个游戏使用两个不同的对话框:一个用来指示这个游戏已经暂停而另一个来指示游戏结束。首先,为每个对话框定义一个整数:

static final int DIALOG_PAUSED_ID = 0;

static final int DIALOG_GAMEOVER_ID = 1;

然后,为每一个ID用一个switch case定义这个onCreateDialog(int) 回调函数:

protected Dialog onCreateDialog(int id) {

    Dialog dialog;

    switch(id) {

    case DIALOG_PAUSED_ID:

        // do the work to define the pause Dialog

        break;

    case DIALOG_GAMEOVER_ID:

        // do the work to define the game over Dialog

        break;

    default:

        dialog = null;

    }

    return dialog;

}

注意: 在这个例子里,case语句没有具体内容,因为这超出了本章讨论范围。

当是时候显示其中之一的对话框时,使用对话框ID调用showDialog(int):

showDialog(DIALOG_PAUSED_ID);

消除对话框Dismissing a Dialog

当你准备关闭对话框时,你可以通过对这个对话框调用dismiss()来消除它。如果需要,你还可以从这个活动中调用dismissDialog(int) 方法,这实际上将为你对这个对话框调用dismiss() 方法.

如果你想使用onCreateDialog(int) 方法来管理你对话框的状态(就如同在前面的章节讨论的那样),然后每次你的对话框消除的时候,这个对话框对象的状态将由该活动保留。如果你决定不再需要这个对象或者清除该状态是重要的,那么你应该调用removeDialog(int)。这将删除任何内部对象引用而且如果这个对话框正在显示,它将被消除。

使用消除侦听器Using dismiss listeners

如果你希望你的应用程序在一个对话框消亡的时候执行一些流程,那么你应该附着一个on-dismiss侦听器到对话框上。

首先定义DialogInterface.OnDismissListener 接口。这个接口只有一个方法,onDismiss(DialogInterface),将在对话框消亡的时候被调用。然后简单的传递你的OnDismissListener 实现给setOnDismissListener()。

然而, 请注意对话框也可以被“取消”。这是一个表明对话框被用户显示取消的特殊情况。这将在用户按“返回”按钮时发生,或者这个对话框显示的调用cancel() (也许通过对话框上的一个“取消”按钮)。当一个对话框被取消时,这个OnDismissListener 依然会被通知到,但是如果你希望在对话框被显示取消时被通知到(而不是通常的消除方式),那么你应该通过setOnCancelListener()注册一个DialogInterface.OnCancelListener 。

创建警告对话框Creating an AlertDialog

一个警告对话框是对话框的扩展类。它能够构建大多数对话框用户界面并且是推荐使用的对话框类型。你应该在具备如下特性的时候使用它:

·         一个标题

·         一个文本消息

·         1个,2个或3个按钮

·         一个可选项列表(可选的复选框或单选按钮)

为了创建一个警告对话框,使用AlertDialog.Builder 子类。通过AlertDialog.Builder(Context)获取一个构造器然后使用这个类的公共方法来定义警告对话框的所有属性。当得到构造器后,通过create().方法来获取警告对话框对象。

下面的题目说明了如何使用AlertDialog.Builder类来定义不同的警告对话框属性。如果你在onCreateDialog()回调函数中使用下面的代码,你可以返回结果对话框对象来显示它。

增加按钮Adding buttons

为了创建一个如右图所示的包含并行按钮的警告对话框,使用set...Button() 方法:


AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setMessage("Are you sure you want to exit?")

       .setCancelable(false)

       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {

           public void onClick(DialogInterface dialog, int id) {

                MyActivity.this.finish();

           }

       })

       .setNegativeButton("No", new DialogInterface.OnClickListener() {

           public void onClick(DialogInterface dialog, int id) {

                dialog.cancel();

           }

       });

AlertDialog alert = builder.create();

首先,为这个对话框添加一个消息setMessage(CharSequence)。然后,开始函数链并设置该对话框为不能取消not cancelable (因此用户不能使用返回按钮关闭这个对话框)。对每个按钮,使用任一set...Button() 方法,比如setPositiveButton(),该方法接受按钮名称以及一个定义用户选中按钮后所采取动作的DialogInterface.OnClickListener。

注意: 你仅可以为这个警告对话框添加其中一种按钮类型。也就是,你不能包含多个“确定”按钮。这限制了可能的按钮数目只能是3个:确定,中立和否定。这些名字和你按钮的实际功能是技术上无关的,但是应该可以帮助你记录做了什么。

增加一个列表Adding a list

为了创建一个带有可选项列表的警告对话框,如右边所示,可使用setItems()方法:


final CharSequence[] items = {"Red", "Green", "Blue"};

 

AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setTitle("Pick a color");

builder.setItems(items, new DialogInterface.OnClickListener() {

    public void onClick(DialogInterface dialog, int item) {

        Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();

    }

});

AlertDialog alert = builder.create();

首先,用setTitle(CharSequence)方法给对话框添加一个标题。然后,添加用setItems()添加一个可选项列表,该列表接受一组显示的items和一个DialogInterface.OnClickListener 来定义用户选中按钮后所采取动作。

增加复选框和单选按钮

要在对话框里创建一个多选项列表(checkboxes)或者单选项(radio buttons),可分别调用setMultiChoiceItems() 和setSingleChoiceItems() 方法。如果你在onCreateDialog()回调函数中创建这些可选列表,Android会帮你管理列表状态。只要这个活动是激活的,对话框会记住之前选中的items,但如果用户退出这个活动,用户选择将丢失。

注意: 为了在用户离开或暂停这个活动的时候能够保存选择,你必须通过活动生命期Activity Lifecycle来恰当的保存和恢复设置。为了永久保存选项,即使活动进程被完全终止,你需要使用数据存储Data Storage技术。

要创建如右边所示的一个包含单选项列表的警告对话框,使用前面例子中相同的代码,不过需要把setItems()方法替换为setSingleChoiceItems()。


final CharSequence[] items = {"Red", "Green", "Blue"};

 

AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setTitle("Pick a color");

builder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {

    public void onClick(DialogInterface dialog, int item) {

        Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();

    }

});

AlertDialog alert = builder.create();

setSingleChoiceItems() 的第二个参数是一个checkedItem整型数值,指示了基于0的缺省选择项的位置。“-1”代表不会有默认选择项。

创建进度对话框Creating a ProgressDialog

进度对话框ProgressDialog是AlertDialog类的一个扩展,可以为一个未定义进度的任务显示一个旋转轮形状的进度动画,或者为一个指定进度的任务显示一个进度条。这个对话框也能提供按钮,比如一个取消下载的按钮。

可以简单的通过调用ProgressDialog.show()方法来显示一个进度对话框。比如, 可以很简单的得到右边显示的进度对话框,而不必通过onCreateDialog(int)回调管理这个对话框,如下所示:


ProgressDialog dialog = ProgressDialog.show(MyActivity.this, "",

                        "Loading. Please wait...", true);

第一个参数是应用程序上下文Context,第二个是对话框标题(此处为空),第三个是信息,最后这个参数表明进度是否是不确定的(这只和创建进度条有关,下一章会有描述)。

进度对话框的缺省类型是一个旋转轮,如果你想创建一个间隔进度,需要更多的代码,如下章所述。

显示进度条Showing a progress bar

使用动画进度条显示进度:

1.    用类构造器初始化进度对话框,ProgressDialog(Context)。

2.   用setProgressStyle(int)方法设置进度风格为"STYLE_HORIZONTAL"以及设置其它属性,比如消息。

3.   当你准备显示这个对话框时,调用show()或者从onCreateDialog(int)回调中返回ProgressDialog。

4.   你可以通过调用setProgress(int)设置当前进度百分比或者调用incrementProgressBy(int)方法增加进度值。

比如,你的设置可能看起来像这样:

ProgressDialog progressDialog;

progressDialog = new ProgressDialog(mContext);

progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

progressDialog.setMessage("Loading...");

progressDialog.setCancelable(false);

设置很简单。大多数创建代码也用来更新进度。你可能意识到创建另外一个线程来完成这个进度报告的工作是有必要的,进度通过一个对象返回给活动的用户界面线程。如果你对如何通过一个Handler使用另外的线程不熟悉,请参见下面的例子:

Example ProgressDialog with a second thread

这个例子使用了另外一个线程来跟踪进程进度(计数到100)。这个线程在每次进度更新时通过一个句柄Handler发回一条消息Message。主活动然后更新进度对话框。

package com.example.progressdialog;

 

import android.app.Activity;

import android.app.Dialog;

import android.app.ProgressDialog;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

 

public class NotificationTest extends Activity {

    static final int PROGRESS_DIALOG = 0;

    Button button;

    ProgressThread progressThread;

    ProgressDialog progressDialog;

  

    /** Called when the activity is first created. */

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        // Setup the button that starts the progress dialog

        button = (Button) findViewById(R.id.progressDialog);

        button.setOnClickListener(new OnClickListener(){

            public void onClick(View v) {

                showDialog(PROGRESS_DIALOG);

            }

        });

    }

  

    protected Dialog onCreateDialog(int id) {

        switch(id) {

        case PROGRESS_DIALOG:

            progressDialog = new ProgressDialog(NotificationTest.this);

            progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

            progressDialog.setMessage("Loading...");

            progressThread = new ProgressThread(handler);

            progressThread.start();

            return progressDialog;

        default:

            return null;

        }

    }

 

    // Define the Handler that receives messages from the thread and update the progress

    final Handler handler = new Handler() {

        public void handleMessage(Message msg) {

            int total = msg.getData().getInt("total");

            progressDialog.setProgress(total);

            if (total >= 100){

                dismissDialog(PROGRESS_DIALOG);

                progressThread.setState(ProgressThread.STATE_DONE);

            }

        }

    };

 

    /** Nested class that performs progress calculations (counting) */

    private class ProgressThread extends Thread {

        Handler mHandler;

        final static int STATE_DONE = 0;

        final static int STATE_RUNNING = 1;

        int mState;

        int total;

      

        ProgressThread(Handler h) {

            mHandler = h;

        }

      

        public void run() {

            mState = STATE_RUNNING;  

            total = 0;

            while (mState == STATE_RUNNING) {

                try {

                    Thread.sleep(100);

                } catch (InterruptedException e) {

                    Log.e("ERROR", "Thread Interrupted");

                }

                Message msg = mHandler.obtainMessage();

                Bundle b = new Bundle();

                b.putInt("total", total);

                msg.setData(b);

                mHandler.sendMessage(msg);

                total++;

            }

        }

       

        /* sets the current state for the thread,

         * used to stop the thread */

        public void setState(int state) {

            mState = state;

        }

    }

}

创建自定义对话框Creating a Custom Dialog

如果你想为对话框做一个自定义的设计,你可以为对话框窗口创建自己的布局和部件元素。当你定义好布局后,传递根视图对象或者布局资源ID给setContentView(View) 方法。

比如,创建如右图所示对话框:


1.     创建一个XML布局以custom_dialog.xml保存:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

              android:id="@+id/layout_root"

              android:orientation="horizontal"

              android:layout_width="fill_parent"

              android:layout_height="fill_parent"

              android:padding="10dp"

              >

    <ImageView android:id="@+id/image"

               android:layout_width="wrap_content"

               android:layout_height="fill_parent"

               android:layout_marginRight="10dp"

               />

    <TextView android:id="@+id/text"

              android:layout_width="wrap_content"

              android:layout_height="fill_parent"

              android:textColor="#FFF"

              />

</LinearLayout>

这个XML在一个LinearLayout里定义了一个ImageView 和一个TextView.

  2.  设置上面这个布局作为对话框的内容视图并为这个ImageView和TextView元素定义内容:

Context mContext = getApplicationContext();

Dialog dialog = new Dialog(mContext);

 

dialog.setContentView(R.layout.custom_dialog);

dialog.setTitle("Custom Dialog");

 

TextView text = (TextView) dialog.findViewById(R.id.text);

text.setText("Hello, this is a custom dialog!");

ImageView image = (ImageView) dialog.findViewById(R.id.image);

image.setImageResource(R.drawable.android);

 实例化对话框后,用setContentView(int)设置你的自定义布局作为这个对话框的内容视图,以布局资源ID作为参数。现在这个对话框有一个已定义的布局,你可以用findViewById(int)方法从布局中抓取视图对象。

3. 就这样了。你现在可以显示该对话框了,参见Showing A Dialog中的描述。

以基类对话框创建的对话框必须有一个标题。如果你不调用setTitle(),那么标题占用的空间保持为空,但仍然可见。如果你根本不想要一个标题,那你应该使用警告对话框AlertDialog来创建你的自定义对话框。 然而,因为警告对话框可以很简单的通过AlertDialog.Builder 类来创建,你并不需要访问上面使用的setContentView(int) 方法。相反,你必须使用setView(View)。这个方法接受一个视图View 对象,所以你需要在XML中扩充布局的根视图。

要扩充XML布局,用getLayoutInflater() 或getSystemService()方法获取LayoutInflater,然后调用 inflate(int, ViewGroup),这里第一个参数是布局资源ID而第二个参数是根视图的ID。在此处,你可以使用扩充布局来查找视图对象和为ImageView和TextView元素定义内容。然后实例化AlertDialog.Builder 并调用setView(View)为该对话框设置扩充布局。

下面是一个例子,在一个警告对话框中创建自定义布局:

AlertDialog.Builder builder;

AlertDialog alertDialog;

 

Context mContext = getApplicationContext();

LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER);

View layout = inflater.inflate(R.layout.custom_dialog,

                               (ViewGroup) findViewById(R.id.layout_root));

 

TextView text = (TextView) layout.findViewById(R.id.text);

text.setText("Hello, this is a custom dialog!");

ImageView image = (ImageView) layout.findViewById(R.id.image);

image.setImageResource(R.drawable.android);

 

builder = new AlertDialog.Builder(mContext);

builder.setView(layout);

alertDialog = builder.create();

在你的自定义布局中使用警告对话框可以让你利用警告对话框的内置特性比如管理按钮,可选列表,一个标题,一个图标等。

想获取更多信息,请参考Dialog 和 AlertDialog.Builder 类的相关文档。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/iefreer/archive/2009/09/22/4581137.aspx

posted @ 2010-03-31 13:17 seedshopezhong 阅读(409) | 评论 (0)编辑 收藏
     摘要: 创建菜单 Creating Menus菜单是任何应用程序的一个重要部分,提供了透露应用程序功能和设置的通用接口。Android为开发者提供了一个简单的编程接口来实现各种条件下的标准化应用程序菜单。Android提供了三种基础菜单类型:选项菜单Options Menu这是一个活动的主菜单。通过按下设备菜单键来显示它。选项菜单包含两组菜单项:图标菜单Icon Menu这个是当用户按下菜单键时...  阅读全文
posted @ 2010-03-30 23:15 seedshopezhong 阅读(426) | 评论 (0)编辑 收藏

定义布局

布局指的是Activity中的UI的结构。它定义了布局结构并持有所有用户能看到的元素。你可以有两种方式来定义你的布局:

  • 在xml中定义UI元素。Android提供了一整套和各种View类对应的易懂的xml语法,例如各种widget和layout等。
  • 在运行时初始化Layout元素。你的应用程序可以使用代码创建View和ViewGroup对象。

Android框架通过这两种方法并存的方式提供了灵活的UI定义方式。例如,你可以在xml里定义你的应用程序的默认布局,并在运行时中修改屏幕上的对象。

在xml中定义UI的好处是它让你更好的将应用程序的外观和控制行为的代码分开。你的UI描述在应用程序的代码之外,这意味着你可以在不修改代码和重新编译的前提下修改UI。例如,你可以为不同的屏幕朝向,不同的屏幕大小和不同的语言设定不同的布局。另外,在xml中定义布局使得你的UI更加直观和容易调试(xml的结构比java代码更清晰)。本文介绍如何在xml中定义布局。

总的来说,定义UI的xml词汇和相应的类名、方法名是紧密相关的;xml的元素名对应类名,而xml的属性名对应方法名。事实上,它们之间的关系通常非常直接以至于你可以从xml属性名猜出方法名,或者从xml元素猜出java类名。然而,注意不是所有的词汇都是相同的。在某情况下二者的命名有些小的区别。例如,EditText元素有一个text属性对应于一个叫做EditText.setText()的属性。

提示:Common Layout Objects一节中有关于不同布局类型的介绍。在Hello Views教程中也有一些关于创建不同的布局的教程。

Write the XML

为了阅读的方便,UI相关类的API参考文档列出了与类方法相对应的xml属性,包括继承属性。更多关于可用的xml元素和属性,以及xml格式的信息相见Layout Resources一节。

使用Android的xml词汇,你可以很快设计UI布局和它包含的各种屏幕元素,就像使用HTML来创建网页一样——使用一系列嵌套的元素。

Each layout file must contain exactly one root element, which must be a View or ViewGroup object. Once you've defined the root element, you can add additional layout objects or widgets as child elements to gradually build a View hierarchy that defines your layout. For example, here's an XML layout that uses a vertical LinearLayout to hold a TextView and a Button:

每个布局文件必须包含一个根元素,该元素必须是一个View或者ViewGroup对象。一旦你定义了根元素,你就可以加入额外的布局对象或者widget作为其子元素,逐渐构成你的完整布局。例如,这里有一个使用从上到下的LinearLayout布局,它包含了一个TextView和一个Button:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              
android:layout_width="fill_parent"
              
android:layout_height="fill_parent"
              
android:orientation="vertical" >
    
<TextView android:id="@+id/text"
              
android:layout_width="wrap_content"
              
android:layout_height="wrap_content"
              
android:text="Hello, I am a TextView" />
    
<Button android:id="@+id/button"
            
android:layout_width="wrap_content"
            
android:layout_height="wrap_content"
            
android:text="Hello, I am a Button" />
</LinearLayout>

使用xml定义了布局之后,将它保存在Android工程的res/layout目录中才能正确编译。

Load the XML Resource

当你编译你的工程时,每个xml布局文件被编译成一个View资源。你应该在应用程序代码中加载这些布局资源,在Activity.onCreate()回调函数的实现中。调用setContentView(),并将布局资源的引用R.layout.layout_file_name传给它。例如,如果你的布局xml文件名为main_layout.xml:

public void onCreate(Bundle savedInstanceState) {
    
super.onCreate(savedInstanceState);
    setContentView
.(R.layout.main_layout);
}
onCreate()在你的Activity启动时被调用。

Attributes

每个View和ViewGroup对象都支持它们各自的xml属性。有些属性只对某一种View对象有效(例如,TextView支持textSize属性),这些属性对继承这些view的对象也有效。有些属性对View对象普遍有效,因为它们继承于View基类(例如Id属性)。还有的属性被认为是“布局参数”,用来描述View对象的布局属性。

ID

任何View对象可能会有一个整数ID和它相关联,来在一棵树中唯一的指定一个View对象。当应用程序被编译时,该id被当做一个整数处理,但是该ID一般是在布局xml里作为一个字符串出现,作为id属性的值。这是一个所有View对象都有的xml属性(由View基类定义)。在xml标签中定义一个id的语法如下:

android:id="@+id/my_button"

字符串开始的@记号告诉xml解析器应该将后面的部分展开并将其识别为一个ID资源。+表示这是一个新的资源名称,必须将其加入我们的资源集(R.java)。有一些其它的ID资源是由Android框架提供的,当引用它们时,不需要使用+符号,但必须加上Android包名称空间,像这样:

android:id="@android:id/empty"

有了Android包名称空间,我们现在指向的是android.R资源类的对象,而不是本地资源类中的对象。

In order to create views and reference them from the application, a common pattern is to:

为了创建view并从应用程序中引用它们,下面是一个基本的模式:

  1. 定义一个view/widget并给它分配一个唯一的ID:<Button android:id="@+id/my_button"
            
    android:layout_width="wrap_content"
            
    android:layout_height="wrap_content"
            
    android:text="@string/my_button_text"/>
  2. 然后创建一个该View对象并从布局中得到它(一般在onCreate()中):Button myButton = (Button) findViewById(R.id.my_button);

为View对象定义id在创建RelativeLayout(相对布局)中是很重要的。在一个相对布局中,兄弟View(位于树结构的同一层的两节点)可以定义它们相互之间的的相对位置,这需要id来标识View对象。

一个ID不需要在整个树中唯一,但它必须在你正在搜索的树的部分中唯一(常常就是整个树),因此最好尽量使用唯一的id。

Layout Parameters

名称为layout_something的xml布局属性定义了一个View在它所在的ViewGroup中的布局参数。

每个ViewGroup类实现一个嵌套类,该类是ViewGroup.LayoutParams的子类。该类包含了定义每个子View大小和位置的属性。如下图所示,父view group定义了子view的布局参数:


注意每个LayoutParams子类有着它自己的设置值的方法。每个子元素必须定义和其父元素相应的LayoutParams,尽管它可以对它自己的子元素定义不同的LayoutParams。

每个view group包含一个宽度和高度(layout_width和layout_height),每个view必须定义它们。很多LayoutParams也包括可选的margin(空白)和border(边界)。

你可以使用精确值来指定宽度和高度,尽管你可能很少这样使用。更常见的是,你把view对象的大小设为和它的内容相合适,或者尽可能的大将其父对象填满(分别对应wrap_content和fill_parent)。可接受的长度单位在Available Resources文档中。

Layout Position

view的几何性质就是它所在矩形的几何性质。一个view的位置用一对left和top坐标表示,两个长度用width和height来表示。位置和长度的单位是像素。

通过调用getLeft()和getTop()可以得到一个View的位置。这两个方法返回的是相对于其父元素的位置,例如,如果getLeft()返回20,则表示它和其直接父元素的坐边界的距离是20像素。

另外,有一些方法的存在只是为了写程序的方便,如getRight()和getBottom()。getRight()和getLeft()+getWidth()的作用是一样的。

Size, Padding and Margins

一个view的大小使用宽度和高度来描述。一个view实际上有两组高度和宽度值。

第一组成为测量宽度和测量高度。这些长度定义了一个view希望在它的父view中的大小。测量长度可以使用getMesuredWidth()和 getMeasuredHeight()来得到。

第二组为宽度和高度,有时被成为绘制宽度和绘制高度。这些长度定义了view在屏幕上的实际大小。这些值可能和测量长度不同。它们可以通过getWidth()和getHeight()得到。

为了测量它的长度,一个view将它的padding考虑在内。padding使用像素来表示,可以设定上下左右各个方向上的padding。padding可以用来将view产生一个位移。例如,一个2像素的左padding将使view向右偏移2个像素。可以使用setPadding(int,int,int,int)来设定,由getPaddingLeft()getPaddingTop()getPaddingRight() 和 getPaddingBottom()来获取。

虽然一个view可以定义一个padding,但它并不支持margin。view group支持margin。

posted @ 2010-03-30 23:00 seedshopezhong 阅读(387) | 评论 (0)编辑 收藏

用户界面

在一个Android应用程序中,用户界面使用View和ViewGroup对象建立。view和view group有很多种,均为View类的子类。view对象是用户界面的基本单元。View类是widget的父类,widget为系统内置ui对象,如文本框和按钮等等。ViewGroup类为layout类的父类,layout为不同种类的布局结构,例如线型、表格型和相对型。

一个View对象是一个储存屏幕上的一块矩形的布局参数和内容的数据结构。一个View对象处理它自己的测量、布局、绘画、焦点变化、卷屏以及它所处的矩形区域内的键盘/手势交互。作为UI中的一个对象,View也是一个和用户交互信息的地方。

View Hierarchy

在Android平台上,使用一个View和ViewGroup组成的层次结构来定义的一个Activity的UI,如下图所示。这个层次树可以简单也可以复杂,你可以使用Android预定义的widget和layout,也可以用你自己自定义的View来构建它。


为将View层次树显示在屏幕上,你的Activity需要使用setContentView()方法,并将根节点对象传递给他。Android系统接收到这个引用并用它来清除、测量和绘制这个树。根节点要求它的子节点能够绘制自己——每个组节点负责它自己的子view来绘制自己。子View可能会从父View处获得大小和位置,但父对象最后决定子对象的大小。Android解析layout,初始化View并把它们加入它们的父View。由于它们按顺序绘制,如果这些元素相互重叠,最后一个被绘制的元素将位于最上层。"How Android Draws Views"介绍了绘制的细节。

Layout

最常见的定义layout和表示view结构的方法就是使用一个xml layout文件。xml提供了一个人类可读的结构。xml的每个元素是一个View或者ViewGroup对象。View对象为树中的树叶,ViewGroup对象为树中的树枝。

xml元素的名称和它的java类相对应。因此一个<TextView>元素创建一个TextView对象,一个<LinearLayout>元素创建一个LinearLayout的view group。当你加载一个layout资源时,Android系统会基于layout中的元素来初始化这些运行时对象。

例如,一个简单的竖排布局,中间有一个text view和一个按钮,像这样:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              
android:layout_width="fill_parent"
              
android:layout_height="fill_parent"
              
android:orientation="vertical" >
    
<TextView android:id="@+id/text"
              
android:layout_width="wrap_content"
              
android:layout_height="wrap_content"
              
android:text="Hello, I am a TextView" />
    
<Button android:id="@+id/button"
            
android:layout_width="wrap_content"
            
android:layout_height="wrap_content"
            
android:text="Hello, I am a Button" />
</LinearLayout>

注意LinearLayout元素包含TextView和Button。你可以在这个LinearLayout中使用另一个LinearLayout,来创建更复杂的布局。

        提示:你也可以在Java代码中绘制View和ViewGroup,使用addView(View)方法来动态插入View和ViewGroup对象。

有很多方法来布局你的view。使用不同的View group,你可以让View和viewgroup组织为任意的形式。一些预定义的Viewgroup有:LinearLayout, RelativeLayout, TableLayout, GridLayout等。

Widgets

widget是一个View对象,它作为一个和用户交互的接口。Android提供了一系列已经定义好的widget,例如按钮,复选框以及文本框等。还有一些更复杂的widget,例如日期选择器,始终以及缩放控制器等。你也可以定义自己的widget。

UI Events

一旦你在UI中加入View和widget,你可能希望知道用户对他们的操作,这样你就可以进行一些动作。为了得到UI事件,有下列两种方法:

  • 定义一个event listener并在View中注册它。通常这是你监听事件的方法。View类包含一系列嵌套的on<xxx>Listener接口,每个都有一个on<xxx>()回调函数。例如View.OnClickListener(处理View上的点击事件),View.OnTouchListener(处理触摸屏事件),View.OnKeyListener(处理键盘事件)。因此如果你希望你的View处理点击事件,则定义OnClickListener的onClick()方法并使用setOnClickListener()来注册它。
  • 重定义View的一个已有的回调函数。当你实现自己的View类并希望监听某些事件时采用这种方法。

Menus

应用程序菜单是UI的另一个重要部分。菜单提供了一个显示程序功能和设置的可靠接口。最常见的菜单是使用设备上的MENU键呼出的。你也可以使用长按屏幕上某个元素的方式来呼叫出上下文菜单(Context Menu)。

菜单也是用View层次来组织的,但是不要自己定义这个结构。使用onCreateOptionsMenu()和onCreateContextMenu()回调函数并定义菜单的内容。Android会自动建立需要的View结构并绘制其内容。

菜单也需要处理他们的事件,因此不需要注册事件监听器(event listeners)。当菜单项被选择时,onOptionsItemSelected() 或 onContextItemSelected()方法会被调用。

你也可以在xml中定义菜单的项。

Advanced Topics

Adapters

有时候你希望给view group加入动态元素,这些元素不能硬编码,而是需要和外部数据相绑定。为了实现这一点,你需要用AdapterView作为view group,每一个子view使用adapter来进行初始化。

The AdapterView object is an implementation of ViewGroup that determines its child views based on a given Adapter object. The Adapter acts like a courier between your data source (perhaps an array of external strings) and the AdapterView, which displays it. There are several implementations of the Adapter class, for specific tasks, such as the CursorAdapter for reading database data from a Cursor, or an ArrayAdapter for reading from an arbitrary array.

AdapterView 是ViewGroup的一个实现,该view group使用一个给定的Adapter对象来确定它的子View。Adapter作为数据源(例如一个字符串数组)和AdapterView(负责显示)中的一个联系人。有好几种特殊的Adapter实现,例如CursorAdapter(用于从一个Cursor读取数据库数据)和ArrayAdapter(用来从任意数组中读取数据)。

Styles and Themes

可以用样式和主题来自定义标准widget的外观。

  • 一个样式是一个或多个格式属性的集合,这些格式属性可以被作为独立的元素放在layout中。例如你可以定义一个样式,该样式制定了某种字体大小和颜色,并将它制定给某几个View元素。
  • A theme is a set of one or more formatting attributes that you can apply as a unit to all activities in an application, or just a single activity. For example, you could define a theme that sets specific colors for the window frame and the panel background, and sets text sizes and colors for menus. This theme can then be applied to specific activities or the entire application.
  • 一个主题是一个或多个格式属性的集合。这些属性可以对一个应用程序中所有的或一个activity使用。例如,你可以定义一个主题,制定了窗口框的颜色和面板北京颜色,以及菜单的字体字号等。一个主题可以被应用于某些activity或者整个应用程序。

主题和样式是资源。Android提供了一些默认的样式和主题资源给你使用,你也可以定义自己的主题和样式。

posted @ 2010-03-30 22:36 seedshopezhong 阅读(272) | 评论 (0)编辑 收藏
     摘要: Application Fundamentals——应用程序基础知识Key classes——关键类ActivityServiceBroadcastReceiverContentProviderIntentIn this document——在这篇文章中Application Components——应用程序组件Activating components: intents——激活组件:意图Shut...  阅读全文
posted @ 2010-03-29 22:16 seedshopezhong 阅读(1306) | 评论 (0)编辑 收藏
今天收获特别大,为了使用稳定的wifi设置,只能去提取系统中的wifi模块代码,现在除了加密和解密等,其他已经工作正常了,在此写一下其中遇到的困难和学习到的东西:
1.   Preferencescreen中启动activity
这个让我很吃惊,由于以前都是自己编程,从来没考虑到Preferencescreen还能够启动activity的功能,下面来看看它是怎么启动的,该文件名为wireless_setting.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.seedshope.android">
    <!-- Programmatically will have dynamic summary, but we provide the fallback summary. -->
    <PreferenceScreen
        android:key="wifi_settings"
        android:title="@string/wifi_settings"
        android:summary="@string/wifi_settings_summary" >
        <intent
            android:action="android.intent.action.MAIN"
            android:targetPackage="com.seedshope.android"
            android:targetClass="com.seedshope.android.wifi.WifiSettings" />
    </PreferenceScreen>
</PreferenceScreen>

其中com.seedshope.android是工程的包名,com.seedshope.android.wifi.WifiSettings是要启动的类。
一般情况下,这样就ok了,点击对应的Preference就能够启动对应的activity,然而仅仅这样有时这样,并不一定能够启动对应的activity,今天我追了这个问题达3个小时之久,最终看代码发现了一个问题

    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
       if ( (preference == mAirplaneModePreference) &&
                (Boolean.parseBoolean(
                    SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) ) {
            // In ECM mode launch ECM app dialog
           startActivityForResult(
                new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null),
                REQUEST_CODE_EXIT_ECM);
            return true;
        }
        else {
            // Let the intents be launched by the Preference manager
            return false;
       }
    }
   其中WirelessSetting.java中有如上函数,当返回值为true时,你怎么点击Preference也是无济于事,等吧,等到天亮了,都等不到跳转到对应的activity,只有等到它的返回值为false的时候,才可以正常跳转.
    因此要使一个Preference能够正常跳转到另外一个activity,有两个条件,一是xml里面是否设置正确,第二是调用该xml的java类是否在onPreferenceTreeClick这个函数有正确的设置.

2. 定制Preferece风格
Android的Setting里面有对Preference进行文字和图片混合布置,下面我从(Settings.xml)截取了一小段,如下所示
<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res/com.seedshope.android"
    android:title="@string/settings_label"
    android:key="parent">

        <com.seedshope.android.IconPreferenceScreen
            android:title="@string/radio_controls_title"
            settings:icon="@drawable/ic_settings_wireless">
            <intent
                android:action="android.intent.action.MAIN"
                android:targetPackage="com.seedshope.android"
                android:targetClass="com.seedshope.android.WirelessSettings" />
        </com.seedshope.android.IconPreferenceScreen>
</PreferenceScreen>

其中com.seedshope.android.IconPreferenceScreen就是自己定制的风格,其实一个java类,在android系统的路径为com.android.settings.IconPreferenceScreen.也就是packages/apps/Settings/src/com/android/settings/IcomPreferenceScreen.java文件,该文件内容如下:

public class IconPreferenceScreen extends Preference {

    private Drawable mIcon;

    public IconPreferenceScreen(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public IconPreferenceScreen(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setLayoutResource(R.layout.preference_icon);
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.IconPreferenceScreen, defStyle, 0);
        mIcon = a.getDrawable(R.styleable.IconPreferenceScreen_icon);
    }

    @Override
    public void onBindView(View view) {
        super.onBindView(view);
        ImageView imageView = (ImageView) view.findViewById(R.id.icon);
        if (imageView != null && mIcon != null) {
            imageView.setImageDrawable(mIcon);
        }
    }
}
首先继承了Preference,
posted @ 2010-03-29 21:34 seedshopezhong 阅读(4217) | 评论 (1)编辑 收藏

转载:http://www.chinaup.org/docs//toolbox/custom-components.html

构建自定义组件

Android中,你的应用程序程序与View类组件有着一种固定的联系,例如按钮(Button)、 文本框(TextView)可编辑文本框(EditText)列表框(ListView)复选框(CheckBox)单选框(RadioButton)滚动条(Gallery)微调器(Spinner), 等等,还有一些比较先进的有着特殊用途的View组件,例如AutoCompleteTextViewImageSwitcher和 TextSwitcher。除此之外,种类繁多的像 线性布局(LinearLayout)框架布局(FrameLayout), 这样的布局组件(Layout)也被认为是View组件,他们是从View类派生过来的。

你的应用程序就是这些控制组件和布局组件以某种方式结合显示在屏幕上,一般来说这些组件对你来说基本够用,但是你也应该知道你是可以通过类继承创建属于自己的组件,一般可以继承像View、Layouts(布局组件)这样的组件,甚至可以是一些比较高级的控制类组件。下面我们说一下为什么要继承:

  • 你可以为实现某种功能创建一个完全自定义风格的组件,例如用二维的图形创建控制组件实现声音的控制,就像电子控制一样。
  • 你可以把几种组件结合形成一个新的组件,你的组件可能同时包含ComboBox(一个能输入的文本列表)和dual-pane selector control(左右两个List窗口,你可以分配窗口每一项的从属关系)等等。
  • 你可以创建自己的布局组件(Layout)。SDK中的布局组件已经提供了一系列的选项让你打造属于自己的应用程序,但是高级的开发人员会发现根据现有的Layout组件开发新的Layout组件是很有必要的,甚至是完全从底层开发新的组件。
  • 你可以覆盖一个现有组件的显示或功能。例如,改变EditText(可编辑文本)组件在屏幕上的显示方式(可以参考Notepad的例子,里面教你如何创建一个下划线的显示页面)。
  • 你可以捕获像按键按下这样的事件,以一些通用的方法来处理这些事件(一个游戏的例子)。

为了实现某种目标你可能很有必要扩展一个已经存在的View组件,下面我们结合一些例子教你如何去做。

内容:

基本方法(The Basic Approach )
完全自定义组件(Fully Customized Components )
定制组件的例子(Customized Component Example )
组件的混合(或者控制类的混合) (Compound Components (or Compound Controls) )
修改现有组件(Tweaking an Existing Component )
小结(Go Forth and Componentize )

基本方法(The Basic Approach )

下面的一些步骤都比较概括,教你如何创建自己的组件:

  1. 让你的类(Class)继承一个现有的View 类或View的子类。
  2. 重载父类的一些方法:需要重载的父类方法一般以‘on’开头,如onDraw()onMeasure()和 onKeyDown()等等。
    • 这个在Activity 或则 ListActivity 派生中同样适用,你需要重载一些生命周期函数和一些其他功能性的HOOK函数。
  3. 使用你的继承类:一旦你的继承类创建完成,你可以在基类能够使用的地方使用你的继承类,但完成功能就是你自己编写的了。

继承类能够定义在activities里面,这样你能够方便的调用,但是这并不是必要的(或许在你的应用程序中你希望创建一个所有人都可以使用的组件)。

完全自定义组件(Fully Customized Components)

完全自定义组件的方法可以创建一些用于显示的图形组件(graphical components),也许是一个像电压表的图形计量器,或者想卡拉OK里面显示歌词的小球随着音乐滚动。无论那种方式,你也不能单纯的利用组件的结合完成,无论你怎么结合这些现有的组件。

幸运的是,你可以以你自己的要求轻松地创建完全属于自己的组件,你会发现不够用的只是你的想象力、屏幕的尺寸和处理器的性能(记住你的应用程序最后只会在那些性能低于桌面电脑的平台上面运行)。

下面简单介绍如何打造完全自定义的组件:

  1. 最为通用的VIEW类的父类毫无疑问是View类,因此,最开始你要创建一个基于此类的一个子类。
  2. 你可以写一个构造函数从XML文件中提取属性和参数,当然你也可以自己定义这些属性和参数(也许是图形计量器的颜色和尺寸,或者是指针的宽度和幅度等等)
  3. 你可能有必要写自己的事件监听器,属性的访问和修改函数和一些组件本身的功能上的代码。
  4. 如果你希望组件能够显示什么东西,你很有可能会重载 onMeasure() 函数,因而你就不得不重载 onDraw() 函数。当两个函数都用默认的,那么onDraw() 函数将不会做任何事情,并且默认的 onMeasure() 函数自动的设置了一个100x100 —的尺寸,这个尺寸可能并不是你想要的。
  5. 其他有必要重载的on... 系列函数都需要重新写一次。

onDraw()onMeasure()

onDraw()函数将会传给你一个 Canvas 对象,通过它你可以在二维图形上做任何事情,包括其他的一些标准和通用的组件、文本的格式,任何你可以想到的东西都可以通过它实现。

注意: 这里不包括三维图形如果你想使用三维的图形,你应该把你的父类由View改为SurfaceView类,并且用一个单独的线程。可以参考GLSurfaceViewActivity 的例子。

onMeasure() 函数有点棘手,因为这个函数是体现组件和容器交互的关键部分,onMeasure()应该重载,让它能够有效而准确的表现它所包含部分的测量值。这就有点复杂了,因为我们不但要考虑父类的限制(通过onMeasure()传过来的),同时我们应该知道一旦测量宽度和高度出来后,就要立即调用setMeasuredDimension() 方法。

概括的来讲,执行onMeasure()函数分为一下几个阶段:

  1. 重载的onMeasure()方法会被调用,高度和宽度参数同时也会涉及到(widthMeasureSpec 和heighMeasureSpec两个参数都是整数类型),同时你应该考虑你产品的尺寸限制。这里详细的内容可以参考View.onMeasure(int, int) (这个连接内容详细的解释了整个measurement操作)。
  2. 你的组件要通过onMeasure()计算得到必要的measurement长度和宽度从而来显示你的组件,它应该与规格保持一致,尽管它可以实现一些规格以外的功能(在这个例子里,父类能够选择做什么,包括剪切、滑动、提交异常或者用不同的参数又一次调用onMeasure()函数)。
  3. 一旦高度和宽度计算出来之后,必须调用setMeasuredDimension(int width, int height),否则就会导致异常。

一个自定义组件的例子(A Customized Component Example)

在 API Demos 中的CustomView提供了以一个自定义组件的例子,这个自定义组件在 LabelView 类中定义。

LabelView例子涉及到了自定义组件的方方面面:

  • 首先让自定义组件从View类中派生出来。
  • 编写带参数的构造函数(参数可以来源于XML文件)。这里面的一些处理都已经在View父类中完成,但是任然有些Labelview使用的自定义组件特有的新的参数需要处理。
  • 一些标准的Public函数,例如setText()setTextSize()setTextColor()
  • 重载onMeasure()方法来确定组件的尺寸(注意:在LabelView中是通过一个私有函数measureWidth()来实现的)
  • 重载onDraw()函数把Lable显示在提供的canvas上。

在例子中,你可以通过custom_view_1.xml看到自定义组件LabelView的用法。在XML文件中特别要注意的是android:app:两个参数的混合运用,app:参数表示应用程序中被认为是LabelView组件的个体,这些也会作为资源在R类中定义。

组件混合技术Compound Components (or Compound Controls)

如果你不想创建一个完全自定义的组件,而是由几个现有组件的组合产生的新的组件,那么混合组件技术就更加适合。简单的来说,这样把几个现有的组件融合到一个逻辑组合里面可以封装成一个新的组件。例如,一个Combo Box组件可以看作是是一个EditText和一个带有弹出列表的Button组件的混合体。如果你点击按钮为列表选择一项,

在Android中,其实还有其他的两个View类可以做到类似的效果: SpinnerAutoCompleteTextView,,但是Combo Box作为一个例子更容易让人理解。

下面简单的介绍如何创建组合组件:

  1. 一般从Layout类开始,创建一个Layout类的派生类。也许在Combo box我们会选择水平方向的LinearLayout作为父类。记住,其他的Layout类是可以嵌套到里面的,因此混合组件可以是任何组件的混合。注意,正如Activity一样,你既可以使用外部XML文件来声明你的组件,也可以嵌套在代码中。
  2. 在新的混合组件的构造函数中,首先,调用所有的父类的构造函数,传入对应的参数。然后可以设置你的混合组件的其他的一些方面,在哪创建EditText组件,又在哪创建PopupList组件。注意:你同时也可以在XML文件中引入一些自己的属性和参数,这些属性和参数也可以被你的混合组件所使用。
  3. 你也可以创建时间监听器去监听新组件中View类触发的事件,例如,对List选项单击事件的监听,你必须在此时间发生后更新你EditText的值。
  4. 你可能创建自己的一些属性,带有访问和修改方法。例如,允许设置EditText初始值并且提供访问它的方法。
  5. 在Layout的派生类中,你没有必要去重载onDraw()onMeasure()方法,因为Layout会有比较好的默认处理。但是,如果你觉得有必要你也可以重载它。
  6. 你也可能重载一些on系列函数,例如通过onKeyDown()的重载,你可以通过按某个键去选择列表中的对应的值。

总之,把Layout类作为基类有下面几个优点:

  • 正如activity一样,你也可以通过XML文件去声明你的新组件,或者你也可以在代码中嵌套。
  • onDraw()函数和onMeasure()函数是没有必要重载的,两个函数已经做得很好了。
  • 你可以很快的创建你的混合组件,并且可以像单一组件那样使用。

混合组件的例子(Examples of Compound Controls)

In the API Demos project 在API Demos工程中,有两个List类的例子——Example 4和Example 6,里面的SpeechView组件是从LinearLayout类派生过来,实现显示演讲显示功能,对应的原代码是List4.javaList6.java

调整现有组件(Tweaking an Existing Component)

在某些情况下,你可能有更简单的方法去创建你的组件。如果你应经有了一个非常类似的组件,你所要做的只是简单的从这个组件派生出你的组件,重在其中一些有必要修改的方法。通过完全自定义组件的方法你也可以同样的实现,但通过冲View派生产生新的组件,你可以简单获取一些已经存在的处理机制,这些很可能是你所想要的,而没有必要从头开始。

例如,在SDK中有一个NotePad的例子(NotePad application )。该例子演示了很多Android平台实用的细节,例如你会学到从EditView派生出能够自动换行的记事本。这还不是一个完美的例子,因为相比早期的版本来说,这些API已经感变了很多,但它确实说明了一些问题。

如果你还未查看该程序,现在你就可以在Eclipse中导入记事本例程(或仅通过提供的链接查看相应的源代码)。特别是查看NoteEditor.java 中的MyEditText的定义。

下面有几点要注意的地方:

  1. 声明(The Definition)

    这个类是通过下面一行代码来定义的:

    public static class MyEditText extends EditText

    • 它是定义在NoteEditor activity类里面的,但是它是共有的(public),因此如果有必要,它可以通过NoteEditor.MyEditTextNoteEditor外面来调用。
    • 它是static类(静态类),意味着不会出现所谓的通过父类访问数据的“虚态方法”, 这样就使该类成为一个可以不严重依赖NoteEditor的单独类。对于不需要从外部类访问的内联类的创建,这是一个很清晰地思路,保证所产生的类很小,并且允许它可以被其他的类方便的调用。
    • 它是EditText类的扩展,它是我们选择的用来自定义的父类。当我们完成以后,新的类就可以作为一个普通的EditText来使用。

  2. 类的初始化

    一般来说,父类是首先调用的。进一步来说,这不是一个默认的构造函数,而是一个带参数的构造函数。因为EditText是使用从XML布局文件提取出来的参数进行创建,因此我们的构造函数也要取出参数并且将这些参数传递给父类。

  3. 方法重载

    在本例中,仅对onDraw()一个方法进行重载。但你可以很容易地为你的定制组件重载其他需要的方法。

    对于记事本例子来说,通过重载onDraw()方法我们可以在EidtView的画布(canvas)上绘制蓝色的线条(canvas类是通过重写的onDraw()方法传递)。该函数快要结束时要调用super.onDraw()函数。父类的方法是应该调用,但是在这个例子里面,我们是在我们划好了蓝线之后调用的。

  4. 使用定制组件

    现在,我们已经有自己定制的组件了,但是应该怎样使用它呢?在记事本例子中,定制的组件直接在预定义的布局文件中使用,让我们看一看res/layout目录中的note_editor.xml文件。

    <view xmlns:android="http://schemas.android.com/apk/res/android" 
      class="com.android.notepad.NoteEditor$MyEditText" 
      id="@+id/note"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:background="@android:drawable/empty"
      android:padding="10dip"
      android:scrollbars="vertical"
      android:fadingEdge="vertical" /> 
    • 该自定义组件在XML中是作为一个一般的View类来创建的,并且是通过全路径包来描述的。注意这里内联类是通过NoteEditor$MyEditText来表示的,这是Java编程中引用内联类的标准方法。
    • 在定义中的其他属性和参数将传递给定制组件的构造函数,然后才传到EditText构造函数中,因此这些参数也是你使用EditText组件的参数。注意,这里你也可以增加你自己的参数,我们将在下面讨论这个问题。

这就是你全部需要做的,诚然这是一个简单的例子。但问题的关键是:你的需求有多复杂,那么你的自定义组件就有多么复杂。

一个更为复杂的组件可能需要重载更多的on系列函数,并且还要很多特有的函数来充分实现自定义组件的功能。唯一的限制就是你的想象力和你需要组件去执行什么工作。

现在开始你的组件化之旅吧

如你所见,Android提供了一种精巧而又强大的组件模型,让你尽可能的完成你的工作。从简单的组件调整到组件混合,甚至完全自定义组件,灵活的运用这些技术,你应该可以得到一个完全符合你外观要求的的Android程序

posted @ 2010-03-27 16:42 seedshopezhong 阅读(208) | 评论 (0)编辑 收藏
仅列出标题
共2页: 1 2