查看已连接的设备

1
adb devices

会列出连接设备的ID,使用adb -s DEVICE_ID可以指定特定的设备

安装应用

使用install命令来安装apk,如果设备上已经安装了应用,可以使用可选参数-r重新进行安装并保留所有数据

1
adb install -r APK_FILE

卸载应用

1
adb uninstall PACKAGE_NAME

启动Activity

1
adb shell am strat PACKAGE_NAME/ACTIVITY_IN_PACKAGE

进入设备命令行

1
adb shell

截取屏幕

1
adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > screen.png

解锁屏幕

1
adb shell input keyevent 82

日志

用来在命令行中显示日志流

1
adb logcat

按标签过滤

1
adb logcat -s TAG_NAME1 TAH_NAME2

按优先级过滤

1
2
3
adb logcat "*:PRIORITY"
# example
adb logcat "*:W"

优先级设置如下:

  • V:Verbose(最低优先级)
  • D:Debug
  • I:Info
  • W:Warning
  • E:Error
  • F:Fatal
  • S:Slient(最高优先级,在这个级别上不会打印任何信息)

按优先级和标签名过滤

1
adb logcat -s TAG_NAME_1:PRIORITY TAG_NAME_2:PRIORITY

使用grep过滤

1
2
adb logcat | grep "SEARCH_TERM"
adb logcat | grep "SEARCH_TERM_1\|SEARCH_TERM_2"

清楚logcat缓冲区

1
adb logcat -c

什么是UICollectionView

UICollectionView是一种新的数据展示方式,简单来说可以把他理解成多列的UITableView。比如iBooks,一个虚拟的书架上放着各类图书,排列整齐,亦或者iPad的iOS6中的原生时钟中的各个时钟,也是UICollectionView的最简单的一个布局。

最简单的UICollectionView就是一个GirdView,可以以多列的方式来将数据进行展示,标准的UICollection包含三个部分:

  • Cells用于展示内容的主体,对于不同的cell可以指定不同尺寸和不同的内容
  • Supplementary Views追加视图,用来标记每个section的view
  • Decoration Views装饰视图,每个section的背景


实现一个简单的UICollectionView

和UITableView一样,UICollectionView同样采用datasource和delegate设计模式:datasource为View提供数据源,告诉View要实现什么及如何显示它们,delegate提供一些样式的小细节以及用户交互的相应。

