Android DownloadManager

DownloadManager是系统开放给第三方应用使用的类,包含两个静态内部类DownloadManager.Query和DownloadManager.Request。

DownloadManager主要提供了一下主要方法

  • enqueue(Request request):执行下载,返回downloadId,downloadId可用于查询下载信息。
  • remove(long ids):删除下载,若下载中取消下载。会同时删除下载文件和记录。
  • query(Query query)查询下载信息
  • getMaxBytesOverMobile(Context context)通过移动网络下载的最大字节数
  • getMimeTypeForDownloadedFile(long id)得到下载的mineType

通过查看代码我们可以发现还有个CursorTranslator私有静态内部类。这个类主要对Query做了一层代理。将DownloadProvider和DownloadManager之间做个映射。将DownloadProvider中的十几种状态对应到了DownloadManager中的五种状态,DownloadProvider中的失败、暂停原因转换为了DownloadManager的原因。

使用DownloadManager的一般步骤

1.调用DownloadManager.Request开始下载

在开始之前,在AndroidManifest.xml中添加网络访问权限和sdcard写入权限。

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

接着,调用DownloadManager.Request开始下载

DownloadManager downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
String url = "http://v.yingshibao.chuanke.com/cet4/CET4_reading_video/1_zongshu/001_zongshu.mp4";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDestinationInExternalPublicDir("itbox", "zongshu.mp4");
long downloadId = downloadManager.enqueue(request);

DownloadManager.Request一些常用方法:

  • setDestinationInExternalFilesDir 设置文件下载路径
  • allowScanningByMediaScanner() 表示允许MediaScanner扫描到这个文件夹,默认不允许。
  • setTitle()设置下载中通知栏提示的标题
  • setDescription()设置下载中通知栏提示的介绍。
  • setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)表示下载进行中和下载完成的通知栏是否显示。默认只显示下载中通知。VISIBILITY_VISIBLE_NOTIFY_COMPLETED表示下载完成后显示通知栏提示。VISIBILITY_HIDDEN表示不显示任何通知栏提示,这个需要在AndroidMainfest中添加权限android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
  • request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI) 表示下载允许的网络类型,默认在任何网络下都允许下载。有NETWORK_MOBILE、NETWORK_WIFI、NETWORK_BLUETOOTH三种及其组合可供选择。如果只允许wifi下载,而当前网络为3g,则下载会等待。
  • request.setAllowedOverRoaming(boolean allow)移动网络情况下是否允许漫游。
  • request.setMimeType() 设置下载文件的mineType。因为下载管理Ui中点击某个已下载完成文件及下载完成点击通知栏提示都会根据mimeType去打开文件。
  • request.addRequestHeader(String header, String value)添加请求下载的网络链接的http头,比如User-Agent,gzip压缩等

2.下载进度查询

DownloadManager下载过程中,会将下载的数据和下载的状态插入ContentProvider中,所以我们可以通过注册一个ContentObserver,通过ContentObserver不断获取数据,对UI进行更新。

我们主要调用DownloadManager.Query()进行查询,DownloadManager.Query为下载管理对外开放的信息查询类,主要包括以下方法: * setFilterById(long… ids)根据下载id进行过滤 * setFilterByStatus(int flags)根据下载状态进行过滤 * setOnlyIncludeVisibleInDownloadsUi(boolean value)根据是否在download ui中可见进行过滤。 * orderBy(String column, int direction)根据列进行排序,不过目前仅支持DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP和DownloadManager.COLUMN_TOTAL_SIZE_BYTES排序。

// 观察
    class DownloadChangeObserver extends ContentObserver {
        private Handler handler;
        private long downloadId;

    <span class="kd">public</span> <span class="nf">DownloadChangeObserver</span><span class="o">(</span><span class="n">Handler</span> <span class="n">handler</span><span class="o">,</span> <span class="kt">long</span> <span class="n">downloadId</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">(</span><span class="n">handler</span><span class="o">);</span>
        <span class="k">this</span><span class="o">.</span><span class="na">handler</span> <span class="o">=</span> <span class="n">handler</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">downloadId</span> <span class="o">=</span> <span class="n">downloadId</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onChange</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">selfChange</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">updateView</span><span class="o">(</span><span class="n">handler</span><span class="o">,</span> <span class="n">downloadId</span><span class="o">);</span>
    <span class="o">}</span>

<span class="o">}</span>

<span class="n">mContext</span><span class="o">.</span><span class="na">getContentResolver</span><span class="o">().</span><span class="na">registerContentObserver</span><span class="o">(</span>
            <span class="n">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"content://downloads/my_downloads"</span><span class="o">),</span> <span class="kc">true</span><span class="o">,</span>
            <span class="k">new</span> <span class="nf">DownloadChangeObserver</span><span class="o">(</span><span class="n">handler</span><span class="o">,</span> <span class="n">downloadId</span><span class="o">));</span></code></pre></figure>

注册ContentResolver

    mContext.getContentResolver().registerContentObserver(
                Uri.parse("content://downloads/my_downloads"), true,
                new DownloadChangeObserver(handler, downloadId));

updateView()方法,获取进度,通过handle发送消息,更新UI

public void updateView(Handler handler, long downloadId) {
        // 获取状态和字节
        int[] bytesAndStatus = getBytesAndStatus(downloadId);
        //
        handler.sendMessage(handler.obtainMessage(0, bytesAndStatus[0],
                bytesAndStatus[1], bytesAndStatus[2]));
}

getBytesAndStatus(long downloadId)方法用于获取数据和状态

