Android 程序员必须知道的 53 个知识点

1. android 单实例运行方法php

咱们都知道 Android 平台没有任务管理器,而内部 App 维护者一个 Activity history stack 来实现窗口显示和销毁,对于常规从快捷方式运行来看都是 startActivity 可能会使用 FLAG_ACTIVITY_NEW_TASK 标记来打开一个新窗口,好比 Launcher,因此考虑单任务的实现方法比较简单,首先 Android123 纠正下你们一种错误的方法就是直接在androidmanifest.xml 的 application 节点中加入 android:launchMode="singleInstance"这句,其实这样将不会起到任何做用,Apps 内部维护的历史栈做用于 Activity,咱们必须在 activity 节点中加入 android:launchMode="singleInstance" 这句才能保证单实例,固然通常均加在主程序启动窗口的 Activity。html

Android 程序员必须知道的 53 个知识点

2. px 像素如何转为dip 设备独立像素:::
最近有网友问如何将 px 像素转为 dip 独立设备像素,因为 Android 的设备分辨率众多,
目前主流的为 wvga,而不少老的设备为 hvga 甚至低端的 qvga,对于兼容性来讲使用 dip 无非是比较方便的,因为他和
分辨率无关和屏幕的密度大小有关,因此推荐使用。
px= (int) (dip*density+0.5f) //这里 android 开发网提示你们不少网友获取 density(密度)的方法存在问题,
从资源中获取的是静态定义的,通常为 1.0 对于 HVGA 是正好的,而对于 wvga 这样的应该从 WindowsManager 中获取,
WVGA 为 1.5 这里能够再补充一下 dip,sip 的知识java


3. Android 中动态改变 ImageView 大小大小
不少网友可能发如今 layout.xml 文件中定义了 ImageView 的绝对大小后,没法动态修改之后的大小显示,其实 Android
平台在设计 UI 控件时考虑到这个问题,为了适应不一样的 Drawable 能够经过在 xml 的相关 ImageView 中加入
android:scaleType="fitXY" 这行便可,但由于使用了缩放可能会形成当前 UI 有所变形。
使用的前提是限制 ImageView 所在的层,可使用一个内嵌的方法限制显示。android


4. 如何判断 Android 手机当前是否联网?
若是拟开发一个网络应用的程序,首先考虑是否接入网络,在 Android 手机中判断是否联网能够经过
ConnectivityManager 类的 isAvailable()方法判断,
首先获取网络通信类的实例
ConnectivityManager cwjManager=(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
使用 cwjManager.getActiveNetworkInfo().isAvailable(); 来返回是否有效,若是为 True 则表示当前 Android 手机已
经联网,多是 WiFi 或 GPRS、HSDPA 等等,具体的能够经过 ConnectivityManager 类的 getActiveNetworkInfo() 方法判
断详细的接入方式,须要注意的是有关调用须要加入
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
这个权限,android 开发网提醒你们在真机上 Market 和 Browser 程序都使用了这个方法,来判断是否继续,同时在一些
网络超时的时候也能够检查下网络链接是否存在,以避免浪费手机上的电力资源。git


5. Drawable、、 Bitmap、、 Canvas 和和 Paint 的关系
不少网友刚刚开始学习 Android 平台,对于 Drawable、Bitmap、Canvas 和 Paint 它们之间的概念不是很清楚,其实它
们除了 Drawable 外早在 Sun 的 J2ME 中就已经出现了,可是在 Android 平台中,Bitmap、 Canvas 相关的都有所变化。
首先让咱们理解下 Android 平台中的显示类是 View,可是还提供了底层图形类 android.graphics,今天所说的这些均
为 graphics 底层图形接口。
Bitmap - 称做位图,通常位图的文件格式后缀为 bmp,固然编码器也有不少如 RGB56五、RGB888。做为一种逐像素的显
示对象执行效率高,可是缺点也很明显存储效率低。咱们理解为一种存储对象比较好。
Drawable - 做为 Android 平下通用的图形对象,它能够装载经常使用格式的图像,好比 GIF、PNG、JPG,固然也支持 BMP,
固然还提供一些高级的可视化对象,好比渐变、图形等。
Canvas - 名为画布,咱们能够看做是一种处理过程,使用各类方法来管理 Bitmap、GL 或者 Path 路径,同时它能够配
合 Matrix 矩阵类给图像作旋转、缩放等操做,同时 Canvas 类还提供了裁剪、选取等操做。
Paint - 咱们能够把它看作一个画图工具,好比画笔、画刷。他管理了每一个画图工具的字体、颜色、样式。 若是涉
及一些 Android 游戏开发、显示特效能够经过这些底层图形类来高效实现本身的应用。程序员


6. Activity 切换致使的 onCreate 重复执行
部分网友会发现 Activity 在切换到后台或布局从横屏 LANDSCAPE 切换到 PORTRAIT,会从新切换 Activity 会触发一次
onCreate 方法,
咱们能够在 androidmanifest.xml 中的 activit 元素加入这个属性
android:configChanges="orientation|keyboardHidden" 便可,
好比
<activity android:name=".android123"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
同时在 Activity 的 Java 文件中重载 onConfigurationChanged(Configuration newConfig)这个方法,
这样就不会在布局切换或窗口切换时重载 onCreate 等方法。
代码以下:
@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
if (this.getResources().getConfiguration(). orientation == Configuration.ORIENTATION_LANDSCAPE)
{
//land
}
else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
//port
}
}web


7. Android 的的 ImageButton 问题问题
不少网友对 Android 提供的 ImageButton 有个疑问,当显示 Drawable 图片时就不会再显示文字了,
其实解决的方法有两种,第一种就是图片中就写入文字,可是这样解决会增长程序体积,同时硬编码方式会影响多国语
言的发布。第二种解决方法很简单,经过分析能够看到 ImageButton 的 layout,咱们能够直接直接继承,
添加一个 TextView,对齐方式为右侧便可实现 ImageButton 支持文字右侧显示。正则表达式


8. Android 代码优化技术
1.Java 内存控制
对于字符串操做而言若是须要连加这样的操做建议使用 StringBuilder,通过调试不难发现若是你的字符串每次连加,
使用 String 须要的内存开销会远大于 StringBuilder,而后 Android 手机常规的运行内存大约在 128MB 左右,对于运行多任
务就须要考虑了, Android 开发网提示由于 Java 有 GC 不须要手动释放那么分配的时候就要格外的当心,频繁的 GC 操做仍然
是很影响性能的,在调试时咱们能够经过 logcat 查看内存释放状况。
2.循环使用
平时在访问一个属性的时候效率远比一个固定变量低,若是你的循环估计次数经常大于 5,
假设 xxx.GetLength()方法的值通常大于 5,
推荐这样写,
好比
for(int i=0;i<xxx.GetLength();i++)
这里 xxx.GetLength 在每次循环都要调用,必然会影响程序效率,在游戏开发中显得更为明显,
改进的方法应该为
int j=xxx.GetLength()
for(int i=0;i<j;i++)
3.图片的优化
在 Android 平台中 2 维图像处理库 BitmapFactory 作的比较智能,为了减小文件体积和效率,经常不用不少资源文件,
而把不少小图片放在一个图片中,有切片的方式来完成,在 J2ME 中咱们这样是为了将少文件头而解决 MIDP 这些设备的问题,
而 Android 中虽然机型硬件配置都比较高,有关 Android G1 硬件配置能够参考 G1 手机参数以及评测,可是当资源多时这样
的运行效率仍是使人满意的,至少 Dalvik 优化的还不是很够。算法


9. Android 开发进阶之 NIO 非阻塞包(一一)
对于 Android 的网络通信性能的提升,咱们可使用 Java 上高性能的 NIO (New I/O) 技术进行处理,NIO 是从 JDK 1.4
开始引入的,NIO 的 N 咱们能够理解为 Noblocking 即非阻塞的意思,相对应传统的 I/O,好比 Socket 的 accpet()、read()
这些方法而言都是阻塞的。 NIO 主要使用了 Channel 和 Selector 来实现,Java 的 Selector 相似 Winsock 的 Select 模式,
是一种基于事件驱动的,整个处理方法使用了轮训的状态机,若是你过去开发过 Symbian 应用的话这种方式有点像活动对象,
好处就是单线程更节省系统开销,NIO 的好处能够很好的处理并发,对于 Android 网游开发来讲比较关键, 对于多点 Socket
链接而言使用 NIO 能够大大减小线程使用,下降了线程死锁的几率,毕竟手机游戏有 UI 线程,音乐线程,网络线程,管理
的难度可想而知,同时 I/O 这种低速设备将影响游戏的体验。
NIO 做为一种中高负载的 I/O 模型,相对于传统的 BIO (Blocking I/O)来讲有了很大的提升,处理并发不用太多的线程,
省去了建立销毁的时间,若是线程过多调度是问题,同时不少线程可能处于空闲状态,大大浪费了 CPU 时间,同时过多的线
程多是性能大幅降低,通常的解决方案中可能使用线程池来管理调度但这种方法治标不治本。使用 NIO 可使并发的效率
大大提升。固然 NIO 和 JDK 7 中的 AIO 还存在一些区别,AIO 做为一种更新的固然这是对于 Java 而言,若是你开发过 Winsock
服务器,那么 IOCP 这样的 I/O 完成端口能够解决更高级的负载,固然了今天 Android123 主要给你们讲解下为何使用 NIO
在 Android 中有哪些用处。
NIO 咱们分为几个类型分别描述,做为 Java 的特性之一,咱们须要了解一些新的概念,好比 ByteBuffer 类,Channel,
SocketChannel,ServerSocketChannel,Selector 和 SelectionKey。有关具体的使用,Android 开发网将在明天详细讲解。
网友能够在 Android SDK 文档中看下 java.nio 和 java.nio.channels 两个包了 解。
http://www.android123.com.cn/androidkaifa/695.html
了解下这种技术,看看在立刻要作的项目中是否用获得sql


10. Android Theme 和和 Styles 内部定义解析
昨天咱们讲到的有关在 AndroidManifest.xml 中定义 Activity 的 theme 方法来实现无标题的方法,在使用 xml 让你的
Activity 无标题方法 一文中讲到的,不少网友不明白为何这样作,其实在 Android123 之前的文章中屡次提到了 styles
样式定义方法,
今天 Android 开发网再次把一些网友回顾了解下 android 样式的内部定义。
在一个工程的 res/values/theme.xml 中咱们能够方便的定义本身的风格主题,
好比下面的 cwjTheme 中咱们使用了基于 android 内部的白色调的背景 Theme.Light,
设置 windowsNoTitle 为 true 表明没有标题,背景颜色咱们使用了 android 内部定义的透明,
同时设置 listView 控件的样式为 cwjListView,xml 样式代码以下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="cwjTheme" parent="android:Theme.Light">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:listViewStyle">@style/cwjListView</item>
</style>
有关 ListView 控件咱们自定义的风格就是修改下系统 listview 这个控件的每行分隔符样式,这里咱们在工程下
res/drawable 文件夹下放一个图片名为 list_selector 图片,这样咱们的 cwjListView 的代码能够这样写
<style name="cwjListView" parent="@android:style/Widget.ListView">
<item name="android:listSelector">@drawable/list_selector</item>
</style>
</resources>
经过定义 style 能够设置更多,好比让 cwjListView 的字体颜色就加入 textAppearance 属性,
好比
<item name="textAppearance">@android:style/TextAppearance</item>
等等。


11.Android JSON 解析示例代码
来自 Google 官方的有关 Android 平台的 JSON 解析示例,若是远程服务器使用了 json 而不是 xml 的数据提供,在 Android
平台上已经内置的 org.json 包能够很方便的实现手机客户端的解析处理。下面 Android123 一块儿分析下这个例子,帮助
Android 开发者须要有关 HTTP 通信、正则表达式、JSON 解析、appWidget 开发的一些知识。
public class WordWidget extends AppWidgetProvider { //appWidget
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
context.startService(new Intent(context, UpdateService.class)); //避免 ANR,因此 Widget 中开了个服务
}
public static class UpdateService extends Service {
@Override
public void onStart(Intent intent, int startId) {
// Build the widget update for today
RemoteViews updateViews = buildUpdate(this);
ComponentName thisWidget = new ComponentName(this, WordWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
public RemoteViews buildUpdate(Context context) {
// Pick out month names from resources
Resources res = context.getResources();
String[] monthNames = res.getStringArray(R.array.month_names);
Time today = new Time();
today.setToNow();
String pageName = res.getString(R.string.template_wotd_title, monthNames[today.month],
today.monthDay);
RemoteViews updateViews = null;
String pageContent = "";
try {
SimpleWikiHelper.prepareUserAgent(context);
pageContent = SimpleWikiHelper.getPageContent(pageName, false);
} catch (ApiException e) {
Log.e("WordWidget", "Couldn't contact API", e);
} catch (ParseException e) {
Log.e("WordWidget", "Couldn't parse API response", e);
}
//正则表达式处理,有关定义见下面的 SimpleWikiHelper 类
Pattern pattern = Pattern.compile(SimpleWikiHelper. WORD_OF_DAY_REGEX);
Matcher matcher = pattern.matcher(pageContent);
if (matcher.find()) {
updateViews = new RemoteViews(context.getPackageName(),R.layout.widget_word);
String wordTitle = matcher.group(1) ;
updateViews.setTextViewText(R.id.word_title, wordTitle);
updateViews.setTextViewText(R.id.word_type, matcher.group(2));
updateViews.setTextViewText(R.id.definition, matcher.group(3).trim());
String definePage = res.getString(R.string.template_define_url, Uri.encode(wordTitle));
Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage)); //这里是打开相
应的网页,因此 Uri 是 http 的 url,action 是 view 即打开 web 浏览器
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0 /* no requestCode */,
defineIntent, 0 /* no flags */);
updateViews.setOnClickPendingIntent(R.id.widget, pendingIntent); //单击 Widget 打开
Activity
} else {
updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_message);
CharSequence errorMessage = context.getText(R.string.widget_error);
updateViews.setTextViewText(R.id.message, errorMessage);
}
return updateViews;
}
@Override
public IBinder onBind(Intent intent) {
// We don't need to bind to this service
return null;
}
}
}
有关网络通信的实体类,以及一些常量定义以下:
public class SimpleWikiHelper {
private static final String TAG = "SimpleWikiHelper";
public static final String WORD_OF_DAY_REGEX =
"(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}";
private static final String WIKTIONARY_PAGE =
"http://en.wiktionary.org/w/api.php?action=query&prop=revisions&titles=%s&" +
"rvprop=content&format=json%s";
private static final String WIKTIONARY_EXPAND_TEMPLATES =
"&rvexpandtemplates=true";
private static final int HTTP_STATUS_OK = 200;
private static byte[] sBuffer = new byte[512];
private static String sUserAgent = null;
public static class ApiException extends Exception {
public ApiException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
} public ApiException(String detailMessage) {
super(detailMessage);
}
}
public static class ParseException extends Exception {
public ParseException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
}
public static void prepareUserAgent(Context context) {
try {
// Read package name and version number from manifest
PackageManager manager = context.getPackageManager();
PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
sUserAgent = String.format(context.getString(R.string.template_user_agent),
info.packageName, info.versionName);
} catch(NameNotFoundException e) {
Log.e(TAG, "Couldn't find package information in PackageManager", e);
}
}
public static String getPageContent(String title, boolean expandTemplates)
throws ApiException, ParseException {
String encodedTitle = Uri.encode(title);
String expandClause = expandTemplates ? WIKTIONARY_EXPAND_TEMPLATES : "";
String content = getUrlContent(String.format(WIKTIONARY_PAGE, encodedTitle, expandClause));
try {
JSONObject response = new JSONObject(content);
JSONObject query = response.getJSONObject("query");
JSONObject pages = query.getJSONObject("pages");
JSONObject page = pages.getJSONObject((String) pages.keys().next());
JSONArray revisions = page.getJSONArray("revisions");
JSONObject revision = revisions.getJSONObject(0);
return revision.getString("*");
} catch (JSONException e) {
throw new ParseException("Problem parsing API response", e);
}
}
protected static synchronized String getUrlContent(String url) throws ApiException {
if (sUserAgent == null) {
throw new ApiException("User-Agent string must be prepared");
}
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(url);
request.setHeader("User-Agent", sUserAgent) ; //设置客户端标识
try {
HttpResponse response = client.execute(request);
StatusLine status = response.getStatusLine();
if (status.getStatusCode() != HTTP_STATUS_OK) {
throw new ApiException("Invalid response from server: " +status.toString());
}
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent(); //获取 HTTP 返回的数据流
ByteArrayOutputStream content = new ByteArrayOutputStream();
int readBytes = 0;
while ((readBytes = inputStream.read(sBuffer)) != -1) {
content.write(sBuffer, 0, readBytes); //转化为字节数组流
}
return new String(content.toByteArray()); //从字节数组构建 String
} catch (IOException e) {
throw new ApiException("Problem communicating with API", e);
}
}
}
有关整个每日维基的 widget 例子比较简单,主要是帮助你们积累经常使用代码,
了解 Android 平台 JSON 的处理方式,毕竟不少 Server 仍是 Java 的。

 


12.Android 中使用定时器 TimerTask 类介绍
在 Android 平台中须要反复按周期执行方法可使用 Java 上自带的 TimerTask 类,
TimerTask 相对于 Thread 来讲对于资源消耗的更低,
除了使用 Android 自带的 AlarmManager 使用 Timer 定时器是一种更好的解决方法。
咱们须要引入 import java.util.Timer; 和 import java.util.TimerTask;
private Timer mTimer = new Timer(true);
private TimerTask mTimerTask;
mTimerTask = new TimerTask()
{
public void run()
{
Log.v("android123","cwj");
}
};
mTimer.schedule(mTimerTask, 5000,1000); //在 1 秒后每 5 秒执行一次定时器中的方法,
好比本文为调用 log.v 打印输出。
若是想取消能够调用下面方法,取消定时器的执行
while(!mTimerTask.cancel());
mTimer.cancel();
最后 Android123 提示你们,若是处理的东西比较耗时仍是开个线程比较好,
Timer 仍是会阻塞主线程的执行,更像是一种消息的执行方式。
固然比 Handler 的 postDelay 等方法更适合处理计划任务。

 


13.Android 应用应用 Icon 大小在不一样分辨率下定义
对于 Android 平台来讲,不一样分辨率下 Icon 的大小设计有着不一样的要求,对于目前主流的 HDPI 即 WVGA 级别来讲,一般 hdpi
的应用 icon 大小为 72x72,而标准的 mdpi 即 hvga 为 48x48,对于目前 HTC 和 Motorola 推出的一些 QVGA 的使用了 ldpi,图
标为 32x32,
常见的 Android 图标大小设计规范以下表所示:
Launcher::::
36 x 36 px
48 x 48 px
72 x 72 px
MenuMenuMenu::::
36 x 36 px
48 x 48 px
72 x 72 px
Status Bar:
24 x 24 px
32 x 32 px
48 x 48 px
TabTabTab::::
24 x 24 px
32 x 32 px
48 x 48 px
Dialog::::
24 x 24 px
32 x 32 px
48 x 48 px
List View::::
24 x 24 px
32 x 32 px
48 x 48 px
对于 android 界面设计的安全色,以下:
而对于系统自带默认程序的图标,下面为 png 的透明格式,直接鼠标右键另存为便可:
看看 sdk 文档上的关于界面图标的详细说明。

 