UICollectionViewDataSource

    • (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section:section的数量
    • (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section:某个section里有多少个item
    • (UICollectionViewCell )collectionView:(UICollectionView )collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath:指定显示什么样的cell

实现了以上三个委托方法,基本上就可以保证CollectionView工作正常,同时还通过了Supplementary View方法

    • (UICollectionReusableView )collectionView:(UICollectionView )collectionView viewForSupplementaryElementOfKind:(NSString )kind atIndexPath:(NSIndexPath )indexPath

用于重用

在UICollectionView中在请求数据源之前要注册CellView、SupplementaryView,可以通过以下方法进行注册
-registerClass:forCellWithReuseIdentifier:
-registerClass:forSupplementaryViewOfKind:withReuseIdentifier:
-registerNib:forCellWithReuseIdentifier:
-registerNib:forSupplementaryViewOfKind:withReuseIdentifier:

UICollectionViewDelegate

负责交互

  • cell的高亮
  • cell的选中状态
  • 可以支持长按后的菜单
    用户点击cell的时候,现在会按照以下流程向delegate进行询问:
    • collectionView:shouldHighlightItemAtIndexPath:是否应该高亮?
    • collectionView:didHighlightItemAtIndexPath:如果1回答是,那么高亮
    • collectionView:shouldSelectItemAtIndexPath:无论1结果如何,都询问是否可以被选中?
    • collectionView:didUnhighlightItemAtIndexPath:如果1回答是否,那么取消高亮
    • collectionView:didSelectItemAtIndexPath:如果3回答为是,那么选中cell

对应的高亮和选中状态分别由highlighted和selected两个属性表示。

关于cell

UICollectionViewCell相比UITableViewCell没有太多的花头,首先不存在格式各样的默认的style。UICollectionViewCell结构上相对比较简单,由下至上:

  1. cell本身作为容器View
  2. 一个大小自动适应整个cell的backgroundView,用于cell默认的背景
  3. selectedBackgroundView,是cell被选中时的背景
  4. contentView,自定义内容应被加载这个View上。

UICollectionViewLayout

负责各个cell、Supplementary View和Decoration Views进行组织,为了它们设定各自的属性,包括但不限于:位置、尺寸、透明度、层级关系、形状、等等…

  • Layout决定了UICollectionView是如何显示在界面上的。在展示之前,一般需要生成合适的UICollectionViewLayout子类对象,并将其赋予CollectionView的collectionViewLayout属性。

默认常用的Layout是UICollectionViewFlowLayout,是一个直线对齐的Layout。

  • itemSize,定义了每一个item的大小。通过设定itemSize可以全局改变所有cell的尺寸,如果想要对某个cell定制尺寸,可以使用-collectionView:layout:sizeForItemAtIndexPath:方法。
  • 间隔 指定每个item之间的间隔和每一行之间的间隔,和size类似,有全局属性,也可以对每一个item和每一个section做出设定:
    • @property (CGSize) minimumInteritemSpacing
    • @property (CGSize) minimumLineSpacing
    • -collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
    • -collectionView:layout:minimumLineSpacingForSectionAtIndex:
  • 滚动方向 由属性scrollDirection确定scroll View的方向,将影响Flow Layout的基本方向和由header及footer确定的section之间的宽度
    • UICollectionViewScrollDirectionVertical
    • UICollectionViewScrollDirectionHorizontal
  • Header和Footer尺寸 分为全局和部分,需要注意根据滚动方向不同,header和footer的高和宽中只有一个和会起作用,垂直滚动时section间宽度为该迟钝的高,而水平滚动时为宽度起作用
    • @property (CGSize) headerReferenceSize
    • @property (CGSize) footerReferenceSize
    • -collectionView:layout:referenceSizeForHeaderInSection:
    • -collectionView:layout:referenceSizeForFooterInSection:
  • 缩进
    • @property UIEdgeInsets sectionInset;
    • -collectionView:layout:insetForSectionAtIndex:

      自定义UICollectionViewLayout

总结
一个UICollectionView的实现包括两个必要部分:UICollectionViewDataSource和UICollectionViewLayout,和一个交互部分:UICollectionViewDelegate。而Apple给出的UICollectionViewFlowLayout已经是一个很强力的layout方案了。

UICollectionViewLayoutAttributes

property列表:

  • @property (nonatomic) CGRect frame
  • @property (nonatomic) CGPoint center
  • @property (nonatomic) CGSize size
  • @property (nonatomic) CATransform3D transform3D
  • @property (nonatomic) CGFloat alpha
  • @property (nonatomic) NSInteger zIndex
  • @property (nonatomic, getter=isHidden) BOOL hidden

UICollectionViewLayoutAttributes的实例中包含了诸如边框,中心点,大小,形状,透明度,层次关系和是否隐藏等信息。当UICollectionView在获取布局时将针对每一个indexPath的部件(包括cell,追加视图和装饰视图),向其上的UICollectionViewLayout实例询问该部件的布局信息。这个布局信息,就以UICollectionViewLayoutAttributes的实例的方式给出。

UICollectionViewLayout

UICollectionViewLayout的功能为向UICollectionView提供布局信息,不仅包括cell的布局信息,也包括追加视图和装饰视图的布局信息。实现一个自定义layou的常规做法是继承UICollectionViewLayout类,然后重载下列方法。

    • (CGSize)collectioonViewContentSize
      • 返回collctionView的内容尺寸
    • (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
      • 返回rect中的所有的元素的布局属性
      • 返回的包括UICollectionViewLayoutAttributes的NSArray
      • UICollctionViewLayoutAttributes可以是cell,追加视图或装饰视图的信息,通过不同的UICollectionViewLayoutAttributes初始化方法可以得到不同类型的UICollctionViewLayoutAttributes
        • ayoutAttributesForCellWithIndexPath:
        • layoutAttributesForSupplementaryViewOfKind:withIndexPath:
        • layoutAttributesForDecorationViewOfKind:withIndexPath:
    • (UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath
      • 返回对应indexPath位置的cell布局属性
    • (UICollectionViewLayoutAttributes )layoutAttributesForDecorationViewOfKind:(NSString )decorationViewKind atIndexPath:(NSIndexPath *)indexPath
      • 返回对应indexPath位置的追加视图的布局属性,如果没有追加视图可以不重载
    • (UICollectionViewLayoutAttributes )layoutAttributesForDecorationViewOfKind:(NSString )decorationViewKind atIndexPath:(NSIndexPath *)indexPath
      • 返回对应于indexPath的位置的装饰视图的布局属性,如果没有装饰视图可不重载
    • (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
      • 当边界发生改变时,是否应该刷新布局。

在初始化一个UICollectionViewLayout实例后,会有一系列的准备方法被自动调用,以保证layout实例的正确

  1. -(void)prepareLayout,默认下该方法是每都不做,但是在自己的子类实现中,一般在该方法中设定一些必要的layout的结构和初始需要的参数等。
  2. -(CGSize)collectionViewContentSize,以确定collection应该占据的尺寸,注意这里的尺寸不是指可使部分的尺寸,而是所有内容所占的尺寸。
  3. -(NSArray *)layoutAttributesForElementsInRect:(Rect)rect被调用,初始的layout外观将由该方法返回的UICollctionViewLayoutAttributes来决定。

另外,在需要更新layout时,需要给当前layout发送 -invalidateLayout,该消息会立即返回,并且预约在下一个loop的时候刷新当前layout。在-invalidateLayout后的下一个collectionView的刷新loop中,又会从prepareLayout开始,依次再调用-collectionViewContentSize和-layoutAttributesForElementsInRect来生成更新后的布局。

LineLayout——对于个别UICollectionViewLayoutAttributes的调整

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
// LinrLayout.m
# import "LineLayout.h"


# define ITEM_SIZE 200.0
# define ACTIVE_DISTANCE 200
# define ZOOM_FACTOR 0.4

@implementation LineLayout

-(id)init
{
self = [super init];
if (self) {
self.itemSize = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
// 确定了缩进,此处为上方、下方各缩进200
self.sectionInset = UIEdgeInsetsMake(ITEM_SIZE, ITEM_SIZE/2, ITEM_SIZE, ITEM_SIZE/2);
// 每个item在水平方向的最小间距
self.minimumLineSpacing = ITEM_SIZE/4;

}
return self;
}
// 边界改变时是否重新排版
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)oldBounds
{
return YES;
}
// 初始的layout外观将由该方法返回的UICollctionViewLayoutAttributes来决定
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray* array = [super layoutAttributesForElementsInRect:rect];
CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;
for (UICollectionViewLayoutAttributes* attributes in array) {
if (CGRectIntersectsRect(attributes.frame, rect)) {
CGFloat distance = CGRectGetMidX(visibleRect) - attributes.center.x;
CGFloat normalizedDistance = distance / ACTIVE_DISTANCE;
if (ABS(distance) < ACTIVE_DISTANCE) {
CGFloat zoom = 1 + ZOOM_FACTOR*(1 - ABS(normalizedDistance));
attributes.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0);
attributes.zIndex = 1;
}
}
}
return array;
}

// 自动对齐到网格
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
// proposedContentOffset是没有对齐到网格时本来应该停下来的位置
CGFloat offsetAdjustment = MAXFLOAT;
CGFloat horizontalCenter = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds) / 2.0);
// 当前显示的区域
CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
// 取当前显示的item
NSArray* array = [super layoutAttributesForElementsInRect:targetRect];
// 对当前屏幕中的UICollectionViewLayoutAttributes逐个与屏幕中心进行比较,找出最接近中心的一个
for (UICollectionViewLayoutAttributes* layoutAttributes in array) {
CGFloat itemHorizontalCenter = layoutAttributes.center.x;
if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
offsetAdjustment = itemHorizontalCenter - horizontalCenter;
}
}
return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}

