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

Android高级应用开发(深入篇) stage3(上)- 高级商用界面开发 学习笔记

2014-02-21 15:57 459 查看
一、风格与主题

Style和Theme的区别

1、Theme是针对窗体级别的,改变窗体样式,其实主题就是一个样式,只不过针对activity设置

2、Style是针对窗体元素级别的,改变指定控件或Layout样式

使用Style

创建Style

1、在res\values\ 下创建styles.xml文件

2、添加<resouces>节点(根节点)

3、添加自定义的style及其属性

style的写法通常为:

<style name="MyStyle" [parent="PARENT"]>

<item name="[ATTR]">[VALUE]</>

</style>

其中,parent属性为其父style的名字(可选),通过设置该值,可继承其他style的属性。

当我们需要对现有的style做微小的改变时,这个值非常实用。

[ATTR]为需要设定的属性名,如: android:textColor等,[VALUE]为其预设的值。

如我们需要将控件的文字颜色设为红色,可使用如下style:

<style name="MyStyle">

<item name="android:textColor">#FFFF0000</item>

</style>

为控件指定Style

在Layout中,为控件指定style属性,如:

<TextView

style="@style/MyTextStyle"

/>

Theme

使用方法

1、在AndroidManifest.xml文件中,为Activity指定theme属性(推荐)

2、在Activty创建时调用setTheme函数(必须在setContentView前调用)

系统自带的Theme

Android已经为我们定义好了一些theme,需要是我们可以直接拿来使用。常用的Theme通常如下:

android:theme="@android:style/Theme.Dialog" 将一个Activity显示为能话框模式

android:theme="@android:style/Theme.NoTitleBar" 不显示应用程序标题栏

android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 不显示应用程序标题栏,并全屏

android:theme="@Theme.Light" 背景为白色

android:theme="Theme.Light.NoTitleBar" 白色背景并无标题栏

android:theme="Theme.Light.NoTitleBar.Fullscreen" 白色背景,无标题栏,全屏

android:theme="Theme.Black" 背景黑色

android:theme="Theme.Black.NoTitleBar" 黑色背景并无标题栏

android:theme="Theme.Black.NoTitleBar.Fullscreen" 黑色背景,无标题栏,全屏

android:theme="Theme.Wallpaper" 用系统桌面为应用程序背景

android:theme="Theme.Wallpaper.NoTitleBar" 用系统桌面为应用程序背景,且无标题栏

android:theme="Theme.Wallpaper.NoTitleBar.Fullscreen" 用系统桌面为应用程序背景,无标题栏,全屏

定义自己的Theme

Theme的写法和style很相似,也为:

<style name="MyTheme" [parent="PARENT"] >

<item name="[ATTR]">[VALUE]</>

</style>

Theme的属性在Android的文档中并没有介绍,不过我们可以从系统自带的theme中对其进行了解:

以下我们从Anroid系统本身所带的theme.xml中提取出来的一些常用的属性:

<item name="windowBackground">@android:drawable/screen_background_dark</item>

<item name="windowFrame">@null</item>

<item name="windowNoTitle">false</item>

<item name="windowFullscreen">false</item>

<item name="windowIsFloating">false</item>

<item name="windowContentOverlay">@android:drawable/title_bar_shadow</item>

<item name="windowTitleStyle">@android:style/WindowTitle</item>

<item name="windowTitleSize">25dip</item>

<item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground</item>

<item name="android:windowAnimationStyle">@android:style/Animation.Activity</item>

Style



TitleBar的文字样子也可以更改

Theme

气泡窗口:



使用上面自定义的主题,就是把一个Dialog的Activity背景设为自定义的图片



毛玻璃效果(例如第一次使用软件的Tips):

主题与上一个气泡窗口相同,不过在onCreate中加入



第一个参数是当前对象的标记属性

第二个参数是蒙版属性



换肤

使用Theme进行简单的换肤

1、为不同的皮肤编写Theme

2、在onCreate中为Activity使用不同的Theme

通过改变界面的布局文件进行换肤

1、为不同的皮肤编写不同的布局文件

