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

12、高级商用界面开发_第1张图片

七、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的时候把原来的图标放到当前单元格

 

你可能感兴趣的:(开发)