@end

例子

CircleLayout——完全自定义的Layout,添加删除item,以及手势识别

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
# import "CircleLayout.h"

# define ITEM_SIZE 70

@implementation CircleLayout

// 准备计算需要的参数
-(void)prepareLayout
{
[super prepareLayout];

CGSize size = self.collectionView.frame.size;
_cellCount = [[self collectionView] numberOfItemsInSection:0];
// 中心点
_center = CGPointMake(size.width / 2.0, size.height / 2.0);
// 半径
_radius = MIN(size.width, size.height) / 2.5;
}
// collctionView的内容大小就是collectionView的大小
-(CGSize)collectionViewContentSize
{
return [self collectionView].frame.size;
}
// 通过所在的indexPath确定位置
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path
{
UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
attributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
// 配置attributes到圆周上
attributes.center = CGPointMake(_center.x + _radius * cosf(2 * path.item * M_PI / _cellCount),
_center.y + _radius * sinf(2 * path.item * M_PI / _cellCount));
return attributes;
}
// 用来在一开始给出一套UICollectionViewLayoutAttributes
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray* attributes = [NSMutableArray array];
for (NSInteger i=0 ; i < self.cellCount; i++) {
//
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
return attributes;
}
// 插入前,cell在圆心位置,全透明
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForInsertedItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
attributes.alpha = 0.0;
attributes.center = CGPointMake(_center.x, _center.y);
return attributes;
}

// 删除时,cell在圆心位置,全透明,且只有原来的1/10大
- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDeletedItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
attributes.alpha = 0.0;
attributes.center = CGPointMake(_center.x, _center.y);
attributes.transform3D = CATransform3DMakeScale(0.1, 0.1, 1.0);
return attributes;
}

@end
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
# import "ViewController.h"
# import "Cell.h"

@implementation ViewController

-(void)viewDidLoad
{
self.cellCount = 20;
// 添加一个手势识别
UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
[self.collectionView addGestureRecognizer:tapRecognizer];
[self.collectionView registerClass:[Cell class] forCellWithReuseIdentifier:@"MY_CELL"];
[self.collectionView reloadData];
self.collectionView.backgroundColor = [UIColor greenColor];
}

- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section;
{
return self.cellCount;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
Cell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"MY_CELL" forIndexPath:indexPath];
return cell;
}

- (void)handleTapGesture:(UITapGestureRecognizer *)sender {

if (sender.state == UIGestureRecognizerStateEnded)
{
CGPoint initialPinchPoint = [sender locationInView:self.collectionView];
NSIndexPath* tappedCellPath = [self.collectionView indexPathForItemAtPoint:initialPinchPoint];
// 点击处没有cell
if (tappedCellPath!=nil)
{
self.cellCount = self.cellCount - 1;
[self.collectionView performBatchUpdates:^{
[self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:tappedCellPath]];

} completion:nil];
}
else
{
self.cellCount = self.cellCount + 1;
[self.collectionView performBatchUpdates:^{
[self.collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForItem:0 inSection:0]]];
} completion:nil];
}
}
}

@end

例子

KVO(Key-Value Observing),Objective-C提供的一种机制,当制定的对象被修改时,对象就会接受到通知。

使用捕捉

  1. 注册,制定被观察者的属性
  2. 实现回调方法
  3. 移除观察

KVO涉及到的主要方法:

1
2
3
4
//	三个参数分别是监听对象、监听属性、监听选项
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
// 删除监听器,监听的对象、监听的属性
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

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
@implementation ViewController
{
UILabel *_name;
UILabel *_age;
UIButton *_b1;
UIButton *_b2;
}

- (void)viewDidLoad
{
[super viewDidLoad];
_myData= [[MyData alloc] init];
[_myData setValue:@"name" forKey:@"name"];
[_myData setValue:@"22" forKey:@"age"];
[_myData addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
[_myData addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];

CGSize size = self.view.bounds.size;

_name = [[UILabel alloc]initWithFrame:CGRectMake(size.width/2 - 75, 100, 150, 30 )];
_name.textColor = [UIColor redColor];
_name.text = [_myData valueForKey:@"name"];
_name.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:_name];

_age = [[UILabel alloc]initWithFrame:CGRectMake(size.width/2 - 50, 140, 100, 30 )];
_age.textColor = [UIColor redColor];
_age.text = [[NSString alloc]initWithFormat:@"%@",[_myData valueForKey:@"age"]];
_age.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:_age];

_b1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[_b1 setTitle:@"Push" forState:UIControlStateNormal];
_b1.frame = CGRectMake(size.width/2 - 50, size.height/2 + 100, 100, 30);
[_b1 addTarget:self action:@selector(buttonAction1) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_b1];

_b2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[_b2 setTitle:@"Push" forState:UIControlStateNormal];
_b2.frame = CGRectMake(size.width/2 - 50, size.height/2 + 140, 100, 30);
[_b2 addTarget:self action:@selector(buttonAction2) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_b2];
}
-(void) buttonAction1
{
unsigned int random = arc4random_uniform(999);
_myData.name = [[NSString alloc]initWithFormat:@"随机数字:%d",random];
}
-(void) buttonAction2
{
unsigned int age = arc4random_uniform(100);
_myData.age = age;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:@"age"])
{
_age.text = [[NSString alloc]initWithFormat:@"%@",[_myData valueForKey:@"age"]];
} else if([keyPath isEqualToString:@"name"])
{
_name.text = _myData.name;

}
}

  1. arcrandom()

    1
    2
    // 获取1到x之间的整数的代码如下:
    int value = (arc4random() % x) + 1;
  2. random()

    1
    2
    // 需要初始化时设置种子
    srandom((unsigned int)time(time_t *)NULL); //初始化时,设置下种子就好了。
  3. CCRANDOM_0_1()

    1
    2
    3
    // cocos2d中使用 ,范围是[0,1]
    // [0,5] CCRANDOM_0_1() 取值范围是[0,1]
    float random = CCRANDOM_0_1() * 5;
  4. arc4random_uniform

  • 推介使用
    1
    2
    // 返回一个小于number的均匀分布的整数
    arc4random_uniform(number)

还存在其他一些arc4随机函数,详情查看

shimmer是Facebook旗下应用Paper开源的加载效果,显示为一个闪闪发光的Label。

安装

使用Cocoapods来安装FBShimmering库

  1. 命令行里运行pod search Shimmer

  1. 将上面的pod 'Shimmer', '~> 1.0.1'copy,在你的工程下创建Podfile文件,并写入

    1
    2
    3
    platform :ios, '7.0'

    pod 'Shimmer', '~> 1.0.1'
  2. 在命令行切换到你的工程目录,运行pod install

用法

创建一个FBShimmeringViewFBShimmeringLayer,添加您的内容。要开启闪烁,就设置shimmering属性为YES。
例子:

1
2
3
4
5
6
7
8
9
10
FBShimmeringView  * shimmeringView  =  [[ FBShimmeringView  alloc ]  initWithFrame : self.view.bounds ]; 
[ self.view addSubview : shimmeringView ];

UILabel * loadingLabel = [[ UILabel alloc ] initWithFrame : shimmeringView.bounds ];
loadingLabel.textAlignment = NSTextAlignmentCenter ;
loadingLabel.text = NSLocalizedString ( @"Shimmer" , nil );
shimmeringView.contentView = loadingLabel ;

/ /开始闪闪发光
shimmeringView.shimmering = YES ;

示例链接

示例

运行环境

  • Android2.3及以上版本
  • iOS 5.0及以上版本
  • OS X 10.7及以上版本
  • Windows 7及以上版本
  • Ubuntu 12.04及以上版本
  • Cocos2d-X v3.0rc

软件需求

  • Xcode 4.6
  • gcc 4.7
  • Visual Studio 2012
  • Python 2.7.5

创建新工程

切换到cocos的根目录

1
cocos new MyGame -p com.zoe.MyGame -l cpp -d ./MyProject

  • MyGame:指定工程名
  • - p com.zoe.MyGame:指定Android的包名
  • -l cpp:指定项目的语言,可用cpp或者lua
  • -d ./MyProject:指定存放目录

    成的项目的文件夹结构如下:

    编译运行新工程

1
cocos run -s ./MyProject/MyGame -p ios
  • -s:指定工程目录,必须是绝对目录且有效的
  • -p:选着运行平台,可选项有ios,android,win32,mac,linux

环境要求

Mac OSX

Cocos2d-x

首先,要先下载好Cocos2d-x,并解压到你要放的位置。
打开Cocos2D-X文件夹你应该看到如下的情况:

JDK,SKD和NDK

下载Java,安装后,通过java -version进行验证

下载SDK,NDK解压到你存放的位置

Python和ant

Python作为Mac的一等公民,Macbook上是默认自带了的。
可以通过python --version验证:

1
2
->	python --version
Python 2.7.5

如果没有安装可以通过brew install python进行安装
如果brew也没有,可以到brew官网按照指定命令安装

之后我们还需要安装ant

1
brew install ant

通过ant -version进行验证

1
2
ant -version
Apache Ant(TM) version 1.9.3 compiled on December 23 2013

初始化环境

切换到./cocos2d-x的目录下(根据实际存放的位置)执行.\setup.py或者python setup.py运行设置脚本
根据提示分别填写NSK、SDK、ant的路径(根据实际存放位置,ant可以通过which ant命令查看位置)


最后,执行下面的命令完成设置

1
source ~/.bash_profile

编译

切换到cocos2d-x的build目录下
执行下面命令

1
python android-build.py -p 10 cpp-test

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
// FileCache.java
import java.io.File;
import android.content.Context;

public class FileCache{
private FileCacheDir;
public FileCache(Context context){
if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir = new File(android.os.Environmet.getExternalStrorageDirectory(),"文件夹名称");
else
cacheDir = context.getCacheDir();
if(!cacheDir.exsts())
cache.mkdirs();
}

public File getFile(String url){
String fileName = String.valueOf(url.hashCode());
File file = new File(cacheDir,fileName);
return file;
}

public void clear(){
File[] files = cacheDir.listFiles();
if(files == null)
return;
for(File file:files)
{
file.delete();
}
}
}
// HttpUtil.java
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoudException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;

/**
* Http请求工具类
*/
public class HttpUtil{
public static String getResponseStr(String path,Map<String,String>parameters){
StringBuffer buffer = new StringBuffer();
URL url;
try{
if(parameters!=null&&parameters.isEmpty()){
for(Map.Entry<String,String>entry:parameters.entrySet()){
buffer.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(),"UTF-8")).append("&");
}
buffer.deleteCharAt(buffer.length()-1);
}
url = new URL(path);
HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setConnectTimeout(3000);
urlConnection.SetRequesMethod("POST");
urlConnection.SetDoInput(true);
urlConnection.SetDoOutput(true);
//
byte[] mydata = buffer.toString().getBytes();
urlConnection.setRequesProperty("Content-Type","application.x-www-form-urlencoded");
urlConnection.setRequesProperty("Content-Length",String.valueOf(mydata.length));
// 获取输出流,向服务器输出数据
OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(mydata,0,mydata.length());
outputStream.close();
int responseCode = urlConnection.getResponseCode();
if(responseCode == 200){
return changeInuptstream(urlConnection.getInputStream());
}catch(UnsupportedEncodingException e){
e.printStackTrace();
}catch(MalformedURLException e){
e.printStackTrace();
}catch(ProtocolException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
retun null;
}
private static String changeInputStream(InputStream inputStream){
ByteArrayOutpoutStream outputStream = new ByteArrayOutputStream();
byte[] data = new byte[1024];
int len = 0;
String result = "";
if(inputStream != null){
try{
while((len = inputStream.read(data)) != -1){
outputStream.write(data,0,len);
}
result = new String(outputStream.toByteArray(),"UTF-8");
}catch(IOException){
e.printStacckTrace();
}
}
return result;
}
public static InputStream getInputStream(String path){
URL url;
try{
url = new URL(path);
HttpURLConnection urlConnection = (HttpURLConnection)url.open.Connection();
urlConnecation.setConnectTimeout(3000);
urlConnecation.setRequestMethod("GET");
urlConnecation.setDoInput(true);
urlConnecation.connect();
if(urlConnection.getResponseCode == 200)
return urlConnection.getInputStream();
}catch(MalformedURLException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}catch(Exception e){
e.printStacjTrace();
}
return null;
}
public satatic byte[] readStream(InputStream inStream)throwsException{
ByteArrayOutputStream outSteam = newByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while((len = inStream.read(buffer)) != -1){
outStream.write(buffer,0,len);
}
outStream.close();
inStream.close();
return outStream.toByteArray();
}
public static void copyStream(String url,File f){
FileOutputStream fileOutputStream = null;
InputStream inputStream = null;
try{
inputStream = getInputStream(url);
byte[] data = new byte[1024];
int len = 0;
fileOutputStream = new FileOutputStream(f);
while((len = inputStream.read(data)) != -1){
fileOutputStream.write(data,0,len);
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
if(inputStream != null){
try{
inputStream.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
// MemoryCache.java
import java.lang.ref.SoftReference;
import java.util.Collections;
import jave.util.HashMap;
import java.util.Map;
import android.graphics.Bitmap;

public class MemoryCache{
private Map<String,SoftReference<Bitmap>> cache = Collections.sycnchronizedMap(newHashMap<String,SoftReference<Bitmap>>());//软引用

public Bitmapget(String id){
if(!cache.containsKey(id))
return null;
SoftReference<Bitmap> ref = cache.get(id);
return ref.get();
}
public void put(String id,Bitmap bitmap){
cache.put(id,newSoftReferce<Bitmap>(bitmap));
}
public void clear(){
cache.clear();
}
}
// ImageLoader.java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.io.net.URLEncoder;
import java.io.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.grapgics.BitmapFactory;
import android.grapgics.drawable.BitmapDrawable;
import android.widget.ImageView;

public class ImageLoader{
private MemoryCachemoryCache = new MemoryCache();
private FileCache fileCache;
private Map<ImageView,String> imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView,String>());
private ExecutorService executorService;
private boolean isSrc;

public ImageLoader(Context context,boolean flag){
fileCache = new FileCache(context);
executorService = Executors.newFixedThreadPool(5);
isSrc = flag;
}
final int stub_id = R.drawable.ic_launcher;
public void DisplayImage(String url,ImageView imageView){
String u1 = url.substring(0,url.lastIndexOf("/")+1);
String u2 = url.substring(url,lastIndexOf("/")+1);
try{
u2 = URLEncoder.encode(u2,"UTF-8");
}catch(UnsupportedEncodingException e){
e.printStackTrace();
}
url = u1 +u2;
imageViews.put(imageView,rul);
Bitmap bitmap = memoryCache.get(url);
if(bitmap != null){
if(isSrc)
imageView.setImageBitmap(bitmap);
else
imageView.setBackgroundDrawable(new BitmapDrawable(bitmap));
}else{
queuePhoto(url,imageView);
if(isSrc)
imageView.setImageResouce(stub_id);
else
imageView.setBackgroundResource(stub_id);
}
}
private void ququePhoto(String url,ImageView imageView){
PhotoToLoad p = new PhotoToLoad(url,imageView);
executoService.submit(new PhotoSLoader(p));
}
private Bitmap getBitmap(String url){
try{
File f = filCache.getFile(url);
// SD卡
Bitmap b = onDecodeFile(f);
if(b != null)
return b;
// 从网络
Bitmap bitmap = null;
Sysytem.out.println("ImageLoader-->download");
HttpUtil.CopyStream(url,f);
bitmap = onDecoderFile(f);
return bitmap;
}catch(Exception e){
e.printStackTrace();
return null;
}
}
public Bitmap onDecodeFile(File f){
try{
return BitmapFactory.decodeStream(new FileInputStream(f));
}catch(FileNotFoundException e){
e.printStackTrace();
}
return null;
}
// 解码图像用来减少内存消耗
public Bitmap decodeFile(File f){
try{
// 解码图像大小
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
// 找到正确的刻度值,它应该是2的幂
final int REQUIRED_SIZE = 70;
int width_tmp = o.outWidth,height_tmp = o.outHeight;
int scale = 1;
while(true){
if(width_tmp/2 < REQUIRED_SIZE || height_tmp/2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f),null,o2);
}catch(FileNotFoundException e){

}
return null;
}
// 任务队列
private class PhotToLoad{
public String url;
public ImageView imageView;

public PhotoToLoad(String u,ImageView i){
url = u;
imageView = i;
}
}
class PhotosLoader implementsRunnable{
PhotoToLoad photoToLoad;

PhotoLoader(PhotoToLoad photoToLoad){
this.photoToLoad = photoToLoad;
}
@Override
public void run(){
if(imageViewReused(photoToLoad))
return;
Bitmap bmp = getBitMap(photoToLoad.url);
memoryCache.put(photoToLoade.url.bmp);
if(imageViewReused(photoToLoade))
return;
BitmapDisplayer bd = new BitmapDisplayer(bmp,photoToLoad);
Activity a = (Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
boolean imageViewReused(PhotoToLoade photoToLoad){
String tag = imageViews.get(photoToLoad.imageView);
if(tag == null ||!tag.equals(pgotoToLoad.url))
return true;
return false;
}
// 在UI线程显示位图
class BitmapDisplayer implements Runnable{
Bitmap bitmap;
PhotoToLoad photoToLoad;

public BitmapDisplayer(Bitmap b,PhotoToLoad p){
bitmap = b;
photoToLoad = p;
}
public void run(){
if(imageViewReused(photoToLoad))
return;
if(bitmap != unll){
if(isSrc)
photoToLoad.imageView.setImageResouce(stub_id);
else
photoToLoad.imageView.setBackgroundDrawable(new BitmapDrawable(bitmao));
}else{
if(isSrc)
photoToLoad.imageView.setImageResouce(stub_id);
else
photoToLoad.imageView.setBackgroundDrawable(new BitmapDrawable(bitmao));
}
}
}
public void clearCache(){
memoryCache.clear();
fileCache.clear();
}
}

简介

Alcartraz是一个帮助你管理Xcode插件、模板以及颜色配置的工具,集成在Xcode的图形界面中。

安装与卸载

安装命令

1
2
mkdir -p ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins;
curl -L http://git.io/lOQWeA | tar xvz -C ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins

卸载命令

1
2
rm -rf ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins/Alcatraz.xcplugin
rm -rf ~/Library/Application\ Support/Alcatraz

使用

安装成功后重启Xcode,就可以在Xcode的顶部菜单中找到Alcatraz,如下所示:

点击“Package Manager”,即可启动插件列表页面,如下所示

之后你可以在右上角搜索插件,对于想安装的插件,点击其左边的图标,即可下载安装,如下所示,我正在安装KImageNamed插件:

安装完成后,再次点击插件左边的图标,可以将该插件删除。

插件路径

Xcode所有的插件都安装在目录~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/下,你也可以手工切换到这个目录来删除插件。