2、加载布局文件

3、重新绑定界面控件

横竖屏切换例子

主要依靠onConfigurationChanged方法

定义两套布局进行横竖屏时的切换

onConfigurationChanged起作用,而且保证onCreate不会被调用需要设置android:configChanges包含orientation



还要设置权限



代码实现



提高UI性能

减少主线程的阻塞时间

若一个操作的耗时较长(超过5秒),我们应该将其放入后台线程中执行,只在需要修改UI界面时通知主线程进行修改。

Android已经提供了AsyncTask以实现从主线程生成新的异步任务的方法,下面我们将实现一个DownloadFilesTask,该任务将会在后台下载文件,每当有文件下载完成时,则通知主线程修改进度,全部下载完成时,将弹出对话框通知用户下载已完成。

示例:

当我们需要通过网络下载文件,同时需要我们的下载进度通知UI,我们可以使用如下代码:

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
//在后台线程开始前执行
protected void onPreExecute() {
}

//后台执行函数
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
//下载文件,并在下载完成时通过publishProgress通知UI进行更新
totalSize += Downloader.downloadFile(urls[i]);
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");  //通知用户下载完成
}
}


提高Adapter&AdapterView的效率

1、优化Adapter

2、优化AdapterView

Adapter是数据和AdapterView之间的桥梁,每当需要显示Item时,都会调用getView(),倘若我们的数据量很大,而Adapter的效率很低(如每次都会调用inflate创建新的View),结果会是怎样?

如使用以下代码(效率不好):

public View getView(int position, View convertView, ViewGroup parent) {
View item = mInflater.inflate(R.layout.list_item_icon_text, null);
((TextView) item.findViewById(R.id.text)).setText(DATA[position]);
((ImageView) item.findViewById(R.id.icon)).setImageBitmap(
(position & 1) == 1 ? mIcon1 : mIcon2);
return item;
}


setAdapter时,会getView

getView中执行inflate和找到对象set值两种操作

其中耗时的操作有:

1、inflate是一个IO操作,文件操作,比较耗时

2、findViewById这是一个遍历的过程

解决办法1:重用已生成过的Item View

如简单的使用以下代码,效果将会得到很好的提升

public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item, null);
}else if(convertView != null)
((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]);
((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap(
(position & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}


解决办法2:添加ViewHolder , 避免重复查找需要修改的控件

使用findViewById也是一个耗时的操作,我们可以使用ViewHolder进行缓冲。

这对于Item数量很大或者Item的布局很复杂的情况特别有效

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(DATA[position]);
holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}

static class ViewHolder {
TextView text;
ImageView icon;
}


通过setTag和getTag记住holder

解决方法3:缓存Item的数据

缓存Item的数据

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);

holder.data = DATA[position];
holder.bitmap = (position & 1) == 1 ? mIcon1 : mIcon2;

convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(holder.data);
holder.icon.setImageBitmap(holder.bitmap);
return convertView;
}

static class ViewHolder {
TextView text;
ImageView icon;
String data;
Bitmap bitmap;
}

}


解决方法4:分段显示

类比传统的findByPage

“点击加载更多”:控制下一屏到哪儿及数量

而且加载了一屏的时候,可以预先缓存上一屏或者下一屏

改良后的例子



优化布局文件

当布局文件层次过多,当findViewById的时候就会变慢

而且xml写的越大,实例化解析的时候,先要xml解析出标签,然后用反射找到相关的类

这些都会影响运行速度

1、使用观察布局的工具:Hierarchy Viewer

这是Android中提供的一个观察布局的工具:层级观察器Hierarchy viewer

Hierarchy viewer工具是一个非常好的布局优化工具,同时,你也可以通过它学习他人的布局。

Hierarchy viewer在sdk的tools目录下,打开后最新界面如图所示:



2、使用布局优化工具:Layoutopt

笨重的嵌套布局效率往往非常低下,在Android SDK中提供了一个工具可以帮助我们优化布局,以减少内存消耗,提高应用程序运行性能: Layoutopt

该工具位于SDK的tools目录下,使用方法如下:

layoutopt <directories/files to analyze>

我们可以使用示例代码中的bad_layout.xml来进行测试:

layoutopt bad_layout.xml

他将会返回结果:

11:17 This LinearLayout layout or its LinearLayout parent is useless

也就是说11至17行使用的LinearLayout或其父LinearLayout是多余的,我们完全可以去掉。

3、优化布局的层次结构

优化Activity背景图

某些时候,我们可能希望能够尽可能多的提高Activity哪怕一点点的性能,这时我们可以考虑优化Activity的背景图。

首先我们须知道,在Android的Activity中,不止有你使用SetContentView时使用的View,还包含了一些其他的View。

其根View是一个DecorView,你设置的View就被包含在其中,id为content,如下图所示:



在默认情况下,DecorView就包含了一个默认的背景图,我们接下来的优化就将以此作为出发点。

动态方式:

使用getWindow().setBackgroundDrawable()

getWindow().setBackgroundDrawable()将会改变DecorView中的背景图,从而避免不必要的绘图。

1. 若我们需要在Activity中设置一个全屏的背景图,不应该添加ImageView或在Layout中再设置背景图,而应该调用setBackgroundDrawable()去修改DecorView的背景图

2. 在不需要使用背景图时,我们应将背景图清空

静态方式:自定义主题:

1. 创建文件res/values/theme.xml

<resources>

<style name="Theme.CustomBackground" parent="android:Theme">

<item name="android:windowBackground">@null</item>

</style>

</resources>

可根据需要将windowBackground设置为null或你需要的背景图

2. 在<activity /> 或者 <application />标签中添加android:theme="@style/Theme.CustomBackground"

使用ViewStub

ViewStub是一个看不见的,轻量级的View。它没有尺寸,也不会绘制以及以某种形式参与到布局中来。只有当调用了Inflate之后其中的View才会被实例化,这意味着用ViewStub保留View层次的结构的代价是很低的。

A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime.

When a ViewStub is made visible, or when inflate() is invoked, the layout resource is inflated.

The ViewStub then replaces itself in its parent with the inflated View or Views.

Therefore, the ViewStub exists in the view hierarchy until setVisibility(int) or inflate() is invoked.

The inflated View is added to the ViewStub's parent with the ViewStub's layout parameters.

Similarly, you can define/override the inflate View's id by using the ViewStub's inflatedId property.

For instance:

<ViewStub android:id="@+id/stub"
android:inflatedId="@+id/subTree"
android:layout="@layout/mySubTree"
android:layout_width="120dip"
android:layout_height="40dip" />


The ViewStub thus defined can be found using the id "stub."

After inflation of the layout resource "mySubTree," the ViewStub is removed from its parent.

The View created by inflating the layout resource "mySubTree" can be found using the id "subTree," specified by the inflatedId property.

The inflated View is finally assigned a width of 120dip and a height of 40dip.

The preferred way to perform the inflation of the layout resource is the following:

ViewStub stub = (ViewStub) findViewById(R.id.stub);
View inflated = stub.inflate();


When inflate() is invoked, the ViewStub is replaced by the inflated View and the inflated View is returned.

This lets applications get a reference to the inflated View without executing an extra findViewById().

ViewStub的源码中的inflate方法



使用场景1:

延迟加载不常用的UI控件

当某些控件只在很少的情况下才会使用,我们可以使用ViewStub来延迟加载,以提高UI加载速度及减少内存消耗。





使用场景2:

提高改变布局的速度

1、界面需要频繁切换

2、希望能提高切换速度

使用方法

通过使用ViewStub, 我们可以较小的代价来实现UI的快速切换,而不需重新加载Activity。

这种方式多在通话程序等对UI切换速度要求较高的地方使用。

横竖屏切换的例子:

1、设置Activity的android:configChanges属性为keyboardHidden|orientation

2、为横竖屏分别编写不同的layout

3、创建一个layout,并包含两个ViewStub(分别对应横竖屏)

4、在横竖屏时,通过调用ViewStub.inflate()创建当前View并将另外一个设为GONE

5、绑定并设置控件的状态
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: