您的位置:首页 > 移动开发 > Android开发

Android Drawable绘图学习笔记

2010-06-03 15:03 615 查看
Android Drawable绘图学习笔记

http://fei2008sky.blog.com/category/1/

如何获取 res 中的资源
数据包package:android.content.res
主要类:Resources
Android SDK中的简介:Class for accessing an application’s resources.Class for accessing an application’s resources. This sits on top of the asset manager of the application (accessible through getAssets()) and provides a higher-level API for getting typed data from the assets.
其主要接口按照功能,划分为以下三部分:

getXXXX()

例如:

int getColor(int id)

Drawable getDrawable(int id)

String getString(int id)  直接获取res中存放的资源

InputStream openRawResource(int id)  获取资源的数据流,读取资源数据

void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)  从XML文件中获取数据

Resource为每种资源提供了相应的接口来获取这种资源,除了可以直接获取资源外,还额外提供了以数据流的方式获取资源,这在以后的应用程序开发中会经常使用,那么如何获取Resources了,如下:Resources r = this.getContext().getResources();

如何获取资源中的画图对象
数据包package:android.graphics.drawable
主要类:Drawable
Android SDK中的简介:A Drawable is a general abstraction for “something that can be drawn.” Most often you will deal with Drawable as the type of resource retrieved for drawing things to the screen; the Drawable class provides a generic API for dealing with an underlying visual resource that may take a variety of forms.
看了以上简介,发现Drawable是个virtual class,具体如何画图,需要具体分析Drawable的子类,例如:BitmapDrawable
Android SDK中的简介:A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a BitmapDrawable from a file path, an input stream, through XML inflation, or from a Bitmap object. It can be defined in an XML file with the
<bitmap>
 element.
其主要接口如下:

BitmapDrawable()

BitmapDrawable(Bitmap bitmap)

BitmapDrawable(String filepath)

BitmapDrawable(InputStream is)

void draw(Canvas canvas)

Draw in its bounds (set via setBounds) respecting optional effects such as alpha (set via setAlpha) and color filter (set via setColorFilter).

final Bitmap getBitmap()

final Paint getPaint()

Drawable是个抽象类,在BitmapDrawable中我们就看到位图的具体操作,在仔细看下BitmapDrawable的构造函数,我们就会发现与Resource中的openRawResource()接口是相对应的,就可以通过以下方法来获取位图:
Resources r = this.getContext().getResources();
Inputstream is = r.openRawResource(R.drawable.my_background_image);
BitmapDrawable  bmpDraw = new BitmapDrawable(is);
Bitmap bmp = bmpDraw.getBitmap();

Paint

数据包package:android.graphics

Android SDK中的简介:The Paint class holds the style and color information about how to draw geometries, text and bitmaps. 主要就是定义:画刷的样式,画笔的大小/颜色等。

Typeface
数据包 package:android.graphics
Android SDK中的简介:The Typeface class specifies the typeface and intrinsic style of a font. 主要就是定义:字体。

核心类显示资源
数据包package:android.graphics
主要类:Canvas
Android SDK中的简介:The Canvas class holds the “draw” calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
按照结构的功能,将主要接口分为以下3部分:

boolean clipXXXX() Region区域操作:DIFFERENCE INTERSECT REPLACE REVERSE_DIFFERENCE UNION XOR

void drawXXXX()画图函数

void rotate()  void scale()  void skew() void translate() 画布操作函数

Region在这里需要特殊说明下:Region就是一个区域,也就是画布(Canvas)中的有效区域,在无效区域上draw,对画布没有任何改变。

Drawable类
Drawable是一个通用的抽象类,它的目的是告诉你什么东西是可以画的。你会发现基于Drawable类扩展出各种绘图的类,见下面的表格,当然你可以继承它来创建你自己的绘图类.

Interfaces

AnimatableInterface that drawables suporting animations should implement.
Drawable.CallbackImplement this interface if you want to create an animated drawable that extends 
Drawable
.

Classes

AnimationDrawableAn object used to create frame-by-frame animations, defined by a series of Drawable objects, which can be used as a View object’s background.
BitmapDrawableA Drawable that wraps a bitmap and can be tiled, stretched, or aligned.
ClipDrawableA Drawable that clips another Drawable based on this Drawable’s current level value.
ColorDrawableA specialized Drawable that fills the Canvas with a specified color, with respect to the clip region.
DrawableA Drawable is a general abstraction for “something that can be drawn.” Most often you will deal with Drawable as the type of resource retrieved for drawing things to the screen; the Drawable class provides a generic API for dealing with an underlying visual resource that may take a variety of forms.
Drawable.ConstantState 
DrawableContainer 
DrawableContainer.DrawableContainerState 
GradientDrawableA Drawable with a color gradient for buttons, backgrounds, etc.
InsetDrawableA Drawable that insets another Drawable by a specified distance.
LayerDrawableA Drawable that manages an array of other Drawables.
LevelListDrawableA resource that manages a number of alternate Drawables, each assigned a maximum numerical value.
NinePatchDrawableA resizeable bitmap, with stretchable areas that you define.
PaintDrawableDrawable that draws its bounds in the given paint, with optional rounded corners.
PictureDrawableDrawable subclass that wraps a Picture, allowing the picture to be used whereever a Drawable is supported.
RotateDrawableA Drawable that can rotate another Drawable based on the current level value.

ScaleDrawableA Drawable that changes the size of another Drawable based on its current level value.
ShapeDrawableA Drawable object that draws primitive shapes.
ShapeDrawable.ShaderFactoryBase class defines a factory object that is called each time the drawable is resized (has a new width or height).
StateListDrawableLets you assign a number of graphic images to a single Drawable and swap out the visible item by a string ID value.
TransitionDrawableAn extension of LayerDrawables that is intended to cross-fade between the first and second layer.
 

有三种方法可以定义和实例化一个Drawable:保存一个图片到你工程资源中,使用XML文件来描述Drawable属性或者用一个正常的类去构造。下面我们将讨论两种技术(对一个有开发经验的开发者来说构造并不是最新的技术)。

从资源图像文件中创建
一个比较简单的方法是添加一个图片到你的程序中,然后通过资源文件引用这个文件,支持的文件类型有PNG(首选的) JPG(可接受的)GIF(不建议),显然这种对于显示应用程序的图标跟来说是首选的方法,也可以用来显示LOGO,其余的图片可以用在例如游戏中。

把一个图片资源,添加你的文件到你工程中res/drawable/目录中去,从这里,你就可以引用它到你的代码或你的XML布局中,也就是说,引用它也可以用资源编号,比如你选择一个文件只要去掉后缀就可以了(例如:my_image.png 引用它是就是my_image)。

注意:SDK指出,为了缩小图片的存储空间,在Build的时候又可能对图片进行压缩,如果不想被压缩,可以将图片放在res/raw/目录中。

SDK给出的例子:

LinearLayout mLinearLayout;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Create a LinearLayout in which to add the ImageView
mLinearLayout = new LinearLayout(this);

// Instantiate an ImageView and define its properties
ImageView i = new ImageView(this);
i.setImageResource(R.drawable.my_image);
i.setAdjustViewBounds(true); // set the ImageView bounds to match the Drawable's dimensions
i.setLayoutParams(new Gallery.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

// Add the ImageView to the layout and set the layout as the content view
mLinearLayout.addView(i);
setContentView(mLinearLayout);
}

获取Drawable对象:

Resources res = mContext.getResources();
Drawable myImage = res.getDrawable(R.drawable.my_image);

 

注意:保持每个资源类型的一至,可以保证你项目状态的一致性,就不用担心有许多不同类型的对象来实例化它。例如:如果使用相同的图像资源来实例化两个Drawable对象。然后修改一个Drawables的属性(例如alpha),然后不幸得是这个效果也会出现在另一个对象上去。所以当处理同一个资源的多个实例对象时,不是直接转换为Drawable,而是应该执行tween animation

如何添加资源到ImageView:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tint="#55ff0000"
android:src="@drawable/my_image"/>

 

从XML文件中创建
到如今,你应该比较熟悉按Android的原则去开发一个用户接口,因此,你也应该理解了定义一个XML文件对于对象的作用与灵活的重要性。这个理念无数次用于Drawables.

如果你想创建一个Drawable对象,而这个对象并不依赖于变量或用户的交换,把它定义到XML中去应该是一个不错的方法。即使你期望在你的应用程序中改变其属性来增加用户体验。你应该考虑把对象放入XML中,因为你可以随时修改其属性

当你在你的XML中定义了一个Drawable,保存这个XML文件到你工程目录下res/drawable目录中,然后通过调用Resource.getDrawable()来检索并实例化,传递给它XML文件中的资源ID号。任何Drawable的子类都支持inflate这个方法,这个方法会通过XML来实例化你的程序。任何Drawable都支持XML的扩展来利用特殊的XML属性来帮助定义对象的属性,可以查看任何Drawable子类文档来看如何定义XML文件。

如下定义了一个TransitionDrawable:An extension of LayerDrawables that is intended to cross-fade between the first and second layer. It can be defined in an XML file with the 
<transition>
 element
. Each Drawable in the transition is defined in a nested 
<item>
. 有关TransitionDrawable的详细信息查看http://androidappdocs.appspot.com/reference/android/graphics/drawable/TransitionDrawable.html

将其定义在res/drawable/expand_collapse.xml:

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/pic1"/>
<item android:drawable="@drawable/pic2"/>
</transition>

下面实例化并处理:

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Resources res=getResources();
TransitionDrawable trans=(TransitionDrawable )res.getDrawable(R.drawable.expand_collapse);
ImageView image = (ImageView)findViewById(R.id.ImageView01);
image.setImageDrawable(trans);
trans.startTransition(3000);
}
}

ShapeDrawable
当你想去画一些动态的二维图片,一个ShapeDrawable对象可能会对你有很大的帮助。通过ShapeDrawable,你可以通过编程画出任何你想到的图像与样式。

ShapeDrawable继承了Drawable, 所以你可以调用Drawable里有的函数,比如视图的背景,通过setBackgroundDrawable()设置。当然,你可以在自定义的视图布局中画你的图形,因为ShapeDrawable有自己的draw()方法。你可以在View.OnDraw()方法期间创建一个视图的子类去画ShapeDrawable。

ShapeDrawable类(像很多其他Drawable类型在android.graphics.drawable包)允许你定义drawable公共方法的各种属性。有些属性你可以需要调整,包括透明度,颜色过滤,不透明度,颜色。

例子:

 

public class CustomDrawableView extends View {    private ShapeDrawable mDrawable;

    public CustomDrawableView(Context context) {        super(context);

        int x = 10;        int y = 10;        int width = 300;        int height = 50;

        mDrawable = new ShapeDrawable(new OvalShape());        mDrawable.getPaint().setColor(0xff74AC23);        mDrawable.setBounds(x, y, x + width, y + height);    }

    protected void onDraw(Canvas canvas) {        mDrawable.draw(canvas);    }

显示:

 

CustomDrawableView mCustomDrawableView;

protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    mCustomDrawableView = new CustomDrawableView(this);        setContentView(mCustomDrawableView);}

在XML中定义方法:如果你想用XML文件配置来取代原有布局来画自定义的drawable,于是你自定义的Drawable类必须重载view (Context, AttributeSet) 构造函数。

 

<com.example.shapedrawable.CustomDrawableView    android:layout_width="fill_parent"     android:layout_height="wrap_content"     />

NinePatchDrawable
NinePatchDrawable 绘画的是一个可以伸缩的位图图像,Android会自动调整大小来容纳显示的内容。一个例子就是NinePatch为背景,使用标准的Android按钮,按钮必须伸缩来容纳长度变化的字符

NinePatchDrawable是一个标准的PNG图像,它包括额外的1个像素的边界,你必须保存它后缀为.9.png,并且保持到工程的res/drawable目录中。

这个边界是用来确定图像的可伸缩和静态区域。你可以在左边和上边的线上画一个或多个黑色的1个像素指出可伸缩的部分(你可以需要很多可伸缩部分),它的相对位置在可伸缩部分相同,所以大的部分总是很大的。

你还有可以在图像的右边和下边画一条可选的drawable区域(有效的,内边距线)。如果你的视图对象设置NinePath为背景然后指定特殊的视图字体,它将自行伸缩使所有的文本来适应根据右线与底部线设计好的区域(如果有的话),当然内边距线不包括其中,Android可以使用左边的线与上面的线来定义一个drawable区域。

我们来澄清一下这两条不同的线,左边跟顶部的线来定义哪些图像的像素允许在伸缩时被复制。底部与右边的线用来定义一个相对位置内的图像,视图的内容就放入其中。





使用方法:

 

<Button id="@+id/tiny"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentTop="true"        android:layout_centerInParent="true"        android:text="Tiny"        android:textSize="8sp"        android:background="@drawable/my_button_background"/>

<Button id="@+id/big"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_centerInParent="true"        android:text="Biiiiiiig text!"        android:textSize="30sp"        android:background="@drawable/my_button_background"/>

 

参考:Android SDK   eoeandroid.com     moandroid.com

Android引入第三方jar包的方法

January 6th, 2010 | 1 | No Comments »

在Android平台中可以使用第三方外部JAR库文件,在Eclipse开发环境中,仅仅3步就可以使用第三方.jar文件:
1.在Package Explorer(包浏览器)面板中,右键单击鼠标在弹出的菜单中选择Properties(属性)  
2.选择Java Build Path(Java构建路径),然后选择Libraries标签页.  
3.按下Add External JARs 这个按钮,选择Jar文件即可。  
如果你想包含第三方JARs到你的package包中,创建一个新的目录在你的工程中,选择Add Library… 代替即可,本站提示不用将外部JARs文件放到工程文件夹中就可以正常使用。

在网上查了一下,有人说上面的做法有可能会出错,建议采用下面的步骤:

     右键工程,
     Build path,
    java build path,选择libraries
     在右边的按钮中点击“Add Library”
     选择“User library”,点击“下一步”
     点击“User librarys”按钮
     在出现的界面中点击“New..”按钮
     在弹出的界面中随便起一个名字,点击“确定”
     点击“Add jars”按钮选择第三方jar包,点击“确定”完成操作。
这样的话该jar包会被一起打包到apk中,问题也就解决了!

那么如果将自己的代码封装成一个jar库供其他程序复用呢? 下文给出了导出的具体步骤:(转自:android123.com.cn)

首先创建一个Android工程,确定可以编译通过,移除androidmanifest.xml文件,和相关的程序资源图片,比如res/drawable/icon.png。在Eclipse中我们找到Package Explorer,选择export,如图: 





  接下来在导出对话框中选择Java,JAR file,如下图所示:





  在JAR File Specification这里选择我们需要到处的资源等等,在JAR file:路径选择我们导出后文件的存放位置,在下面的Compress the contents of the JAR file勾选可以压缩生成文件的体积。最后单击Next或Finish即可完成导出,这样可以在以后的工程中导入这个库,起到了代码复用的效果类似Windows 32开发的Dll文件一样。





Android UI事件处理

January 5th, 2010 | 1 | No Comments »

在Android平台上,捕获用户在界面上的触发事件有很多种方法,View类就提供这些方法。你在使用各种View视图来布局界面时,会发现几个公用的回调方法来捕捉有用的UI触发事件,当事件在某个View对象上被触发时,这些方法会被系统框架通过这个对象所调用,例如:当一个View(如一个Button)被点击,onTouchEvent()方法会在该对象上被调用,所以,为了捕获和处理事件,必须去继承某个类,并重载这些方法,以便自己定义具体的处理逻辑,显然,你更容易明白,为什么在你使用View类时会嵌套带有这些回调方法的接口类,这些接口称为event listeners,它是你去获取UI交互事件的工具在你继承View类,以便建立一个自定义组,也许你想继承Button ,  你会更普遍使用事件监听来捕捉用户的互动,在种情况下,你可以使用类的event handlers.来预定义事件的处理方法。

Event Listeners

View类里的event listener是一个带有回调方法的接口,当UI里的组建是被用户触发时,这些方法会被系统框架所调用

onClick()

来自View.OnClickListener 它会被调用当点击这个Item(在触摸模式),或者当光标聚集在这个Item上时按下“确认”键 ,导航键,或者轨迹球。

onLongClick()

来自View.OnLongClickListener.  它会被调用当长按这个Item(在触摸模式),或者当光标聚集在这个Item上时长按 “确认”键 ,导航键,或者轨迹球。

onFocusChange()

来自View.OnFocusChangeListener 它会被调用当光标移到或离开这个Item,

onKey()

来自View.OnKeyListener..它会被调用,当光标移到这个Item,按下和释放一个按键的时候

onTouch()

来自View.OnTouchListener. 它会被调用 ,在这个Item的范围内点触的时候

onCreateContextMenu()

来自View.OnCreateContextMenuListener.  它会被调用, 当上下文菜单被建立时(由于持续的“长按”)  见讨论Creating Menus更多的信息。
这些方法和嵌套接口类都是一一对应的,如果确定其中一种方法处理你的互动事件,你需要在Activity中实现这个带有这个方法的接口,并把它作为匿名类,然后,通过实例的View.set…Listener() 方法来设置监听器(例如,调用setOnClickListener(),来设置OnClickListener做为监听器)

