本文介绍了Support V4包中分享组件ShareCompat、分享私有文件FileProvider、LocalBroadcastManager、WakefulBroadcastReceiver、RoundedBitmapDrawable、ContentLoadingProgressBar、NestedScrollView、SwipeRefreshLayout、SlidingPaneLayout及ExploreByTouchHelper的使用。
分享组件——ShareCompat
ShareCompat 提供了几个功能帮助用户在应用程序之间分享内容并且特别适合分享内容到用户已安装的社交应用程序。
代码示例相关的两个类:SharingReceiverSupport 被配置用来接收带着一个 text/plain 的类型的 ACTION_SEND 和 ACTION_SEND_MULTIPLE 的分享意图(sharing intents)。它提供了使用 ShareCompat 的功能写一个分享目标的示例。SharingSupportProvider 是一个简单的内容提供者(android.content.ContentProvider)提供了访问两个文本文件作为内容流进行分享。
分享文本
1 | ShareCompat.IntentBuilder.from(this) |
通过菜单分享
1 | @Override |
分享单个文件
1 | try { |
分享多个文件
1 | try { |
分享私有文件——FileProvider
FileProvider 是 ContentProvider 的一个特殊的子类,它有利于安全地分享应用相关的文件,通过对一个文件创建content:// Uri
而不是file:/// Uri
。
content URI允许你使用临时访问权限授权读写访问。当你创建一个包含content URI的Intent时,为了发送content URI 到一个客户端应用程序,你可以调用Intent.setFlags()
添加权限。这些权限是提供给客户端应用程序,只要接收Activity的栈处于活动状态。对于一个和Service通讯的Intent,只要Service在运行,权限也是可用的。
比较而言,为了控制访问file:/// Uri
,你不得不修改底层文件的文件系统权限。你提供的权限可应用于任何应用,并且会保持同样的效果直到你修改它们。这个访问级别是从根本上不安全的。通过content URI提高访问文件的安全级别让FileProvider成为Android安全基础的关键部分。
FileProvider的详细说明——(译)FileProvider官方文档
1 | private static final String AUTHORITY = "com.example.android.supportv4.my_files"; |
LocalBroadcastManager
在你的进程中帮助注册和发送意图广播到一个本地对象。这比发送全局广播(android.content.Context#sendBroadcast)有几点好处:你发送的数据不会离开你的应用,因此不需要担心私密数据的泄露。别的应用程序不可能发送这些广播到你的应用上,因此不需要担心他们可以利用安全漏洞。它比通过系统发送一个全局广播更高效。
注册接收广播
1 | // We use this to send broadcasts within our local process. |
发送广播1
mLocalBroadcastManager.sendBroadcast(new Intent(ACTION_STARTED));
WakefulBroadcastReceiver
帮助实现一个BroadcastReceiver的常见模式,接收设备唤醒事件然后开启一个Service,同时确保设备在过渡期间不会睡眠。
这个类所关心的是为你创建和管理一个局部唤醒锁,为了使用它你得添加WAKE_LOCK权限。
示例
WakefulBroadcastReceiver使用startWakefulService()方法开启一个服务做处理。这个方法和 startService()对比,除了开启服务时WakefulBroadcastReceiver控制了一个唤醒锁,其它都一样。startWakefulService()通过intent控制识别唤醒锁。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
public class SimpleWakefulReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// This is the Intent to deliver to our service.
Intent service = new Intent(context, SimpleWakefulService.class);
// Start the service, keeping the device awake while it is launching.
Log.i("SimpleWakefulReceiver", "Starting service @ " + SystemClock.elapsedRealtime());
startWakefulService(context, service);
}
}
Service(在这个例子中,是IntentService)做了一些处理。当它结束后,通过调用completeWakefulIntent(intent)释放了唤醒锁。作为参数的intent和WakefulBroadcastReceiver传递的intent是同一个intent。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36import android.app.IntentService;
import android.content.Intent;
import android.os.SystemClock;
import android.util.Log;
public class SimpleWakefulService extends IntentService {
public SimpleWakefulService() {
super("SimpleWakefulService");
}
@Override
protected void onHandleIntent(Intent intent) {
// At this point SimpleWakefulReceiver is still holding a wake lock
// for us. We can do whatever we need to here and then tell it that
// it can release the wakelock. This sample just does some slow work,
// but more complicated implementations could take their own wake
// lock here before releasing the receiver's.
//
// Note that when using this approach you should be aware that if your
// service gets killed and restarted while in the middle of such work
// (so the Intent gets re-delivered to perform the work again), it will
// at that point no longer be holding a wake lock since we are depending
// on SimpleWakefulReceiver to that for us. If this is a concern, you can
// acquire a separate wake lock here.
for (int i=0; i<5; i++) {
Log.i("SimpleWakefulReceiver", "Running service " + (i+1)
+ "/5 @ " + SystemClock.elapsedRealtime());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
Log.i("SimpleWakefulReceiver", "Completed service @ " + SystemClock.elapsedRealtime());
SimpleWakefulReceiver.completeWakefulIntent(intent);
}
}
RoundedBitmapDrawable
包含一个bitmap并且可以被画成圆角的Drawable。你可以从一个文件路径、输入流或一个android.graphics.Bitmap对象创建一个 RoundedBitmapDrawable 。
1 | // Create a bitmap and set it circular. |
ContentLoadingProgressBar
ContentLoadingProgressBar 实现于 ProgressBar ,显示很短的时间后消失。一旦显示,当完成一个在很大程序上是可变的事件的时间时(从没有,到用户可以感知的数量),为了避免在UI上“闪烁”,进度条将在最短的时间内可见。
1 | <android.support.v4.widget.ContentLoadingProgressBar |
修改ProgressBar的颜色1
mBar.getIndeterminateDrawable().setColorFilter(0xFFFF00FF, PorterDuff.Mode.MULTIPLY);
NestedScrollView
NestedScrollView 就像 ScrollView,但它支持在作为嵌套滚动的父节点和子节点并且兼容老版本。嵌套滚动默认情况下是启用的。
1 | <android.support.v4.widget.NestedScrollView |
SwipeRefreshLayout
SwipeRefreshLayout 应该被用于无论用户何时通过垂直滑动手势都可以刷新页面的内容。实例化这个view的activity应该添加 OnRefreshListener 监听,当下拉刷新手势完成时会收到通知。每次手势的完成 SwipeRefreshLayout 都会通知监听者;监听者主要负责刷新它的内容时做出正确的决定。如果监听者认为这不应该是刷新动作,它必须调用 setRefreshing(false) 取消刷新动画的显示。如果activity只是希望显示进度动画,它应该调用 setRefreshing(true)。如果想禁用手势和进度动画,可以在view上调用 setEnabled(false) 方法。这个布局应该作为通过手势刷新作为一个结果的view的父布局并且只支持一个直接子布局。这个view也将作为手势的目标和将会强制匹配宽度和高度到这个布局。SwipeRefreshLayout 不会提供事件的可访问性;相反,无论这个手势在哪被用到,菜单项必须提供允许内容的刷新。
1 | <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" |
SlidingPaneLayout
SlidingPaneLayout提供一个水平的,多窗格布局用在用户界面的顶层。左边(或第一个)面板作为一个内容列表或浏览器,第二个面板主要作为显示内容的详细视图。
在SlidingPaneLayout中如果子视图的结合宽度超过可用宽度,它们可能会重叠。当这种情况发生时,用户可以通过拖动滑动的最上面的视图的方式,或者使用键盘调整重叠视图的方向。如果拖动内容的子视图可以水平滚动,用户可以从侧边滑动它。
由于这个滑动行为,SlidingPaneLayout可能适用于创建可以顺利适应许多不同屏幕尺寸的布局,在大屏幕上完全展开,在小屏幕上进行折叠。
SlidingPaneLayout不同于在design指导中描述的navigation drawer并且不应该用于同样的场景。SlidingPaneLayout应该被认为作为一种允许双面板布局用自然的方式正常的使用在大屏幕适应到小屏幕的方式。SlidingPaneLayout表达的交互模型意味着物理和两个面板层级之间的直接信息,在该使用navigation drawer代替时没必要使用SlidingPaneLayout。
SlidingPaneLayout适用于包括面板的配对例如一个联系列表和这些联系人互动的子视图,或者一个邮箱列表和一个显示选择的邮箱内容的内容面板。SlidingPaneLayout不适用于包括在你的应用的不同的功能之间进行切换,例如从社交信息流view跳到个人信息view——像这种情况应该使用navigation drawer模型代替。(DrawerLayout实现了这个模型)
像LinearLayout一样,SlidingPaneLayout也支持布局的layout_weight参数的使用在子view上,它决定了在测量完成后怎么分配剩余的空间。它只是和宽度有关。当视图不重叠时,weight的行为和在LinearLayout中是一样的。
当重叠时,weight在一个滑动的面板表示面板在关闭的状态下应该调整大小为填充所有的剩余的空间。Weight用在被覆盖的面板表示面板应该调整大小为填充所有剩余的空间除了一个最小的条,用户可以用它来抓住滑动查看及拉回到关闭状态。
竖屏效果
横屏效果
ExploreByTouchHelper
ExploreByTouchHelper是一个实用类,用在自定义View中实现accessibility,表现为一个View-like逻辑items的集合。它继承 AccessibilityNodeProviderCompat并且简化了很多提供信息的方面accessibility服务和管理accessibility的焦点。这个类目前不支持逻辑的项目层次结构。
客户端应该重写它的抽象方法并使用setAccessibilityDelegate(View, AccessibilityDelegateCompat)附加到主视图上:1
2mAccessHelper = new MyExploreByTouchHelper(someView);
ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
从Android SDK目录可找到源码,或从这里下载