12、高级商用界面开发

一、风格与主题

1>Style与Theme的区别

Theme是针对窗体级别的,改变窗体样式;

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

2>使用Style

【创建Style】

  2.1. 在res\values\ 下创建styles.xml文件。

  2.2.. 添加<resouces>节点(根节点)。

  2.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"  />

3>Theme

[ 使用方法 ]

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

在Activity创建时调用setTheme函数 (必须在setContextView前调用 ).

[ 系统自带的Theme ]

 1 Android已经为我们定义好了一些theme,需要是我们可以直接拿来使用。常用的Theme通常如下:
 2 android:theme="@android:style/Theme.Dialog"   将一个Activity显示为能话框模式
 3 android:theme="@android:style/Theme.NoTitleBar"  不显示应用程序标题栏
 4 android:theme="@android:style/Theme.NoTitleBar.Fullscreen"  不显示应用程序标题栏,并全屏
 5 android:theme="@Theme.Light"  背景为白色
 6 android:theme="Theme.Light.NoTitleBar"  白色背景并无标题栏 
 7 android:theme="Theme.Light.NoTitleBar.Fullscreen"  白色背景,无标题栏,全屏
 8 android:theme="Theme.Black"  背景黑色
 9 android:theme="Theme.Black.NoTitleBar"  黑色背景并无标题栏
10 android:theme="Theme.Black.NoTitleBar.Fullscreen"    黑色背景,无标题栏,全屏
11 android:theme="Theme.Wallpaper"  用系统桌面为应用程序背景
12 android:theme="Theme.Wallpaper.NoTitleBar"  用系统桌面为应用程序背景,且无标题栏
13 android:theme="Theme.Wallpaper.NoTitleBar.Fullscreen"  用系统桌面为应用程序背景,无标题栏,全屏

[ 定义自己的Theme ]

 1 Theme的写法和style很相似,也为:
 2 <style name="MyTheme" [parent="PARENT"] >
 3 <item name="[ATTR]">[VALUE]</>
 4 </style>
 5 
 6 Theme的属性在Android的文档中并没有介绍,不过我们可以从系统自带的theme中对其进行了解:
 7 以下我们从Anroid系统本身所带的theme.xml中提取出来的一些常用的属性:
 8 
 9 <item name="windowBackground">@android:drawable/screen_background_dark</item>  
10 <item name="windowFrame">@null</item>  
11 <item name="windowNoTitle">false</item>  
12 <item name="windowFullscreen">false</item>  
13 <item name="windowIsFloating">false</item>  
14 <item name="windowContentOverlay">@android:drawable/title_bar_shadow</item>  
15 <item name="windowTitleStyle">@android:style/WindowTitle</item>  
16 <item name="windowTitleSize">25dip</item>  
17 <item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground</item>  
18 <item name="android:windowAnimationStyle">@android:style/Animation.Activity</item>  

4>示例

范例1:使用Style及Theme

参见sundy.android.demo.uiadv.style.StyleActivity.java

该示例展示了如何使用自定义的style及theme

范例2:气泡窗口

参见sundy.android.demo.uiadv.style.BubbleThemeActivity.java

范例3:毛玻璃效果

参见sundy.android.demo.uiadv.style.BlurThemeActivity.java

 

二、换肤

2.1>使用Theme进行简单的换肤

通过改变Theme,可对Activity及控件的风格进行切换,多在需要改变控件显示效果时使用。

参见sundy.android.demo.uiadv.skin.ThemeSkinActivity.java

---------------

为不同的皮肤编写Theme;

在onCreate中为Activity使用不同的Theme;

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

通过改变布局文件,我们可以对整个UI有较大幅度的修改。当使用Theme无法满足换肤需求时,我们可以修改Activity使用的layout。

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

加载布局文件;

重新绑定界面控件;

2.3>Case:横竖屏翻转主题Theme切换

 

三、提高UI性能

3.1>减少主线程的阻塞时间

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

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

示例:

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

 1  private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
 2     //在后台线程开始前执行
 3     protected void onPreExecute() {
 4     }
 5     //后台执行函数
 6      protected Long doInBackground(URL... urls) {
 7          int count = urls.length;
 8          long totalSize = 0;
 9          for (int i = 0; i < count; i++) {
10              //下载文件,并在下载完成时通过publishProgress通知UI进行更新
11              totalSize += Downloader.downloadFile(urls[i]);
12              publishProgress((int) ((i / (float) count) * 100));
13          }
14          return totalSize;
15      }
16      protected void onProgressUpdate(Integer... progress) {
17          setProgressPercent(progress[0]);  //设置完成进度
18      }
19     //在后台线程完成后执行
20      protected void onPostExecute(Long result) {
21          showDialog("Downloaded " + result + " bytes");  //通知用户下载完成
22      }
23  }

3.2>提高Adapter&AdapterView的效率

1,优化Adapter    2, 优化AdapterView

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

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

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

【重用已生成过的Item View】

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

 1 public View getView(int position, View convertView, ViewGroup
 2 parent) {
 3     if (convertView == null) {
 4          convertView =
 5 mInflater.inflate(R.layout.item, null);
 6     }else if(convertView != null)
 7     ((TextView)
 8 convertView.findViewById(R.id.text)).setText(DATA[position]);
 9     ((ImageView)
10 convertView.findViewById(R.id.icon)).setImageBitmap(
11     (position & 1) == 1 ? mIcon1 :
12 mIcon2);
13     return convertView;
14 }

【添加ViewHolder ,避免重复查找需要修改的控件】

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

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

 1 public View getView(int position, View convertView, ViewGroup parent) {
 2     ViewHolder holder;
 3     if (convertView == null) {
 4          convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
 5          holder = new ViewHolder();
 6          holder.text = (TextView) convertView.findViewById(R.id.text);
 7          holder.icon = (ImageView) convertView.findViewById(R.id.icon);
 8          convertView.setTag(holder);
 9     } else {
10          holder = (ViewHolder) convertView.getTag();
11     }
12     holder.text.setText(DATA[position]);
13     holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
14     return convertView;
15 }
16 static class ViewHolder {
17     TextView text;
18     ImageView icon;
19 }

【缓存Item的数据】

 1 若获取Item中的数据需要的时间较长,我们也可以将其缓存在ViewHolder中
 2  public View getView(int position, View convertView, ViewGroup parent) {
 3     ViewHolder holder;
 4     if (convertView == null) {
 5          convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
 6          holder = new ViewHolder();
 7          holder.text = (TextView) convertView.findViewById(R.id.text);
 8          holder.icon = (ImageView) convertView.findViewById(R.id.icon);
 9           holder.data = DATA[position];
10           holder.bitmap = (position & 1) == 1 ? mIcon1 : mIcon2;
11           
12          convertView.setTag(holder);
13     } else {
14          holder = (ViewHolder) convertView.getTag();
15     }
16     holder.text.setText(holder.data);
17     holder.icon.setImageBitmap(holder.bitmap);
18     return convertView;
19 }
20  
21 static class ViewHolder {
22     TextView text;
23     ImageView icon;
24     String data;
25     Bitmap bitmap;
26  }
27 }

【分段显示】

类比传统的findByPage。

 

3.3>优化布局文件

【使用观察布局的工具: Hierarchy Viewer】

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

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

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

【使用布局优化工具: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.4>优化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"

 

3.5>使用ViewStub

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

 【延迟加载不常用的UI控件】

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

参考 sundy.android.demo.uiadv.performance.DelayLoadActivity.java

 【提高改变布局的速度】

需要的使用场景:1界面需要频繁切换.  2希望能提高切换速度。

使用方法:

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

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

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

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

5绑定并设置控件的状态。

3.6>图片优化细则

控制一定范围内的缩放;

减少调色板颜色数量来控制图片大小;

四、自定义控件

4.1>什么时候使用自定义控件

在实际开发过程中,我们常会发现Android的控件无法完全满足我们的需求,而该需求在我们项目中又会经常使用,这时我们就应当使用自定义的控件。

4.2>实现自定义控件的一般步骤

(1) 选择一个和我们的需求最接近的Android控件

(2) 在XML文件中添加自定义属性(可选)

(3) 重写构造函数

(4) 通过代码或布局文件添加子控件

(5) 重载需要修改的函数

(6) 在代码或XML文件中创建控件

 

五、动画

5.1>分类

【Property Animation】 ViewAnimation ,ObjectAnimation。

【VIew Animation】 TweenAnimation

  常用属性AnimationListener监听

  渐变动画(AlphaAnimation);

  旋转动画(RotateAnimation);

  缩放(ScaleAnimation);

  位移动画(TranslateAnimation);

【Drawable Animation】FrameAnimation

5.2>Interpolator

Interpolator定义了动画的执行过程中会如何改变。

在这里我们会介绍几个常用的变化效果,更多的效果请大家可以自己下来继续研究。

【CycleInterpolator】循环效果,该动画会循环被执行指定次数。

【AccelerateInterpolator】加速效果

【DecelerateInterpolator】减速效果

【AccelerateDecelerateInterpolator】先加速后减速的效果

5.3>总结使用方法

【创建动画】

在xml中定义