下面是为一个按钮设置监听器的例子:

// Create an anonymous implementation of OnClickListener
private OnClickListener mCorkyListener = new OnClickListener() {
public void onClick(View v) {
// do something when the button is clicked
}
};

protected void onCreate(Bundle savedValues) {
...
// Capture our button from layout
Button button = (Button)findViewById(R.id.corky);
// Register the onClick listener with the implementation above
button.setOnClickListener(mCorkyListener);
...
}

}
下面这个列子我们会发现用Activity去实现OnClickListener接口,并作为它的一部分,会更方便,而不必去加载额外的类和对象

public class ExampleActivity extends Activity implements OnClickListener {
protected void onCreate(Bundle savedValues) {
...
Button button = (Button)findViewById(R.id.corky);
button.setOnClickListener(this);
}

// Implement the OnClickListener callback
public void onClick(View v) {
// do something when the button is clicked
}
...
}

这里注意一下,以上的例子可以看出onClick()是没有返回值的,但是有些事件处理方法是必须带返回值,它取决于的具体的事件,有些那么做的原因,看下面的例子:
onLongClick()
它返回的布尔值表明你已经完成了这个事件的处理,还是应该把它继续传下去。返回true表明已经处理完成并且停止了传递,如果返回为false表明事件还没有完成,或者它还需要继续被传递给其他的监听器
onKey()
它返回的布尔值表明你已经完成了这个事件的处理,还是应该把它继续传下去。返回true表明已经处理完成并且停止了传递,如果返回为false表明事件还没有完成,或者它还需要继续被传递给其他的监听器
onTouch()
它返回的布尔值表明你是否已经完成了这次事件的行动,重要的是后面可能还有很多后续的行动,这样,如果你返回false,表明在接到下一次的后续行动中,你还没有完成之前行为也没有意向去处理随后的行动,因此,在这个事件的后续行动中将不会再被调用。 如fingure手势,或最终行动事件
记住:我们所关注的事件肯定是发生在高亮聚集的焦点,它从总视图(顶级的)被一级一级的向下传递,直到我们想要关注的组件,当焦点聚集在这个视图(或视图中的子视图)时 ,你能够使用dispatchKeyEvent() 作为一种代替方法,来捕获在视图上的按键事件,你还可以使用onKeyDown()和onKeyUp().来捕获所有事件内的交互活动

注意:在 Android 框架中会调用event handlers先处理事件,然后会适当的传递给二级默认的预定义handlers中;因此 如果返回true,将会停止这个事件的传递,View中默认事件处理方法的回调也会被阻止。因此,当你返回true肯定表明你是要终止这个事件的延续。(这个地方有点不懂。。。原文是:Android will call event handlers first and then the appropriate default handlers from the class definition second. As such, returningtrue from these event listeners will stop the propagation of the event to other event listeners and will also block the callback to the default event handler in the View. So be certain that you want to terminate the event when you return true.)

Event Handlers

如果您建立一个继承于View自定义组件,然后您可以定义一些回调方法用作默认的事件处理程序。该文件中关于Building Custom Components,您会学习一些共用的回调方法用于事件处理,其中包括:

onKeyDown(int, KeyEvent)
 – Called when a new key event occurs.
onKeyUp(int, KeyEvent)
 – Called when a key up event occurs.
onTrackballEvent(MotionEvent)
 – Called when a trackball motion event occurs.
onTouchEvent(MotionEvent)
 – Called when a touch screen motion event occurs.
onFocusChanged(boolean, int, Rect)
 – Called when the view gains or loses focus.
还有其他一些方法,这不属于View类,但可以直接影响到你处理事件的方式,所以在布局内管理更复杂的事件可以考虑到这些方法:

Activity.dispatchTouchEvent(MotionEvent)
 – This allows your 
Activity
 to intercept all touch events before they are dispatched to the window.
ViewGroup.onInterceptTouchEvent(MotionEvent)
– This allows a 
ViewGroup
 to watch events as they are dispatched to child Views.
ViewParent.requestDisallowInterceptTouchEvent(boolean)
– Call this upon a parent View to indicate that it should not intercept touch events with
onInterceptTouchEvent(MotionEvent)
.
 

Touch Mode

当用户在使用方向键或轨迹球浏览用户界面时,有必要给于一个焦点在可操作的组件上(如一个Button),使用户可以看到它将接受输入命令。如果设备有触摸功能,那么,当用户与界面的交互就不再需要有一个高亮在组件上,或一个焦点在view上,因此,模式的互动名为”触摸模式”。对于一个触摸设备,一旦有用户接触屏幕时,该设备将进入触摸模式.在点触某个View后,只有的它的方法isFocusableInTouchMode()返回为真时,才会有聚集焦点,如文本编辑工具。其他的界面只可以点触,但不会聚集焦点(高亮),如button 被点触时就不会聚集焦点,当它被按下时只会调用on-click监听器的回调方法。
任何时候用户接触方向键或者滚动轨迹球时,该设备将退出触摸模式,并聚集焦点,用户可以恢复与用户界面的键盘交互,而不必在屏幕上。触摸模式的状态是由整个系统来维持的(all windows and activities),要查询目前所处的状态,你可以调用isInTouchMode()方法来获得,看看设备目前是否处于触摸模式。

Handling Focus

系统框架将处理日常的焦点移动来响应用户的输入,它包刮改变焦点(当界面是被移除,隐藏,或者作为一个新的View变为可用状态),通过isFocusable()这个方法我们可以知道view是否具有接受焦点的资格,也可以通过setFocusable().来设置view接受焦点的资格,对应在触摸模式下,你可以调用isFocusableInTouchMode().来获知是否有焦点来响应点触,也可以通过setFocusableInTouchMode().来设置是否有焦点来响应点触的资格.

系统框架控制焦点移动到另一个组件的算法是在某一方向上邻近的组件,在极个别情况下,默认的算法可能不符合开发者的预想要求,在这种情况下,你可以覆写下列XML属性的布局文件: nextFocusDown , nextFocusLeft , nextFocusRight ,和nextFocusUp 设置他们的值来明确
焦点从当前界面移动下个界面的Id。例如:

<LinearLayout
android:orientation="vertical"
... >
<Button android:id="@+id/top"
android:nextFocusUp="@+id/bottom"
... />
<Button android:id="@+id/bottom"
android:nextFocusDown="@+id/top"
... />
</LinearLayout>

一般来说,在这个垂直布局,浏览的焦点会从第一个按钮开始,不会是从第二个或者其他的,现在topButtont已经通过nextFocusUp (反之亦然)确定了bottom.

通常如果你想宣布用户界面具有焦点的资格 (如果这个界面在传统上是没有的),可以在xml布局里去加上的android:focusable的属性,并设置它的值,您也可以宣布在触摸模式下具有焦点的资格,同样也只在xml里添android:focusableInTouchMode.的属性,并设置它的值. 当用户请求在某个界面聚集焦点时,会调用requestFocus().这个方法。监听到焦点活动(获得焦点或失去焦点都会被通知),会调用onFocusChange(),这个方法,这也是上节所讨论的Event Listeners。

总结:

对于UI控件事件的处理,需要设定相应的监听器,并实现相应的事件处理程序。这儿有两种实现方法:一是定义一个OnClickListener 类的实例,并使用setOnClickListener等绑定监听器;二是[b]用Activity去实现OnClickListener接口,并作为它的一部分,这样会更方便,省去了加载额外的类和对象的时间。[/b]

对于支持触摸屏的手机,可以设定触摸模式的UI,可以使用[b]isInTouchMode()来获得触摸模式的状态。[/b]

UI处理的另一个重点是焦点的设定及其切换。焦点设置:通过[b]setFocusable或者setFocusableInTouchMode设置可以接受焦点,通过isFocusableisFocusableInTouchMode获取是否可以接受焦点;焦点切换:编写XML布局文件的nextFocusDown 等属性设置。[/b]

参考:http://developer.android.com/guide/topics/ui/ui-events.html

          eoeAndroid社区组

 

Android如何绘制视图

January 3rd, 2010 | 1 | No Comments »

当一个活动接收到焦点时,它将被要求绘制它的布局。Android框架将处理这个绘画的过程,但是活动必须提供它的布局层次的根节点。