public int[] getBytesAndStatus(long downloadId) {
        int[] bytesAndStatus = new int[] { -1, -1, 0 };
        DownloadManager.Query query = new DownloadManager.Query()
                .setFilterById(downloadId);
        Cursor c = null;
        try {
            c = downloadManager.query(query);
            if (c != null && c.moveToFirst()) {
                // 当前下载的字节
                bytesAndStatus[0] = c
                        .getInt(c
                                .getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                // 总字节数
                bytesAndStatus[1] = c
                        .getInt(c
                                .getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                // 状态
                bytesAndStatus[2] = c.getInt(c
                        .getColumnIndex(DownloadManager.COLUMN_STATUS));
            }
        } finally {
            if (c != null) {
                c.close();
            }
        }
        return bytesAndStatus;
    }

handle接收到消息,更新UI

private class MyHandler extends Handler {
        private ViewHolder holder;

    <span class="kd">public</span> <span class="nf">MyHandler</span><span class="o">(</span><span class="n">ViewHolder</span> <span class="n">holder</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// TODO Auto-generated constructor stub</span>
        <span class="k">this</span><span class="o">.</span><span class="na">holder</span> <span class="o">=</span> <span class="n">holder</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">handleMessage</span><span class="o">(</span><span class="n">Message</span> <span class="n">msg</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">.</span><span class="na">handleMessage</span><span class="o">(</span><span class="n">msg</span><span class="o">);</span>

        <span class="k">switch</span> <span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="na">what</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">case</span> <span class="mi">0</span><span class="o">:</span>
            <span class="kt">int</span> <span class="n">status</span> <span class="o">=</span> <span class="o">(</span><span class="n">Integer</span><span class="o">)</span> <span class="n">msg</span><span class="o">.</span><span class="na">obj</span><span class="o">;</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">isDownloading</span><span class="o">(</span><span class="n">status</span><span class="o">))</span> <span class="o">{</span>
                <span class="c1">//</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setVisibility</span><span class="o">(</span><span class="n">View</span><span class="o">.</span><span class="na">VISIBLE</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setMax</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setProgress</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">download</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="s">"暂停"</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">downloadSize</span><span class="o">.</span><span class="na">setVisibility</span><span class="o">(</span><span class="n">View</span><span class="o">.</span><span class="na">VISIBLE</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">downloadPrecent</span><span class="o">.</span><span class="na">setVisibility</span><span class="o">(</span><span class="n">View</span><span class="o">.</span><span class="na">VISIBLE</span><span class="o">);</span>
                <span class="k">if</span> <span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="na">arg2</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setIndeterminate</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
                    <span class="n">holder</span><span class="o">.</span><span class="na">downloadSize</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="s">"0M/0M"</span><span class="o">);</span>
                <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
                    <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setIndeterminate</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
                    <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setMax</span><span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="na">arg2</span><span class="o">);</span>
                    <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setProgress</span><span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="na">arg1</span><span class="o">);</span>
                    <span class="n">holder</span><span class="o">.</span><span class="na">downloadPrecent</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">getPercent</span><span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="na">arg1</span><span class="o">,</span>
                            <span class="n">msg</span><span class="o">.</span><span class="na">arg2</span><span class="o">));</span>
                    <span class="c1">//</span>
                    <span class="n">holder</span><span class="o">.</span><span class="na">downloadSize</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">getSize</span><span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="na">arg1</span><span class="o">)</span> <span class="o">+</span> <span class="s">"/"</span>
                            <span class="o">+</span> <span class="n">getSize</span><span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="na">arg2</span><span class="o">));</span>
                <span class="o">}</span>
            <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
                <span class="c1">// 停止下载</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setVisibility</span><span class="o">(</span><span class="n">View</span><span class="o">.</span><span class="na">GONE</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setMax</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">progress</span><span class="o">.</span><span class="na">setProgress</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">download</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="s">"下载"</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">downloadSize</span><span class="o">.</span><span class="na">setVisibility</span><span class="o">(</span><span class="n">View</span><span class="o">.</span><span class="na">GONE</span><span class="o">);</span>
                <span class="n">holder</span><span class="o">.</span><span class="na">downloadPrecent</span><span class="o">.</span><span class="na">setVisibility</span><span class="o">(</span><span class="n">View</span><span class="o">.</span><span class="na">GONE</span><span class="o">);</span>
                <span class="k">if</span> <span class="o">(</span><span class="n">status</span> <span class="o">==</span> <span class="n">DownloadManager</span><span class="o">.</span><span class="na">STATUS_FAILED</span><span class="o">)</span> <span class="o">{</span>

                <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">status</span> <span class="o">==</span> <span class="n">DownloadManager</span><span class="o">.</span><span class="na">STATUS_SUCCESSFUL</span><span class="o">)</span> <span class="o">{</span>

                <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>

                <span class="o">}</span>
            <span class="o">}</span>
            <span class="k">break</span><span class="o">;</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

3.下载成功监听

下载完成后,下载管理会发出DownloadManager.ACTION_DOWNLOAD_COMPLETE这个广播,并传递downloadId作为参数。通过接受广播我们可以打开对下载完成的内容进行操作。

4.响应通知栏点击

  • 下载中通知栏点击

点击下载中通知栏提示,系统会对下载的应用单独发送Action为DownloadManager.ACTION_NOTIFICATION_CLICKED广播。intent.getData为content://downloads/all_downloads/29669,最后一位为downloadId。如果同时下载多个应用,intent会包含DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS这个key,表示下载的的downloadId数组。

  • 响应下载完成通知栏点击

下载完后会调用下面代码进行处理,从中我们可以发现系统会调用View action根据mimeType去查询。所以可以利用我们在介绍的DownloadManager.Request的setMimeType函数。

这样我们就可以利用DownloadManager进行下载操作,不过DownloadManager没有暂停操作。

扩展阅读

Android系统下载管理DownloadManager功能介绍及使用示例

Android下载管理DownloadManager功能扩展和bug修改

Android自定义控件-DragSortListview

drag-sort-listview是一个支持拖拽排序和左右滑动删除功能的自定义ListView,重写了 TouchInterceptor (TI) 类来提供更加优美的拖拽动画效果。包含如下特性 * 完美的拖拽支持 (应该没有视觉问题) * 在拖动的时候提供更平滑的滚动列表滚动 * 支持每个ListItem高度的多样性 * Public startDrag() and stopDrag() methods. * 有公开的接口可以自定义拖动的View DragSortListView 适用于带有任何优先级的列表:收藏夹、播放列表、备忘录等。笔者认为这是目前Android开源实现拖动排序操作最完美的方案。

一. 如何使用

  • (1) 数据重排. 拖拽排序重排ListView底层的数据顺序。由于DSLV 不知道您是如何组织您的数据的,所以重新组织数据必须由您自己通过实现相关的接口来实现。
  • (2) 开始/停止拖动. 通过调用DSLV的 startDrag() 和 stopDrag() 函数来启动或者停止拖动操作。 DragSortController这个助手类,提供了所有常用的 开始/停止/删除 拖拽操作功能。
  • (3) Floating View(拖动的View). 通过实现 FloatViewManager 接口可以控制 拖动的View 的视觉效果和行为。这样您可以显示任何内容作为 拖动的View,并且可以在拖动过程中更新其位置和显示状态。 DragSortController 助手类已经实现了该接口并提供了一些易用的实现方式。
  • 第一条是必须的。如上所述 第二条和第三条可以通过 DragSortController 助手类实现。通过研究示例项目中的代码 可以更加深入的理解上述内容。

二. XML 布局描述

  • DragSortListView 可以和标准的View一样在XML布局文件中声明。 在示例项目中提供了几种演示 。新加的额外属性定义如下,描述的格式如下
  • : (, <默认值>) <属性描述>.

1.XML 属性

  • collapsed_height: (dimension, 1px) 拖动其实位置占位符的高度。不能为0.
  • drag_scroll_start: (float, 0.3) 拖动时开始滚动ListView的区域(为DSLV 高度的分数值,在0到1之前)
  • max_drag_scroll_speed: (float, 0.5) 默认线性加速的拖动时滚动的最大速度。单位:像素/毫秒。
  • float_alpha: (float, 1.0) 拖动View的透明度。取值0到1 , 1代表不透明。
  • slide_shuffle_speed: (float, 0.7) 拖动View下方的View挤走的动画速度。
  • drop_animation_duration: (int, 150) 拖动放下时候的动画时间。
  • remove_animation_duration: (int, 150) 删除一个ListView的空白区域消失的动画时间。
  • track_drag_sort: (bool, false) 调试的选项。
  • use_default_controller: (bool, true) DSLV是否创建一个默认的DragSortController 对象,并且设置如下属性的值。如果该属性为false,则如下的属性忽略。
  • float_background_color: (color, BLACK) 拖动View的北背景色。默认情况下拖动View是当前拖动的Item的图像缓存。
  • drag_handle_id: (id, 0) ListItem中的一个View的资源id(或者ListItem的根view)。这个id用来识别“拖动的手柄”,只有当点击该view的时候才会触发拖动操作。使用默认DragSortController并且支持拖动操作的时候需要设置该属性。
  • sort_enabled: (bool, true) 是否启用拖动排序功能(如果您只需要左右滑动删除,则无需启用排序功能)
  • drag_start_mode: (enum, “onDown”) 设置启动拖动的手势。
  • “onDown”:当用户按下拖动手柄的时候启动拖动操作。
  • “onDrag”: 当用户按下拖动手柄开始拖动的时候启动拖动操作。
  • “onLongPress”:在拖动手柄上长按时候启动拖动操作。
  • remove_enabled: (bool, false) 是否启用拖动删除的功能。
  • remove_mode: (enum, “flingRight”) 设置启用删除的手势。
  • “clickRemove”:点击click_remove_id对应的View来删除。
  • “flingRight”: 快速向右滑动。
  • “flingLeft”: 快速向左滑动。
  • “slideRight”:向左滑动的时候,Floating View会变得透明。透明后释放,删除Item。
  • “slideLeft”: 同上,向右滑动。
  • click_remove_id: (id, 0) 当 remove_mode="clickRemove"并且remove_enabled="true"时候指定的删除一个Item的View。DragSortController使用了该属性。

