对于"索引类型的图片",即肯定包含PLTE调色板的Png图片,就可以通过对调色板的修改,再通过CRC算法生成新的验证码,再还原成新图片就可以得到所要的效果,为此,我写了如下的类.
/* 用于PNG图片变色的类 使用要求,需要美工将原始图片的需要换色的点 设置为纯红色0xff0000 */
import Java.io.*;
import java.lang.*;
import javax.microedition.lcdui.*;
public class coloredImage {
public coloredImage() {
}
public Image getColoredImage(String s, int newcolor) {
try {
byte[] pixel;
InputStream is = getClass().getResourceAsStream(s);
int i = 0;
while (is.read() != -1) {
i++;
}
pixel = new byte[i];
is = null;
is = getClass().getResourceAsStream(s);
is.read(pixel);
imgConvert(pixel, newcolor);
return (Image.createImage(pixel, 0, pixel.length));
} catch (Exception e) {
return null;
}
}
public void imgConvert(byte content[], int color) {
try {
int start = 0;
int newcolor = -1;
for (int idx = 0; idx < content.length; idx++) {
if (content[idx] == 0x50 && content[idx + 1] == 0x4c
&& content[idx + 2] == 0x54 && content[idx + 3] == 0x45) {
start = idx;
break;
}
} for (int idx = 0; idx < 4; idx++) {
newcolor = pixelConvert(content[start + idx], newcolor);
}
int r, g, b, length;
length = (content[start - 4] & 0xff << 24) |
(content[start - 3] & 0xff << 16) |
(content[start - 2] & 0xff << 8) |
(content[start - 1] & 0xff);
for (int i = 0; i < length; i++) {
r = content[start + 4 + i] & 0xff;
g = content[start + 4 + i + 1] & 0xff;
b = content[start + 4 + i + 2] & 0xff;
if (r == 255 && g == 0 && b == 0) {
r = color >> 16 & 0xff;
g = color >> 8 & 0xff;
b = color & 0xff;
content[start + 4 + i] = (byte) r;
content[start + 4 + i + 1] = (byte) g;
content[start + 4 + i + 2] = (byte) b;
}
newcolor = pixelConvert(content[start + 4 + i], newcolor);
newcolor = pixelConvert(content[start + 4 + i + 1], newcolor);
newcolor = pixelConvert(content[start + 4 + i + 2], newcolor);
}
newcolor = ~newcolor;
content[start + 4 + length] = (byte) (newcolor >> 24);
content[start + 4 + length + 1] = (byte) (newcolor >> 16);
content[start + 4 + length + 2] = (byte) (newcolor >> 8);
content[start + 4 + length + 3] = (byte) (newcolor);
} catch (Exception e) {}
}
/** * CRC检验算法 * @param pixel 像素 * @param color 颜色值 * @return */
public int pixelConvert(byte pixel, int color) {
int tmp = pixel & 0xff;
color ^= tmp;
for (int idx = 0; idx < 8; idx++) {
if ((color & 1) != 0) {
color = color >>> 1 ^ 0xedb88320;
} else {
color >>>= 1;
}
}
return color;
}
}这个类提供了一个转换图片颜色的方法getColoredImage,只要将源图片的路径以及需要转换的新颜色作为参数调用就可以得到转换后的图片.
相应的我把我的主类写出来供大家参考其用法. import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
import com.nokia.mid.UI.*;
import java.io.*;
public class pengzhuang extends MIDlet {
class test extends FullCanvas {
Image[] a;
byte[] pix;
coloredImage ci;
public test() {
ci = new coloredImage();
a = new Image[4];
a[0] = ci.getColoredImage("/char.png", 0x0000ff);
a[1] = ci.getColoredImage("/char.png", 0x00ff00);
a[2] = ci.getColoredImage("/char.png", 0xffffff);
a[3] = ci.getColoredImage("/char.png", 0x00ffff);
}
public void keyPressed(int i) {
}
public void paint(Graphics g) {
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(a[0], 0, 0, 0);
g.drawImage(a[1], 30, 30, 0);
g.drawImage(a[2], 60, 60, 0);
g.drawImage(a[3], 90, 90, 0);
}
}
private Display display;
test t;
public pengzhuang() {
try {
t = new test();
} catch (Exception e) {
}
display = Display.getDisplay(this);
}
public void startApp() {
display.setCurrent(t);
}
public void pauseApp() {
}
public void destroyApp(boolean boo) {
}
}
其中用到的源图是
Png图片换色的方法
运行程序后的效果为
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.POWER_LOW);
String locationProvider = locationManager.getBestProvider(criteria,
true);
Location location = locationManager
.getLastKnownLocation(locationProvider);
return location;
}
// 根据wifi获取当前位置
private Location getCurrentLocationWifi(Context context) {
Location location=null;
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
try
{
WifiManager wifiManager=(WifiManager)context.getSystemService(Context.WIFI_SERVICE);
if(wifiManager.isWifiEnabled())
{
location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
}
}
catch(Exception e)
{
}
return location;
}
// 根据基站获取当前的位置
private Location getCurrentLocationAGPS() {
Location location = null;
if (telephonyManager.getCellLocation() == null) {
}
GsmCellLocation gcl = (GsmCellLocation) telephonyManager
.getCellLocation();
int cid = gcl.getCid();
int lac = gcl.getLac();
int mcc = Integer.valueOf(telephonyManager.getNetworkOperator()
.substring(0,
3));
int mnc = Integer.valueOf(telephonyManager.getNetworkOperator()
.substring(3,
5));
try {
// 组装JSON查询字符串
JSONObject holder = new JSONObject();
holder.put("version", "1.1.0");
holder.put("host", "maps.google.com");
// holder.put("address_language", "zh_CN");
holder.put("request_address", true);
JSONArray array = new JSONArray();
JSONObject data = new JSONObject();
data.put("cell_id", cid); // 25070
data.put("location_area_code", lac);// 4474
data.put("mobile_country_code", mcc);// 460
data.put("mobile_network_code", mnc);// 0
array.put(data);
holder.put("cell_towers", array);
// 创建连接,发送请求并接受回应
DefaultHttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://www.google.com/loc/json");
StringEntity se = new StringEntity(holder.toString());
post.setEntity(se);
HttpResponse resp = client.execute(post);
HttpEntity entity = resp.getEntity();
BufferedReader br = new BufferedReader(
new InputStreamReader(entity.getContent()));
StringBuffer resultStr = new StringBuffer();
String readLine = null;
while ((readLine = br.readLine()) != null) {
resultStr.append(readLine);
}
JSONObject jsonResult = new JSONObject(resultStr.toString());
JSONObject jsonLocation = jsonResult.getJSONObject("location");
double jsonLat = jsonLocation.getDouble("latitude");
double jsonLon = jsonLocation.getDouble("longitude");
location = new Location("AGPS");
location.setLatitude(jsonLat);
location.setLongitude(jsonLon);
} catch (Exception e) {
// TODO: handle exception
}
return location;
}
前言
本章内容为Android 开发者指南的 Framework Topics/User Interface/Notifications/Status Bar Notifications 章节,译为" 状态栏通知" ,版本为Android 4.0 r1 ,翻译来自:" 呆呆大虾" ,欢迎访问他的微博:" http://weibo.com/popapa " ,再次感谢" 呆呆大虾" !期待你一起参与翻译Android 的相关资料,联系我over140@gmail.com 。
声明
欢迎转载,但请保留文章原始出处:)
博客园: http://www.cnblogs.com/
Android 中文翻译组: http://androidbox.sinaapp.com/
状态栏通知
译者署名: 呆呆大虾
译者微博: http://weibo.com/popapa
版本: Android 4.0 r1
原文
http://developer.android.com/guide/topics/ui/notifiers/notifications.html
快速查看
· 状态栏( status bar )通知允许应用程序以不干扰当前 activity 的方式将事件通知用户。
· 可以给通知绑定一个意图( intent ),当用户点击时系统会执行此意图。
在本文中
基础知识
管理通知
创建通知
更新通知
添加声音
添加振动
添加闪光
其他特性
创建自定义的展开 View
关键类
Notification
NotificationManager
状态栏( status bar )通知将一个图标填加到系统的状态栏中(包含一条可选的提示文本信息),并将一条展开信息添加到通知窗口中。当用户选中展开信息时, Android 将执行一个此通知已定义的意图 Intent (通常用于弹出一个 Activity )。你还可以对通知进行配置,用设备提供的声音、振动、闪光来提醒用户。
当后台服务( Service )需要对某个事件发出提醒并且需要用户响应时,状态栏通知就能发挥作用了。后台服务从来不会 启动 Activity 来接收用户的交互,取而代之的是应该创建一个状态栏通知,在用户点选后再由通知来启动 Activity 。
以下截图展示了一个左侧带有通知图标的状态栏:
下图展示了 “Notifications” 窗口内的通知展开信息。用户可通过下拉状态栏(或在 Home 菜单里选中通知 )来显示这个通知窗口。
基础知识
Activity 或者 Service 都能初始化一个状态栏通知。可因为 Activity 只有在活动状态并获得焦点时才能执行操作,所以还是建议用 Service 来创建状态栏通知。这样,即使用户正在使用其他程序或者设备已经休眠时,仍然可以从后台创建通知。要创建一个通知,须用到两个类: Notification 类和 NotificationManager 类。
用 Notification 类的一个实例来定义状态栏通知的属性,比如图标、展开信息,以及播放声音等附属设置。 NotificationManager 是一个 Android 系统服务,用于管理和运行所有通知。 NotificationManager 不能被实例化,为了把 Notification 传给它,你可以用 getSystemService() 方法获取一个 NotificationManager 的引用。在需要通知用户时再调用 notify() 方法将 Notification 对象 传给它。
要创建一个状态栏通知:
1. 获取 NotificationManager 的引用:
String ns = Context . NOTIFICATION_SERVICE ;
NotificationManager mNotificationManager = ( NotificationManager ) getSystemService ( ns );
2. 实例化 Notification :
int icon = R . drawable . notification_icon ;
CharSequence tickerText = "Hello" ;
long when = System . currentTimeMillis ();
Notification notification = new Notification ( icon , tickerText , when );
3. 指定通知的展开信息和 Intent :
Context context = getApplicationContext ();
CharSequence contentTitle = "My notification" ;
CharSequence contentText = "Hello World!" ;
Intent notificationIntent = new Intent ( this , MyClass . class );
PendingIntent contentIntent = PendingIntent . getActivity ( this , 0 , notificationIntent , 0 );
notification . setLatestEventInfo ( context , contentTitle , contentText , contentIntent );
4. 将 Notification 对象传给 NotificationManager :
private static final int HELLO_ID = 1 ;
mNotificationManager . notify ( HELLO_ID , notification );
好了,现在用户已经能看到通知了。
管理通知
系统服务 NotificationManager 管理着所有的通知,只能通过 getSystemService() 方法来获取它的引用。例如:
String ns = Context . NOTIFICATION_SERVICE ;
NotificationManager mNotificationManager = ( NotificationManager ) getSystemService ( ns );
如果想要发送状态栏通知,通过 notify(int, Notification) 传递 Notification 对象给 NotificationManager 即可。第一个参数是 Notification 的唯一 ID ,第二个参数是 Notification 对象。 ID 在整个应用程序范围内唯一标识 Notification 。 Notification 需要更新;应用程序可能管理着多种不同的通知,在用户通过各自定义的 Intent 返回应用程序时必须能选择正确的动作执行之,因此上述参数是必需的。
要实现用户从通知窗口内点选后自动清除状态栏通知,请在 Notification 对象中加入 “FLAG_AUTO_CANCEL” 标志。也可以传入通知 ID 用 cancel(int) 手动清除,或者用 cancelAll() 清除所有你创建的通知。
创建通知
Notification 对象定义了通知消息显示在状态栏和通知窗口上的细节内容,以及其他提醒设置(比如:声音、闪光等)。
状态栏通知必须 包括以下内容:
· 状态栏图标
· 展开窗口 view 的标题和展开信息(除非用了 自定义展开 view )
· PendingIntent ,当通知被点选时执行
状态栏通知的可选设置包括:
· 状态栏提示信息
· 提醒声音
· 震动设置
· LED 灯闪光设置
Notification 的基础库( 译者注:原文是 starter-kit ,但综合上下文并非 “ 初学者套件 ” 的意思,这里译为基础库) 里包含了构造方法 Notification(int, CharSequence, long) 和 setLatestEventInfo(Context, CharSequence, CharSequence, PendingIntent) 方法。这已经可以定义 Notification 的所有设置。以下代码段演示了对通知基本的设置:
int icon = R . drawable . notification_icon ; // icon from resources
CharSequence tickerText = "Hello" ; // ticker-text
long when = System . currentTimeMillis (); // notification time
Context context = getApplicationContext (); // application Context
CharSequence contentTitle = "My notification" ; // expanded message title
CharSequence contentText = "Hello World!" ; // expanded message text
Intent notificationIntent = new Intent ( this , MyClass . class );
PendingIntent contentIntent = PendingIntent . getActivity ( this , 0 , notificationIntent , 0 );
// the next two lines initialize the Notification, using the configurations above
Notification notification = new Notification ( icon , tickerText , when );
notification . setLatestEventInfo ( context , contentTitle , contentText , contentIntent );
更新通知
应用程序可以在事件正在进行时更新状态栏通知。比如,前一条短信还未读,可又来了一条新短信,短信程序为了正确显示未读短信的总数,可以更新已有的通知。此时,更新原有通知要比向 NotificationManager 新增一条通知更合理些,因为避免了通知窗口的显示混乱。
因为 NotificationManager 对每个通知都用一个整数 ID 进行了唯一标识,新的通知内容可以用 setLatestEventInfo() 方法方便地进行修改,然后再次调用 notify() 显示出来。
除了 Context 、展开信息的标题和文本外,可以利用对象的成员值修改每个属性。要修改通知的文本信息,只能对 contentTitle 和 contentText 参数赋新值并调用 setLatestEventInfo() , 然后再调用 notify() 方法来更新通知。(当然,如果已经创建了 自定义扩展 view ,那么标题和文本的修改就无效了)。
添加声音
可以用缺省提示音(由用户指定)或者程序指定声音来提醒用户。
要使用用户缺省提示音,给 defaults 属性添加 “DEFAULT_SOUND” :
notification . defaults |= Notification . DEFAULT_SOUND ;
要使用应用程序指定的声音,则传递一个 Uri 引用给 sound 属性。以下例子使用已保存在设备 SD 卡上的音频文件作为提示音:
notification . sound = Uri . parse ( "file:///sdcard/notification/ringer.mp3" );
在下面的例子里,音频文件从内部 MediaStore 类的 ContentProvider 中获取:
notification . sound = Uri . withAppendedPath ( Audio . Media . INTERNAL_CONTENT_URI , "6" );
这时,已知有资源 ID 为 6 的媒体文件,并且已添加到 Uri 内容中。如果不知道确切的 ID ,则必须先用 ContentResolver 查询 MediaStore 中所有可用的资源。关于使用 ContentResolver 的详细信息请参阅 Content Providers 文档。
如果期望在用户响应通知或取消通知前,声音一直持续循环播放,可以把 “FLAG_INSISTENT” 加入 flags 属性中。
注意: 如果 defaults 属性包含了 “DEFAULT_SOUND” ,则缺省提示音将覆盖 sound 属性里指定的声音。
添加振动
可以用缺省震动模式或程序指定的振动模式来提醒用户。
要用缺省震动模式,给属性 defaults 添加 “DEFAULT_VIBRATE” 即可:
notification . defaults |= Notification . DEFAULT_VIBRATE ;
要自定义震动模式,须给 vibrate 属性传递一个 long 类型的数组:
long [] vibrate = { 0 , 100 , 200 , 300 };
notification . vibrate = vibrate ;
长整型数组定义了震动开和关交替的时间(毫秒)。第一个数是开始振动前的等待时间(震动关闭),第二个数是第一次开启振动的持续时间,第三个数是下一次关闭时间,如此类推。振动模式的持续时间没有限制,但不能设置为重复振动。
注意: 如果 defaults 属性包含了 “DEFAULT_VIBRATE” ,则缺省的震动模式将会覆盖 vibrate 属性里指定的模式。
添加闪光
要想用 LED 闪光来提醒用户,可以执行缺省闪光模式(如果可用的话),也可以自定义闪光的颜色和模式。
要使用缺省的闪光设置,给属性 defaults 添加 “DEFAULT_LIGHTS” 即可:
notification . defaults |= Notification . DEFAULT_LIGHTS ;
要自定义颜色和模式,则须指定 ledARGB 属性(指颜色)、 ledOffMS 属性(闪光关闭毫秒数)、 ledOnMS 属性(闪光开启毫秒数),并在 “flags ” 属性里加入 “FLAG_SHOW_LIGHTS” :
notification . ledARGB = 0xff00ff00 ;
notification . ledOnMS = 300 ;
notification . ledOffMS = 1000 ;
notification . flags |= Notification . FLAG_SHOW_LIGHTS ;
上例实现了绿色光闪烁 300 毫秒间歇 1 秒的闪光。每个设备的 LED 灯不可能支持所有颜色的发光,不同的设备所能支持的颜色也各不相同,因此硬件将按照最接近的颜色来发光。绿色是最常见的提醒色。
其他特性
利用 Notification 的属性和标志位,可以给通知添加更多的特性。
下面列出了其中一些常用的特性:
“FLAG_AUTO_CANCEL” 标志
在 flags 属性中增加此标志,则在通知窗口点选后能自动取消通知。
“FLAG_INSISTENT” 标志
在 flags 属性中增加此标志,则在用户响应前一直循环播放声音。
“FLAG_ONGOING_EVENT” 标志
在 flags 属性中增加此标志,则将通知放入通知窗口的 “ 正在运行 ” ( Ongoing )组中。表示应用程序正在运行 —— 进程仍在后台运行,即使应用程序不可见(比如播放音乐或接听电话)。
“FLAG_NO_CLEAR” 标志
在 flags 属性中增加此标志,表示通知不允许被 “ 清除通知 ” 按钮清除。这在期望通知保持运行时特别有用。
number 属性
表示通知所代表的事件数量。此数字显示在状态栏图标上。要利用此属性,必须在第一次创建通知时设为 1 。(如果只是在更新通知时才把此值从 0 改成任意大于 0 的数,则数字不会显示出来。)
iconLevel 属性
表示通知图标当前的 LevelListDrawable 等级。通过改变这个值,可以在状态栏中显示图标的动画,这个值与 LevelListDrawable 中 drawable 的定义相关。详情请参阅 LevelListDrawable 。
程序能自定义更多特性,详情请参阅 Notification 。
创建自定义的展开 View
默认情况下,通知窗口中的展开视图( view )包括基本的标题和文本信息。这是由 setLatestEventInfo() 的 contentTitle 和 contentText 参数指定的。不过仍可以用 RemoteViews 来自定义一个展开视图的布局。右侧的截图就展示了一个自定义展开视图的例子,其中用到了 LinearLayout 布局中的 ImageView 和 TextView 。
要自定义展开信息的布局,需要实例化一个 RemoteViews 对象,并将它传递给 Notification 的 contentView 属性,同时把 PendingIntent 传给 contentIntent 属性。
通过例子是对创建自定义展开视图最好的理解方式:
1. 为展开视图新建 XML 布局,建立一个名为 custom_notification_layout.xml 的布局文件,内容如下:
<LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "horizontal"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:padding = "3dp"
>
<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 = "#000"
/>
</LinearLayout>
此布局用于展开视图,但 ImageView 和 TextView 的内容还需要由应用程序来定义。 RemoteViews 提供了一些方便的方法来定义这些内容。
2. 在应用程序代码里,用 RemoveViews 的方法来定义图片和文字。然后把 RemoteViews 对象传给 Notification 的 contentView 属性,如下所示:
RemoteViews contentView = new RemoteViews ( getPackageName (), R . layout . custom_notification_layout );
contentView . setImageViewResource ( R . id . image , R . drawable . notification_image );
contentView . setTextViewText ( R . id . text , "Hello, this message is in a custom expanded view" );
notification . contentView = contentView ;
如上:先把程序 package 名和布局资源 ID 传给 RemoteViews 的构造方法。然后用 setImageViewResource() 和 setTextViewText() 定义 ImageView 和 TextView 的内容,分别把 View 对象的资源 ID 、所赋的内容作为参数传入。最后,把 RemoteViews 对象传给 Notification 的 contentView 属性。
3. 由于自定义视图不需要执行 setLatestEventInfo() 方法,因此必须用 contentIntent 属性来定义一个通知所要执行的意图 Intent ,如下:
Intent notificationIntent = new Intent ( this , MyClass . class );
PendingIntent contentIntent = PendingIntent . getActivity ( this , 0 , notificationIntent , 0 );
notification . contentIntent = contentIntent ;
4. 现在通知可以如常发送了:
mNotificationManager . notify ( CUSTOM_VIEW_ID , notification );
RemoteViews 类还包含了一些方法,便于在通知的展开视图里增加 Chronometer 或 ProgressBar 。 关于用 RemoteViews 创建自定义布局的详细信息,请参阅 RemoteViews 类的参考文档。
注意: 当建立一个自定义展开视图时,必须特别小心,保证自定义的布局能正常工作在不同的设备方向和分辨率下。这个建议对于所有在 Android 上创建的 View 布局都是适用的,但在这种情况下尤为重要,因为布局实际可用的屏幕区域非常有限。不要把自定义布局设计得过于复杂,并且一定要在各种环境配置下进行测试。
附件是word形式和自己的实验