绘画从布局的根节点开始。它被要求来测量和绘制布局树。绘画通过遍历布局树并渲染每个和失效区域相交的视图来处理。相应的,每个视图组负责请求绘制它的子视图(通过draw() 方法)而每个视图负责画它自己因为这个树是顺序遍历的,这意味着先画父节点(也就是在屏幕后面),然后按照树中出现的顺序画其同层次节点。

框架将不会画不在失效区域的视图,而且还将会帮你画视图背景。

你可以强制一个视图被重画,通过调用invalidate()。

绘画布局共有两步:一个度量过程和一个布局过程。度量过程在measure(int, int)里实现且是一个自顶向下的视图树遍历。每个视图在递归时往下推送尺寸规格。在度量过程的最后,每个视图都已经保存了自己的度量。第二个过程发生在layout(int, int, int, int) 中并且也是自顶向下。在这个过程中,每个父节点负责定位它的所有子节点,通过使用在度量过程中计算得到的尺寸。

当一个视图的measure()方法返回时,它的getMeasuredWidth()和getMeasuredHeight() 值必须被设置,以及所有这个视图子节点的值。一个视图的度量的宽度和高度值必须符合父视图引入的限制。这确保在度量过程之后,所有父节点接受所有它们的子节点的度量值。一个父视图可能会在其子视图上多次调用measure()方法。比如,父视图可能会通过未指定的尺寸调用measure来发现它们的大小,然后使用实际数值再次调用measure(),如果所有子视图未做限制的尺寸总合过大或过小(也即是,如果子视图之间不能对各自占据的空间达成共识的话,父视图将会干预并设置第二个过程的规则)。

要开始一个布局,可调用requestLayout()。这个方法通常在视图认为它自己不再适合它当前的边界的情况下被调用。

度量过程使用两个类来交流尺寸。View.MeasureSpec类被视图用来告诉它们的父视图它们想如何被度量和定位。基础的LayoutParams类仅仅描述了视图想有多大(高和宽)。对于每个维度,它可以指定下面之一:

·         一个准确的数值

·         FILL_PARENT, 这意味着视图想和父视图一样大(减掉填充padding)。

·         WRAP_CONTENT, 这意味着视图只想有刚好包装其内容那么大(加上填充)

对于不同的ViewGroup子类,有相应的LayoutParams子类。比如,相对布局RelativeLayout有它自己的LayoutParams子类,这包含了能够让子视图横向和竖向居中显示的能力。

度量规格(MeasureSpecs)被用来沿着树从父到子的下传度量需求。一个MeasureSpecs可以是下面三种模式之一:

·         UNSPECIFIED: 这被父视图用来决定其子视图期望的尺寸。比如,一个线性布局可能在它的子视图上调用measure() on its child,通过设置其高度为UNSPECIFIED 以及一个宽度为EXACTLY 240,来找出这个子视图在给定240像素宽度的情况下需要显示多高。

·         EXACTLY: 这被父视图用来给子视图强加一个准确的尺寸。子视图必须使用这个大小,并确保其所有的后代将适合这个尺寸。

·         AT_MOST: 这被父视图用来给子视图强加一个最大尺寸。子视图必须确保它自己以及所有的后代都适合这个尺寸。

参考:SDK文档http://developer.android.com/guide/topics/ui/how-android-draws.html

Android风格与主题

January 2nd, 2010 | 1 | No Comments »

Android xml风格和主题文件的编写,是涉及到整个程序界面美观的因素之一。较好的应用风格和主题,可以实现美观而统一的界面,这就犹如Web开发中的CSS。

Styles和Themes都是资源,存放在res/values文件夹下。

什么是Style,什么是Theme?

Style:是一个包含一种或者多种格式化属性的集合,我们可以将其用为一个单位用在布局XML单个元素当中。比如,我们可以定义一种风格来定义文本的字号大小和颜色,然后将其用在View元素的一个特定的实例。

Theme:是一个包含一种或者多种格式化属性的集合,我们可以将其为一个单位用在应用中所有的Activity当中或者应用中的某个Activity当 中。比如,我们可以定义一个Theme,它为window frame和panel 的前景和背景定义了一组颜色,并为菜单定义可文字的大小和颜色属性,可以将这个Theme应用在你程序当中所有的Activity里。

Style和Theme的XML文件结构

对每一个Styles和Themes,给<style>元素增加一个全局唯一的名字,也可以选择增加一个父类属性。在后边我们可以用这个名字来应用风格,而父类属性标识了当前风格是继承于哪个风格。在<style>元素内部,申明一个或者多个<item>,每一个<item>定义了一个名字属性,并且在元素内部定义了这个风格的值。

风格

1.在res/values 目录下新建一个名叫style.xml的文件。

2.对每一个风格和主题,给<style>element增加一个全局唯一的名字,也可以选择增加一个父类属性。在后边我们可以用这个名字来应用风格,而父类属性标识了当前风格是继承于哪个风格。

3.在<style>元素内部,申明一个或者多个<item>,每一个<item>定义了一个名字属性,并且在元素内部定义了这个风格的值。

4.你可以应用在其他XML定义的资源。
下面SDK提供的Style的例子:(SDK提供的程序会有style/Text找不到的错误,目前还不知道怎么解决)

<?xml version=”1.0″ encoding=”utf-8″?>
<resources>
   <style name=”SpecialText” parent=”@style/Text”>
   <item name=”android:textSize”>18sp</item>
   <item name=”android:textColor”>#008</item>
</style>
</resources>

上面的样式可以用在单个view中如:

<EditText id=”@+id/text1″
style=”@style/mytext
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”Hello, World!” />

现在这个EditText组件的所表现出来的风格就为我们在上边的XML文件中所定义的那样。

编写一个简单的Style:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="SpecialText" >
<item name="android:textSize">18sp</item>
<item name="android:textColor">#EC9237</item>
</style>
<style name="SpecialText2" >
<item name="android:textSize">26sp</item>
<item name="android:textColor">#FF7F7C</item>
<item name="android:fromAlpha">0.0</item>
<item name="android:toAlpha">0.0</item>
</style>
</resources>

应用的编写:

<TextView
style="@style/SpecialText2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<EditText android:text="@+id/EditText01"
style="@style/SpecialText"
android:id="@+id/EditText01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></EditText>

最终实现的界面为:





当然,Android也有很多预定义的主题:





主题

就像Style一样,Theme依然在<style>元素里边申明,也是以同样的方式引用。不同的是通过在Android Manifest中定义的<application>和<activity>元素将主题添加到整个程序或者某个 Activity,但是主题是不能应用在某一个单独的View里。

下边是SDK中主题的一个例子:(SDK提供的程序会有Error:no resource found that matches the given name: panelForegroundColor等错误,目前还不知道怎么解决这些错误)

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CustomTheme">
<item name="android:windowNoTitle">true</item>
<item name="windowFrame">@drawable/screen_frame</item>
<item name="windowBackground">@drawable/screen_background_white</item>
<item name="panelForegroundColor">#FF000000</item>
<item name="panelBackgroundColor">#FFFFFFFF</item>
<item name="panelTextColor">?panelForegroundColor</item>
<item name="panelTextSize">14</item>
<item name="menuItemTextColor">?panelTextColor</item>
<item name="menuItemTextSize">?panelTextSize</item>
</style>
</resources>


注意我们用了@符号和?符号来应用资源。@符号表明了我们应用的资源是前边定义过的(或者在前一个项目中或者在Android 框架中)。问号?表明了我们引用的资源的值在当前的主题当中定义过。通过引用在<item>里边定义的名字可以做到(panelTextColor 用的颜色和panelForegroundColor中定义的一样)。这中技巧只能用在XML资源当中

在程序中使用主题的方法:

protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setTheme(android.R.style.Theme_Light);    
      setContentView(R.layout.linear_layout_3);
}

在mainfest.xml中应用主题:
为了在成用当中所有的Activity当中使用主题,你可以打开AndroidManifest.xml 文件,编辑<application>标签,让其包含android:theme属性,值是一个主题的名字,如下:
<application android:theme=”@style/CustomTheme”>
如果你只是想让你程序当中的某个Activity拥有这个主题,那么你可以修改<activity>标签。

编写的简单的一个Theme:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CustomTheme" parent="android:Theme.Black">
<item name="android:windowNoTitle">true</item>
<item name="android:testSize">14sp</item>
<item name="android:textColor">#FFFF0000</item>
</style>
</resources>

Android中提供了几种内置的资源,有好几种主题你可以切换而不用自己写。比如你可以用对话框主题来让你的Activity看起来像一个对话框。在manifest中定义如下:<activity android:theme=”@android:style/Theme.Dialog”>如果你喜欢一个主题,但是想做一些轻微的改变,你只需要将这个主题添加为父主题。比如我们修改Theme.Dialog主题。我们来继承Theme.Dialog来生成一个新的主题。<style name=”CustomDialogTheme” parent=”@android:style/Theme.Dialog”>继承了Theme.Dialog后,我们可以按照我们的要求来调整主题。我们可以修改在Theme.Dialog中定义的每个item元素的值,然后我们在Android Manifest 文件中使用CustomDialogTheme 而不是 Theme.Dialog 。

一下列出了其他预定义的主题:





Android调试工具及方法

January 1st, 2010 | 1 | No Comments »

Logcat

Dump一份系统消息的日志。这些消息包括模拟器抛出错误时的堆栈跟踪。

Android Log

一个记录日志的类,用来将消息写入模拟器上的日志文件中。如果你在DDMS上运行logcat的话你可以就实时查看消息。在你的代码中加入几个写日志方法的调用。
为了使用Log类,你只需要调用Log.v()(详细),Log.d()(debug),Log.i()(information),Log.w()(warning) 或者 Log.e()(error),根据你想获得的日志信息来选择相应的方法

Log.i("MyActivity", "MyClass.getView() — Requesting item number " + position)
你可以用logcat来读取这些信息

Traceview

Android可以保存一个日志用来记录被调用的方法以及该方法被调用的次数,通过Traceview你可以在一个图形化的界面中查看这个日志文件。

adb

Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器的状态。

发出Android命令: 你可以在你的开发机上的命令行或脚本上发布Android命令,使用方法:

adb [-d|-e|-s <serialNumber>] <command>

进入Shell:adb shell

查询模拟器/设备实例
adb devices 列出模拟器的序列号和状态

给特定的模拟器/设备实例发送命令
如果有多个模拟器/设备实例在运行,在发布adb命令时需要指定一个目标实例。 这样做,请使用-s 选项的命令。在使用的-s 选项是
adb -s <serialNumber> <command>
如:db -s emulator-5556 install helloWorld.apk

安装软件:
adb install <path_to_apk>

从模拟器/设备中拷入或拷出文件
可以使用adbpull ,push 命令将文件复制到一个模拟器/设备实例的数据文件或是从数据文件中复制。install 命令只将一个.apk文件复制到一个特定的位置,与其不同的是,pull 和 push 命令可令你复制任意的目录和文件到一个模拟器/设备实例的任何位置。
从模拟器或者设备中复制文件或目录,使用(如下命):
adb pull <remote> <local>
将文件或目录复制到模拟器或者设备,使用(如下命令)
adb push <local> <remote>
在这些命令中, <local> 和<remote> 分别指通向自己的发展机(本地)和模拟器/设备实例(远程)上的目标文件/目录的路径
下面是一个例子::
adb push foo.txt /sdcard/foo.txt

查询日志 adb logcat

Stopping the adb Server
在某些情况下,你可能需要终止Android 调试系统的运行,然后再重新启动它。 例如,如果Android 调试系统不响应命令,你可以先终止服务器然后再重启,这样就可能解决这个问题.
用kill-server 可以终止adb server。你可以用adb发出start-server命令来重新启动服务器.

设备上调试和测试的设置

Android提供了众多的设置使你可以更容易的调试和测试程序。要进入开发设置页面,在模拟器中转到Dev Tools > Development Settings。在该设置页面有以下选项:

Debug app:选择要调试的程序。你不需要设定其关联至调试器,但是设定这个值有两个效果:
在调试的时候,如果你在一个断点处暂停了过长的时间,这个设定会防止Android抛出一个错误

这个设定使你可以选择“等待调试器”选项,使程序只有在调试器关联上之后才启动

Wait for Debugger:阻塞所选的程序的加载直到有调试器关联上,这样你就可以在onCreate()中设置断点,这对于调试一个Activity的启动进程是非常重要的。当你对该选项进行了更改,任何正在运行的程序的实例都会被终止。你只有在上面的选项中选择了一个调试程序才能够选中该选项。你也可以在代码中添加waitForDebugger()来实现同样的功能。

Immediately destroy activities:告诉系统一旦一个activity停止了就销毁该activity(例如当Android释放内存的时候)。这对于测试代码onFreeze(Bundle)/onCreate(android.os.Bundle)是非常有用的,否则会比较困难。如果你的程序没有保存状态,那么选择这个选项很可能会引发很多问题。

Show screen updates:对于任何正在被重绘的screen sections都会在其上闪现一个粉红色的矩形。这对于发现不必要的screen绘制是很有必要的。

Show CPU usage:在屏幕上方显示CPU信息,显示有多少CPU资源正在被使用。上方红色条显示总的CPU使用率,它下方绿色的条显示CPU用在compositing the screen上的时间。注意:在没有重启模拟器之前,一旦你开启了该功能就不能关闭。

Show screen FPS:显示当前的帧率。这对于查看游戏达到的总的帧率是非常有用的。注意:在没有重启模拟器之前,一旦你开启了该功能就不能关闭。

Show background:当没有activity screens可见时,显示一个背景模式。一般是不会出现的,仅仅在Debug的时候会出现。

设定的选项在模拟器重启之后仍然有效,如果要取消设定的选项,在取消设定以后还要重启模拟器,才能生效。

重要的调试小提示

快速的堆栈dump

要在模拟器上获得一个堆栈dump,你可以通过adb shell登入,用“ps”找到你想要的进程,然后“kill -3”,

堆栈跟踪信息就会记录到日志文件中了。

在模拟器屏幕上显示有用信息

设备上可以显示诸如CPU利用率或者对重绘区域的边缘高亮显示等有用信息,在开发设置窗口可以打开或者关闭这些功能。

从模拟器上获取系统状态信息(dumpstate)

你可以通过Dalvik Debug Monitor Service工具来获得dumpstate信息。

从模拟器上获取程序状态信息(dumpsys)

你可以通过Dalvik Debug Monitor Service工具来获得dumpsys信息。

获取无线连接信息

你可以通过Dalvik Debug Monitor Service工具来获得无线连接信息。在Device菜单,选择“Dump radio state”

日志记录跟踪数据

你可以在一个activity中通过调用android.os.Debug.startMethodTracing()来用日志来记录方法调用和其他跟踪数据。

日志记录Radio Data

默认情况下,radio信息是不会记录在系统中的(因为数据量巨大)。然而,你可以通过下面的命令来开启radio记录
adb shell
logcat -b radio

运行adb

Andoid中自带了一个叫adb的工具,该工具功能强大,可以移动并同步文件到模拟器,转发端口。在模拟器上运行一个UNIX shell。

从模拟器上获取屏幕截图

Dalvik Debug Monitor Server (DDMS)可以从模拟器上获取屏幕截图

利用调试帮助类

     Android为了开发者的方便提供了诸如util.LogDebug等帮助类

命令行运行程序的方法

参考:http://www.javaeye.com/topic/201856  http://www.javaeye.com/topic/149227

进入shell 

引用
C:/Documents and Settings/mawenjian>adb shell
看一下命令的帮助 

引用
# am -help 
am -help 
Error: Unknown command: -help 
usage: am [start|instrument] 
       am start [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>] 
                [-c <CATEGORY> [-c <CATEGORY>] …] 
                [-e <EXTRA_KEY> <EXTRA_VALUE> [-e <EXTRA_KEY> <EXTRA_VALUE> ...] 
                [-n <COMPONENT>] [-D] [<URI>] 
       am instrument [-e <ARG_NAME> <ARG_VALUE>] [-p <PROF_FILE>] 
                [-w] <COMPONENT>
好了,试一下启动浏览器 

引用
# am start -n com.google.android.browser/com.google.android.browser.BrowserActivity 
am start -n com.google.android.browser/com.google.android.browser.BrowserActivity 
Starting: Intent { comp={com.google.android.browser/com.google.android.browser.BrowserActivity} }
ok,成功了 
大家试试下面的命令吧: 

引用
# am start -a android.intent.action.VIEW -d http://mwjian.javaeye.com  am start -a android.intent.action.VIEW -d http://mwjian.javaeye.com  Starting: Intent { action=android.intent.action.VIEW data=http://mwjian.javaeye.com } 
 

# am start -a android.intent.action.CALL -d tel:88888888 
am start -a android.intent.action.CALL -d tel:88888888 
Starting: Intent { action=android.intent.action.CALL data=tel:88888888 } 

# am start -a android.intent.action.ALL_APPS 
am start -a android.intent.action.ALL_APPS 
Starting: Intent { action=android.intent.action.ALL_APPS } 

# am start -a android.intent.action.VIEW geo:0,0?q=shanghai 
am start -a android.intent.action.VIEW geo:0,0?q=shanghai 
Starting: Intent { action=android.intent.action.VIEW data=geo:0,0?q=shanghai }

 

本文参考:Android手册 www.cnblogs.com/jacktu 万能的G.cn

Android生命周期

January 1st, 2010 | 1 | No Comments »

 

在 Android 中,多数情况下每个程序都是在各自独立的 Linux 进程中运行的。当一个程序或其某些部分被请求时,它的进程就“出生”了;当这个程序没有必要再运行下去且系统需要回收这个进程的内存用于其他程序时,这个 进程就“死亡”了。可以看出,Android 程序的生命周期是由系统控制而非程序自身直接控制。这和我们编写桌面应用程序时的思维有一些不同,一个桌面应用程序的进程也是在其他进程或用户请求时被创 建,但是往往是在程序自身收到关闭请求后执行一个特定的动作(比如从 main 函数中 return)而导致进程结束的。要想做好某种类型的程序或者某种平台下的程序的开发,最关键的就是要弄清楚这种类型的程序或整个平台下的程序的一般工作 模式并熟记在心。在 Android 中,程序的生命周期控制就是属于这个范畴——我的个人理解:)

在 Android 系统中,当某个 activity调用 startActivity(myIntent) 时,系统会在所有已经安装的程序中寻找其 intent filter 和 myIntent 最匹配的一个 activity,启动这个进程,并把这个 intent 通知给这个 activity。这就是一个程序的“生”。比如我们在 Home application 中选择 “Web browser”,系统会根据这个 intent 找到并启动 Web browser 程序,显示 Web browser 的一个 activity 供我们浏览网页(这个启动过程有点类似我们在在个人电脑上双击桌面上的一个图标,启动某个应用程序)。在 Android 中,所有的应用程序“生来就是平等的”,所以不光 Android 的核心程序甚至第三方程序也可以发出一个 intent 来启动另外一个程序中的一个 activity。Android 的这种设计非常有利于“程序部件”的重用。

  一个 Android 程序的进程是何时被系统结束的呢?通俗地说,一个即将被系统关闭的程序是系统在内存不足(low memory)时,根据“重要性层次”选出来的“牺牲品”。一个进程的重要性是根据其中运行的部件和部件的状态决定的。各种进程按照重要性从高到低排列如 下:
  1. 前台进程。这样的进程拥有一个在屏幕上显示并和用户交互的 activity 或者它的一个IntentReciver 正在运行。这样的程序重要性最高,只有在系统内存非常低,万不得已时才会被结束。
  2. 可见进程。在屏幕上显示,但是不在前台的程序。比如一个前台进程以对话框的形式显示在该进程前面。这样的进程也很重要,它们只有在系统没有足够内存运行所有前台进程时,才会被结束。
  3. 服务进程。这样的进程在后台持续运行,比如后台音乐播放、后台数据上传下载等。这样的进程对用户来说一般很有用,所以只有当系统没有足够内存来维持所有的前台和可见进程时,才会被结束。
  4. 后台进程。这样的程序拥有一个用户不可见的 activity。这样的程序在系统内存不足时,按照 LRU 的顺序被结束。
  5. 空进程。这样的进程不包含任何活动的程序部件。系统可能随时关闭这类进程。
 

从某种意义上讲,垃圾收集机制把程序员从“内存管理噩梦”中解放出来,而 Android 的进程生命周期管理机制把用户从“任务管理噩梦”中解放出来。我见过一些 Nokia S60 用户和 Windows Mobile 用户要么因为长期不关闭多余的应用程序而导致系统变慢,要么因为不时查看应用程序列表而影响使用体验。Android 使用 Java 作为应用程序 API,并且结合其独特的生命周期管理机制同时为开发者和使用者提供最大程度的便利。

Activity lifecycle

Activity有三种基本状态:

Active:处于屏幕前景(当前task的栈顶Activity处于Active状态),同一时刻只能有一个Activity处于Active状态;

Paused状态:处于背景画面画面状态,失去了焦点,但依然是活动状态;

stopped:不可见,但依然保持所有的状态和内存信息。

可以调用finish()结束处理Paused或者stopped状态的Activity。

各种状态之间通过下列的函数调用转换:

void onCreate(Bundle savedInstanceState)

void onStart()

void onRestart()

void onResume()

void onPause()

void onStop()

void onDestroy()


Activity的生命周期可以分为三组:

The entire lifetime of an activity happens between the first call to
onCreate()
 through to a single final call to 
onDestroy()
.

The visible lifetime of an activity happens between a call to
onStart()
 until a corresponding call to 
onStop()
.

The foreground lifetime of an activity happens between a call to
onResume()
 until a corresponding call to
onPause()
.





 

保存Activity状态

To capture that state before the activity is killed, you can implement an
onSaveInstanceState()
 method for the activity. Android calls this method before making the activity vulnerable to being destroyed — that is, before
onPause()
 is called. It passes the method a 
Bundle
 object where you can record the dynamic state of the activity as name-value pairs. When the activity is again started, the Bundle is passed both to 
onCreate()
 and to a method that’s called after 
onStart()
,
onRestoreInstanceState()
, so that either or both of them can recreate the captured state.

Unlike 
onPause()
 and the other methods discussed earlier,
onSaveInstanceState()
 and 
onRestoreInstanceState()
are not lifecycle methods. They are not always called. Because
onSaveInstanceState()
 is not always called, you should use it only to record the transient state of the activity, not to store persistent data.
 Use
onPause()
 for that purpose instead.


启动另一个Activity的过程

The current activity’s 
onPause()
 method is called.
Next, the starting activity’s 
onCreate()
onStart()
, and 
onResume()
methods are called in sequence.
Then, if the starting activity is no longer visible on screen, its 
onStop()
 method is called.

service生命周期

A service can be used in two ways:

It can be started and allowed to run until someone stops it or it stops itself. In this mode, it’s started by calling
Context.startService()
 and stopped by calling 
Context.stopService()
. It can stop itself by calling
Service.stopSelf()
 or
Service.stopSelfResult()
. Only one 
stopService()
 call is needed to stop the service, no matter how many times 
startService()
was called.

It can be operated programmatically using an interface that it defines and exports. Clients establish a connection to the Service object and use that connection to call into the service. The connection is established by calling
Context.bindService()
, and is closed by calling
Context.unbindService()
. Multiple clients can bind to the same service. If the service has not already been launched, 
bindService()
 can optionally launch it.

相关的方法:

void onCreate()

void onStart(Intent intent)

void onDestroy()


The 
onCreate()
 and 
onDestroy()
 methods are called for all services, whether they’re started by
Context.startService()
 or
Context.bindService()
. However, 
onStart()
 is called only for services started by
startService()
.

If a service permits others to bind to it, there are additional callback methods for it to implement:

IBinder onBind(Intent intent)

boolean onUnbind(Intent intent)

void onRebind(Intent intent)






 

Broadcast receiver lifecycle

只有一个方法:void onReceive(Context curContext, Intent broadcastMsg)

A process with an active broadcast receiver is protected from being killed. But a process with only inactive components can be killed by the system at any time, when the memory it consumes is needed by other processes.

This presents a problem when the response to a broadcast message is time consuming and, therefore, something that should be done in a separate thread, away from the main thread where other components of the user interface run. If
onReceive()
 spawns the thread and then returns, the entire process, including the new thread, is judged to be inactive (unless other application components are active in the process), putting it in jeopardy of being killed. The solution to this problem is for 
onReceive()
 to start a service and let the service do the job, so the system knows that there is still active work being done in the process.

进程的生命周期

Android根据其重要性在内存不足的时候移去重要性最低的进程。重要性由高到低为:

前台进程

可见进程

服务进程

后台进程

空进程

注意:Because a process running a service is ranked higher than one with background activities, an activity that initiates a long-running operation might do well to start a service for that operation, rather than simply spawn a thread — particularly if the operation will likely outlast the activity. 比如播放MP3的时候就要启动一个service。

Android进程与线程

January 1st, 2010 | 1 | No Comments »

当某个组件第一次运行的时候,Android启动了一个进程。默认的,所有的组件和程序运行在这个进程和线程中。

也可以安排组件在其他的进程或者线程中运行

进程

组件运行的进程由manifest file控制。组件的节点 — <activity>, <service>, <receiver>, 和 <provider> — 都包含一个 process 属性。这个属性可以设置组件运行的进程:可以配置组件在一个独立进程运行,或者多个组件在同一个进程运行。甚至可以多个程序在一个进程中运行——如果这些程序共享一个User ID并给定同样的权限。<application> 节点也包含 process 属性,用来设置程序中所有组件的默认进程。

