• 12、高级商用界面开发


    一、风格与主题

    1>Style与Theme的区别

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

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

    2>使用Style

    【创建Style】

      2.1. 在resvalues 下创建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 动画效果的定义应放在 resanim目录下。
    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的时候把原来的图标放到当前单元格

  • 相关阅读:
    python深浅拷贝
    pyinstaller打包py文件为exe方法
    python学习笔记3-关于文件的复制、重命名、移动、删除操作
    BeautifulSoup4 print() 输出中文乱码解决方法
    进程和线程的开启效率
    python3 使用pymysql
    python3 __file__
    Flask Template ( 模板学习)
    响应对象
    nginx 以及 uwsgi 的配置
  • 原文地址:https://www.cnblogs.com/androidsj/p/3972552.html
Copyright © 2020-2023  润新知