三. Listeners

DragSortListView 是个 ListView,因此需要一个 ListAdapter 来提供每个List Item。拖动排序是通过DSLV中的一些接口添加到ListAdapter的条目中的。可以有两种方式来设置DSLV 的各种接口: (1)通过setListener() 函数来设置 (2)让自定义的 ListAdapter 实现这些接口,当调用 DragSortListView.setAdapter()函数时候,会检测实现的接口并设置 setListener()。

1.DragSortListView.DropListener

  • DropListener接口有一个回调函数: public void drop(int from, int to);
  • 该函数在拖动排序完成的时候调用;例如 拖动View被放下了。参数 from 是ListView开始拖动的位置,to 是被放下的位置。该接口非常重要,否则DSLV无法完成拖动操作。
  • 如果 DSLV 对象的 android:choiceMode 不是 "none",并且您的 ListAdapter 没有稳定的ID,您则必须在 drop(from, to)中调用 DragSortListView.moveCheckState(int from, int to)。

2.DragSortListView.RemoveListener

  • DSLV 提供了手势来删除拖动的View。 完成一个删除手势后,DSLV 调用RemoveListener 接口的函数: public void remove(int which);
  • 在ListAdapter中位置为 which 的条目需要被删除。是否真的删除或者只是标记删除由您自己实现。
  • 在拖动操作之外您也可以删除条目。在任何时候都可以调用 DragSortListView.removeItem(int position) .
  • 如果 DSLV 对象的 android:choiceMode 不是 "none",并且您的 ListAdapter 没有稳定的ID,您则必须在 remove(which)中调用 DragSortListView.moveCheckState(int from, int to)

3.DragSortListView.DragListener

  • DragListener 中的回调接口为 public void drag(int from, int to);
  • 当拖动的View位于一个新条目上方的时候 会调用该函数。 to 是当前潜在的放下位置, from是前一个位置。 TI 实现中提供了这个接口,目前还没使用场景举例。

4.DragSortListView.DragSortListener

这只是一个继承了上面3个接口的便利接口。

四.FloatViewManager

  • 该接口用来创建、更新和销毁拖动的View。通过函数 setFloatViewManager() 来设置。 SimpleFloatViewManager演示了其用法,该类是个简单的实现。用拖动List Item的快照实现一个拖动的View。
  • 如果您需要实现不同的拖动View则可以提供自己的FloatViewManager。 在 onCreateFloatView() 函数中,确保返回的View有个固定的尺寸 (不要使用 MATCH_PARENT! 虽然对于宽度来说用 MATCH_PARENT 也是可以的). DSLV 会根据您拖动View上的ViewGroup.LayoutParams 参数来测量该View。如果没有LayoutParams参数则会使用 WRAP_CONTENT 和 MATCH_PARENT 来测量器高度和宽度。

五.Drag start/stop

  • 在DragSortListView 0.3.0以后,拖动开始和停止完全取决于您。在DSLV中调用startDrag() 或者 stopDrag() 即可。需要注意的是,如果在调用startDrag() 函数的时候没有Touch事件,则拖动操作不会启动。在通常情况下您都无需关系开始拖动的操作,DragSortController 已经帮助你做好了。

DragSortController

  • DragSortController 类实现了一些常用的拖拽操作和删除List item的操作。该类实现了 View.OnTouchListener 接口来监听点击事件。同时也实现了FloatViewManager 接口。如果您在XML文件中没有使用默认的DragSortController,则您需要调用DSLV对象的 setFloatViewManager() 和 setOnTouchListener() 函数来设置一个DragSortController 。
  • DragSortController的默认行为认为List Item启用了拖动操作并且具有一个“拖动手柄”View,在DragSortController 中设置了“拖动手柄”View的资源id(如果 use_default_controller 是 true的话,拖动手柄的ID可以在XML文件中设置)。如果点击事件发生在一个List Item中的拖动手柄View上并且检测到了启动拖动操作的手势,则会启动一个拖动操作。

更多信息参考API

从EclipseADT迁移到AndroidStudio

AndroidStudio是Google I/O 2013大会上推出的Android开发环境,基于IntelliJ IDEA,和EclipseADT类似。AndroidStudio目前尚处于测试版,还是存在一些Bug的,当然取代EclipseADT只是时间问题,现在大部分应该都还是在使用EclipseADT开发,但是有必要适应一下这个新工具。AudioStudio使用Gradle来构建项目,什么是Gradle呢?Gradle是一个自动化编译部署测试工具,类似于AntMaven

下载安装过程不多说,本文主要介绍如果将EclipseADT的项目迁移到AndroidStudio。

1. 为项目生成Gradle所需的文件

  • 我们先看看EclipseADT中项目的节本结构

  • 接下来为项目生成Gradle所需的相关文件,这里注意一下,ADT版本必须是22以上的,不是的在SDK Manager中更新到最新版本。接下来,在项目上右键->Export,然后选择Generate Gradle build files。

  • 一路next,直到finish。搞定,这时候已经生成的Gradle的相关文件,看下一现在的结构。

2. 从AudioStudio中导入项目

  • 打开AudioStudio,选择Import Project,选择项目的目录。OK即可导入。

  • 主要就是build.gradle文件。

扩展阅读

解析HTML

一、作用

  • (1) 从一个url、文件或string获得html并解析
  • (2) 利用dom遍历或css选择器查找、提取数据
  • (3) 操作html元素
  • (4) 根据白名单去除用于提交的非法数据防止xss攻击
  • (5) 输出整齐的html

二、方法介绍

1、解析一个HTML字符串

 Document doc = Jsoup.parse(html);
 提供了两种:
 (1) Jsoup.parse(String html)
 (2) Jsoup.parse(String html, String baseUri)
 第二种方法能够将输入的HTML解析为一个新的文档 (Document),参数 baseUri 是用来将相对 URL 转成绝对URL,并指定从哪个网站获取文档。如这个方法不适用,你可以使用 第一种方法来解析成HTML字符串。只要解析的不是空字符串,就能返回一个结构合理的文档,其中包含(至少) 一个head和一个body元素。
 一旦拥有了一个Document,你就可以使用Document中适当的方法或它父类 Element和Node中的方法来取得相关数据。

2、解析一个body

 Document doc = Jsoup.parseBodyFragment(html);
 Element body = doc.body();
 parseBodyFragment 方法创建一个空壳的文档,并插入解析过的HTML到body元素中。假如你使用正常的 Jsoup.parse(String html) 方法,通常你也可以得到相同的结果,但是明确将用户输入作为 body片段处理,以确保用户所提供的任何糟糕的HTML都将被解析成body元素。
 Document.body() 方法能够取得文档body元素的所有子元素,与 doc.getElementsByTag("body")相同。

3、从一个URL加载一个Document

 Document doc = Jsoup.connect("http://example.com/").get();
 String title = doc.title();
 connect(String url) 方法创建一个新的 Connection, 和 get() 取得和解析一个HTML文件。如果从该URL获取HTML时发生错误,便会抛出 IOException,应适当处理。
 Connection 接口还提供一个方法链来解决特殊请求,具体如下:
 Document doc = Jsoup.connect("http://example.com")
                     .data("query", "Java")
                     .userAgent("Mozilla")
                     .cookie("auth", "token")
                     .timeout(3000)
                     .post();
 这个方法只支持Web URLs (http和https 协议); 假如你需要从一个文件加载,可以使用parse(File in, String charsetName) 代替。

4、根据一个文件加载Document对象

 Jsoup.parse(File in, String charsetName, String baseUri) 
 File input = new File("/tmp/input.html");
 Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

 parse(File in, String charsetName, String baseUri) 这个方法用来加载和解析一个HTML文件。如在加载文件的时候发生错误,将抛出IOException,应作适当处理。
 baseUri 参数用于解决文件中URLs是相对路径的问题。如果不需要可以传入一个空的字符串。
 另外还有一个方法parse(File in, String charsetName) ,它使用文件的路径做为 baseUri。 这个方法适用于如果被解析文件位于网站的本地文件系统,且相关链接也指向该文件系统。

5、使用dom方法来遍历一个Document对象

 File input = new File("/tmp/input.html");
 Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

 Element content = doc.getElementById("content");
 Elements links = content.getElementsByTag("a");
 for (Element link : links) {
     String linkHref = link.attr("href");
     String linkText = link.text();
 }

(1)查找元素

 getElementById(String id)
 getElementsByTag(String tag)
 getElementsByClass(String className)
 getElementsByAttribute(String key) (and related methods)
 Element siblings: siblingElements(), firstElementSibling(), lastElementSibling();nextElementSibling(), previousElementSibling()
 Graph: parent(), children(), child(int index)

(2)元素数据

 attr(String key)获取属性attr(String key, String value)设置属性
 attributes()获取所有属性
 id(), className() and classNames()
 text()获取文本内容text(String value) 设置文本内容
 html()获取元素内HTMLhtml(String value)设置元素内的HTML内容
 outerHtml()获取元素外HTML内容
 data()获取数据内容(例如:script和style标签)
 tag() and tagName()

(3)操作HTML和文本

 append(String html), prepend(String html)
 appendText(String text), prependText(String text)
 appendElement(String tagName), prependElement(String tagName)
 html(String value)

6、使用选择器语法来查找元素

 File input = new File("/tmp/input.html");
 Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

 Elements links = doc.select("a[href]"); //带有href属性的a元素
 Elements pngs = doc.select("img[src$=.png]");//扩展名为.png的图片

 Element masthead = doc.select("div.masthead").first();//class等于masthead的div标签
 Elements resultLinks = doc.select("h3.r > a"); //在h3元素之后的a元素
 jsoup elements对象支持类似于CSS (或jquery)的选择器语法,来实现非常强大和灵活的查找功能。.
 这个select 方法在Document, Element,或Elements对象中都可以使用。且是上下文相关的,因此可实现指定元素的过滤,或者链式选择访问。
 Select方法将返回一个Elements集合,并提供一组方法来抽取和处理结果。

(1)Selector选择器概述

 tagname: 通过标签查找元素,比如:a
 ns|tag: 通过标签在命名空间查找元素,比如:可以用 fb|name 语法来查找 <fb:name> 元素
 #id: 通过ID查找元素,比如:#logo
 .class: 通过class名称查找元素,比如:.masthead
 [attribute]: 利用属性查找元素,比如:[href]
 [^attr]: 利用属性名前缀来查找元素,比如:可以用[^data-] 来查找带有HTML5 Dataset属性的元素
 [attr=value]: 利用属性值来查找元素,比如:[width=500]
 [attr^=value], [attr$=value], [attr*=value]: 利用匹配属性值开头、结尾或包含属性值来查找元素,比如:[href*=/path/]
 [attr~=regex]: 利用属性值匹配正则表达式来查找元素,比如: img[src~=(?i)\.(png|jpe?g)]
 *: 这个符号将匹配所有元素

(2)Selector选择器组合使用

 el#id: 元素+ID,比如: div#logo
 el.class: 元素+class,比如: div.masthead
 el[attr]: 元素+class,比如: a[href]

(3)任意组合,比如:a[href].highlight

 ancestor child: 查找某个元素下子元素,比如:可以用.body p 查找在"body"元素下的所有p元素
 parent > child: 查找某个父元素下的直接子元素,比如:可以用div.content > p 查找 p 元素,也可以用body > * 查找body标签下所有直接子元素
 siblingA + siblingB: 查找在A元素之前第一个同级元素B,比如:div.head + div
 siblingA ~ siblingX: 查找A元素之前的同级X元素,比如:h1 ~ p
 el, el, el:多个选择器组合,查找匹配任一选择器的唯一元素,例如:div.masthead, div.logo

(4)伪选择器selectors

 :lt(n): 查找哪些元素的同级索引值(它的位置在DOM树中是相对于它的父节点)小于n,比如:td:lt(3) 表示小于三列的元素
 :gt(n):查找哪些元素的同级索引值大于n,比如: div p:gt(2)表示哪些div中有包含2个以上的p元素
 :eq(n): 查找哪些元素的同级索引值与n相等,比如:form input:eq(1)表示包含一个input标签的Form元素
 :has(seletor): 查找匹配选择器包含元素的元素,比如:div:has(p)表示哪些div包含了p元素
 :not(selector): 查找与选择器不匹配的元素,比如: div:not(.logo) 表示不包含 class=logo 元素的所有 div 列表
 :contains(text): 查找包含给定文本的元素,搜索不区分大不写,比如: p:contains(jsoup)
 :containsOwn(text): 查找直接包含给定文本的元素
 :matches(regex): 查找哪些元素的文本匹配指定的正则表达式,比如:div:matches((?i)login)
 :matchesOwn(regex): 查找自身包含文本匹配指定正则表达式的元素
 注意:上述伪选择器索引是从0开始的,也就是说第一个元素索引值为0,第二个元素index为1等

7、从元素抽取属性,文本和HTML

 *要取得一个属性的值,可以使用Node.attr(String key) 方法
 *对于一个元素中的文本,可以使用Element.text()方法
 *对于要取得元素或属性中的HTML内容,可以使用Element.html(), 或 Node.outerHtml()方法

 示例:
  String html = "<p>An <a href='http://example.com/'><b>example</b></a> link.</p>";
  Document doc = Jsoup.parse(html);//解析HTML字符串返回一个Document实现
  Element link = doc.select("a").first();//查找第一个a元素

  String text = doc.body().text(); // "An example link"//取得字符串中的文本
  String linkHref = link.attr("href"); // "http://example.com/"//取得链接地址
  String linkText = link.text(); // "example""//取得链接地址中的文本

  String linkOuterH = link.outerHtml(); // "<a href="http://example.com"><b>example</b></a>"
  String linkInnerH = link.html(); // "<b>example</b>"//取得链接内的html内容
 上述方法是元素数据访问的核心办法。此外还其它一些方法可以使用:
  Element.id()
  Element.tagName()
  Element.className() and Element.hasClass(String className)
 这些访问器方法都有相应的setter方法来更改数据.

8、URL 处理

 *在你解析文档时确保有指定base URI,然后
 *使用 abs: 属性前缀来取得包含base URI的绝对路径。代码如下: 
 Document doc = Jsoup.connect("http://www.open-open.com").get();

 Element link = doc.select("a").first();
 String relHref = link.attr("href"); // == "/"
 String absHref = link.attr("abs:href"); // "http://www.open-open.com/"
 在HTML元素中,URLs经常写成相对于文档位置的相对路径: <a href="/download">...</a>. 当你使用 Node.attr(String key) 方法来取得a元素的href属性时,它将直接返回在HTML源码中指定定的值。
 假如你需要取得一个绝对路径,需要在属性名前加 abs: 前缀。这样就可以返回包含根路径的URL地址attr("abs:href")
 因此,在解析HTML文档时,定义base URI非常重要。
 如果你不想使用abs: 前缀,还有一个方法能够实现同样的功能 Node.absUrl(String key)

更多请参考官网

   API
jar包

Activity跳转动画

Activity跳转动画是指一个Activity跳转到另外一个Activity的动画效果,我们可以通过Acitivity的overridePendingTransition()方法实现跳转动画。

这个函数接受两个参数:

  • 第一个参数为第一个Activity退出时的动画
  • 第二个参数为第二个Activity进入时的动画

这个方法必须在startActivity方法或者finish方法之后调用。

示例:

Intent intent = new Intent(MainActivity.this,OtherActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.push_up_in,R.anim.push_up_out);

Android 4.1(API16)提供了一个新类ActivityOptions,用来实现Activity的切换动画。

ActivityOptions类提供了三个方法

  • makeScaleUpAnimation() 创建一个动画,能够从屏幕指定的位置和指定的大小拉伸一个活动窗口。例如,当打开一个应用时,Android 4.1的主屏幕使用了这个方法。
  • makeThumbnailScaleUpAnimation() 创建一个动画,能够从屏幕指定的位置和提供的缩略图拉伸一个活动窗口。例如,在Android 4.1的最近使用程序窗口中,当往回一个应用程序时使用了这个动画。
  • makeCustomAnimation() 创建一个动画,由你自己的资源所定义:一个用来定义活动开启的动画,一个用来定义活动被关闭的动画。

通过这三个方法中的一个就可以创建一个ActivityOptions示例,然后调用toBundle()方法获取一个Bundle对象,传递给startActivity方法。

示例:

Intent intent = new Intent(MainActivity.this,OtherActivity.class);
ActivityOptions opts = ActivityOptions.makeCustomAnimation(MainActivity.this, R.anim.fade, R.anim.hold);
startActivity(intent, opts.toBundle());

使用Support Library实现ActionBar

随着AndroidDesign的Holo风格越来越普及,Android应用程序也有了自己的设计风格,微信5.2也转向的Holo风格,ActionBar是Holo风格中重要的元素,接下来我将简单介绍ActionBar如何应用到项目中。

ActionBar是android3.0之后(API Level 11)才加入的。在此之前一般是使用Github上的开源项目ActionBarSherlock来兼容低版本。2013年google发布了v7 appcompact library,可以让开发者在android 2.1(API level 7)以及以上版本上使用ActionBar。我们这里主要讲解一下使用v7 appcompact library来实现ActionBar。

扩展阅读:ActionBarSherlock和ActionBar Compatibility区别

ActionBar的显示与隐藏

如果还没有v7支持库,打开SDK Manager安装即可

SDK Manager

安装之后,在sdk目录下的extras目录中可应该可以找到v4,v7,v13支持库,v7中包含了三个项目,ActionBar只需要使用v7中的appcompat项目。

v7 appcompat

接下来新建一个Android Project,我使用的是eclipse,然后导入v7中得appcompat项目,这里要注意一下,appcompat项目是包含资源文件的(比如ActionBar的背景图片),只导入jar包是不行的,我们的工程需要关联appcompat项目,如何关联请参考Android Library项目的使用。

在清单文件中配置activity的Theme,可以在application中配置全局Theme,appcompat提供了三种Theme:

  • 黑色主题:@Style/Theme.AppCompat
  • 白色主题:@Style/Theme.AppCompat.Light
  • 白色主题,黑色ActionBar:@Style/Theme.AppCompat.Light.DarkActionBar

也可以自定义Style继承上面几种Theme。

配置完主题之后,我们需要将Activity继承ActionBarActivity,如果需要使用v4包中的FragmentActivity也不需要担心,因为ActionBarActivity继承了FragmentActivity。

在Activity中调用getSupportActionBar()方法可以获取ActionBar对象,ActionBar默认是显示的,如果想隐藏可以调用ActionBar.hide()方法,显示则调用ActionBar.show();

        ActionBar actionBar = getSupportActionBar();
        if (actionBar.isShowing()) {// 判断ActionBar是否显示
            actionBar.hide();// 隐藏ActionBar
        } else {
            actionBar.show();// 显示ActionBar
        }

使用Logo替换icon

默认的,ActionBar调用应用图标。如果在或者元素中,指定logo属性,ActionBar将使用logo替代icon

ActionBar一些常用的方法

  • setBackgroundDrawable(Drawable drawable):为ActionBar设置背景。
  • setDisplayHomeAsUpEnabled(boolean b):是否显示返回的按钮。
  • setDisplayShowHomeEnabled(boolean b);是否显示icon
  • setDisplayShowTitleEnabled(boolean b);是否显示标题
  • setDisplayShowCustomEnabled(boolean b);是否显示自定义view
  • setIcon();设置Icon
  • setTitle();设置标题
  • getSelectedNavigationIndex()获取选择条目的索引。

添加Action Item

当你开启Activity时,系统通过调用Activity的onCreateOptionsMenu()方法来放置action items。使用这个方法inflate一个定义所有action items的菜单资源。

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@+id/action_search"
          android:icon="@drawable/ic_action_search"
          android:title="@string/action_search"/>
    <item android:id="@+id/action_compose"
          android:icon="@drawable/ic_action_compose"
          android:title="@string/action_compose" />
</menu>

然后调用Activity的onCreateOptionsMenu()方法中添加将所有的action item添加到ActionBar上。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu items for use in the action bar
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.main_activity_actions, menu);
    return super.onCreateOptionsMenu(menu);
}

想要item直接显示在actionbar上,需要在标签中,添加一个showAsAction="ifRoom"属性。

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
    <item android:id="@+id/action_search"
          android:icon="@drawable/ic_action_search"
          android:title="@string/action_search"
          yourapp:showAsAction="ifRoom"  />
    ...
</menu>

如果没有足够的空间,它将以悬浮菜单的样式显示。

使用support library的属性

注意上面的showAsAction 属性使用了一个自定义命名空间。在使用support library定义的属性必须自定义命名空间。因为这些属性在一些老的Android设备上不存在。

如果同时指定了title和icon属性,action item默认只显示icon.如果需要显示标题,需要为showAsAction属性添加withText值。

<item yourapp:showAsAction="ifRoom|withText" ... />

如果icon可用并且actionbar 空间不足时,title将不显示。

尽管可能你不需要显示title,但是仍然要指定title属性的值。 * 如果空间不足,菜单将以悬浮状态显示,并且只显示title。 * 如果action item只显示icon,用户可以通过长按条目显示title。

你也可以设置showAsAction属性的值为always,让一个action item一直显示,但是你最好不要让一个action item一直显示。这样做在窄屏上出现一些布局适配的问题。

处理条目点击####

当用户点击一个条目时,系统将点击的MenuItem传递给Activity的onOptionsItemSelected() 方法。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle presses on the action bar items
    switch (item.getItemId()) {
        case R.id.action_search:
            openSearch();
            return true;
        case R.id.action_compose:
            composeMessage();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

向上返回的按钮的id是android.R.id.home 所以通过下面代码就能实现点击返回按钮返回的功能。

 setDisplayHomeAsUpEnabled(true);//显示返回箭头
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle presses on the action bar items
    switch (item.getItemId()) {
        case android.R.id.home:
                finish();
        default:
            return super.onOptionsItemSelected(item);
    }
}

使用分离的ActionBar

ActionBar可以分割成屏幕上方和屏幕下方两部分来显示(如上图所示)。分割的ActionBar可以让空间能够更合理的利用。

为了实现分离的ActionBar,必须做以下两件事情:

  1. 添加uiOptions="splitActionBarWhenNarrow"到元素或 元素中。这个属性只能在API 14+上起作用,低版本将忽略这个属性。

  2. 为了支持低版本,在元素中添加一个元素,

<manifest ...>
    <activity uiOptions="splitActionBarWhenNarrow" ... >
        <meta-data android:name="android.support.UI_OPTIONS"
                   android:value="splitActionBarWhenNarrow" />
    </activity>
</manifest>

添加一个Action View

Action View 提供了一些复杂操作的快速入口,不需要改变Acitivity或者Fragment,并且不用替换ActionBar.例如,如果你想进行搜索,你可以通过给actionbar添加一个SearchView组件来实现(如图所示)。 我们可以通过给acionlayout属性指定一个布局资源或者给actionViewClass属性指定一个组件类来添加一个Action View.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
    <item android:id="@+id/action_search"
          android:title="@string/action_search"
          android:icon="@drawable/ic_action_search"
          yourapp:showAsAction="ifRoom|collapseActionView"
          yourapp:actionViewClass="android.support.v7.widget.SearchView" />
</menu>

注意showAsAction属性包含了一个collapseActionView值。

我们可以在onCreateOptionsMenu()方法中配置action view。通过静态方法MenuItemCompat.getActionView()可以获取一个到action view对象。这个方法需要传递一个MenuItem对象。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_activity_actions, menu);
    MenuItem searchItem = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    // Configure the search info and add any event listeners
    ...
    return super.onCreateOptionsMenu(menu);
}

处理可折叠的 action view

为了节省action bar的空间,可以将action view折叠成一个action button。当这个action button被选中时,action view将会被展开。只要为showAsAction属性添加一个collapseActionView值就可以让action view变的可折叠。

当用户点击action button,action view能够自动的展开,不需要在onOptionsItemSelected()方法中进行点击处理。但是如果将其返回值设置为true,action view将不会被展开。

当点下手机的返回按钮或者action bar的返回按钮,action view将会被折叠。

通过OnActionExpandListener监听器,可以监听到action view折叠和展开。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.options, menu);
    MenuItem menuItem = menu.findItem(R.id.actionItem);
    ...

<span class="c1">// When using the support library, the setOnActionExpandListener() method is</span>
<span class="c1">// static and accepts the MenuItem object as an argument</span>
<span class="n">MenuItemCompat</span><span class="o">.</span><span class="na">setOnActionExpandListener</span><span class="o">(</span><span class="n">menuItem</span><span class="o">,</span> <span class="k">new</span> <span class="n">OnActionExpandListener</span><span class="o">()</span> <span class="o">{</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onMenuItemActionCollapse</span><span class="o">(</span><span class="n">MenuItem</span> <span class="n">item</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// Do something when collapsed</span>
        <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>  <span class="c1">// Return true to collapse action view</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onMenuItemActionExpand</span><span class="o">(</span><span class="n">MenuItem</span> <span class="n">item</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// Do something when expanded</span>
        <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>  <span class="c1">// Return true to expand action view</span>
    <span class="o">}</span>
<span class="o">});</span>

}

添加 Action Provider

类似action view,action provider通过自定义布局来替换一个操作按钮.但是action provider控制所有的动作行为并且在被点击时能够显示一个子菜单。

通过为actionViewClass属性设置一个ActionProvider类,来添加action provider.也可以通过继承ActionProvider来创建自定义的action provider.Android提供了一些action provider,例如ShareActionProvider。

由于每一个ActionProvider类定义自己的动作行为,所以不需要通过onOptionsItemSelected()方法来设置其点击事件,但是你仍然可以通过此方法来设置其他操作,也可以通过onPerformDefaultAction()来设置别的操作。

如果action Provider提供一个子菜单,用户打开列表或者选中一个子菜单,activity将不调用onOptionsItemSelected()。

使用ShareActionProvider

使用ShareActionProvider添加一个分享操作需要一下步骤:

  • 设置actionProviderClass属性值为ShareActionProvider类.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
    <item android:id="@+id/action_share"
          android:title="@string/share"
          yourapp:showAsAction="ifRoom"
          yourapp:actionProviderClass="android.support.v7.widget.ShareActionProvider"
          />
    ...
</menu>

  • 定义你想要分享的Intent。在onCreateOptionsMenu()方法中调用 MenuItemCompat.getActionProvider() 获取ShareActionProvider对象,然后调用ShareActionProvider的setShareIntent()设置分享意图。

private ShareActionProvider mShareActionProvider;

@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_activity_actions, menu);

<span class="c1">// Set up ShareActionProvider's default share intent</span>
<span class="n">MenuItem</span> <span class="n">shareItem</span> <span class="o">=</span> <span class="n">menu</span><span class="o">.</span><span class="na">findItem</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">action_share</span><span class="o">);</span>
<span class="n">mShareActionProvider</span> <span class="o">=</span> <span class="o">(</span><span class="n">ShareActionProvider</span><span class="o">)</span>
        <span class="n">MenuItemCompat</span><span class="o">.</span><span class="na">getActionProvider</span><span class="o">(</span><span class="n">shareItem</span><span class="o">);</span>
<span class="n">mShareActionProvider</span><span class="o">.</span><span class="na">setShareIntent</span><span class="o">(</span><span class="n">getDefaultIntent</span><span class="o">());</span>

<span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">onCreateOptionsMenu</span><span class="o">(</span><span class="n">menu</span><span class="o">);</span>

}

/ Defines a default (dummy) share intent to initialize the action provider. * However, as soon as the actual content to be used in the intent * is known or changes, you must update the share intent by again calling * mShareActionProvider.setShareIntent() / private Intent getDefaultIntent() { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("image/"); return intent; }

默认情况下,ShareActionProvider根据用户选择的频繁度进行排序,越频繁的越靠上,最频繁的直接作为一个action bar的一个默认分享按钮存在。并且排序信息保存在一个名为DEFAULT_SHARE_HISTORY_FILE_NAME的私有文件中。如果使用ShareActionProvider或其子类只进行一种操作的话,可以继续使用这个历史文件而不需要其它操作。如果你用SharedActionProvider或子类同时进行多种不同的操作,那么每一个ShareActionProvider应当分别指定自己的历史文件去维护自己的历史记录。通过调用setShareHistoryFileName()并且创建一个xml的文件来创建不同的历史文件。

创建自定义的ActionProvider

要创建自定义的ActionProvider只需要简单的继承ActionProvider类,并且实现下列方法。

  • OnCreateActionView()这个方法用来获取action view。使用从构造器中接收的Context对象,获取一个LayoutInflater对象的实例,并且用XML资源来填充操作视窗,然后注册事件监听器。

public View onCreateActionView(MenuItem forItem) {
    // Inflate the action view to be shown on the action bar.
    LayoutInflater layoutInflater = LayoutInflater.from(mContext);
    View view = layoutInflater.inflate(R.layout.action_provider, null);
    ImageButton button = (ImageButton) view.findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Do something...
        }
    });
    return view;
}

  • onPerformDefaultAction()在选中悬浮菜单中的菜单时,系统会调用这个方法,并且操作提供器应该这对这个选中的菜单项执行默认的操作。但是,如果你的操作提供器提供了一个子菜单,即使是悬浮菜单中一个菜单项的子菜单,那么也要通过onPrepareSubMenu()回调方法来显示子菜单。这样onPerformDefaultAction()在子菜单显示时就不会被调用。注意:实现了onOptionsItemSelected()回调方法的Activity或Frament对象能够通过处理item-selected事件(并且返回true)来覆盖操作提供器的默认行为,这种情况下,系统不会调用onPerformDefaultAction()方法。

添加导航选项标签

action bar 选项卡可以让用户非常方便的浏览和切换不同的视图,并且能够适配不同的屏幕。例如:当屏幕足够宽的时候,选项卡可以和action button并肩显示(如下图1),当在窄屏上,它将与action bar分离显示(如下图2)。在一些情况下,Android系统将会在action bar中以下拉列表的形式显示选项卡条目用来确保最合适的适配。

为action bar添加选项卡,一般需要以下步骤:

  1. 实现ActionBar.TabListener接口,这个接口提供了一些选项卡事件的回调函数。

  2. 创建ActionBar.Tab,通过调用setTabListener()方法设置 ActionBar.TabListener。setText()方法可以用来设置选项卡的标题。

  3. 调用addTab()方法来将所有的选项卡添加到action bar上。

示例:

实现ActionBar.TabListener

public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

<span class="cm">/** Constructor used each time a new tab is created.
  * @param activity  The host Activity, used to instantiate the fragment
  * @param tag  The identifier tag for the fragment
  * @param clz  The fragment's Class, used to instantiate the fragment
  */</span>
<span class="kd">public</span> <span class="nf">TabListener</span><span class="o">(</span><span class="n">Activity</span> <span class="n">activity</span><span class="o">,</span> <span class="n">String</span> <span class="n">tag</span><span class="o">,</span> <span class="n">Class</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">clz</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">mActivity</span> <span class="o">=</span> <span class="n">activity</span><span class="o">;</span>
    <span class="n">mTag</span> <span class="o">=</span> <span class="n">tag</span><span class="o">;</span>
    <span class="n">mClass</span> <span class="o">=</span> <span class="n">clz</span><span class="o">;</span>
<span class="o">}</span>

<span class="cm">/* The following are each of the ActionBar.TabListener callbacks */</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onTabSelected</span><span class="o">(</span><span class="n">Tab</span> <span class="n">tab</span><span class="o">,</span> <span class="n">FragmentTransaction</span> <span class="n">ft</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">// Check if the fragment is already initialized</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">mFragment</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// If not, instantiate and add it to the activity</span>
        <span class="n">mFragment</span> <span class="o">=</span> <span class="n">Fragment</span><span class="o">.</span><span class="na">instantiate</span><span class="o">(</span><span class="n">mActivity</span><span class="o">,</span> <span class="n">mClass</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
        <span class="n">ft</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">android</span><span class="o">.</span><span class="na">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">content</span><span class="o">,</span> <span class="n">mFragment</span><span class="o">,</span> <span class="n">mTag</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
        <span class="c1">// If it exists, simply attach it in order to show it</span>
        <span class="n">ft</span><span class="o">.</span><span class="na">attach</span><span class="o">(</span><span class="n">mFragment</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onTabUnselected</span><span class="o">(</span><span class="n">Tab</span> <span class="n">tab</span><span class="o">,</span> <span class="n">FragmentTransaction</span> <span class="n">ft</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">mFragment</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// Detach the fragment, because another one is being attached</span>
        <span class="n">ft</span><span class="o">.</span><span class="na">detach</span><span class="o">(</span><span class="n">mFragment</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onTabReselected</span><span class="o">(</span><span class="n">Tab</span> <span class="n">tab</span><span class="o">,</span> <span class="n">FragmentTransaction</span> <span class="n">ft</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">// User selected the already selected tab. Usually do nothing.</span>
<span class="o">}</span>

}

注意:在这里不能调用fragment transaction的commit()方法,否则系统可能会出现异常,也不能添加这些fragment到返回栈。

接下来创建每一个需要添加到ActionBar上的ActionBar.Tab。注意,必须调用ActionBar的setNavigationMode(NAVIGATION_MODE_TABS)使选项卡可用。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Notice that setContentView() is not used, because we use the root
    // android.R.id.content as the container for each fragment

<span class="c1">// setup action bar for tabs</span>
<span class="n">ActionBar</span> <span class="n">actionBar</span> <span class="o">=</span> <span class="n">getSupportActionBar</span><span class="o">();</span>
<span class="n">actionBar</span><span class="o">.</span><span class="na">setNavigationMode</span><span class="o">(</span><span class="n">ActionBar</span><span class="o">.</span><span class="na">NAVIGATION_MODE_TABS</span><span class="o">);</span>
<span class="n">actionBar</span><span class="o">.</span><span class="na">setDisplayShowTitleEnabled</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>

<span class="n">Tab</span> <span class="n">tab</span> <span class="o">=</span> <span class="n">actionBar</span><span class="o">.</span><span class="na">newTab</span><span class="o">()</span>
                   <span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">string</span><span class="o">.</span><span class="na">artist</span><span class="o">)</span>
                   <span class="o">.</span><span class="na">setTabListener</span><span class="o">(</span><span class="k">new</span> <span class="n">TabListener</span><span class="o">&lt;</span><span class="n">ArtistFragment</span><span class="o">&gt;(</span>
                           <span class="k">this</span><span class="o">,</span> <span class="s">"artist"</span><span class="o">,</span> <span class="n">ArtistFragment</span><span class="o">.</span><span class="na">class</span><span class="o">));</span>
<span class="n">actionBar</span><span class="o">.</span><span class="na">addTab</span><span class="o">(</span><span class="n">tab</span><span class="o">);</span>

<span class="n">tab</span> <span class="o">=</span> <span class="n">actionBar</span><span class="o">.</span><span class="na">newTab</span><span class="o">()</span>
               <span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">string</span><span class="o">.</span><span class="na">album</span><span class="o">)</span>
               <span class="o">.</span><span class="na">setTabListener</span><span class="o">(</span><span class="k">new</span> <span class="n">TabListener</span><span class="o">&lt;</span><span class="n">AlbumFragment</span><span class="o">&gt;(</span>
                       <span class="k">this</span><span class="o">,</span> <span class="s">"album"</span><span class="o">,</span> <span class="n">AlbumFragment</span><span class="o">.</span><span class="na">class</span><span class="o">));</span>
<span class="n">actionBar</span><span class="o">.</span><span class="na">addTab</span><span class="o">(</span><span class="n">tab</span><span class="o">);</span>

}

此外,我们还可以通过结合ViewPager来实现滑动切换视图。

添加下拉列表导航

Activity还提供了一个下拉列表的导航,如上图所示。下拉列表适用于不频繁的操作,如果操作频繁,请使用选项卡导航。

创建一个下拉列表导航需要以下步骤:

  1. 创建一个SpinnerAdapter,这个适配器用于填充列表。
  2. 实现ActionBar.OnNavigationListener用于监听列表选中操作。
  3. 调用setNavigationMode(NAVIGATION_MODE_LIST).设置导航模式为列表模式。
  4. 调用setListNavigationCallbacks()方法设置监听器。

ActionBar样式

ActionBar允许自定义ActionBar的颜色,字体颜色,按钮样式等等。你可以使用Android的样式和主题框架通过指定的样式属性来自定义ActionBar。

注意:使用的所有背景图片必须为9patch图片,并且图片要小于40dp高和30dp宽。

通用样式

自定义ActionBar样式可以通过继承Widget.AppCompat.ActionBar来实现。查看Widget.AppCompat.ActionBar源码

<style name="Widget.AppCompat.ActionBar" parent="Widget.AppCompat.Base.ActionBar">
</style>

<style name="Widget.AppCompat.Base.ActionBar" parent=""> <item name="displayOptions">useLogo|showHome|showTitle</item> <item name="divider">?attr/dividerVertical</item> <item name="height">?attr/actionBarSize</item> <item name="homeLayout">@layout/abc_action_bar_home</item>

    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"titleTextStyle"</span><span class="nt">&gt;</span>@style/TextAppearance.AppCompat.Widget.ActionBar.Title<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"subtitleTextStyle"</span><span class="nt">&gt;</span>@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle
    <span class="nt">&lt;/item&gt;</span>

    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"background"</span><span class="nt">&gt;</span>@drawable/abc_ab_transparent_dark_holo<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"backgroundStacked"</span><span class="nt">&gt;</span>@drawable/abc_ab_stacked_transparent_dark_holo<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"backgroundSplit"</span><span class="nt">&gt;</span>@drawable/abc_ab_bottom_transparent_dark_holo<span class="nt">&lt;/item&gt;</span>

    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"actionButtonStyle"</span><span class="nt">&gt;</span>@style/Widget.AppCompat.ActionButton<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"actionOverflowButtonStyle"</span><span class="nt">&gt;</span>@style/Widget.AppCompat.ActionButton.Overflow<span class="nt">&lt;/item&gt;</span>

    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"progressBarStyle"</span><span class="nt">&gt;</span>@style/Widget.AppCompat.ProgressBar.Horizontal<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"indeterminateProgressStyle"</span><span class="nt">&gt;</span>@style/Widget.AppCompat.ProgressBar<span class="nt">&lt;/item&gt;</span>

</style>

background:设置actionbar的背景。

backgroundstacked:设置tab的背景

backButtonStyle:设置按钮的样式

displayOptions:显示选项

divider:两个action按钮之间的分割线。

titleTextStyle:标题样式

ActionItem样式

自定义ActionItem样式可以通过继承Widget.AppCompat.ActionButton来实现。查看Widget.AppCompat.ActionButton源码

   <style name="Widget.AppCompat.ActionButton" parent="Widget.AppCompat.Base.ActionButton">
    </style>

<span class="nt">&lt;style</span> <span class="na">name=</span><span class="s">"Widget.AppCompat.Base.ActionButton"</span> <span class="na">parent=</span><span class="s">""</span><span class="nt">&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:background"</span><span class="nt">&gt;</span>?attr/actionBarItemBackground<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:paddingLeft"</span><span class="nt">&gt;</span>12dip<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:paddingRight"</span><span class="nt">&gt;</span>12dip<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:minWidth"</span><span class="nt">&gt;</span>@dimen/abc_action_button_min_width<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:minHeight"</span><span class="nt">&gt;</span>?attr/actionBarSize<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:gravity"</span><span class="nt">&gt;</span>center<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:maxLines"</span><span class="nt">&gt;</span>2<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"textAllCaps"</span><span class="nt">&gt;</span>@bool/abc_config_actionMenuItemAllCaps<span class="nt">&lt;/item&gt;</span>
<span class="nt">&lt;/style&gt;</span></code></pre></figure>

Tab导航样式

自定义Tab导航样式可以通过继承Widget.AppCompat.ActionBar.TabView来实现。查看Widget.AppCompat.ActionBar.TabView源码

    <style name="Widget.AppCompat.ActionBar.TabView"
           parent="Widget.AppCompat.Base.ActionBar.TabView">
    </style>

<span class="nt">&lt;style</span> <span class="na">name=</span><span class="s">"Widget.AppCompat.Base.ActionBar.TabView"</span> <span class="na">parent=</span><span class="s">""</span><span class="nt">&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:background"</span><span class="nt">&gt;</span>@drawable/abc_tab_indicator_ab_holo<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:gravity"</span><span class="nt">&gt;</span>center_horizontal<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:paddingLeft"</span><span class="nt">&gt;</span>16dip<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:paddingRight"</span><span class="nt">&gt;</span>16dip<span class="nt">&lt;/item&gt;</span>
    <span class="nt">&lt;item</span> <span class="na">name=</span><span class="s">"android:minWidth"</span><span class="nt">&gt;</span>80dip<span class="nt">&lt;/item&gt;</span>
<span class="nt">&lt;/style&gt;</span></code></pre></figure>

下拉列表样式

自定义下拉列表样式可以通过继承Widget.AppCompat.Spinner.DropDown.ActionBar来实现。查看Widget.AppCompat.Spinner.DropDown.ActionBar源码

   <style name="Widget.AppCompat.Spinner.DropDown.ActionBar"
           parent="Widget.AppCompat.Base.Spinner">
    </style>

<style name="Widget.AppCompat.Base.Spinner" parent=""> <item name="spinnerMode">dropdown</item> <item name="android:popupBackground">@drawable/abc_menu_dropdown_panel_holo_dark</item> <item name="android:dropDownSelector">@drawable/abc_list_selector_holo_dark</item> <item name="android:dropDownVerticalOffset">0dip</item> <item name="android:dropDownHorizontalOffset">0dip</item> <item name="android:dropDownWidth">wrap_content</item> <item name="android:gravity">left|center_vertical</item> <item name="android:clickable">true</item> <item name="android:background">@drawable/abc_spinner_ab_holo_dark</item> </style>

扩展阅读:

官方ActionBar开发指南

创建带有选项卡的滑动视图

ActionBarCompat and I/O 2013 App Source

ActionBar Training

ActionBar Design Pattern