所有的组件在此进程的主线程中实例化,系统对这些组件的调用从主线程中分离。并非每个对象都会从主线程中分离。一般来说,响应例如View.onKeyDown()用户操作的方法和通知的方法也在主线程中运行。这就表示,组件被系统调用的时候不应该长时间运行或者阻塞操作(如网络操作或者计算大量数据),因为这样会阻塞进程中的其他组件。可以把这类操作从主线程中分离。

当更加常用的进程无法获取足够内存,Android可能会关闭不常用的进程。下次启动程序的时候会重新启动进程。

当决定哪个进程需要被关闭的时候, Android会考虑哪个对用户更加有用。如Android会倾向于关闭一个长期不显示在界面的进程来支持一个经常显示在界面的进程。是否关闭一个进程决定于组件在进程中的状态,参见后面的章节Component Lifecycles.

线程

即使为组件分配了不同的进程,有时候也需要再分配线程。比如用户界面需要很快对用户进行响应,因此某些费时的操作,如网络连接、下载或者非常占用服务器时间的操作应该放到其他线程。

线程通过java的标准对象Thread 创建. Android 提供了很多方便的管理线程的方法:— Looper 在线程中运行一个消息循环; Handler 传递一个消息; HandlerThread 创建一个带有消息循环的线程。

远程调用Remote procedure calls

Android有一个远程调用(RPCs) 的轻量级机制— 通过这个机制,方法可以在本地调用,在远程执行(在其他进程执行),还可以返回一个值。要实现这个需求,必须分解方法调用,并且所有要传递的数据必须是操作系统可以访问的级别。从本地的进程和内存地址传送到远程的进程和内存地址并在远程处理和返回。返回值必须向相反的方向传递。Android提供了做以上操作的代码,所以开发者可以专注于实现RPC的接口。

一个RPC接口只能包含方法。所有的方法都是同步执行的(直到远程方法返回,本地方法才结束阻塞),没有返回值的时候也是如此。

简单来说,这个机制是这样的:使用IDL (interface definition language)定义你想要实现的接口, aidl 工具可以生成用于java的接口定义,本地和远程都要使用这个定义。它包含2个类,见下图:





inner类包含了所有的管理远程程序(符合IDL描述的接口)所需要的代码。所有的inner类实现了IBinder 接口.其中一个在本地使用,可以不管它的代码;另外一个叫做Stub继承了 Binder 类。为了实现远程调用,这个类包含RPC接口。开发者可以继承Stub类来实现需要的方法

一般来说,远程进程会被一个service管理(因为service可以通知操作系统这个进程的信息并和其他进程通信),它也会包含aidl 工具产生的接口文件,Stub类实现了远处那个方法。服务的客户端只需要aidl 工具产生的接口文件。

以下是如何连接服务和客户端调用:

·服务的客户端(本地)会实现onServiceConnected() 和onServiceDisconnected() 方法,这样,当客户端连接或者断开连接的时候可以获取到通知。通过 bindService() 获取到服务的连接。

· 服务的 onBind() 方法中可以接收或者拒绝连接,取决它收到的intent (intent通过 bindService()方法连接到服务). 如果服务接收了连接,会返回一个Stub类的实例.

· 如果服务接受了连接,Android会调用客户端的onServiceConnected() 方法,并传递一个Ibinder对象(系统管理的Stub类的代理),通过这个代理,客户端可以连接远程的服务。

以上的描述省略很多RPC的机制。请参见Designing a Remote Interface Using AIDL 和 IBinder 类。

线程安全的方法

在某些情况下,方法可能调用不止一个的线程,因此需要注意方法的线程安全。

对于可以远程调用的方法,也要注意这点。当一个调用在Ibinder对象中的方法的程序启动了和Ibinder对象相同的进程,方法就在Ibinder的进程中执行。但是,如果调用者发起另外一个进程,方法在另外一个线程中运行,这个线程在和IBinder对象在一个线程池中;它不会在进程的主线程中运行。例如,一个service从主线程被调用onBind() 方法,onBind() 返回的对象(如实现了RPC的Stub子类)中的方法会被从线程池中调用。因为一个服务可能有多个客户端请求,不止一个线程池会在同一时间调用IBinder的方法。因此IBinder必须线程安全。

简单来说,一个content provider 可以接收其他进程的数据请求。即使ContentResolver和ContentProvider类没有隐藏了管理交互的细节,ContentProvider中响应这些请求的方法(query(), insert(), delete(), update(), and getType() )— 是在content provider的线程池中被调用的,而不是ContentProvider的本身进程。因为这些方法可能是同时从很多线程池运行的,所以这些方法必须要线程安全。

Android基础知识

December 30th, 2009 | 1 | No Comments »

Android特性:

Application framework enabling reuse and replacement of components

Dalvik virtual machine optimized for mobile devices

Integrated browser based on the open source WebKit engine

Optimized graphics powered by a custom 2D graphics library; 3D graphics based on the OpenGL ES 1.0 specification (hardware acceleration optional)

SQLite for structured data storage

Media support for common audio, video, and still image formats (MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF)

GSM Telephony (hardware dependent)

Bluetooth, EDGE, 3G, and WiFi (hardware dependent)

Camera, GPS, compass, and accelerometer (hardware dependent)

Rich development environment including a device emulator, tools for debugging, memory and performance profiling, and a plugin for the Eclipse IDE

框架:





应用程序部件

Activity

Services

Content Providers

Broadcast receiver

Manifest 文件

  整个程序的功能清单,包括包的定义、版本声明,应用程序Activity、ICON、service、intent filter等的声明。

  在Eclipse中可以通过Manifest Editor方便编辑(右键打开)。

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest . . . >
    <application . . . >
        <activity android:name=”com.example.project.FreneticActivity”
                  android:icon=”@drawable/small_pic.png”
                  android:label=”@string/freneticLabel” 
                  . . .  >
        </activity>
        . . .
    </application>
</manifest>

Activities and Tasks

一个task就是用户感觉上的”应用程序”,task 是一组相互关联的activity集合组成的栈(这些activity可能属于多个应用程序). 栈底就是应用程序启动时显示的第一个activity。栈顶activity就是用户在屏幕上看到的activity。当一个activity启动一个新的activity时,新的activity就会被压入栈中。当用户点击”back ” 按钮时,位于栈顶的activity将会被弹出栈。我们举个例子,假设task中有两个activity,分别是 A 和 B,其中A是位于栈顶的activity,用户点击”back”后, A就被弹出栈, B 就成为了新的栈顶activity,然后 activity B 会显示在屏幕上。

通常来讲,task栈中的activity不会被改变排列顺序,只有出栈和入栈操作。

A task is a stack of activities, not a class or an element in the manifest file. So there’s no way to set values for a task independently of its activities. Values for the task as a whole are set in the root activity. For example, the next section will talk about the “affinity of a task”; that value is read from the affinity set for the task’s root activity.

一个task中的所有activity是作为一个整体来移动的–移到前台后者后台。就个例子,假设当前 task 栈中有ABCD4个activity,A是栈顶activity,也就是在屏幕上显示的activity,用户点击了”home”键,启动了一个新的应用程序,则 ABCD 4个activity都会被移动到后台。过了一会,用户又点击了”home”键,并重新选择了先前的程序,则 ABCD 都被移动到前台,A 仍然作为栈顶activity 被显示在屏幕上,然后用户点击了”back”,A被弹出栈,B作为新的栈顶activity被显示在屏幕上。

  前面所讲的都是task的默认行为,我们可以通过某些方法改变task 的默认行为。

  不同的task里的activity是由affinity来区别的.默认情况下每个task里面的activities都有相同的affinity。但是可以通过<activity> 标签里taskAffinity属性来修改某个activity的affinity,此时需要设置FLAG_ACTIVITY_NEW_TASK标志,并且allowTaskReparenting 属性设置为true。关于这点的文档解释是:As described earlier, a new activity is, by default, launched into the task of the activity that called 
startActivity()
. It’s pushed onto the same stack as the caller. However, if the Intent object passed to
startActivity()
 contains the 
FLAG_ACTIVITY_NEW_TASK
 flag, the system looks for a different task to house the new activity. Often, as the name of the flag implies, it’s a new task. However, it doesn’t have to be. If there’s already an existing task with the same affinity as the new activity, the activity is launched into that task. If not, it begins a new task.

注意:在使用FLAG_ACTIVITY_NEW_TASK属性(singleTask和singleInstance类似)时,无法回退到调用者。