14.Android 控件美化 Shape 你会用吗?
若是你对 Android 系统自带的 UI 控件感受不够满意,能够尝试下自定义控件,咱们就以 Button 为例, 很早之前 Android123
就写到过 Android Button 按钮控件美化方法里面提到了 xml 的 selector 构造。固然除了使用 drawable 这样的图片外今天
Android 开发网谈下自定义图形 shape 的方法,对于 Button 控件 Android 上支持如下几种属性 shape、gradient、stroke、
corners 等。
咱们就以目前系统的 Button 的 selector 为例说下:
<shape>
<gradient
android:startColor="#ff8c00"
android:endColor="#FFFFFF"
android:angle="270" />
<stroke
android:width="2dp"
android:color="#dcdcdc" />
<corners
android:radius="2dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
</shape>
对于上面,这条 shape 的定义,分别为渐变,
在 gradient 中
startColor 属性为开始的颜色,
endColor 为渐变结束的颜色,
下面的 angle 是角度。接下来是 stroke 能够理解为边缘,
corners 为拐角这里 radius 属性为半径,最后是相对位置属性 padding。
对于一个 Button 完整的定义能够为
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" >
<shape>
<gradient
android:startColor="#ff8c00"
android:endColor="#FFFFFF"
android:angle="270" />
<stroke
android:width="2dp"
android:color="#dcdcdc" />
<corners
android:radius="2dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
</shape>
</item> <item android:state_focused="true" >
<shape>
<gradient
android:startColor="#ffc2b7"
android:endColor="#ffc2b7"
android:angle="270" />
<stroke
android:width="2dp"
android:color="#dcdcdc" />
<corners
android:radius="2dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
</shape>
</item> <item>
<shape>
<gradient
android:startColor="#ff9d77"
android:endColor="#ff9d77"
android:angle="270" />
<stroke
android:width="2dp"
android:color="#fad3cf" />
<corners
android:radius="2dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
</shape>
</item>
</selector> 注意 Android123 提示你们,以上几个 item 的区别主要是体如今 state_pressed 按下或 state_focused 获
得焦点时,当当来判断显示什么类型,而没有 state_xxx 属性的 item 能够看做是常规状态下。
15. Android 开发者应该保持如下特质
Android123 推荐新手应该遵循
1. 深读 SDK 文档
2. 深读 SDK 的 APIDemo 和 Samples
3. 掌握 GIT 开源代码
4. 多了解 Android 开源项目,学习别人的手法写程序。
16. Android 数组排序常见方法
Android 的数组排序方式基本上使用了 Sun 原生的 Java API 实现,经常使用的有 Comparator 接口实现 compare 方法和
Comparable 接口的 compareTo 方法,咱们对于一个数组列表好比 ArrayList 能够经过这两个接口进行排序和比较,这里
Android123 给你们一个例子
private final Comparator cwjComparator = new Comparator() {
private final Collator collator = Collator.getInstance();
public final int compare(Object a, Object b) {
CharSequence a = ((Item) a).sName;
CharSequence b = ((Item) b).sID;
return collator.compare(a, b);
}
};
咱们的 ArrayList 对象名为 mList,则执行排序能够调用方法 Collections.sort(mList, cwjComparator);
17.Android 控件控件 TextProgressBar 进度条上显文字
Android 系统的进度条控件默认的设计的不是很周全,好比没有包含文字的显示,那么如何在 Android 进度条控件上显
示文字呢? 来自 Google 内部的代码来了解下,主要使用的 addView 这样的方法经过覆盖一层 Chronometer 秒表控件来实现,
整个代码以下
public class TextProgressBar extends RelativeLayout implements OnChronometerTickListener {
public static final String TAG = "TextProgressBar";
static final int CHRONOMETER_ID = android.R.id.text1;
static final int PROGRESSBAR_ID = android.R.id.progress;
Chronometer mChronometer = null;
ProgressBar mProgressBar = null;
long mDurationBase = -1;
int mDuration = -1;
boolean mChronometerFollow = false;
int mChronometerGravity = Gravity.NO_GRAVITY;
public TextProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public TextProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TextProgressBar(Context context) {
super(context);
} //Android 开发网提示关键部分在这里
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
int childId = child.getId();
if (childId == CHRONOMETER_ID && child instanceof Chronometer) {
mChronometer = (Chronometer) child;
mChronometer.setOnChronometerTickListener(this);
// Check if Chronometer should move with with ProgressBar
mChronometerFollow = (params.width == ViewGroup.LayoutParams.WRAP_CONTENT);
mChronometerGravity = (mChronometer.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK);
} else if (childId == PROGRESSBAR_ID && child instanceof ProgressBar) {
mProgressBar = (ProgressBar) child;
}
}
@android.view.RemotableViewMethod
public void setDurationBase(long durationBase) {
mDurationBase = durationBase;
if (mProgressBar == null || mChronometer == null) {
throw new RuntimeException("Expecting child ProgressBar with id " +
"'android.R.id.progress' and Chronometer id 'android.R.id.text1'");
}
// Update the ProgressBar maximum relative to Chronometer base
mDuration = (int) (durationBase - mChronometer.getBase());
if (mDuration <= 0) {
mDuration = 1;
}
mProgressBar.setMax(mDuration);
}
public void onChronometerTick(Chronometer chronometer) {
if (mProgressBar == null) {
throw new RuntimeException(
"Expecting child ProgressBar with id 'android.R.id.progress'");
}
// Stop Chronometer if we're past duration
long now = SystemClock.elapsedRealtime();
if (now >= mDurationBase) {
mChronometer.stop();
} int remaining = (int) (mDurationBase - now);
mProgressBar.setProgress(mDuration - remaining);
if (mChronometerFollow) {
RelativeLayout.LayoutParams params;
params = (RelativeLayout.LayoutParams) mProgressBar.getLayoutParams();
int contentWidth = mProgressBar.getWidth() - (params.leftMargin + params.rightMargin);
int leadingEdge = ((contentWidth * mProgressBar.getProgress()) /
mProgressBar.getMax()) + params.leftMargin;
int adjustLeft = 0;
int textWidth = mChronometer.getWidth() ;
if (mChronometerGravity == Gravity.RIGHT) {
adjustLeft = -textWidth;
} else if (mChronometerGravity == Gravity.CENTER_HORIZONTAL) {
adjustLeft = -(textWidth / 2);
}
leadingEdge += adjustLeft;
int rightLimit = contentWidth - params. rightMargin - textWidth;
if (leadingEdge < params.leftMargin) {
leadingEdge = params.leftMargin;
} else if (leadingEdge > rightLimit) {
leadingEdge = rightLimit;
}
params = (RelativeLayout.LayoutParams) mChronometer.getLayoutParams();
params.leftMargin = leadingEdge;
mChronometer.requestLayout();
}
}
}
18. Android 内存管理-SoftReference 的使用
不少时候咱们须要考虑 Android 平台上的内存管理问题,Dalvik VM 给每一个进程都分配了必定量的可用堆内存,当咱们
处理一些耗费资源的操做时可能会产生 OOM 错误(OutOfMemoryError)这样的异常,Android123 观察了下国内的相似 Market
客户端设计,基本上都没有采用很好的内存管理机制和缓存处理。
若是细心的网友可能发现 Android Market 客户端载入时,每一个列表项的图标是异步刷新显示的,但当咱们快速的往下
滚动到必定数量好比 50 个,再往回滚动时可能咱们看到了部分 App 的图标又从新开始加载,固然这一过程多是从 SQLite
数据库中缓存的,可是在内存中已经经过相似 SoftReference 的方式管理内存。
在 Java 中内存管理,引用分为四大类,强引用 HardReference、弱引用 WeakReference、软引用 SoftReference 和虚引
用 PhantomReference。它们的区别也很明显,HardReference 对象是即便虚拟机内存吃紧抛出 OOM 也不会致使这一引用的对
象被回收,而 WeakReference 等更适合于一些数量很少,但体积稍微庞大的对象,在这四个引用中,它是最容易被垃圾回收
的,而咱们对于显示相似 Android Market 中每一个应用的 App Icon 时能够考虑使用 SoftReference 来解决内存不至于快速回
收,同时当内存短缺面临 Java VM 崩溃抛出 OOM 前时,软引用将会强制回收内存,最后的虚引用通常没有实际意义,仅仅观
察 GC 的活动状态,对于测试比较实用同时必须和 ReferenceQueue 一块儿使用。
对于一组数据,咱们能够经过 HashMap 的方式来添加一组 SoftReference 对象来临时保留一些数据,同时对于须要反复
经过网络获取的不常常改变的内容,能够经过本地的文件系统或数据库来存储缓存,但愿给国内作 App Store 这样的客户端
一些改进建议。
19. 反射在 Android 开发中的利弊
因为 Android 2.2 的推出,不少新的 API 加入致使不少项目移植须要考虑使用 Java 的反射机制 Reflection 来动态调用,
动态调用的好处就是不须要使用引用文件,直接经过 JDK 中声明好的方法直接调用,自己原理基于 JVM 的,从 Java 1.5 开始
支持,原理上就是根据类名而不实例化对象的状况下,得到对象的方法或属性而直接调用。
Android 开发时反射能帮助咱们多少?
1. 有些网友可能发现 Android 的 SDK 比较封闭,不少敏感的方法常规的用户没法编译,咱们若是翻看了 代码直接在反
射中声明动态调用便可。好比不少 internal 或 I 开头的 AIDL 接口都可以经过反射轻松调用。
2. 反射对于 Android123 来讲更重要的是考虑到应用的兼容性,咱们目前主要兼容从 Android 1.5 到 2.2 的项目,API
Level 从 3 到 8 能够方便的扩充,调用前咱们预留一个标志位声明该 API 的最低以及最高的 API Level 为多少能够调用。
3. 对于调试 Java 的反射是功臣了,在 Logcat 中咱们能够看到出错的地方确定有相似 java.lang.reflect.XXX 的字样,
这种自检机制能够帮助咱们方便的调试 Android 应用程序。
反射的缺点有哪些:
(1). 由于是动态执行的,效率天然没有预编译时引用现有的库效率高,就像平时咱们 Win32 开发时,能够不用 h 文件,
直接经过 GetProcAddress 同样去动态获取方法的地址。固然效率要根据复杂程度而决定,通常稍微复杂的处理性能损失可
能超过 20%,对于一些复杂的涉及 Java 自动类型转换判断,执行时间多是直接引用的上千倍,因此最终咱们调试时必须考
虑性能问题。
(2). 由于反射是动态的,因此须要处理不少异常,否则 Dalvik 崩溃出 Force Close 的几率会大不少,很简单的一个反
射就须要至少 3 个异常捕获,自己 try-catch 效率就不是很高,天然进一步影响运行效率,对于 Android 开发咱们必须考虑
这些问题。
(3). 反射由于致使代码臃肿,天然稍微复杂的几个方法实用反射将会致使代码可读性和维护性下降,若是很抽象的调
用 Android 开发网强烈不推荐这种方法。 最后要说的是 Reflection 并非 Java 的专利, 微软的.Net 也一样支持,同时
更多的动态语言如 Ruby 等均支持这一特性。
20.AsyncTask 对比对比 Thread 加加 Handler
不少网友可能发现 Android 平台不少应用使用的都是 AsyncTask,而并不是 Thread 和 Handler 去更新 UI,
这里 Android123 给你们说下他们到底有什么区别,咱们平时应该使用哪一种解决方案。
从 Android 1.5 开始系统将 AsyncTask 引入到 android.os 包中,过去在很早 1.1 和 1.0 SDK 时其实官方将其命名为
UserTask,其内部是 JDK 1.5 开始新增的 concurrent 库,作过 J2EE 的网友可能明白并发库效率和强大性,比 Java 原始的
Thread 更灵活和强大,但对于轻量级的使用更为占用系统资源。
Thread 是 Java 早期为实现多线程而设计的,比较简单不支持 concurrent 中不少特性在同步和线程池类中须要本身去实
现不少的东西,对于分布式应用来讲更须要本身写调度代码,而为了 Android UI 的刷新 Google 引入了 Handler 和 Looper
机制,它们均基于消息实现,有事可能消息队列阻塞或其余缘由没法准确的使用。
Android 开发网推荐你们使用 AsyncTask 代替 Thread+Handler 的方式,不只调用上更为简单,通过实测更可靠一些,
Google 在 Browser 中大量使用了异步任务做为处理耗时的 I/O 操做,好比下载文件、读写数据库等等,它们在本质上都离不
开消息,可是 AsyncTask 相比 Thread 加 Handler 更为可靠,更易于维护,但 AsyncTask 缺点也是有的好比一旦线程开启即
dobackground 方法执行后没法给线程发送消息,仅能经过预先设置好的标记来控制逻辑,固然能够经过线程的挂起等待标志
位的改变来通信,对于某些应用 Thread 和 Handler 以及 Looper 可能更灵活。
21. Android Drawable 叠加处理方法
你们可能知道 Bitmap 的叠加处理在 Android 平台中能够经过 Canvas 一层一层的画就好了,
而 Drawable 中如何处理呢? 除了使用 BitmapDrawable 的 getBitmap 方法将 Drawable 转换为 Bitmap 外,今天 Android123
给你们说下好用简单的 LayerDrawable 类,LayerDrawable 顾名思义就是层图形对象。
下面直接用一个简单的代码表示:
Bitmap bm = BitmapFactory.decodeResource(getResources(),R.drawable.cwj);
Drawable[] array = new Drawable[3];
array[0] = new PaintDrawable(Color.BLACK); //黑色
array[1] = new PaintDrawable(Color.WHITE); //白色
array[2] = new BitmapDrawable(bm); //位图资源
LayerDrawable ld = new LayerDrawable(array); //参数为上面的 Drawable 数组
ld.setLayerInset(1, 1, 1, 1, 1); //第一个参数 1 表明数组的第二个元素,为白色
ld.setLayerInset(2, 2, 2, 2, 2); //第一个参数 2 表明数组的第三个元素,为位图资源
mImageView.setImageDrawable(ld);
上面的方法中 LayerDrawable 是关键,Android 开发网提示 setLayerInset
方法原型为 public void setLayerInset (int index, int l, int t, int r, int b) 其中第一个参数为层的索引号,
后面的四个参数分别为 left、top、right 和 bottom。
对于简单的图片合成咱们能够将第一和第二层的 PaintDrawable 换成 BitmapDrawable 便可实现简单的图片合成。
22. onRetainNonConfigurationInstance 和和 getLastNonConfigurationInstance
不少网友可能知道 Android 横竖屏切换时会触发 onSaveInstanceState,
而还原时会产生 onRestoreInstanceState,
可是 Android 的 Activity 类还有一个方法名为
onRetainNonConfigurationInstance 和 getLastNonConfigurationInstance 这两个方法。
咱们能够经过
onRetainNonConfigurationInstance 代替 onSaveInstanceState,好比距离 2
@Override
public Object onRetainNonConfigurationInstance()
{
//这里须要保存的内容,在切换时不是 bundle 了,咱们能够直接经过 Object 来代替
return obj;
}
在恢复窗口时,咱们能够不使用 onRestoreInstanceState,而代替的是 getLastNonConfigurationInstance 方法。
咱们能够直接在 onCreate 中使用,好比
Object obj = getLastNonConfigurationInstance();
最终 obj 的内容就是上次切换时的内容。
这里 Android123 提醒你们,每次 Activity 横竖屏切换时 onCreate 方法都会被触发。
23. Android 中中 String 资源文件的 format 方法方法
不少时候咱们感性 Google 在设计 Android 时遵照了大量 MVC 架构方式,可让写公共代码、美工和具体逻辑开发人员
独立出来。
有关 Android 的资源文件 values/strings.xml 中如何实现格式化字符串呢?
这里 Android123 举个简单的例子,以及最终可能会用到哪些地方。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">cwj_Demo</string>
<string name="hello">android 开发网</string>
</resources>
上面是一段简单的字符串资源文件,没有用到格式化,由于比较简单直接描述了意思,
当咱们设计一个相似 Delete xxx File ? 的时候,咱们可能须要在 Java 中动态获取 xxx 的名称,因此定义资源时使
用格式化能够轻松解决,不须要一堆 String 去拼接或 StringBuffer 一个一个 append 这样的愚蠢方法,
看例子
<string name="alert">Delete %1$s File</string>
这里%1$s 表明这是一个字符串型的,若是是整数型能够写为%1$d,相似 printf 这样的格式化字符串函数,
固然若是包含了多个须要格式化的内容,则第二个能够写为%2$s 或%2$d 了,那么最终在 Java 中如何调用呢? 看下面的
例子:
例一: 整数型的
<string name="alert">I am %1$d years old</string> 定义的是这样的
固然,咱们杜绝意外状况,好比冒出个 secret 这样的 string 类型的,注意上面是%1$d 不是%1$s,因此默认标准的合并
成为
int nAge=23;
String sAgeFormat = getResources().getString(R.string.alert);
String sFinalAge = String.format(sAgeFormat, nAge);
这样执行完后,就组成了 I am 23 years old,是否是很方便啊.
固然了,下面看下 String 字符串时的状况.
例二: 字符串型的
String sName="cwj"
String sCity="Shanghai"
资源定义为
<string name="alert2">My name is %1$s , I am form %2$s</string>
则 Java 中只须要 String sInfoFormat = getResources().getString(R.string.alert2);
String sFinalInfo=String.format(sInfoFormat, sName, sCity);
咱们看到了整个,整个定义相似 MFC 的 CString::Format 或 Mac OS 中的 NSLog,可是须要显示相似 C#中那样显示的标
出参数的数字,好比%1 或%n,这里数字表明参数的第 n 个。
本行最终 sFinalInfo 显示的内容为 My name is cwj , I am form Shanghai 。
固然了你有什么不懂的地方能够来函至 android123@163.com
24. Android 工程内嵌资源文件的两种方法
Android 软件通常处理大的资源经过 sdcard 好比在线下载资源到 sdcard,
而 apk 中内嵌资源或二进制文件时通常使用下面的两种方法:
方法一
res/raw 目录下存放,好比 cwj.dat 一个二进制文件,咱们能够读取能够直接
InputStream is=context.getResources().openRawResource(R.raw.cwj);
方法二
工程根目录下的 assets 文件夹中存放,好比 assets/cwj.dat 这样咱们使用下面的代码
AssetManager am = context.getAssets();
InputStream is = am.open(cwj.dat);
这里 Android123 提示你们 Google 的 Android 系统处理 Assert 有个 bug,在 AssertManager 中不能处理单个超过 1MB 的
文件,否则会报异常具体数值你们能够测试下传个稍大的文件,咱们在两年前的文章中有提到,而第一种 raw 没这个限制可
以放个 4MB 的 Mp3 文件没问题。
25. Android 自定义 View 以及以及 layout 属性全攻略
对于 Android 系统的自定义 View 可能你们都熟悉了,对于自定义 View 的属性添加,以及 Android 的 Layout 的命名空
间问题,不少网友还不是很清楚,今天 Android123 一块儿再带你们温习一下
CwjView myView=new CwjView(context);
若是用于游戏或整个窗体的界面,
咱们可能直接在 onCreate 中 setContentView(myView); 固然若是是控件,咱们可能会须要从 Layout 的 xml 中声明,比

<cn.com.android123.CwjView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
固然,咱们也能够直接从父类声明好比
<View class="cn.com.android123.CwjView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
上面咱们仅用了父类 View 的两个属性,均来自 android 命名空间,而名称为 layout_width 或 layout_height,咱们自
定义的控件可能有更多的功能,好比
<cn.com.android123.CwjView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
cwj:age="22"
cwj:university="sjtu"
cwj:city="shanghai"
/>
咱们能够看到上面的三个属性,是咱们自定义的。
做为标准 xml 规范,可能还包含了相似
xmlns:android="http://schemas.android.com/apk/res/android" 这样的语句,对于定义完整的 View,咱们的命名空
间为 cwj,
这里能够写为
xmlns:cwj=http://schemas.android.com/apk/res/cn.com.android123.cwjView 或
xmlns:cwj=http://schemas.android.com/apk/res/android 均可以。
对于定义的 cwj 命名空间和 age、university 以及 city 的三个属性咱们如何定义呢? 在工程的 res/values 目录中咱们
新建一个 cwj_attr.xml 文件,编码方式为 utf-8 是一个好习惯,内容以下
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<declare-styleable name="CwjView">
<attr name="age" format="integer" />
<attr name="city" format="string" />
<attr name="university" format="string" />
</declare-styleable>
</resources>
这里咱们可能对 format 不是很熟悉,目前 Android 系统内置的格式类型有 integer 好比 ProgressBar 的进度值,float
好比 RatingBar 的值多是 3.5 颗星,boolean 好比 ToggleButton 的是否勾选,string 好比 TextView 的 text 属性,
固然除了咱们常见的基础类型外,Android 的属性还有特殊的好比 color 是用于颜色属性的,能够识别为#FF0000 等类
型,固然还有 dimension 的尺寸类型,好比 23dip,15px,18sp 的长度单位,还有一种特殊的为 reference,通常用于
引用@+id/cwj @drawable/xxx 这样的类型。 固然何时用 reference 呢? 咱们就以定义一个颜色为例子, <attr
name="red" format="color|reference" /> 这里咱们用了逻辑或的运算符,定义的红色是颜色类型的,同时能够被引
用 固然,对于咱们自定义的类中,咱们须要使用一个名为 obtainStyledAttributes 的方法来获取咱们的定义。在我
们自定义 View 的构造方法(Context context, AttributeSet attrs)的重载类型中能够用
public CwjView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.cwj_attr);
mAge = a.getInteger(R.styleable.CwjView_age, 22);
mCity = a.getString(R.styleable.CwjView_city, "shanghai");
mUniversity= a.getString(R.styleable.CwjView_university, "sjtu");
a.recycle(); //Android123 提示你们不要忘了回收资源
}
这样类的全局成员变量 mAge、mCity 就获取了咱们须要的内容,固然根据 layout 中的数值咱们自定义的 CwjView 须要
动态的处理一些数据的状况,可使用 AttributeSet 类的 getAttributeResourceValue 方法获取。
public CwjView(Context context, AttributeSet attrs)
{
super(context, attrs);
resId = attrs.getAttributeResourceValue("cn.com.android123.CwjView", "age", 100);
resId = attrs.getAttributeResourceValue("cn.com.android123.CwjView", "city", "shanghai");
//resID 就能够任意使用了
}
以上两种方法中,参数的最后一个数值为默认的,若是您有不明白的地方能够来函到 android123@163.com 咱们会在第
一时间回复。
26. 自定义 Android 主题风格 theme.xml 方法方法
在 Android 中能够经过自定义主题风格方式来实现个性化以及复用,
首先咱们建立 theme.xml 主题文件,保存位置为工程的 res/values/theme.xml ,
这里咱们能够能够为主题起一个名称,好比 CWJ,这里去除了 xml 的文件头
<?xml version="1.0" encoding="utf-8"?>这行,
咱们在工程中只需在 androidmanifest.xml 文件的 Activity 节点中加入
android:theme="@style/Theme.CWJ" 属性,则这个 Activity 就使用了这种主题风格,
整个 xml 的关键代码以下:
<resources>
<style name="Theme.CWJ" parent="android:Theme">
<item name="android:windowBackground">@drawable/android123</item>
</style>
</resources>
其中上面的代码中,咱们定义设置全局 android:windowBackground 即背景值为/res/drawable 中的 android123 图片为
背景,更多的属性定义能够参考 view 的 layout xml 属性设置,好比咱们设置全部字体颜色、大致大小和样式,能够在 style
节点中加入
<item name="android:textColor">#fff</item>
<item name="android:textSize">14sp</item>
<item name="android:textStyle">bold</item>
固然咱们能够将上面的 android123 的图片改进下,使用一个 xml 文件替代,好比使用 bitmap 对象,则
/res/drawable/android123.xml 的完整代码变为
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/cwj_image"
android:tileMode="repeat" />
这里咱们使用了一个 bitmap 对象来解析 cwj_image 图片,固然这里能够识别各类类型的图片,
其中 android:tileMode 是 bitmap 的内部属性,其中 tileMode 设置为 repeat 表明重复,这样能够节省 bitmap 资源,
好比咱们的背景是一层楼,那么全屏能够显示一样的为 5 层效果,而图片仅是一层大小,对于资源利用相对更高。
固然 bitmap 的属性 tileMode 的值为 repeat 外还有其余的值好比 clamp、mirror,这些值并无在 SDK 中并无找到定
义,经过上次 Android 开发网的 Android 自定义 View 以及 layout 属性全攻略 一文,咱们能够联想到 bitmap 属于
android.graphics.Bitmap 包,因为是 android 框架,因此下载 git 的 base 包,找到该类,类的实例化时 android123 已经
在 Android 自定义 View 以及 layout 属性全攻略 说的很清楚,因此咱们定位到 res\values 中找到 attr.xml 有关 bitmap 的
定义便可,有关 bitmap 的更多属性如 antialias、filter 和 dither 均可以找到使用。
27. android 调试工具 monkey 压力测试实战
不少 Android 开发者可能由于没有充分测试本身的软件形成很容易出现 FC(Force Close)的问题,这里咱们能够经过使
用 Android 固件中自带的 monkey 工具来作软件的压力测试,monkey 工具能够模拟各类按键,触屏,轨迹球、activity 等事
件,这里 Android123 提示你们说白了 monkey 就是一个小猴子随机狂玩你的 android 软件,看看会不会产生异常。 具体
的使用咱们经过 Android SDK 给咱们的 adb 调试桥连接设备或模拟器,进入 Linux Shell 状态,固然咱们能够输入 adb shell
获取设备的 shell,也能够直接经过 adb 命令执行,好比说 adb shell monkey 来查看 monkey 工具中的参数说明,如
图:
咱们要测试的 apk 文件要在 android 设备中已经安装,固然模拟器中也能够测试的。执行 adb shell monkey -p
cn.com.android123.cwj -v 100 咱们执行这句的中包含了 p 参数,这里表明已安装软件的 packageName,而 v 表明查看 monkey
生成的详细随机事件名,最后的数字 100 为咱们测试的随机事件数量为 100.有关更多的测试方法,请查看上图中的参数,整
个测试比较简单单颇有效,不妨试试。
28. 自定义 View
有关 Android 的自定义 View 的框架今天咱们一块儿讨论下,对于常规的游戏,
咱们在 View 中须要处理如下几种问题:
1.控制事件
2.刷新 View
3. 绘制 View
1. 对于控制事件今天咱们只处理按键事件 onKeyDown,之后的文章中将会讲到屏幕触控的具体处理 onTouchEvent 以及
Sensor 重力感应等方法。
2. 刷新 view 的方法这里主要有 invalidate(int l, int t, int r, int b) 刷新局部,四个参数分别为左、上、右、
下。整个 view 刷新 invalidate(),刷新一个矩形区域 invalidate(Rect dirty) ,刷新一个特性 Drawable,
invalidateDrawable(Drawable drawable) ,执行 invalidate 类的方法将会设置 view 为无效,最终致使 onDraw 方法被重
新调用。因为今天的 view 比较简单,Android123 提示你们若是在线程中刷新,除了使用 handler 方式外,能够在 Thread 中
直接使用 postInvalidate 方法来实现。
3. 绘制 View 主要是 onDraw()中经过形参 canvas 来处理,相关的绘制主要有 drawRect、drawLine、drawPath 等等。
view 方法内部还重写了不少接口,其回调方法能够帮助咱们判断出 view 的位置和大小,
好比
onMeasure(int, int) Called to determine the size requirements for this view and all of its children. 、
onLayout(boolean, int, int, int, int) Called when this view should assign a size and position to all of its
children 和 onSizeChanged(int, int, int, int) Called when the size of this view has changed. 具体的做用,
你们能够用 Logcat 获取当 view 变化时每一个形参的变更。
下面 cwjView 是咱们为从此游戏设计的一个简单自定义 View 框架,咱们能够看到在 Android 平台自 定义 view 仍是很简
单的,同时 Java 支持多继承能够帮助咱们不断的完善复杂的问题。
public class cwjView extends View {
public cwjView(Context context) {
super(context);
setFocusable(true); //容许得到焦点
setFocusableInTouchMode(true); //获取焦点时容许触控
}
@Override
protected Parcelable onSaveInstanceState() { //处理窗口保存事件
Parcelable pSaved = super.onSaveInstanceState();
Bundle bundle = new Bundle();
//dosomething
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) { //处理窗口还原事件
Bundle bundle = (Bundle) state;
//dosomething
super.onRestoreInstanceState(bundle.getParcelable("cwj"));
return;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) //处理窗口大小变化事件
{
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec); //若是不让父类处理记住调用 setMeasuredDimension
}
@Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom)
{
super.onLayout (changed,left,top, ight,bottom) ;
}
@Override
protected void onDraw(Canvas canvas) {
Paint bg = new Paint();
bg.setColor(Color.Red);
canvas.drawRect(0, 0, getWidth()/2, getHeight()/2, bg); //将 view 的左上角四分之一填充为红色
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event); //让父类处理屏幕触控事件
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) { //处理按键事件,响应的轨迹球事件为 public boolean
onTrackballEvent (MotionEvent event)
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
break;
case KeyEvent.KEYCODE_DPAD_CENTER: //处理中键按下
break;
default:
return super.onKeyDown(keyCode, event);
}
return true;
} } 上面咱们能够看到 onMeasure 使用的是父类的处理方法,若是咱们须要解决自定义 View 的大小,能够尝试下
面的方法
@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{
height = View.MeasureSpec.getSize(heightMeasureSpec);
width = View.MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width,height);//这里面是原始的大小,须要从新计算能够修改本行
//dosomething
}
29. Canvas 和和 Paint 实例实例
昨天咱们在 Android 游戏开发之旅三 View 详解中提到了 onDraw 方法,
有关详细的实现咱们今天主要说下 Android 的 Canvas 和 Paint 对象的使用实例。
Canvas 类主要实现了屏幕的绘制过程,其中包含了不少实用的方法,好比绘制一条路径、区域、贴图、画点、 画线、渲
染文本,下面是 Canvas 类经常使用的方法,固然 Android 开发网提示你们不少方法有不一样的重载版本,参数更灵活。

void drawRect(RectF rect, Paint paint) //绘制区域,参数一为 RectF 一个区域
void drawPath(Path path, Paint paint) //绘制一个路径,参数一为 Path 路径对象
void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) //贴图,参数一就是咱们常规的 Bitmap 对
象,参数二是源区域(Android123 提示这里是 bitmap),参数三是目标区域(应该在 canvas 的位置和大小),参数四是 Paint
画刷对象,由于用到了缩放和拉伸的可能,当原始 Rect 不等于目标 Rect 时性能将会有大幅损失。
void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) //画线,参数一块儿始点
的 x 轴位置,参数二起始点的 y 轴位置,参数三终点的 x 轴水平位置,参数四 y 轴垂直位置,最后一个参数为 Paint 画刷对
象。
void drawPoint(float x, float y, Paint paint) //画点,参数一水平 x 轴,参数二垂直 y 轴,第三个参数为 Paint
对象。
void drawText(String text, float x, float y, Paint paint) //渲染文本,Canvas 类除了上面的还能够描绘文字,
参数一是 String 类型的文本,参数二 x 轴,参数三 y 轴,参数四是 Paint 对象。
void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) //在路径上绘制文
本,相对于上面第二个参数是 Path 路径对象 从上面来看咱们能够看出 Canvas 绘制类比较简单同时很灵活,实现通常的
方法一般没有问题,同时能够叠加的处理设计出一些效果,不过细心的网友可能发现最后一个参数均为 Paint 对象。若是我
们把 Canvas 当作绘画师来看,那么 Paint 就是咱们绘画的工具,好比画笔、画刷、颜料等等。
Paint 类经常使用方法:
void setARGB(int a, int r, int g, int b) 设置 Paint 对象颜色,参数一为 alpha 透明通道
void setAlpha(int a) 设置 alpha 不透明度,范围为 0~255
void setAntiAlias(boolean aa) //是否抗锯齿
void setColor(int color) //设置颜色,
这里 Android 内部定义的有 Color 类包含了一些常见颜色定义
void setFakeBoldText(boolean fakeBoldText) //设置伪粗体文本
void setLinearText(boolean linearText) //设置线性文本
PathEffect setPathEffect(PathEffect effect) //设置路径效果
Rasterizer setRasterizer(Rasterizer rasterizer) //设置光栅化
Shader setShader(Shader shader) //设置阴影
void setTextAlign(Paint.Align align) //设置文本对齐
void setTextScaleX(float scaleX) //设置文本缩放倍数,1.0f 为原始
void setTextSize(float textSize) //设置字体大小
Typeface setTypeface(Typeface typeface) //设置字体,Typeface 包含了字体的类型,粗细,还有倾斜、颜色等。
void setUnderlineText(boolean underlineText) //设置下划线
最终 Canvas 和 Paint 在 onDraw 中直接使用
@Override
protected void onDraw(Canvas canvas) {
Paint paintRed=new Paint();
paintRed.setColor(Color.Red);
canvas.drawPoint(11,3,paintRed); //在坐标 11,3 上画一个红点
}
下一次 Android123 将会具体讲到强大的 Path 路径,和字体 Typeface 相关的使用。
30. View 类详解
在 Android 游戏开发之旅二中咱们讲到了 View 和 SurfaceView 的区别,
今天 Android123 从 View 类开始着重的介绍 Android 图形显示基类的相关方法和注意点。
自定义 View 的经常使用方法:
onFinishInflate() 当 View 中全部的子控件均被映射成 xml 后触发
onMeasure(int, int) 肯定全部子元素的大小
onLayout(boolean, int, int, int, int) 当 View 分配全部的子元素的大小和位置时触发
onSizeChanged(int, int, int, int) 当 view 的大小发生变化时触发
onDraw(Canvas) view 渲染内容的细节
onKeyDown(int, KeyEvent) 有按键按下后触发
onKeyUp(int, KeyEvent) 有按键按下后弹起时触发
onTrackballEvent(MotionEvent) 轨迹球事件
onTouchEvent(MotionEvent) 触屏事件
onFocusChanged(boolean, int, Rect) 当 View 获取或失去焦点时触发
onWindowFocusChanged(boolean) 当窗口包含的 view 获取或失去焦点时触发
onAttachedToWindow()当 view 被附着到一个窗口时触发
onDetachedFromWindow() 当 view 离开附着的窗口时触发,Android123 提示该方法和
onAttachedToWindow() 是相反的。
onWindowVisibilityChanged(int) 当窗口中包含的可见的 view 发生变化时触发
以上是 View 实现的一些基本接口的回调方法,通常咱们须要处理画布的显示时,
重写 onDraw(Canvas)用的的是最多的:
@Override
protected void onDraw(Canvas canvas) {
//这里咱们直接使用 canvas 对象处理当前的画布,好比说使用 Paint 来选择要填充的颜色
Paint paintBackground = new Paint();
paintBackground.setColor(getResources().getColor(R.color.xxx)); //从 Res 中找到名为 xxx 的 color 颜色定义
canvas.drawRect(0, 0, getWidth(), getHeight(), paintBackground); //设置当前画布的背景颜色为 paintBackground
中定义的颜色,以 0,0 做为为起点,以当前画布的宽度和高度为重点即整块画布来填充。
具体的请查看 Android123 将来讲到的 Canvas 和 Paint,在 Canvas 中咱们能够实现画路径,图形,区域,线。
而 Paint 做为绘画方式的对象能够设置颜色,大小,甚至字体的类型等等。 } 固然还有就是处理窗口还原状态问题(一
般用于横竖屏切换),除了在 Activity 中能够调用外,开发游戏时咱们尽可能在 View 中使用相似
@Override
protected Parcelable onSaveInstanceState() {
Parcelable p = super.onSaveInstanceState();
Bundle bundle = new Bundle();
bundle.putInt("x", pX);
bundle.putInt("y", pY);
bundle.putParcelable("android123_state", p);
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
dosomething(bundle.getInt("x"), bundle.getInt("y")); //获取刚才存储的 x 和 y 信息
super.onRestoreInstanceState(bundle.getParcelable("android123_state"));
return;
}
在 View 中若是须要强制调用绘制方法 onDraw,可使用 invalidate()方法,它有不少重载版本,同时在线程中的
postInvailidate()方法将在 Android 游戏开发之旅六中的 自定义 View 完整篇讲到。
31. View 和和 SurfaceView
在 Android 游戏当中充当主要的除了控制类外就是显示类,在 J2ME 中咱们用 Display 和 Canvas 来实现这些,而 Google
Android 中涉及到显示的为 view 类,Android 游戏开发中比较重要和复杂的就是显示和游戏逻辑的处理。这里咱们说下
android.view.View 和 android.view.SurfaceView。SurfaceView 是从 View 基类中派生出来的显示类,直接子类有
GLSurfaceView 和 VideoView,能够看出 GL 和视频播放以及 Camera 摄像头通常均使用 SurfaceView,到底有哪些优点呢?
SurfaceView 能够控制表面的格式,好比大小,显示在屏幕中的位置,最关键是的提供了 SurfaceHolder 类,使用 getHolder
方法获取,相关的有:
Canvas lockCanvas()
Canvas lockCanvas(Rect dirty) 、
void removeCallback(SurfaceHolder.Callback callback)、
void unlockCanvasAndPost(Canvas canvas) //控制图形以及绘制,
而在 SurfaceHolder.Callback 接口回调中能够经过下面三个抽象类能够本身定义具体的实现,好比第一个更改格式和
显示画面。
abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
abstract void surfaceCreated(SurfaceHolder holder)
abstract void surfaceDestroyed(SurfaceHolder holder)
对于 Surface 相关的,Android 底层还提供了 GPU 加速功能,因此通常实时性很强的应用中主要使用 SurfaceView 而
不是直接从 View 构建,同时 Android123 将来后面说到的 OpenGL 中的 GLSurfaceView 也是从该类实现。
32. Android 程序内存管理必读
不少开发者都是从 J2ME 或 J2EE 上过来的,对于内存的使用和理解并非很到位,Android 开发网本次给你们一些架构
上的指导,防止出现豆腐渣工程的出现。Android 做为以 Java 语言为主的智能平台对于咱们开发一些高性能和质量的软件来
说了解 Android 程序内存管理机制是必须的。 Android 的 Dalvik VM 在基础方面和 Sun JVM 没有什么大的区别仅仅是字节码
的优化,咱们要知道何时用 gc 何时用 recycle 以及到底用不用 finalization,由于 Java 对内存的分配只须要 new
开发者不须要显示的释放内存,可是这样形成的内存泄露问题的概率反而更高。
1.对于常规开发者而言须要了解 Java 的四种引用方式,好比强引用,软引用,弱引用以及虚引用。一些复杂些的程序
在长期运行极可能出现相似 OutOfMemoryError 的异常。
2.并不要过多的期望 gc,不用的对象能够显示的设置为空,好比 obj=null,这里 Android123 提示你们,java 的 gc 使
用的是一个有向图,判断一个对象是否有效看的是其余的对象能到达这个对象的顶点,有向图的相对于链表、二叉树来讲开
销是可想而知。
3.Android 为每一个程序分配的对内存能够经过 Runtime 类的 totalMemory() freeMemory() 两个方法获取 VM 的一些内存
信息,对于系统 heap 内存获取,能够经过 Dalvik.VMRuntime 类的 getMinimumHeapSize() 方法获取最小可用堆内存,同时
显示释放软引用能够调用该类的 gcSoftReferences() 方法,获取更多的运行内存。
4.对于多线程的处理,若是并发的线程不少,同时有频繁的建立和释放,能够经过 concurrent 类的线程池解决线程创
建的效率瓶颈。
5. 不要在循环中建立过多的本地变量。 有关 Android 和 Java 的系统性能分析,Android123 将在之后的文章中详细讲
述如何调试 Java 分析内存泄露以及 Android 上的 gdb 调试器分析得出内存性能改进。
33. Android 中内嵌字体实现个性化
在 Android 中咱们的应用能够灵活的内嵌本身的字体文件,实现各个手机上能够正常的显示个性化文字,咱们都知道
TextView 的 setTypeface 方法能够设置目标文字的显示特性,好比字体、颜色、粗体、斜体等。 咱们直接找一个 TrueTypeFont
的字体文件即.ttf,对于 Win32 系统的用户能够直接在 Windows/fonts 文件夹中能找到不少。 好比微软雅黑就不错,但是体
积太大,因为 Android 的 Assets 类有单个文件 1MB 体积的限制,咱们先找个英文字体作测试。这里咱们将字体文件
android123.ttf 放到工程的 assets 文件夹的 fonts 目录中。
Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/android123.ttf");
TextView tv = (TextView)findViewById(R.id.text);
tv.setTypeface(tf); //设置 TextView 的风格
tv.setText("CWJ Test");
tv.setTextSize(12);
tv.setTextColor(Color.RED);
34. 获取和设置 ListView 的选择项
获取当前选中项 int curPos = listView.getFirstVisiblePosition(); 固然是用 getItemAtPosition(int nPos)方法
也能够 ,设置当前选择位置 listView.setSelectedPosition(lastPos) ; 对于基于 AbsListView 为基类的 ListView 等控
件都可以使用这种方法。
35. android.text.format 文件大小和日期解析类
不少网友可能直接将本身的 J2ME 项目生硬的移植到 Android 平台,其实 Google 为咱们提供好了文件大小和时间日期解
析类,它位于 android.text.format 这个包中,它提供了强大的标准化解析方法:
1. IP 地址解析类 在 android.text.format.Formatter 中提供了 String formatIpAddress(int addr) 这个方法能够轻
松方便的将 socket 中的 int 型转成相似 127.0.0.1 的 IP 格式,须要注意的是 Linux 平台的字节顺序,即小字节序、低字节
序 little-endian。
2. 文件大小解析类 细心的网友可能还看到了 android.text.format.Formatter 中的 formatFileSize 方法,该方法
String formatFileSize (Context context, long number) ,第二个参数是 long 型,通常为 File 对象的最后修改时间或
建立时间的方法,最终返回相似 12KB、5Bytes 的值,20MB 的字符串。
3. 日期时间解析类 ,该类位于 android.text.format.DateFormat 这个 package 中,该类提供了 Java 中的三种时间对
象,Android123 提示你们下面三种方法为静态能够直接调用,以下:
final static CharSequence format(CharSequence inFormat, Date inDate) //传入 Date 对象
Given a format string and a Date object, returns a CharSequence containing the requested date.
final static CharSequence format(CharSequence inFormat, Calendar inDate) //Calendar 对象
Given a format string and a Calendar object, returns a CharSequence containing the requested date.
final static CharSequence format(CharSequence inFormat, long inTimeInMillis) //long 对象
Given a format string and a time in milliseconds since Jan 1, 1970 GMT, returns a CharSequence containing the
requested date.
咱们可能看到了第一个参数均为 inFormat 这是一个 CharSequence 接口的 String 类型,它提供了灵活的时间格式解析
字符串描述,Android 开发网提示你们注意大小写要区分,如
April 6, 1970 at 3:23am 例子,那么 inFormat 参数的写法和最终执行的结果以下对照,下面就以 Android123 的 CWJ
生日为例子以下
"MM/dd/yy h:mmaa" -> "11/03/87 11:23am"
"MMM dd, yyyy h:mmaa" -> "Nov 3, 1987 11:23am"
"MMMM dd, yyyy h:mmaa" -> "November 3, 1987 11:23am"
"E, MMMM dd, yyyy h:mmaa" -> "Tues, November 3, 1987 11:23am"
"EEEE, MMMM dd, yyyy h:mmaa" -> "Tuesday, Nov 3, 1987 11:23am"
对于判断一个时间是否为 24 小时制式能够经过 android.text.format.DateFormat 类的 static
boolean is24HourFormat(Context context)方法来判断。
36. Android 代码性能优化技巧
目前来讲 Android 2.2 的 JIT 性能有了本质的提升,不过对于老版本的程序提升 Java 执行效率还有不少语言特色来讲,
今天 Android123 提到的不是语法糖,而是基础的问题,对于 Java 1.5 以后将会有明显的改进。下面的例子来自 SDK: static
class Foo {
int mSplat;
}
Foo[] mArray = ... 上面的静态类 Foo 的执行效果和性能,咱们分三个方法 zero、one 和 two 来作对比。
public void zero() { //大多数人可能简单直接这样写
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray.mSplat;
}
}
public void one() { //经过本地对象改进性能
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray.mSplat;
}
}
public void two() { //推荐的方法,经过 Java 1.5 的新语法特性能够大幅改进性能
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}
zero() is slowest, because the JIT can't yet optimize away the cost of getting the array length once for
every iteration through the loop. one() is faster. It pulls everything out into local variables, avoiding the
lookups. Only the array length offers a performance benefit. two() is fastest for devices without a JIT, and
indistinguishable from one() for devices with a JIT. It uses the enhanced for loop syntax introduced in version
1.5 of the Java programming language.
37. Android 开发注意点 Part One
Android 已经的不少细节问题咱们经过平台开发总结不断完善这个列表,若是你有相关的内容能够联
系 android123@163.com . 1、AssetManager - 已知单个文件处理不能大于 1MB,因此若是资源很大,建议使用 Zip 格
式压缩存放。 2、ScrollView 中嵌入 ListView - 这个做法可能会出现你的 ListView 仅仅显示 1 行半。 3、Android
自带的 Zip 处理类对文件名编码没法识别,也没有提供显示的设置方法,在 zlib 中写死了。 4、使用一些资源对象记
住关闭,好比对于文件流对象最后 FileOutputStream os = xxx; try {
//dosomething
} finally {
os.close(); //显示的使用 finally 关闭文件对象。
} 对于 Cursor 而言,在移动位置时首先判断 Cursor 是否为空,最终使用完仍然须要 close 方法,
若是重用,可使用 deactivate 方法释放当前资源,经过 requery 方法再次查询。 5、SDK 中标记为 deprecated 字样
的,常规状况下是有更好的方法能够替代,短时间内能够放心使用。这些方法通常高版本的 SDK 均可以向上兼容,目 前还没有发
现 Android 放弃某些 API 的支持。 6、Notification 的 Intent 没法传递到目标的 Activity,Service 和 Broardcast 没
有测试过,中途须要经过 PendingIntent,可能这里出现了问题。
38. Android 上上 HTTP 协议通信状态获取
一般状况下轻量级的 Http 传输 Android 平台能够直接使用 Sun Java 的 HttpURLConnection 类方法处理,好比果本身定
义一次请求 header 能够经过 setRequestProperty 设置,而咱们须要获取的 Http Web Server 状态能够经过
HttpURLConnection.getResponseCode() 的方法获取。 固然 Http 协议返回值常见的有 200 为成功,400 为请求错误,404
为未找到,500 为服务器内部错误,403 无权查看,302 为重定向等等。 对于 Android 平台提供更完善的 Apache 类有
HttpClient 、HttpPost、HttpResponse、HttpGet 和 HttpEntity,其中对于数据报头 header 构造经过 HttpEntity,而返回
状态值能够经过 HttpResponse 获取。 有关 Android 客户端和 Server 通信类相关的开发咱们将会在之后文章中作大量实
例介绍。
39. Android 布局布局 Java 代码构造法
通常状况下对于 Android 程序布局咱们每每使用 XML 文件来编写,这样能够提升开发效率,可是考虑到代码的安全性以
及执行效率,能够经过 Java 代码执行建立,虽然 Android 编译过的 xml 是二进制的,可是加载 xml 解析器的效率对于资源
占用仍是比较大的,通常一个简单的 TextView,好比 <TextView
android:id="@+id/textControl "
android:layout_width="100px"
android:layout_height="wrap_content" /> 能够等价于下面的 Java 代码: LinearLayout.LayoutParams
textParams = new LinearLayout.LayoutParams(100, LayoutParams.WRAP_CONTENT); //宽度为 100px,高为自适应最小的
高度 // setOrientation(VERTICAL); 设置布局为垂直 TextView textControl = new TextView(this);//若是从一
个 XXXLayout.,好比 LinearLayout 为 View 的基类时这里 this 应该换成为建立改类的 Context
textControl.setText("Android 开发网欢迎您");
addView( textControl, textParams ); 固然 Java 处理效率比 XML 快得多,可是对于一个复杂界面的编写,可能需
要一些套嵌考虑,若是你思惟灵活的话,使用 Java 代码来布局你的 Android 应用程序是一个更好的方法。
40. 测试测试 Android 软件性能主要方法
对于 Android 平台上软件的性能测试能够经过如下几种方法来分析效率瓶颈,目前 Google 在 Android 软件开发过程当中
已经引入了多种测试工具包,好比 Unit 测试工程,调试类,还有模拟器的 Dev Tools 均可以直接反应执行性能。 1. 在
模拟器上的 Dev Tools 能够激活屏幕显示当前的 FPS,CPU 使用率,能够帮助咱们测试一些 3D 图形界面的性能。 2. 一
般涉及到网络应用的程序,在效率上和网速有不少关系,这里须要屡次的调试才能实际了解。 3. 对于逻辑算法的效率
执行,咱们使用 Android 上最广泛的,计算执行时间来查看: long start = System.currentTimeMillis();
//android 开发网提示这里作实际的处理 do something
long duration = System.currentTimeMillis() - start; 最终 duration 保存着实际处理该方法须要的毫秒
数。这里相似 Win32 上的 GetTickCount,在 Win 32 和 Symbian 上都提供了高精度的性能计数器和低阶计时器,这里在 Dalvik
VM 上的 Java 层这种方法对于通常的应用足以。 4. GC 效率跟踪,若是你执行的应用比较简单,能够在 DDMS 中查看下 Logcat
的 VM 释放内存状况,大概模拟下那些地方能够缓存数据或改进算法的。 5. 线程的使用和同步,Android 平台上给咱们
提供了丰富的多任务同步方法,但在深层上并无过多的好比自旋锁等高级应用,不过对于 Service 和 appWidget 而言,他
们实际的产品中都应该以多线程的方式处理,以释放 CPU 时间,对于线程和堆内存的查看这些均可以在 DDMS 中看到。 更
多的调试和性能测试方法 Android123 将在之后的内容中出现。
41. Splash Screen 开场屏在 Android 中的实现
不少网友可能发现近期 Tencent 推出的手机 QQ Android 版包含了一个开场屏 Splash Screen 载入效果,一般游戏或大
型软件打开时可能须要一个释放解析资源的过程,须要一个前台的动画播放和后台的逻辑处理线程配合,固然对于简单的软
件也能够加一个 Splash Screen 做为美化。在 Android 平台上如何实现呢? 首先建立一个 Activirty,在 SetContentView
时直接经过 ImageView 建立一个全屏的图片,Android123 提示你们还要考虑好分辨率和当前设备一致,onCreate 添加代码
以下: new Handler().postDelayed(new Runnable(){ // 为了减小代码使用匿名 Handler 建立一个延时的调用
public void run() {
Intent i = new Intent(SplashScreen. this, Main.class); //经过 Intent 打开最终真正的主界面 Main
这个 Activity
SplashScreen.this.startActivity(i); //启动 Main 界面
SplashScreen.this.finish(); //关闭本身这个开场屏
}
}, 5000); //5 秒,够用了吧
42. Android 的的 Activity 你知多少呢?
看到这个标题不少网友确定回答,我知道 Activity 是 Android 上的窗口基类,了解 Activity 的生命周期好比 onCreate
onStop 等,呵呵,按照这样说 Android123 还知道 Activity 的实现实际上是从 ApplicationContext,而 ApplicationContext
是从 Context 这个抽象类派生而来的,固然咱们看到显示的是 View 或者 ViewGroup,固然今天说的不是这些东西,而是不少
网友来问的 Android 为何不设计一个任务管理器,固然从 Android 1.5 开始 ActivityManager 类提供了 restartPackage
能够关闭一个程序,须要加上<uses-permission android:name="android.permission.RESTART_PACKAGES"/>这个权限,不
过咱们注意到,长按 Home 键能够看到之前程序的运行,同时能够快速的切换回来。这就是 Android 独有的程序生命周期管
理机制 Activity 历史栈。 咱们在一个普通的程序主窗口 A 中打开了一个窗口 B,而窗口 B 打开了窗口 C,可是按下 Back
键后结果出乎了预期,是的这就是 Activity 的 history stack 的缘由,在数据结构中栈是 FIFO 的,阻止咱们不肯意看的情
况的发生则能够在打开新 Activity 时加上标记 FLAG_ACTIVITY_NO_HISTORY,代码以下: Intent i= new Intent(this,
cwj.class);
i.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); //Android 开发网提示你们相关的还有
Intent.FLAG_ACTIVITY_CLEAR_TOP,都试试
startActivity(i); 固然更多的程序 Activity 控制能够再 androidmanifest.xml 中定义。
43. JSONObject 在在 Android 上的应用
若是你过去开发过 AJAX 应用,相信对 JSONObject 不会陌生吧,做为基于 JavaScript 的数据交换格式,能够直接代替
Xml,这里 Android 从 1.0 开始就彻底支持 JSONObject。在平时应用中直接引入 import org.json.JSONObject;便可方便使
用。固然同类的还有 SOAP。 在常规使用方便 JSONObject 对象能够实现相似 Bundle 或 Parcel 能够封装数据,代替一个
XML 的 ITEM,但最大的优点是能够执行一些简单的方法,好比说 getString、has、put、getBoolean、getInt 等数据类型的
存取操做。Android123 提示你们对于常规的项目开发,今天本文不考虑 Server 端的布局,在 Android 平台上处理这些比较
简单,主要是一些 http 的请求处理。能够直接引入 import org.apache.http.xxx 来实现 web server 层的数据交换,若是
你没有专业的 Server 开发技术,能够经过简单的 Web 配合 JSON 方式快速实现本身的交互式应用。
44. Android 高性能文件类 MemoryFile
不少网友抱怨 Android 处理底层 I/O 性能不是很理想,若是不想使用 NDK 则能够经过 MemoryFile 类实现高性能的文件
读写操做。MemoryFile 顾名思义就是内存文件的意思,若是你过去从事过 Win32 开发,那么它的原理就是 MapViewOfFile(),
固然开发过 Linux 的网友可能很快就联想到了 mmap(),是的该类就是他们的托管代码层封装,位于 android.os.MemoryFile
这个位置,从 Android 1.0 开始就被支持。 MemoryFile 适用于哪些地方呢? 对于 I/O 须要频繁操做的,主要是和外部
存储相关的 I/O 操做,MemoryFile 经过将 NAND 或 SD 卡上的文件,分段映射到内存中进行修改处理,这样就用高速的 RAM
代替了 ROM 或 SD 卡,性能天然提升很多,对于 Android 手机而言同时还减小了电量消耗。Android123 提示网友该类实现的
功能不是不少,直接从 Object 上继承,经过 JNI 的方式直接在 C 底层执行。 主要的构造方法 MemoryFile(String name, int
length) ,这里第二个参数为文件大小,须要说明的是 Android 的 MemoryFile 和传统的 mmap 还有一点点区别,毕竟是手机,
它内部的内存管理方式 ashmem 会从内核中回收资源。毕竟目前部分低端机型的 RAM 也比较吃紧。 synchronized
boolean allowPurging(boolean allowPurging) //容许 ashmem 清理内存,线程安全同步的方式。
void close() //关闭,由于在 Linux 内部 mmap 占用一个句柄,不用时必定要释放了
InputStream getInputStream() 返回读取的内容用 Java 层的 InputStream 保存
OutputStream getOutputStream() 把一个 OutputSream 写入到 MemoryFile 中
boolean isPurgingAllowed() //判断是否容许清理
int length() //返回内存映射文件大小 下面就是咱们熟悉的,读写细节,主要是对字符数组的操做,这里你们要计算好
每一个文件类型的占用,同时考虑到效率对于本身分配的大小考虑粒度对齐。
int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count) 具体的实际应用,Android 开发网
将在下次和你们讲到。
45. TextUtils 类类-Android 字符串处理类
对于字符串处理 Android 为咱们提供了一个简单实用的 TextUtils 类,若是处理比较简单的内容不用去思考正则表达式
不妨试试这个在 android.text.TextUtils 的类,主要的功能以下: 是否为空字符 static
boolean isEmpty(CharSequence str) 拆分字符串 public static String[] split (String text, String expression) ,
Android 开发网提示你们仔细看例子以下 String.split() returns [''] when the string to be split is empty. This
returns []. This does not remove any empty strings from the result. For example split("a,", "," ) returns {"a",
""}. 拆分字符串使用正则 public static String[] split (String text, Pattern pattern) 肯定大小写是否有效在当
前位置的文本 TextUtils.getCapsMode(CharSequence cs, int off, int reqModes) 使用 HTML 编码这个字符串 static
String TextUtils.htmlEncode(String s)
46. InputSream 输入流转 String 字符串,,, Android 开发工具类
在 Android 平台上使用 Java 层处理 I/O 时主要使用流,这里 Android 开发网给你们一个方便的类,能够处理 InputStream
输入流转为 String 字符串,在效率上,咱们使用了字符串拼接 StringBuilder 类减小内存碎片以及 BefferedReader 类实现
一个缓存。 private String Stream2String(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is), 16*1024); //强制缓存大小为
16KB,通常 Java 类默认为 8KB
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) ! = null) { //处理换行符
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
} }
47. layout 资源包含,,, android 开发必读
有时候咱们在一个 Android 程序中可能会复用布局文件,这时能够在一个 xml 文件中复用过去的布局文件, 可是和常规
的使用不一样的是,须要加上相似包含头文件同样的 include 关键字,好比下面咱们须要包含 layout 文件夹下的 view.xml 布
局文件,须要<include layout="@layout/view" /> 这样下,完整的以下, 你们能够试一试。 <?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"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cwj"
/>
<include layout="@layout/view" />
<include android:id="@+id/block" layout="@layout/item" /> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/android123"
/>
</LinearLayout>


48.Android 控件开发之 ToggleButton 原理原理
在 Android 平台上比较有特点的就是 ToggleButton 控件,虽然它的功能和 CheckBox 有些相似,可是他们的用处仍是有
必定的区别好比 ToggleButton 本来有图片装饰,经过 ToggleButton 能够很清楚的显示某些状态。它们均从 Button 为基类
的 CompoundButton 中实现,其真假事件从 Checkable 来实现。
public abstract class CompoundButton extends Button implements Checkable {
private boolean mChecked; //状态是否选中
private int mButtonResource;
private boolean mBroadcasting;
private Drawable mButtonDrawable; //按钮的图标
private OnCheckedChangeListener mOnCheckedChangeListener; //选中状态改变监听
private OnCheckedChangeListener mOnCheckedChangeWidgetListener;
private static final int[] CHECKED_STATE_SET = {
R.attr.state_checked
};
public CompoundButton(Context context) {
this(context, null);
}
public CompoundButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CompoundButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a =context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.CompoundButton,
defStyle, 0);
Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);
if (d != null) {
setButtonDrawable(d);
}
boolean checked = a .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false);
setChecked(checked);
a.recycle(); //显式的 GC
}
public void toggle() {
setChecked(!mChecked);
}
@Override
public boolean performClick() {
toggle();
return super.performClick();
}
public boolean isChecked() {
return mChecked;
}
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState(); //更新当前状态的按钮图标
if (mBroadcasting) {
return;
}
mBroadcasting = true;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
}
if (mOnCheckedChangeWidgetListener != null) {
mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
}
mBroadcasting = false;
}
}
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
}
void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) {
mOnCheckedChangeWidgetListener = listener;
}
public static interface OnCheckedChangeListener {
void onCheckedChanged(CompoundButton buttonView, boolean isChecked);
}
public void setButtonDrawable(int resid) {
if (resid != 0 && resid == mButtonResource) {
return;
}
mButtonResource = resid;
Drawable d = null;
if (mButtonResource != 0) {
d = getResources().getDrawable(mButtonResource);
}
setButtonDrawable(d);
}
public void setButtonDrawable(Drawable d) {
if (d != null) {
if (mButtonDrawable != null) {
mButtonDrawable.setCallback(null);
unscheduleDrawable(mButtonDrawable) ;
}
d.setCallback(this);
d.setState(getDrawableState());
d.setVisible(getVisibility() == VISIBLE, false);
mButtonDrawable = d;
mButtonDrawable.setState(null);
setMinHeight(mButtonDrawable.getIntrinsicHeight());
}
refreshDrawableState();
}
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
boolean populated = super.dispatchPopulateAccessibilityEvent(event);
if (!populated) {
int resourceId = 0;
if (mChecked) {
resourceId = R.string.accessibility_compound_button_selected;
} else {
resourceId = R.string.accessibility_compound_button_unselected;
}
String state = getResources().getString(resourceId);
event.getText().add(state);
event.setChecked(mChecked);
}
return populated;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final Drawable buttonDrawable = mButtonDrawable;
if (buttonDrawable != null) {
final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
final int height = buttonDrawable.getIntrinsicHeight();
int y = 0;
switch (verticalGravity) {
case Gravity.BOTTOM:
y = getHeight() - height;
break;
case Gravity.CENTER_VERTICAL:
y = (getHeight() - height) / 2;
break;
}
buttonDrawable.setBounds(0, y, buttonDrawable.getIntrinsicWidth(), y + height);
buttonDrawable.draw(canvas);
}
}

@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
@Override
protected void drawableStateChanged() { //android123 提示状态改变时须要更换按钮的图标
super.drawableStateChanged();
if (mButtonDrawable != null) {
int[] myDrawableState = getDrawableState();
mButtonDrawable.setState(myDrawableState);
invalidate();
}
}
@Override
protected boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who) || who == mButtonDrawable;
}
static class SavedState extends BaseSavedState {
boolean checked;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
checked = (Boolean)in.readValue(null);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeValue(checked);
}
@Override
public String toString() {
return "CompoundButton.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " checked=" + checked + "}";
}
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
@Override
public Parcelable onSaveInstanceState() {
// Force our ancestor class to save its state
setFreezesText(true);
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.checked = isChecked();
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChecked(ss.checked);
requestLayout();
}
}
从上面来看咱们知道 CompuundButton 的实现相对繁琐了些,主要是考虑状态是否已经选中等状况的消息通知,Android
开发网提醒你们而 ToggleButton 相对 CompuundButton 增长的给用户而言主要是开关的文字显示。
public class ToggleButton extends CompoundButton {
private CharSequence mTextOn;
private CharSequence mTextOff;
private Drawable mIndicatorDrawable;
private static final int NO_ALPHA = 0xFF;
private float mDisabledAlpha;
public ToggleButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a =context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.ToggleButton, defStyle, 0);
mTextOn = a.getText(com.android.internal.R. styleable.ToggleButton_textOn);
mTextOff = a.getText(com.android.internal.R.styleable.ToggleButton_textOff);
mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.ToggleButton_disabledAlpha, 0.5f) ;
syncTextState();
a.recycle();
}
public ToggleButton(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.buttonStyleToggle);
}
public ToggleButton(Context context) {
this(context, null);
}
@Override
public void setChecked(boolean checked) {
super.setChecked(checked);
syncTextState();
}
private void syncTextState() {
boolean checked = isChecked();
if (checked && mTextOn != null) {
setText(mTextOn);
} else if (!checked && mTextOff != null) {
setText(mTextOff);
}
}
public CharSequence getTextOn() {
return mTextOn;
}
public void setTextOn(CharSequence textOn) {
mTextOn = textOn;
}
public CharSequence getTextOff() {
return mTextOff;
}
protected void onFinishInflate() {
super.onFinishInflate();
updateReferenceToIndicatorDrawable(getBackground());
}
@Override
public void setBackgroundDrawable(Drawable d) {
super.setBackgroundDrawable(d);
updateReferenceToIndicatorDrawable(d);
}
private void updateReferenceToIndicatorDrawable(Drawable backgroundDrawable) {
if (backgroundDrawable instanceof LayerDrawable) {
LayerDrawable layerDrawable = (LayerDrawable) backgroundDrawable;
mIndicatorDrawable =layerDrawable.findDrawableByLayerId(com.android.internal.R.id.toggle);
}
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (mIndicatorDrawable != null) {
mIndicatorDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
}
}
}


49. AsyncTask 实例代码演示 Android 异步任务
上次咱们讲到了 Android 提供了一个较线程更简单的处理多任务的方法 AsyncTask 异步任务类,相对于线程来讲
AsyncTask 对于简单的任务处理更安全,其内部的实现方法使用了 Android 的 Handler 机制, 对于常见的文件下载可使用
AsyncTask 类来处理,在 Browser 浏览器中就是用了该类下载 Web 服务器 URL 的 Favicon 图标。 首先 Android123 以简单
的下载例子演示该类的大体结构,以下
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls);
publishProgress((int) ((i / (float) count)100));
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
//最终咱们执行
DownloadFilesTask().execute(url1, url2, url3); //便可。
//在 Android 浏览器中下载 Favicon 图标的实现以下:
class DownloadTouchIcon extends AsyncTask<String, Void, Bitmap> {
private final ContentResolver mContentResolver;
private final Cursor mCursor;
private final String mOriginalUrl;
private final String mUrl;
private final String mUserAgent;
/* package */
BrowserActivity mActivity;
public DownloadTouchIcon(BrowserActivity activity, ContentResolver cr, Cursor c, WebView view) { //构造
方法
mActivity = activity;
mContentResolver = cr;
mCursor = c;
mOriginalUrl = view.getOriginalUrl();
mUrl = view.getUrl();
mUserAgent = view.getSettings().getUserAgentString();
}
public DownloadTouchIcon(ContentResolver cr, Cursor c, String url) { //实现本类的构造
mActivity = null;
mContentResolver = cr;
mCursor = c;
mOriginalUrl = null;
mUrl = url;
mUserAgent = null;
}
@Override
public Bitmap doInBackground(String... values) { //返回 Bitmap 类型
String url = values[0];
AndroidHttpClient client = AndroidHttpClient.newInstance(mUserAgent);
HttpGet request = new HttpGet(url);
HttpClientParams.setRedirecting(client.getParams() , true); //处理 302 等重定向问题
try {
HttpResponse response = client.execute(request);
if (response.getStatusLine().getStatusCode() == 200) { //若是 OK
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream content = entity.getContent(); //将图标保存到 InputStream 中,由于是二进制内

if (content != null) {
Bitmap icon = BitmapFactory.decodeStream( //从流中取出 Bitmap,这里使用了 BitmapFactory
类的静态方法 decodeStream
content, null, null);
return icon;
}
}
}
} catch (IllegalArgumentException ex) {
request.abort();
} catch (IOException ex) {
request.abort();
} finally {
client.close();
}
return null;
} @Override
protected void onCancelled() {
if (mCursor != null) {
mCursor.close();
}
} @Override
public void onPostExecute(Bitmap icon) {
if (mActivity != null) {
mActivity.mTouchIconLoader = null;
} if (icon == null || mCursor == null || isCancelled()) {
return;
}
//最终图标要保存到浏览器的内部数据库中,系统程序均保存为 SQLite 格式,Browser 也不例外,由于图片是二进制的
因此使用字节数组存储数据库的 BLOB 类型
final ByteArrayOutputStream os = new ByteArrayOutputStream();
icon.compress(Bitmap.CompressFormat.PNG, 100, os); //将 Bitmap 压缩成 PNG 编码,质量为 100%存储
ContentValues values = new ContentValues(); //构造 SQLite 的 Content 对象,这里也可使用 raw sql 代替
values.put(Browser.BookmarkColumns.TOUCH_ICON,os.toByteArray()); //写入数据库的
//Browser.BookmarkColumns.TOUCH_ICON 字段
if (mCursor.moveToFirst()) {
do {
mContentResolver.update(ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
mCursor.getInt(0)),values, null, null);
} while (mCursor.moveToNext());
}
mCursor.close();
}
}
本次 Android 开发网经过两个 AsyncTask 类演示了多种类型的任务构造,这里你们注意返回类型,本节演示了 Android
平台上 Content Provider、AsyncTask、Bitmap、HTTP 以及 Stream 的相关操做,你们如何想很快提升开发水平其实只要理
解 Google 如何去实现 Android 系统常规构架就能够轻松入门谷歌移动平台。

50. Android 自定义 View 实例实例 AnalogClock 源码源码 针对 Android 底层 View 的直接构造不少网友没有实战经验,本次 Android 开发网结合目前平台开源代码一块儿经过 AnalogClock 类来理解 View 的直接继承。AnalogClock 就是 Home Screen 上的那个带有两根指针的表盘类。它的实现咱们直 接从开源代码能够了解到: public class AnalogClock extends View { private Time mCalendar; private Drawable mHourHand; //时针 private Drawable mMinuteHand; //分针 private Drawable mDial; //表盘背景 private int mDialWidth; //表盘宽度 private int mDialHeight; //表盘高度 private boolean mAttached; //附着状态 private final Handler mHandler = new Handler(); //定一个 Handler 类实现更新时间 private float mMinutes; private float mHour; private boolean mChanged; //时间是否改变 public AnalogClock(Context context) { this(context, null); } public AnalogClock(Context context, AttributeSet attrs) { this(context, attrs, 0); } public AnalogClock(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); Resources r = mContext.getResources(); TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.AnalogClock, defStyle, 0); mDial = a.getDrawable(com.android.internal.R.styleable.AnalogClock_dial); //加载表盘资源 if (mDial == null) { mDial = r.getDrawable(com.android.internal.R.drawable.clock_dial); } mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour); //加载时针图片资 源 if (mHourHand == null) { mHourHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_hour); } mMinuteHand = a.getDrawable(com.android.internal.R. styleable.AnalogClock_hand_minute); //加载分针图 片 if (mMinuteHand == null) { mMinuteHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_minute); } mCalendar = new Time(); //获取当前系统时间 mDialWidth = mDial.getIntrinsicWidth(); //获取表盘图片的宽度 mDialHeight = mDial.getIntrinsicHeight(); //高度,同上 } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (!mAttached) { mAttached = true; IntentFilter filter = new IntentFilter();//注册一个消息过滤器,获取时间改变时区,改变 action filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_TIME_CHANGED) ; filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); getContext().registerReceiver(mIntentReceiver, filter, null, mHandler); } mCalendar = new Time(); onTimeChanged(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mAttached) { getContext().unregisterReceiver(mIntentReceiver); //反注册消息过滤器 mAttached = false; } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); float hScale = 1.0f; float vScale = 1.0f; if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) { hScale = (float) widthSize / (float) mDialWidth; } if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) { vScale = (float )heightSize / (float) mDialHeight; } float scale = Math.min(hScale, vScale); setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec), resolveSize((int) (mDialHeight * scale), heightMeasureSpec)); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mChanged = true; } //主要的绘图重写 View 的 onDraw 方法,咱们能够看到经过 canvas 实例直接屏幕 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); boolean changed = mChanged; if (changed) { mChanged = false; } int availableWidth = mRight - mLeft; int availableHeight = mBottom - mTop; int x = availableWidth / 2;