简介

SpriteFrameCache 主要服务于多张碎图合并出来的纹理图片。这种纹理在一张大图中包含了多张小图,直接通过TextureCache引用会有诸多不便,因而衍生出来精灵框帧的处理方式,即把截取好的纹理信息保存在一个精灵框帧内,精灵通过切换不同的框帧来显示出不同的图案。

SpriteFrameCache

SpriteFrameCache的内部封装了一个Map<std::string, SpriteFrame*> _spriteFrames对象。key为帧的名称。SpriteFrameCache一般用来处理plist文件(这个文件指定了每个独立的精灵在这张“大图”里面的位置和大小),该文件对应一张包含多个精灵的大图,plist文件可以使用TexturePacker制作。

SpriteFrameCache的常用接口和TextureCache类似,不再赘述了,唯一需要注意的是添加精灵帧的配套文件一个plist文件和一张大的纹理图。下面列举了SpriteFrameCache常用的方法:(详细API请看SpriteFrameCache API)

获取单例的SpriteFrameCache对象。sharedSpriteFrameCache方法在3.0中已经弃用。

1
SpriteFrameCache* cache = SpriteFrameCache::getInstance();

销毁SpriteFrameCache对象。

1
SpriteFrameCache::destroyInstance();

使用SpriteFrameCache获取指定的精灵帧,创建精灵对象。

1
2
3
4
SpriteFrameCache *frameCache = SpriteFrameCache::getInstance(); 
frameCache->addSpriteFramesWithFile("boy.plist","boy.png");//boy.png里集合了boy1.png,boy2.png这些小图
auto frame_sp = Sprite::createWithSpriteFrameName("boy1.png");//从SpriteFrameCache缓存中找到boy1.png这张图片.
this->addChild(frame_sp,2);

SpriteFrameCache vs. TextureCache

  • SpriteFrameCache精灵框帧缓存。顾名思义,这里缓存的是精灵帧SpriteFrame,它主要服务于多张碎图合并出来的纹理图片。这种纹理在一张大图中包含了多张小图,直接通过TextureCache引用会有诸多不便,因而衍生出来精灵框帧的处理方式,即把截取好的纹理信息保存在一个精灵框帧内,精灵通过切换不同的框帧来显示出不同的图案。

  • 跟TextureCache功能一样,将SpriteFrame缓存起来,在下次使用的时候直接去取。不过跟TextureCache不同的是,如果内存池中不存在要查找的图片,它会提示找不到,而不会去本地加载图片。

  1. TextureCache时最底层也是最有效的纹理缓存,缓存的是加载到内存中的纹理资源,也就是图片资源。
  2. SpriteFrameCache精灵框帧缓存,缓存的时精灵帧。
  3. SpriteFrameCache是基于TextureCache上的封装。缓存的是精灵帧,是纹理指定区域的矩形块。各精灵帧都在同一纹理中,通过切换不同的框帧来显示出不同的图案。

几天前Facebook开源了旗下应用Paper所使用的动画引擎Pop。使用动态动画(dynamic animations)而不是传统的静态动画(static animations),Pop使常见的滑动(scrolling)、弹跳(bouncing)、折叠(unfolding)等效果充满了活力,也使Paper给人耳目一新的感觉。

为什么需要Pop

从第一代iPhone开始,iOS系统在对静态动画(static animations)方面的支持就很出色。苹果提供的Core Animation framework,使我们方便的使用线性动画(linear)、淡入效果(ease-in)、淡出效果(ease-out)等动画。

手势交互的革新迎来了新的一轮软件设计的浪潮。人们可以用手指直接操作屏幕上的元素,(而不是想以前,需要一支笔),这就降低了交互的间接性(反着说,就是交互更直接),于是,人们又提出更进一步的要求:既然触屏可以得到处理,那么不同速度的划屏操作也应该得到处理。

当我们在2010年连个创办Push Pop Press公司的时候,我们的目标就是创造一种可行的、基于物理效果(physics-everywhere)的体验。我们在寻求一种可以使人们非常愉悦、轻松的使用整个应用的解决方案,就像我们使用UIScrollView那样的顺滑。

Popular就是这种理念的最新表现,它使你可以使用你熟悉而且强大的Core Animation进行编程,并且它能够捕获手势操作的速度,更好的反应用户的意图。Paper给我们了一个重新定义这种理念和其背后的动画引擎的机会。