activity在task中加载方式主要有四种:
  1.standard(the default mode):一个intent发过来之后,都会产生一个新的activity来响应这个intent;
  2.singleTop:保持stack顶端的那个activity.也就是说,如果一个intent是由stack顶端的activity处理的话,将不会新建一个新的activity来处理,而是由原来的那个activity处理;
  3.singleTask:对于这个activity则会新建一个task.也就是说,对于这个activity,会分配一个新的affinity;
  4.singleInstance:identical to “
singleTask
“,并保证这个activity唯一.

既然对初始加载的过程有所规定,那么也就很自然地会有结束过程的一些方式:
  acitivity在退出的时候,必然会影响到task的stack,因此,android对acitivity的行为方式也做了规定.
  1.alwaysRetainTaskState属性.保证stack不变.默认状态下,一个task长期不活动的话,会退化到root activity,也就是弹出stack,只保留底部的一个acitivity.
  2.clearTaskOnLaunch属性.这个会在acitivity加载的时候清空task的stack.
  3.finishOnTaskLaunch属性.某种程度上说这个属性保证了某项task是唯一的.因为当一个同类的task加载的时候,如果此属性为true,那么就会先退出之前的task,然后再加载这个task.

如果FLAG_ACTIVITY_CLEAR_TOP属性为真,则结束activity时,所有处于该activity堆栈之上的activity都会同时清除,FLAG_ACTIVITY_CLEAR_TOP is most often used in conjunction with FLAG_ACTIVITY_NEW_TASK. When used together, these flags are a way of locating an existing activity in another task and putting it in a position where it can respond to the intent.

OPhone SDK

December 30th, 2009 | 1 | No Comments »

OPhone介绍

OPhone是基于Linux面向移动互联网的终端基础软件及系统解决方案。OPhone SDK是专为OPhone平台设计的软件开发套件,它包括OPhone API,OPhone模拟器,开发工具,示例代码和SDK帮助文档。OPhone SDK 1.5兼容Android SDK 1.5,因此开发者在开发OPhone应用的时候可以同时使用OPhone API和Android API。
OPhone SDK支持两种类型的应用开发:
Apk OPhone 应用

Web Widget 应用

Apk OPhone 应用

OPhone应用是基于java语言开发的应用程序。OPhone SDK提供了一些OPhone API来拓展OPhone平台的能力,如主屏API、本地搜索API、视频通话API等。若想了解更多关于OPhone API的内容,请参考 OPhone API 参考文档

Web Widget 应用

除了基于java的OPhone应用外,OPhone还支持Widget应用开发。Widget是一个采用HTML、JavaScript和CSS等网络技术的应用程序。若想了解更多关于Widget的内容,请参考 Widget 介绍文档


is the logo of OPhone.
SDK的安装:http://www.ophonesdn.com/documentation/ophone/gettingstarted/installing_sdk.html

SDK的配置

为了调试运行的方便,可以添加SDK的子目录<your_sdk_dir>/tools到系统的环境变量PATH中。

在Linux下,请编辑文件~/.bash_profile或~/.bashrc,在该文件的末尾添加如下内容:
export PATH=${PATH}:<your_sdk_dir>/tools


在Windows下,鼠标右键点击我的电脑, 选择弹出菜单中的属性,在弹出窗口的高级卡片页下,点击按钮环境变量。在弹出的窗口中双击PATH,在变量值的输入窗口中添加内容
;<your_sdk_dir>/tools


在Mac OS X机器上,与Linux环境下配置相同,找到home目录下的.bash_profile文件,并添加配置。如果没有找到该文件,则可以自己创建一个。

将tools目录加入PATH环境变量后,SDK提供的工具可以在文件系统的任何位置被调用运行,而不必每次指定SDK安装的完整路径名。请注意,在SDK升级时,如果改变了SDK的安装路径,该PATH变量的内容也同时需要更新。

创建AVD

为了能够在OPhone模拟器上运行你的应用程序,你需要首先创建一个模拟器下的虚拟设备(AVD)。所谓AVD就是模拟器运行时使用的一组配置, 用来描述模拟的OPhone平台。请按照以下步骤创建AVD。

打开命令行窗口(比如在Windows环境下运行“命令提示符”,或者Mac/Linux环境下运行“终端”。

选择一个开发使用的目标设备,并创建一个新的AVD。命令行如下: 
   android create avd –name OMS –target 1 
命令行中的target id 是 “1″,因此被创建AVD的目标设备是OPhone支持设备列表里面的 “Android 1.5″.

然后,命令行上会有是否创建用户设备配置的询问。如果回答“yes”,一系列关于设备各方面配置的问题将会列出,需要你的确认。每个问题都有一个默认值在括号中标出,如果输入为空(直接输入回车),这些默认答案将会被选择。如果第一个问题,关于是否创建用户设备配置的回答是“no”,则所有配置都将是使用默认值。

现在一个名称为OMS的AVD已经创建完成。你可以使用命令行“android list avd”来检查刚刚创建的AVD是否存在。

在Eclipse IDE中添加OPhone库

为了程序编译调试中能使用到OPhone API, 需要添加一个用户库(User Library)到Eclipse中,库文件对应于OPhone SDK所在目录下的oms.jar。 具体步骤如下:

运行Eclipse,菜单上选择Window > Preferences…, 打开属性编辑窗

从弹出的编辑窗中选择 Java Build Path > User Libraries

在用户库(User Libraries)窗口中点击New,打开New User Libraries对话框。 输入OPhone 1.5,并点击OK

选中新添加的OPhone,并点击Add JARs。选择OPhone SDK目录下的文件oms.jar,点击OK。现在OPhone用户库已经添加到Eclipse IDE的编译目录中,当一个新的OPhone项目被创建,你可以添加该OPhone用户库到你的项目的编译路径(Java Build Path)下。

在上一窗口中,双击OPhone下的Javadoc Location,将会弹出窗口Javadoc For ‘oms.jar’。 选择弹出窗口中的Javadoc in archive,并选定你的Eclipse IDE目录下的plugins/ophone.sdk.doc_VERSION.jar文件,然后确定退出。

安装Eclipse插件 – ODT

ODT是为在Eclipse IDE下进行OPhone应用开发而提供的Eclipse插件。 如果要使用Eclipse作为调试和编译的集成开发环境,则需要首先安装ODT。

你可以在SDK目录中找到ODT安装包: sdk_folder/tools/ophone/ODT-0.9.0.zip 。

安装于Eclipse 3.4 (Ganymede)
运行Eclipse,然后选择Help > Software Updates…

在弹出的窗口中点击:Available Software

点击Add Site…,然后点击Archive…

选中之前准备好的ODT安装文件,并确认返回。

在返回的配置窗口,将会列出待添加的插件。点击复选框项目 OPhone Development Tools,点击Install…

在后续的安装窗口, “OPhone development Tools”将会被选中,点击Finish完成安装。

重启动Eclipse。

其他Eclipse版本,请根据Eclipse的帮助文件进行相应的插件添加。

最后,当Eclipse重新启动,配置你的SDK的目录:

Eclipse菜单选择Window > Preferences…,打开配置窗口。

从左侧控制面板选择 OPhone配置项。

在配置项的内容面板, 点击Browse…,指定OPhone SDK安装的路径。

点击Apply,然后点击OK

安装Eclipse 插件 (WDT)

如果你使用Eclipse作为Widget应用的开发环境,你还需要安装Eclipse插件WDT。WDT是Widget Develpment Tools的缩写,它集成了Wdiget工程开发需要的工具。这些工具可以实现Widget工程的创建、源代码的编辑、运行和调试等功能。WDT功能强大,可扩展性好,让Widget的开发变得更加简单和快捷。

你可以在OPhone SDK中找到WDT安装包: sdk_folder/tools/ophone/JIL-WDT-1.1.zip 。

下面是WDT的安装过程:

安装于Eclipse 3.4 (Ganymede)
运行Eclipse,然后选择Help > Software Updates…

在弹出的窗口中点击:Available Software

点击Add Site…,然后点击Archive…

选中之前准备好的WDT安装文件,并确认返回。

在返回的配置窗口,将会列出待添加的插件。点击复选框项目 Widget Development Tools,点击Install…

在后续的安装窗口, “Widget Development Tools”将会被选中,点击Finish完成安装。

重启动Eclipse。

其他Eclipse版本,请根据Eclipse的帮助文件进行相应的插件添加。

最后,当Eclipse重新启动,配置你的SDK的目录:

Eclipse菜单选择Window > Preferences…,打开配置窗口。

从左侧控制面板选择 Widget配置项。

在配置项的内容面板, 点击Browse…,指定OPhone SDK安装的路径。

点击Apply,然后点击OK

参考:http://www.ophonesdn.com/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息