1 动画效果的定义应放在 res\anim目录下。
2 以下这两段段代码,通过在x轴连续7次的横向移动,实现了一个震动效果的动画:
3 cycle_7.xml
4 <cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android" android:cycles="7" />
5 
6 shake.xml
7 <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:toXDelta="10" android:duration="1000" android:interpolator="@anim/cycle_7" />

通过代码创建

1 Animation anim = new TranslateAnimation(0, 10, 0, 0);
2 anim.setDuration(1000);
3 anim.setInterpolator(new CycleInterpolator(7));
4 这段代码同样实现了一个震动效果。

【为View指定动画】

通过调用View.startAnimation, 即可立即启动动画效果。

【监控动画的执行状态】

通过为动画设定AnimationListener,我们可以知道动画的执行状况:

开始、完成、重复执行。

 

5.4>Android动画实现原理

图形变换通过矩阵实现。图形变换是图形学中的基本知识。简单来说就是,每种变换都是一次矩阵运算。在 Android 中,Canvas 类中包含当前矩阵,当调用 Canvas.drawBitmap (bmp, x, y, Paint) 绘制时,android 会先把 bmp 做一次矩阵运算,然后将运算的结果显示在 Canvas 上。这样,编程人员只需不断修改 Canvas 的矩阵并刷新屏幕,View 里的对象就会不停的做图形变换,动画就形成了。

【动画运行模式】

独占模式:即程序主线程进入一个循环,根据动画指令不断刷新屏幕,直到动画结束。

中断模式:即有单独一个线程对时间计数,每隔一定的时间向主线程发通知,主线程接到通知后更新屏幕。

【Animation类】

每个动画都重载了父类的 applyTransformation 方法,这个方法会被父类的 getTransformation 方法调用。另外每个动画还有个 initialize 方法,完成初始化工作。

【Interpolater类】

 

【Transformation类】

Transformation 记录了仿射矩阵 Matrix,动画每触发一次,会对原来的矩阵做一次运算, View 的 Bitmap 与这个矩阵相乘就可实现相应的操作(旋转、平移、缩放等)。

Transformation 类封装了矩阵和 alpha 值,它有两个重要的成员,一是 mMatrix,二是 mAlpha。

【View中实现动画的过程】

view 创建动画对象,设置动画属性,调用 invalidate 刷新屏幕,启动动画;

invalidate 方法触发了 onDraw 函数;

在 onDraw 函数中:

调用动画的 getTransformation 方法,得到当前时间点的矩阵

将该矩阵设置成 Canvas 的当前矩阵

调用 canvas 的 drawBitmap 方法,绘制屏幕。

判断 getTransformation 的返回值,若为真,调用 invalidate 方法,刷新屏幕进入下一桢;若为假,说明动画完成。

5.5>为Acitivty指定动画效果

修改Activity Theme:

 1 1. 在styles.xml中输入以下代码:
 2 <style name="AnimationActivity"
 3   parent="@android:style/Animation.Activity" >   
 4 <item name="android:activityOpenEnterAnimation">@anim/push_left_in</item>        
 5 <item name="android:activityOpenExitAnimation">@anim/push_left_out</item>         
 6 <item name="android:activityCloseEnterAnimation">@anim/push_right_in</item>      
 7 <item name="android:activityCloseExitAnimation">@anim/push_right_out</item>     
 8 </style>  
 9 2. 然后在themes.xml中
10 <style name="ThemeActivity">   
11 <item name="android:windowAnimationStyle">@style/AnimationActivity</item>      
12 <item name="android:windowNoTitle">true</item>     
13 </style>  
14 3. 在AndroidManifest.xml中为Activity指定theme.

使用代码设定:

通过调用 overridePendingTransition() 可以实时修改Activity的切换动画。

但需注意的是:该函数必须在调用startActivity()或finishe后立即调用,且只有效一次。

六、Drag

七、Sample:ViewFlipper实现最常见应用

1,定义四个动画 , fade_left_in fade_left_out fade_right_in fade_right_out

2,定义layout文件 。 <ViewAnimator> or <ViewFlipper>

3,写代码 , onCreate() 取得Flipper对象设置好属性 。

4,事件 , GestureDetector . 

5, onFling()  , 设置 viewFlipper设置动画以及调用下一个ViewGroup 

 

八、案例(模仿Apple程序列表的抖动效果)

Steps:

1, 通过PackageManager 加载应用图标到GridView里 

2,定义好item的shake动画 , longclick的时候 , 启动动画 

3,随着鼠标的移动 , 判断进入到了哪个Dock (哪个单元格区域)PointToPosition,从区域转换为index , 然后交换单元格视图(Swap)(先交换adapter ,绑定视图,视图就交换了)

4,Drop的时候把原来的图标放到当前单元格

 

posted on 2014-09-15 11:58  大米稀饭  阅读(365)  评论(0编辑  收藏  举报