目标

Pop在三个不同的纬度提供了相应的工具。第一,我们想使一些常见动画在使用上更加的简单便利,除了iOS内建的4种静态动画(static animations),Pop增加三种原始的动画类型:弹簧效果(spring),衰变效果(decay)和自定义效果(custom)。

弹簧效果和衰变效果是动态动画,正是它们让Paper充满了活力动感。弹簧动画使Paper上的元素优雅的弹跳(attractive bounce)。衰变动画可以使元素的移动(movement)缓慢的停止。这两种动画都是把速度作为输入,并且在处理用户手势时的理想方案之一。

第二,Pop是一个可灵活扩展的库。自定义动画(custom animation)允许开发者插自己的动画代码,使你可以容易的创造出独特的动画效果。通过把动画从展示层(display)解耦,Pop是一个适用范围更广的库,你可以对任何对象属性做动态的改变,就像做动画那样。

第三,我们的目标是构建一个开发者有好但是功能强大的编程模型。比如,Core Animation开发者应该对下面的API比较熟悉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@interface CALayer
/* Attach an animation to the layer. */
- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key;

/* Remove all animation attached to the layer. */
- (void)removeAllAnimations;

/* Remove any animation attached to the layer for 'key'. */
- (void)removeAnimationForKey:(NSString *)key;

/* Returns an array containing the keys of all animations currently attached to the receiver. */
- (NSArray *)animationKeys;

/* Returns the animation added to the layer with identifier 'key', or nil if no such animation exists. */
- (CAAnimation *)animationForKey:(NSString *)key;

@end

开发者可以通过这些接口,使用Core Animation来开始和结束动画。最显著的一点是,它也支持查询正在中的动画,这是终端动画和构建流畅的用户界面的关键。下面是Popular实现相同功能提供的API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@interface NSObject (pop)

/* Attach an animation to the layer. */
- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)kay;

/* Remove all animations attached to the layer. */
- (void)pop_removeAllAnimations;

/* Remove any animation attached to the layer for 'key'. */
- (void)pop_removeAnimationForKey:(NSString *)key;

/* Returns an array containing the keys of all animations currently attached to the receiver. */
- (NSArray *)pop_animationKeys;

/* Returns the animation added to the layer with identifier 'key', or nil if no such animation exists. */
- (POPAnimation *)pop_animationForKey:(NSString *)key;

@end

最基本的动画类型是POPAnimation,由于Pop是在NSObject上扩展的类别,所以可以在任何对象上做动画。除了上面提到的区别外,这些API接口形式在其他方面都没有什么区别。

下面的例子展示了如何对layer的bounds属性做弹簧动画:

1
2
3
4
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
anim.toValue = [NSValue valueWithCGRect:CGRectMake(0,0,400,400)];
[layer pop_addAnimation:anim forKey:@"size"];

如果你会使用Core Animation构建动画,那么你也会用Pop,它们在使用上是相同的。

  • UITapGestureRecognizer(点一下)
  • UIPinchGestureRecognizer(两指往内或往外拨动,就是常用的缩放)
  • UIRotationGestureRecognizer(旋转)
  • UISwipeGestureRecognizer(滑动)
  • UIPanGestureRecognizer(拖动,慢速移动)
  • UILongPressGestureRecognizer(长按)

UIGestureRecognizer的继承关系如下:

使用手势的步骤

  1. 创建手势实例,指定一个回调方法,当手势开始,改变或结束时,回调方法被调用。
  2. 添加到需要识别的View中,每个手势只能对应一个View,当屏幕触摸在View的边界内,如果手势和预定的一样,就会回调方法。

ps:一个手势只能对应一个View,但一个View可以对应多个手势。

Pan拖动手势

1
2
3
4
5
6
UIImageView *snakeImageView = [[UIImageView alloc] initWithImage:[UIImage imageName:@"snake.png"]];
snakeImageView.frame = CGReckMake(50,50,100,160);
UIPanGestureRecognizer *panGestureRecongnizer = [[UIPanGestureRecoginizer alloc] initWithTarget:self action:@selector(handlePan:)];
[snakeImageView addGestureRecognizer:panGestureRecongnizer];
[self.view setBackgroundColor:[UIColor whiteColor]];
[self.view addSubview:snakeImageView];

添加手势回调方法

1
2
3
4
5
6
- (void) handlePan:(UIPanGestureRecognizer*) recognizer
{
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointZero inView:self.view];
}

Pinch缩放手势

1
2
3
4
5
6
7
8
UIPinchGestureRecoginzer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:")];
[snakeImageView addGestureRecognizer:pinchGestureRecognizer];

- (void)handlePinch:(UIPinchGestureRecongnizer*) recognizer
{
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform,recognizer.scale, recognizer.scale);
recognizer.scale = 1;
}

Rotation旋转手势

1
2
3
4
5
6
7
8
UIRotationGestureRecognizer *rotateRecognizer = [[UIRotationGestureRecognizer alloc]  initWithTarget:self action:@selector(handleRotate:)];  
[snakeImageView addGestureRecognizer:rotateRecognizer];

- (void) handleRotate:(UIRotationGestureRecognizer*) recognizer
{
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
recognizer.rotation = 0;
}

多个View添加手势

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
- (void)viewDidLoad  
{
[super viewDidLoad];

UIImageView *snakeImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"snake.png"]];
UIImageView *dragonImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dragon.png"]];
snakeImageView.frame = CGRectMake(120, 120, 100, 160);
dragonImageView.frame = CGRectMake(50, 50, 100, 160);
[self.view addSubview:snakeImageView];
[self.view addSubview:dragonImageView];

for (UIView *view in self.view.subviews) {
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];

UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];

UIRotationGestureRecognizer *rotateRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotate:)];

[view addGestureRecognizer:panGestureRecognizer];
[view addGestureRecognizer:pinchGestureRecognizer];
[view addGestureRecognizer:rotateRecognizer];
[view setUserInteractionEnabled:YES];
}
[self.view setBackgroundColor:[UIColor whiteColor]];
}

监听手势

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
- (void) handlePan:(UIPanGestureRecognizer*) recognizer  
{
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointZero inView:self.view];

if (recognizer.state == UIGestureRecognizerStateEnded) {

CGPoint velocity = [recognizer velocityInView:self.view];
CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
CGFloat slideMult = magnitude / 200;
NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);

float slideFactor = 0.1 * slideMult; // Increase for more of a slide
CGPoint finalPoint = CGPointMake(recognizer.view.center.x + (velocity.x * slideFactor),
recognizer.view.center.y + (velocity.y * slideFactor));
finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width);
finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height);

[UIView animateWithDuration:slideFactor*2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
recognizer.view.center = finalPoint;
} completion:nil];

}

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import java.io.IOException;

public final class MIUIUtils{
private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";
private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";
private static final String KEY_MIUI_INTERNAL_STORAGE = "ro.miui.internal.storage";

public static boolean isMIUI() {
try {
final BuildProperties prop = BuildProperties.newInstance();
return prop.getProperty(KEY_MIUI_VERSION_CODE, null) != null
|| prop.getProperty(KEY_MIUI_VERSION_NAME, null) != null
|| prop.getProperty(KEY_MIUI_INTERNAL_STORAGE, null) != null;
} catch (final IOException e) {
return false;
}
}
}
//工具类
import android.os.Environment;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

public class BuildProperties{
private final Properties properties;

private BuildProperties() throws IOException {
properties = new Properties();
properties.load(new FileInputStream(new File(Environment.getRootDirectory(), "build.prop")));
}
public boolean containsKey(final Object key) {
return properties.containsKey(key);
}

public boolean containsValue(final Object value) {
return properties.containsValue(value);
}

public Set<Entry<Object, Object>> entrySet() {
return properties.entrySet();
}

public String getProperty(final String name) {
return properties.getProperty(name);
}

public String getProperty(final String name, final String defaultValue) {
return properties.getProperty(name, defaultValue);
}

public boolean isEmpty() {
return properties.isEmpty();
}

public Enumeration<Object> keys() {
return properties.keys();
}

public Set<Object> keySet() {
return properties.keySet();
}

public int size() {
return properties.size();
}

public Collection<Object> values() {
return properties.values();
}

public static BuildProperties newInstance() throws IOException {
return new BuildProperties();
}
}

import android.os.Build;

import java.lang.reflect.Method;

pulic final class FlymeUtils{
public static boolean isFlyme(){
try{
// Invoke Build.hasSmartBar()
Invoke Method method = Build.class.getMethod("hasSmartBar");
return method != null;
}catch(final Exception e){
return false;
}
}
}

如何使用

  • UIButton+Bootstrap,NSString+FontAwesome,FontAwesome.ttf文件添加进工程
  • 导入UIButton+Bootstrap.h
  • Info.plist中的Fonts provided by application中包含FontAwesome.ttf

创建自定义的UIButoon(把type属性设置为Custom)
设置风格

1
2
[yourButton primaryStyle];
[yourOtherButton successStyle];

设置图标

1
2
[yourButton addAwesomeIcon:FAIconBookmark beforeTitle:YES];
[yourOtherButton addAwesomeIcon:FAIconCalendar beforeTitle:NO];

CommonUtil

  • boolean hasSDCard() 判断设备是否存在SD卡
  • String getRootFilePath() 返回文件系统的根目录,存在SD卡返回SD卡目录
  • boolean checkNetState(Context context) 判断是否存在网络
  • void showToask(Context context,String tip) 显示Toast通知
  • int getScreenWidth(Context context)\int getScreenHeight(Context context) 返回屏幕的宽\长

FileHelper

  • boolean fileIsExist(String filePath) 判断文件是否存在
  • InputStream readFile(String filePath) 获取文件的input流
  • boolean createDirectory(String filePath) 创建文件目录
  • boolean deleteDirectroy(String filePath) 删除文件目录
  • boolean writeFile(String filePath,InputStream inputStream) 写入字符串到某文件

添加Action Bar

Android 3.0及以上版本

Android 3.0(API level 11)开始,所有使用Theme.Holo主题的Activity都内置了action bar,此主题是targetSdkVersion或minSdkVersion属性大于等于11时的默认主题。

1
2
3
<manifest ...>
<uses-sdk android:minSdkVersion="11" .../>
</manifest>

如果创建一个自定义主题,需呀确保它使用一个Theme.Holo主题作为父主题

支持Android 2.1及以上版本

可以通过天剑Support库或者v7 appcompat库集成Action Bar到低版本

  1. activity继承ActionBarActivity

    1
    public class MainActivity extends ActionBarActivity {...}
  2. 在mainfest文件中设置application或设置单独的activity的主题为Theme.AppCompat

    1
    <activity android:theme="@style/Theme.AppCompat.Light" ... >

移除action bar

可以把该Activity的主题设置为Theme.Holo.NoActionBar

1
<activity android:theme=@"android:style/Theme.Holo.NoActionBar">

也可以在运行时通过调用hide()来隐藏action bar

1
2
ActionBar actionBar = getActionBar();
actionBar.hide();

也可以用show()来重新显示ActionBar。

添加Action项

当acitivity第一次被启动时,系统会调用activity中的onCreateOptionsMenu()来构建Action和滑动菜单。菜单项通过XML格式的菜单资源来包装。
在此XML文件中,通过将元素声明为带android:showAsAction="ifRoom"属性,你可以让一个菜单项显示为action项。通过这种方式,菜单项将会仅当有空间时才显示在action bar中,便于快速访问。如果没有空间的话,菜单项将会显示在滑动菜单中。
如果菜单项同时具有标题和文本——带有android:titleandroid:icon属性——则action项默认只显示图标。如果需要显示文本标题,只要在android:showAsAction属性中加入withText即可

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id=@"+id/menu_save"
android:icon=@"drawable/ic_menu_save"
android:title=@"string/menu_save"
android:showAsAction="ifRoom|withText"/>
</menu>

在Activity中实现onCreateOptionMenu()回调方法来inflate菜单资源从而获取Menu对象。

1
2
3
4
5
6
7
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_action_menu,menu);
return super.onCreateOptionsMenu(menu);
}

用户选中一个action项,activity将会收到一个对onPtionsItemSelected()调用,并传入android:id属性给出的ID——选项菜单中的所有项都会被收到该回调方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case R.id.action_search:
openSearch();
return true;
case R.id.action_settings:
openSettings();
return true;
default:
return super.onOptionsItemSelected(item);
}
}

使用split action bar

Android 4.0(API level 14)及以上版本时,action bar增加一个名为”split action bar”的模式。启用split action bar后,如果在窄屏上(比如纵向显示的手持设备)运行activity,屏幕底部将会显示一个单独的bar,用于显示所有的action项,把action bar拆分开、让多个action项分开显示,可以确保在窄屏上以合理的间隔显示所有的action项,并为顶部的导航条和标题栏留下足够的空间。
启用split action bar只要把uiOptions=”splitActionBarWhenNarrow”加入你的manifest元素即可。

用应用程序图标导航

默认情况下,应用程序图标显示在action bar的左侧。如果需要,可以把让图标成为一个action项。作为用户对图标Action的响应,应用程序必须执行以下两件事之一:

  • 返回应用程序的初始home activity
  • 应用程序向上回退至上一层
    当用户触摸图标时,系统会调用Activity的onOptionsItemSelected()方法,并带入android.R.id.homeID,作为响应,你应该启动home activity或让用户回到应用程序的上一侧。
    如果你把返回home activity作为应用程序图标的响应,则你应该在Intent中包含FLAG_ACTIVITY_CLEAR_TOP标志。如果你要启动的Activity已经存在于当前任务中了,则用这个标志,可以将所有在其之上的Activity都销毁,并把该activity推到前台。因为回到home等同于返回的Action,通常不应该创建一个新的home activity实例,所以加入这个标志是非常重要的。否则的话,当前的任务栈中可能会有一长串activity,其中包含了home activity的多个实例。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Override
    public boolean onOptionsItemSeleted(MenuItem item) {
    switch(item.getItemId()) {
    case android.R.id.home:
    Intent intent = new Intent(this,HomeActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLER_TOP);
    startActivity(intent);
    return true;
    }
    default:
    return super.onPtionsItemSelected(item);
    }

用户可以从其他应用程序中进入当前Activity,这时你也许还想加入FLAG_ACTIVITY_NEW_TASK标志,次标志表示,不管用户导航至home还是返回上一层,新的activity都不加入当前任务栈,而是放入一个属于你的应用程序的栈。比如说,如果用户通过其他应用提交一个itent,启动了一个属于你的应用的activity,然后选中了action bar图标来回到home或向上回退一级,FLAG_ACTIVITY_CLEAR_TOP标志将会启动属于你的应用程序任务中的Activity(而不是当前任务)。系统或是启动一个新的任务并把这个你的这个新activity作为根activity,或者,假如后台任务中存在该activity的实例的话,则把任务推到前台,该activity会收到onNewIntent()。因此,如果你的activity要从其他应用接收intent(声明了任何通用的intent过来器),你通常应该在intent中加入FLAG_ACTIVITY_NEW_TASK

1
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

如果用图标来返回home activity,请注意自Android 4.0(API level 14)开始,你必须显式地调用setHomeButtonEnabled(true)将图标用作action项(之前的版本中,图标默认就是作为action项使用的)。

向上导航

调用ActionBar的setDisplayHomeAsEnabled(true)

1
2
3
4
5
6
7
8
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);

setContentView(R.id.main);
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
...
}

当用户触摸图标时,系统会调用activity的onOptionsItemSelected()方法,并带入android.R.id.homeID.
记得在Intent上使用FLAG_ACTIVITY_CLEAR_TOP标志,这样就不会创建已存在的父activity的新实例。
manifest文件中声明父Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<application ...>
<activity
android:name="com.example.myfirstapp.MainActivity" ...>
...
</activity>
<activity
android:name="com.example.myfirstapp.DisplayMessageActivity"
android:label="@string/title_activity_display_message"
android:parentActivityName="com.example.myfirstapp.MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.myfirstapp.MainActivity" />
</activity>
</application>

ActionBar风格化

Android的基本主题有暗、淡两个


在manifest文件中设置applicationactivityandroid:theme属性

1
<application android:theme="@android:style/Theme.Holo.Light" ... />

使用Support库时,必须使用Theme.AppCompat主题替代

  • Theme.AppCompat,一个“暗”的主题
  • Theme.AppCompat.Light,一个“淡”的主题
  • Theme.AppCompat.Light.DarkActionBar,一个带有“暗” action bar 的“淡”主题

    自定义

    为了自定义主题,通过重写actionBarStyle属性来改变action bar的背景。通过指定一个drawable资源来重写background属性。

Android 3.0可以这样写
res/values/themes.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- the theme applied to the application or activity -->
<style name="CustomActionBarTheme"
parent="@android:style/Theme.Holo.Light.DarkActionBar">
<item name="android:actionBarStyle">@style/MyActionBar</item>
</style>

<!-- ActionBar styles -->
<style name="MyActionBar"
parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse">
<item name="android:background">@drawable/actionbar_background</item>
</style>
</resources>

然后,将你的主题应该到你的 app 全局或单个的 activity 之中:

1
<application android:theme="@style/CustomActionBarTheme" ... />

Android 2.1 版本可以这么写
res/values/themes.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- the theme applied to the application or activity -->
<style name="CustomActionBarTheme"
parent="@style/Theme.AppCompat.Light.DarkActionBar">
<item name="android:actionBarStyle">@style/MyActionBar</item>

<!-- Support library compatibility -->
<item name="actionBarStyle">@style/MyActionBar</item>
</style>

<!-- ActionBar styles -->
<style name="MyActionBar"
parent="@style/Widget.AppCompat.Light.ActionBar.Solid.Inverse">
<item name="android:background">@drawable/actionbar_background</item>

<!-- Support library compatibility -->
<item name="background">@drawable/actionbar_background</item>
</style>
</resources>

然后,将你的主题应该到你的 app 全局或单个的 activity 之中:

1
<application android:theme="@style/CustomActionBarTheme" ... />

自定义本文颜色

  • action bar的标题:指定textColor属性
  • action bar的页签:重写 actionBarTabTextStyle
  • action bar按钮:重写 actionMenuTextColor
    Android 3.0
    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
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    <!-- the theme applied to the application or activity -->
    <style name="CustomActionBarTheme"
    parent="@style/Theme.Holo">
    <item name="android:actionBarStyle">@style/MyActionBar</item>
    <item name="android:actionBarTabTextStyle">@style/MyActionBarTabText</item>
    <item name="android:actionMenuTextColor">@color/actionbar_text</item>
    </style>

    <!-- ActionBar styles -->
    <style name="MyActionBar"
    parent="@style/Widget.Holo.ActionBar">
    <item name="android:titleTextStyle">@style/MyActionBarTitleText</item>
    </style>

    <!-- ActionBar title text -->
    <style name="MyActionBarTitleText"
    parent="@style/TextAppearance.Holo.Widget.ActionBar.Title">
    <item name="android:textColor">@color/actionbar_text</item>
    </style>

    <!-- ActionBar tabs text styles -->
    <style name="MyActionBarTabText"
    parent="@style/Widget.Holo.ActionBar.TabText">
    <item name="android:textColor">@color/actionbar_text</item>
    </style>
    </resources>

Android 2.1

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
36
37
38
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- the theme applied to the application or activity -->
<style name="CustomActionBarTheme"
parent="@style/Theme.AppCompat">
<item name="android:actionBarStyle">@style/MyActionBar</item>
<item name="android:actionBarTabTextStyle">@style/MyActionBarTabText</item>
<item name="android:actionMenuTextColor">@color/actionbar_text</item>

<!-- Support library compatibility -->
<item name="actionBarStyle">@style/MyActionBar</item>
<item name="actionBarTabTextStyle">@style/MyActionBarTabText</item>
<item name="actionMenuTextColor">@color/actionbar_text</item>
</style>

<!-- ActionBar styles -->
<style name="MyActionBar"
parent="@style/Widget.AppCompat.ActionBar">
<item name="android:titleTextStyle">@style/MyActionBarTitleText</item>

<!-- Support library compatibility -->
<item name="titleTextStyle">@style/MyActionBarTitleText</item>
</style>

<!-- ActionBar title text -->
<style name="MyActionBarTitleText"
parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Title">
<item name="android:textColor">@color/actionbar_text</item>
<!-- The textColor property is backward compatible with the Support Library -->
</style>

<!-- ActionBar tabs text -->
<style name="MyActionBarTabText"
parent="@style/Widget.AppCompat.ActionBar.TabText">
<item name="android:textColor">@color/actionbar_text</item>
<!-- The textColor property is backward compatible with the Support Library -->
</style>
</resources>

自定义Tab Indicator

重写actionBarTabStyrle指定navigation tabs

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
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false" android:state_selected="false"
android:state_pressed="false"
android:drawable="@drawable/tab_unselected" />
<item android:state_focused="false" android:state_selected="true"
android:state_pressed="false"
android:drawable="@drawable/tab_selected" />
<item android:state_focused="true" android:state_selected="false"
android:state_pressed="false"
android:drawable="@drawable/tab_unselected_focused" />
<item android:state_focused="true" android:state_selected="true"
android:state_pressed="false"
android:drawable="@drawable/tab_selected_focused" />
<item android:state_focused="false" android:state_selected="false"
android:state_pressed="true"
android:drawable="@drawable/tab_unselected_pressed" />
<item android:state_focused="false" android:state_selected="true"
android:state_pressed="true"
android:drawable="@drawable/tab_selected_pressed" />
<item android:state_focused="true" android:state_selected="false"
android:state_pressed="true"
android:drawable="@drawable/tab_unselected_pressed" />
<item android:state_focused="true" android:state_selected="true"
android:state_pressed="true"
android:drawable="@drawable/tab_selected_pressed" />
</selector>

Android 3.0

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CustomActionBarTheme"
parent="@style/Theme.Holo">
<item name="android:actionBarTabStyle">@style/MyActionBarTabs</item>
</style>

<style name="MyActionBarTabs"
parent="@style/Widget.Holo.ActionBar.TabView">
<!-- tab indicator -->
<item name="android:background">@drawable/actionbar_tab_indicator</item>
</style>
</resources>

Android 2.1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CustomActionBarTheme"
parent="@style/Theme.AppCompat">
<item name="android:actionBarTabStyle">@style/MyActionBarTabs</item>

<!-- Support library compatibility -->
<item name="actionBarTabStyle">@style/MyActionBarTabs</item>
</style>

<style name="MyActionBarTabs"
parent="@style/Widget.AppCompat.ActionBar.TabView">
<item name="android:background">@drawable/actionbar_tab_indicator</item>

<item name="background">@drawable/actionbar_tab_indicator</item>
</style>
</resources>

Action Bar 覆盖叠加

  • 启动叠加模式
    自定义主题 设置android:windowActionBarOverlay的属性为true

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- android 3.0-->
    <resources>
    <style name="CustomActionBarTheme"
    parent="@android:style/Theme.Holo">
    <item name="android:windowActionBarOverlay">true</item>
    </style>
    </resources>
    <!-- android 2.1 -->
    <resources>
    <style name="CustomActionBarTheme"
    parent="@android:style/Theme.AppCompat">
    <item name="android:windowActionBarOverlay">true</item>

    <!-- Support library compatibility -->
    <item name="windowActionBarOverlay">true</item>
    </style>
    </resources>
  • 指定布局的顶部边距

    1
    2
    3
    4
    5
    6
    7
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="?android:attr/actionBarSize">
    <!-- Support库中 移除android:前缀 paddingTop="?attr/actionBarSize" -->
    ...
    </RelativeLayout>

添加一个Action View

action view是一个显示于action bar中的widget,用于替代某个action项按钮.

在菜单资源中声明一个action view项,使用android:actionLayoutandroid:actionViewClass属性指定一个layout资源或所需的widget类即可。比如:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id=@"+id/menu_search"
android:title=@"string/menu_search"
android:icon=@"drawable/ic_menu_search"
android:showAsAction="ifRomm|collapseActionView"
android:actionViewClass="android.widget.SearchView" />
</menu>

注意android:showAsAction属性还包含了collapseActionView。这是可选项,表示action view应该折叠进入按钮中,当用户选中了按钮,action view就会展开。否则,action viewm默认就是可见的。
action view可以通过下单项的ID调用findItem(),然后调用getActionView()

1
2
3
4
5
6
7
8
@Override
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.options,menu);
SearchView searchView = (SavechView)menu.findItem(R.id.menu_search).getActionView();
// ...

return super.onCreateOptionsMenu(menu);
}

存根Activity,用这个Activity来加载其他的Activity,为了重复使用Activity使用,它的子类必须实现onCreate()方法。可以在onCreate()方法中调用finish()方法,这时Activity跳过生命周期直接调用onDestroy()方法。
activity-alias具体属性有:

  • android:targetActivity 目标Activity,这个属性的值必须是声明
  • android:name alias的唯一标识
  • android:enabled 是否运行aliasActivity加载targetActivity,缺省为true
  • android:exported 是否运行其他的Application通过使用aliasActivity来加载targetActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<activity
android:name="com.example.aliasactivitydemo.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias
android:name="AndroidAlias"
android:targetActivity="MainActivity"
android:label="Alias"
android:icon="@drawable/ic_launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>

示例



ExpendableListActivity

一个具有可展开list,其中的item通过ExpandableListAdapter接口来绑定数据源。当用户选择其中某一项时可以自己去定义处理方法。ExpendableListActivity含有一个ExpandableView对象,用两层的方法来展示数据,第一层是组,第二层是子项。使用自定义的xml来制定布局,则ExpandableListView一定要用”@id/android:list”作为id,另外使用一个id@“@id/android/empty”来表示空的list.

main.xml

1
2
3
4
5
6
7
8
9
10
11
12
<ExpandableListView 
android:id="@id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:drawSelectorOnTop="false"
/>
<TextView
android:id="@id/android:empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="No data"/>

child.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView android:id="@+id/child"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="50px"
android:paddingTop="5px"
android:paddingBottom="5px"
android:textSize="20px"
android:text="NO data"/>

</LinearLayout>

group.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView android:id="@+id/group"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="60px"
android:paddingTop="10px"
android:paddingBottom="10px"
android:textSize="26px"
android:text="No data"
/>
</LinearLayout>

MainActivity.java

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
36
37
38
39
40
41
42
43
List<Map<String,String>> groups = new ArrayList<Map<String,String>>();
Map<String, String> group1 = new HashMap<String, String>();
Map<String, String> group2 = new HashMap<String, String>();
group1.put("group", "DOTA");
group2.put("group","DiaBlo");
groups.add(group1);
groups.add(group2);
//
List<Map<String, String>> child1 = new ArrayList<Map<String,String>>();
Map<String, String> childData1 = new HashMap<String, String>();
Map<String, String> childData2 = new HashMap<String, String>();
childData1.put("child", "Dota1");
childData2.put("child", "Dota2");
child1.add(childData1);
child1.add(childData2);
//
List<Map<String, String>> child2 = new ArrayList<Map<String,String>>();
Map<String, String> child2Data1 = new HashMap<String, String>();
Map<String, String> child2Data2 = new HashMap<String, String>();
Map<String, String> child2Data3 = new HashMap<String, String>();
child2Data1.put("child", "DiaBlo1");
child2Data2.put("child", "DiaBlo2");
child2Data3.put("child", "DiaBlo3");
child2.add(child2Data1);
child2.add(child2Data2);
child2.add(child2Data3);
//
List<List<Map<String,String>>> childs = new ArrayList<List<Map<String,String>>>();
childs.add(child1);
childs.add(child2);
SimpleExpandableListAdapter adapter =
new SimpleExpandableListAdapter(
this, //context
groups, //一级目录数据
R.layout.group, //一级目录样式布局文件
new String[]{"group"}, //指定一级目录数据的key
new int[]{R.id.group}, //指定一级目录数据显示控件的id
childs, //二级目录的数据
R.layout.child, //二级目录样式布局文件
new String[]{"child"}, //指定二级目录数据的
new int[]{R.id.child} //指定二级目录数据显示控件的
);
setListAdapter(adapter);

示例


  1. 先创建一个NSURL
  2. 在通过NSURL创建NSURLRequest,可以指定缓存规则和超时时间
  3. 创建NSURLConnection实例,指定NSURLRequest和delegate对象,如果创建失败,则返回nil,如果创建成功则创建一个NSMutableData的实例用来存储数据。
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
NSURLRequest *request = [NSUELRequest requestWithURL:[NSURL URLWithString:@"http://www.sina.com.cn/"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(connection){
// 创建成功
}
else{
// 创建失败
}

# pragma mark- NSUrlConnectionDelegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//接受一个服务端回话,再次一般初始化接受数据的对象
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// 每个中间data
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// 连接结束

}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// 链接错误
}

Shutdown命令,h/r/s分别代表关机、重启、睡眠,再加上执行时间(yymmddhhmm)即可

比如sudo shutdown -h 1404081900