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
2. px像素如何转为dip设备独立像素java
最近有网友问如何将px像素转为dip独立设备像素,因为Android的设备分辨率众多,目前主流的为wvga,而不少老的设备为hvga甚至低端的qvga,对于兼容性来讲使用dip无非是比较方便的,因为他和分辨率无关和屏幕的密度大小有关,因此推荐使用。 px= (int) (dip*density+0.5f) //这里android开发网提示你们不少网友获取density(密度)的方法存在问题,从资源中获取的是静态定义的,通常为1.0对于HVGA是正好的,而对于wvga这样的应该从WindowsManager中获取,WVGA为1.5android
这里能够再补充一下dip,sip的知识git
3. Android中动态改变ImageView大小 web
不少网友可能发如今layout.xml文件中定义了ImageView的绝对大小后,没法动态修改之后的大小显示,其实Android平台在设计UI控件时考虑到这个问题,为了适应不一样的Drawable能够经过在xml的相关ImageView中加入android:scaleType="fitXY" 这行便可,但由于使用了缩放可能会形成当前UI有所变形。使用的前提是限制ImageView所在的层,可使用一个内嵌的方法限制显示。正则表达式
4. 如何判断Android手机当前是否联网? shell
若是拟开发一个网络应用的程序,首先考虑是否接入网络,在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程序都使用了这个方法,来判断是否继续,同时在一些网络超时的时候也能够检查下网络链接是否存在,以避免浪费手机上的电力资源。数据库
5. Drawable、Bitmap、Canvas和Paint的关系 json
不少网友刚刚开始学习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
}
}
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
了解下这种技术,看看在立刻要作的项目中是否用获得
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);
}Pattern pattern = Pattern.compile(SimpleWikiHelper.WORD_OF_DAY_REGEX); //正则表达式处理,有关定义见下面的SimpleWikiHelper类
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 pxMenu
36 x 36 px
48 x 48 px
72 x 72 pxStatus Bar
24 x 24 px
32 x 32 px
48 x 48 pxTab
24 x 24 px
32 x 32 px
48 x 48 pxDialog
24 x 24 px
32 x 32 px
48 x 48 pxList 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~255void 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完整篇讲到。