j2me实际开发中ui、io包是很重要的。
Package javax.microedition.lcdui Description
UI API为MIDP应用程序的用户界面实现提供了一套图形
User Interface
MIDP主要标准已经起草,移动信息设备已被考虑在内(也就是移动电话和寻呼机)。这些设备在很多方面和桌面系统不同,特别是UI部分。当设计UI API时,下面UI相关的需求是很重要的:
* 设备和应用应该对那些并不熟练使用电脑的人来说是有用的。
* 设备和应用应该在用户不能全身心关注应用的情况下是有用的。例如:多个手机型设备可以通过一只手来操作。
* 设备之间的表格元素和UI概念都有区别,特别是和桌面系统。例如,显示区域大小更小,输入设备并不总是包括指针设备。
* MID(Mobile Internet Devices)上运行的应用程序应该有和本地程序相兼容的UI,这样用户会容易使用。
鉴于设备须拥有运行MIDP并满足上述要求的能力,MIDPEG认为移动设备UI不应该简单是Java UI即AWT(Abstract Windowing Tookit)。理由如下:
* AWT为桌面电脑而设计并针对这些设备做了优化,所以它同样受到这个基础(针对桌面电脑)的制约。
* 当用户和AWT交互时,事件对象会被动态创建。这些对象是短暂存在的并只有在系统执行相关事件时才存在。基于这样一点,事件对象变成了垃圾并必须被系统GC回收。而MID设备的有限的CPU和存储子系统无法处理这种行为。
* AWT有一套基于桌面的图形集。MID设备无法提供给这个图形集所需要的图形支持。例如,AWT为窗口管理(window management)提供大量支持(例如:overlapping windows, window resize等)。MID设备只有小显示屏,它对于AWT来说没有大窗口和布局管理支持,但这些在MID设备中并部需要。
* AWT声明特定用户交互模型(user interaction model)。AWT组建集被设计来用指针设备操作(例如鼠标和输入笔)。正如前面所涉及到的,这个要求只有少部分MID设备能满足,而大部分MID设备只有一个用来用户输入的keypad。
Structure of the MIDP UI API
MIDP UI逻辑上由两类API组成:高级API和低级API(high-level API & low-level API).
高级API是为那些客户端部分运行在MID上的商用程序设计。对于这些应用,跨设备的可移植性很重要。为了达到这个可移植性要求,高级API使用一个高层次的抽象并提供非常少的外观上的控制。如下深入地介绍了这个抽象:
* 显示到MID屏幕上的实际图像由这个抽象的实现来完成。应用不能定义组件的虚拟外观(例如形状、颜色、字体等)。
* navigation、scrolling和其它原型交互组件由这个实现来封装,并且应用不关心这些交互。
* 应用不能访问具体的输入设备,像特定的独立的键(specific individual key)
换句话说,当使用高级API时,潜在的实现类(未来的这个抽象的实现类)将要对设备硬件和本地UI样式做必要的适应。提供高级API的类是Screen的子类.
在令一方面低级API提供了非常少的抽象。这个API为那些需要精确定位(precise placement)、图形元素控制(control of graphic elements)和对低级别输入事件进行访问的应用设计。一些应用还需要访问特别的或设备特定的特定。这种应用一个典型的例子就是一个游戏应用。
使用低级别API时,一个应用可以:
* 全面控制显示屏上所画的图像
* 监听事件原型例如键按下和弹起。
* 访问具体的按键和其它输入设备。
提供低级API的类是Canvas和Graphics.
基于低级API编写的应用不能保证可移植性,因为低级API提供了访问特定设备的特定细节的方法。如果应用不使用这些功能(依赖于特定设备的特定功能),它就可以被移植。强烈建议应用尽可能地只使用低级API中平台独立的部分。这意味着应用不要直接假设任何键在实际设备中是存在的,除了那些Canvas类中已经定义过的键,并且它们不依赖于特定屏幕大小。当然,应用程序游戏键事件的映射机制应该用来代替具体的键,并且应用程序应当询问屏幕的大小以使自己随之调整。
Class Hierarchy
MIDP UI的核心抽象对象是一个可显示的对象,它封装了特定于设备的用户输入图形渲染。在同一时刻只有一个可显图形是可见的,并且用户只能看见该图形并与它的内容内容交互。
Screen类是Displayable的一个子类,它关注所有用户和高级UI组件的交互。Screen的子类处理渲染、交互、遍历和滚动,应用中只有高级事件被传递。
这种设计背后的两难抉择是MIDP基于不同的显示器和输入。这些不同意味着component layout, scrolling和 focus traversal在不同的设备上会有不同的实现。如果一个程序需要依赖于这些因素,那么它就不具备可移植性。简单的整个屏幕会把UI组织成为可以管理的块,这使得UI易用易学。
Displayable对象分为三类:
* 封装了一个复杂UI组件的Screen(例如:List或TextBox)。这些Screen的结构是预定义的,应用程序不能给这些组件再添加组件。
* 可以包含Item对象的Generic screen组件(如Form类的实例)。应用可以用随意的用text、image和其它组件来组装Form对象。所以,强烈建议Form对象保持简单性这样的话它们就可以被用来包含少量的、紧密相关的UI组件。
* 低等级API的显示对象。(像Canvas类的子类)
每个Displayable有一个title,一个Ticker和一个依附的Command集。
Display类扮演着display manager角色,它为每一个活动的MIDlet创建实例(Display实例)并提供获取设备显示能力的方法。调用Display的setCurrent()方法可以让一个Displayable处于可见(visible)。当一个Diplayable被调用了setCurrent(),它就替换了前一个处于current的Displayable.
Class Overview
预计大部分的应用程序都会用像List、TextBox和Alert这些预定义的构件来组织屏幕。这些类如下使用:
* List:当用户需要从预定义的选择集合中选择时用。
* TextBox: 要求文本输入时用。
* Alert: 显示包含文本和图片的临时信息。
Form是一个特别的类,它用于当预定义的构件不够用的情况。例如:一个应用也许包含了2个TextField或者1个TextField和一个简单的ChoiceGroup,尽管Form类允许组件的任意组合,但是开发者还是应该始终考虑有限的显示大小和创建简单的Form。
Form被设计来包容一组小量的紧密管理的UI元素。这些元素是Item的子类:ImageItem、StringItem、TextField、ChoiceGroup、Gauge和CustomItem。ImageItem和StringItem类使得F有关Form和Alert的特定操作更加简单。通过创建CustomItem的子类,开发者可以引入一个新的可见和可交互的Item。如果组件大小不适合于屏幕,那么该组件的实现就应该使得form可滚动或实现一些可以在新屏幕弹出或当用户编辑钙元素时展开的组件。
Interplay with Application Manager
像API中其它的资源一样,UI同样受控于MIDP应用管理器规则。UI要求来自AMS(Application management software)的以下几种状态:
* getDisplay()可以在MIDlet构造器到destroyApp()方法返回之间调用。
* Display对象始终是一致的直到destroyApp()被调用。
* Displayable对象通过setCurrent()调用时,不会被AMS改变。
AMS假定应用程序遵守MIDlet事件,表现如下:
* startApp - 应用会为第一个屏幕调用setCurrent()。AMS使得当startApp()返回时Displayable真正可见。注意startApp()可以被调用多次,如果之间调用了pauseApp()。这意味着初始化不能发生,应用程序也不会偶然的用setCurrent()切换到另外一个屏幕。
* pauseApp - 应用应该尽可能地释放线程。同样地,如果当应用重新处于活动状态时要以另外一个屏幕开始,那么这个屏幕应该用setCurrent()来设置。
* destroyApp - 应用会删除所创建的对象。
Event Handling
用户交互导致事件,同时事件的实现通过事件相应的回调通知应用程序。有4类UI回调:
* 高级API的Abstract Command
* 表示单个键按下和放开的低级事件(如果指针可用的话还包括指针事件)
* Canvas类的paint()方法调用。
* Display类的callSerially()方法请求Runnable对象的run()方法调用。
所有UI回调都是序列化的,所以它们永远不会并行发生。那就是说,前一个调用的任何其它的回调都被返回之前不会再有回调被调用。这个性质使应用确保前一个用户事件处理已经完成之后下一个事件才会被发送。如果多个UI回调处于等待,下一个回调会在前一个回调完成之后尽快被调用。这种实现方式保证来自callSerially()方法调用的run()方法紧随任何等待满足的重绘请求(repaint request)之后。
回调序列化规则(callback serialization rule)有一个异常类,当Canvas.servicRepaints方法被调用时会抛出。这个方法导致Canvas.paint方法将被调用并等待它完成。即使servicRepaints的调用者是在一个活动回调中的它本身这种情况也会发生。以下是深入的讨论:http://java.sun.com/javame/reference/apis/jsr118/javax/microedition/lcdui/package-summary.html#concurrency
以下回调经过互相关联的序列化:
注意:Timer事件不被认为是UI事件。Timer回调可以和UI事件回调同步执行,尽管安排在同一个Timer上的TimerTask回调互相之间是序列化过的。应用程序使用timer时需要保护它们的数据不被其它timer线程和UI事件回调同事访问。作为选择,应用程序可以拥有它们自己的使用Display.callSerially的timer回调,这样它就可以用用UI事件回调序列化的timer事件来触发工作。
Abstract Commands
由于MIDP UI是高度抽象的,它不能支持任何实在的用户交互技术像软按钮或菜单。同样,像焦点遍历或滚动条这样的低级用户交互对于应用程序而言是不可见的。MIDP应用定义Command,应用实现会列出基于软按键、菜单或为该设备指定的所有适当的机制。
用Displayable类的addCommand方法可以将Command安装到Displayable(Canvas 或 Screen)。
设备的本地类型会假定Command的特定类型被放置在标准位置上。例如:“go-back”操作会始终映射到右侧软按键。Command类允许应用程序来传达一个指示给实现类,这样这些标准映射就说生效。
Command实现实际上不实现任何Command语义。一个Command的属性仅仅被用来映射到UI。一个Command的实际语义永远都是you程序里的CommandListener来实现。
Command对象拥有的属性:
* Lable: 作为提示显示给用户。一个单个的Command有2个版本的lable:长和短。具体实现决定长、短lable和给定的情形是否适应。例如:一种实现可以选择一个软按键附件的已给定的Command的短版本和一个菜单中的该Command的长版本。
* Type:一个Command的用途。具体实现会使用这个Command的type来把它准确的映射到设备的UI中。例如,拥有近似类型的Command可能会发现在UI中特定位置上离的很近。经常设备会有Command映射位置和特定操作的策略。例如,一个“backward navigation” Command可能在某个特定设备上会被永远的映射到右侧软按键上,但是它又会被另外一个不同设备映射到左侧软按键上。Command类提供固定的Command type来提供给MIDlet告知设备这个Command的实现意图的能力。应用程序可以使用BACK Command type为Command来完成backward navigation操作。上面所提到的设备,它们的type信息会被用来声明给特定的软按键。
* 优先权:定义同类型Command之间的相对重要性。一个低优先权值的Command比同样类型的高优先权值的Command更重要。如果可能,相比一个不重要的Command,一个更重要的Command会先执行,或更易取得。
public class YourItemSelectedListener implements OnItemSelectedListener { public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { String selected = parent.getItemAtPosition(pos).toString(); } public void onNothingSelected(AdapterView parent) { // Do nothing. } }
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
谢谢
谢谢
我解决了,adapter 有个通知数据改变的update方法
黑名单 自动静音
当呼叫者属于黑名单列表 自动静音
[代码 步骤]
本着先易后难的原则 先介绍黑名单列表的制作:其会列出所有联系人列表 以CheckBox形式 可以添加/移除 黑名单
1. 定义所需布局:list.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/btnOK" android:layout_gravity="right" android:text="OK" android:layout_width="100dip" android:layout_height="wrap_content" /> </LinearLayout>
2. 初始化View
public void initial(){ initialView(); } public void initialView(){ lView = (ListView)findViewById(R.id.list); btnOK = (Button)findViewById(R.id.btnOK); }
3. 定义ContactsAdapter 用于列出使用联系人
public class ContactsAdapter extends BaseAdapter { Activity activity; public ContactsAdapter(Activity a){ activity = a; } @Override public int getCount() { // TODO Auto-generated method stub return cursor.getCount(); } @Override public Object getItem(int arg0) { // TODO Auto-generated method stub return arg0; } @Override public long getItemId(int arg0) { // TODO Auto-generated method stub return arg0; } @Override public View getView(int arg0, View arg1, ViewGroup arg2) { // TODO Auto-generated method stub CheckBox rb = new CheckBox(activity); rb.setText(" "+getNameById(arg0)+" |"+getNumberById(arg0)); return rb; } }
其中 getNameById() getNumberById() 可以根据position 返回联系人的名字和号码
public String getNameById(int id){ cursor.moveToPosition(id); int index = cursor.getColumnIndex(People.NAME); return cursor.getString(index); } public String getNumberById(int id){ cursor.moveToPosition(id); int index = cursor.getColumnIndex(People.NUMBER); return cursor.getString(index); } 其中 cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);
3. ContactsAdapter 实例化
adapter = new ContactsAdapter(this); lView.setAdapter(adapter);
4. 当按下btnOK 得到所有黑名单 并返回给前Activity:BlacklistMain
btnOK.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub loadContactsChecked(); } });
5. loadContactsChecked 用于获取所有选中联系人列表 并返回之
public void loadContactsChecked(){ List<String> list = new ArrayList<String>(); for(int i=0;i<lView.getCount();i++){ CheckBox rButton = (CheckBox)lView.getChildAt(i); if(rButton.isChecked()){ list.add(getNumberById(i)); } } sendBack(list); } public void sendBack(List<String> l){ Intent intent = new Intent(); Bundle bundle = new Bundle(); String[] sArray = new String[l.size()]; l.toArray(sArray); bundle.putStringArray("phone", sArray); intent.putExtras(bundle); this.setResult(RESULT_OK, intent); this.finish(); }
至此 黑名单列表选取 已经完成 下面讲自动静音功能 文件为:BlacklistMain
布局:main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" > <Button android:id="@+id/buttonMain" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="黑名单" /> <Button android:id="@+id/buttonClear" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="清屏" /> <Button android:id="@+id/buttonList" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="列举" /> </LinearLayout> <TextView android:id="@+id/status" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
1. 与BlacklistManager 即:黑名单列表选取 连接 的代码
* 单击buttonMain 跳转至BlacklistManager
findViewById(R.id.buttonMain).setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub sendGo(); } });
sendGo() 实现:
public void sendGo(){ Intent i = new Intent(BlockMain.this,BlockManager.class); this.startActivityForResult(i, ACTIVITY_CONTACTS_DO_CHECKED); }
其中 ACTIVITY_CONTACTS_DO_CHECKED 为int 用于标记具体是那个startActivity 之用
public final static int ACTIVITY_CONTACTS_DO_CHECKED = 20;
* Activity 返回处理 即:从Intent中取出所有选中联系人列表 并存入List<String> 中
protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch(requestCode){ case ACTIVITY_CONTACTS_DO_CHECKED: Bundle b = data.getExtras(); String[] s = b.getStringArray("phone"); blockList.clear(); for(String i:s){ blockList.add(i); } showBloclist(); break; } }
下面主要说下 电话呼叫拦截且静音 代码
1. 继承PhoneStateListener 并实现其中的onCallStateChanged() 即:根据不同电话状态做定制化
public class CustomPhoneCallListener extends PhoneStateListener { @Override public void onCallStateChanged(int state, String incomingNumber){ switch(state){ case TelephonyManager.CALL_STATE_IDLE: textStatus.append("\n"+"status:CALL_STATE_IDLE"); aManager.setRingerMode(AudioManager. RINGER_MODE_NORMAL); break; case TelephonyManager.CALL_STATE_OFFHOOK: textStatus.append("\n"+"status:CALL_STATE_OFFHOOK"); break; case TelephonyManager.CALL_STATE_RINGING: textStatus.append("\n"+"status:CALL_STATE_RINGING:"+incomingNumber); showBloclist(); if(isBlock(incomingNumber)){ aManager.setRingerMode(AudioManager. RINGER_MODE_SILENT); showToast("Call-number:"+incomingNumber+"|silent"); } else { showToast("Call-number:"+incomingNumber); } break; } super.onCallStateChanged(state, incomingNumber); } }
其中 isBlock() 用于判断来电号码是否在黑名单中 实现为:
public boolean isBlock(String s){ //to load all String stored in List<String> to String[] String[] bArray = new String[blockList.size()]; blockList.toArray(bArray); for(String s1:bArray){ if(s1.replace("-", "").equals(s)){ return true; } } return false; }
对了 在最后别忘了监听电话状态 即:
public void managerCallListener(){ aManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); tManager = (TelephonyManager)getSystemService( TELEPHONY_SERVICE); cpListener = new CustomPhoneCallListener(); tManager.listen(cpListener, PhoneStateListener. LISTEN_CALL_STATE); }
3. emulator 运行截图:
* 添加黑名单 选取:137 128
* 以号码:137 呼叫该emulator 即:gsm call 137
最后 BS android ! 不知道什么原因 开始在emulator 老失败 后来发现其不能打/接听电话 一拨出电话 就提示: Not register on network. 没办法 只有重新创建emulator再试
至于怎么以指导号码向该emulator打电话 以前的博客有 大家自己找一下吧!
done!!!
因为这仅仅为demo 那样写要文件不断切换 比较麻烦 所以这样写 简便点~~~~