@blog.justoneplanet.info

日々勉強

androidでクリップボードのテキストを取得する

めも。

■取得

ClipboardManager clipboardManager = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE);
Log.e("face", (String) clipboardManager.getText());

■保存

ClipboardManager clipboardManager = (ClipboardManager) mActivity.getSystemService(CLIPBOARD_SERVICE);
clipboardManager.setText(result);

android開発で既存のプロジェクトをimportする

毎回微妙にはまるのでそろそろ書いておく。

■import

eclipseの.projectディレクトリがある場合は、「File > import > Existing Projects into Workspace」で行う。

jarなどがある場合

ディレクトリ構成などが異なってるとリンクが切れたりするので、「Packeage Explorer > プロジェクトルートで右クリック > Properties > Java Build Path > Libraries」で再設定する。

compiler

以下のようなエラーが出る場合がある。

Android requires compiler compliance level 5.0 or 6.0. Found ‘1.7’ instead. Please use Android Tools > Fix Project Properties.

「環境設定 > Java > Compiler」でCompiler compliance levelを1.6にする。

■R.java

ビルドできる状態ならば勝手に生成されるのだが、極々稀に自動生成が開始されなかったりするので、その場合は再起動したりするといいと思う。

■テストコード

上述の方法でインポートする。但し、ディレクトリ構成が異なっていてテスト対象が見つけられずエラーが出る場合は、「Package Explorer > Propaties > Java Build Path > Projects」でプロジェクトをセットしなおす。

GHAsyncTestCaseを使ってASIHTTPRequestの非同期通信のテストをする

ドキュメントが分かりにくかったので自分で書いてみた。

■テスト対象のクラス

ASIHTTPRequestを使った普通のクラス。

MyHTTP.h

#import <Foundation/Foundation.h>
#import "MyHTTPDelegate.h"

@class ASIHTTPRequest;
@class MyHTTPDelegate;

@interface MyHTTP : NSObject {
	id <MyHTTPDelegate> delegate;
}
@property (nonatomic, retain) id delegate;
- (void)loadData;
- (void)requestFinished:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;
@end

MyHTTP.m

以下のように実装する。ASIHTTPRequestを使った事があるならばお手の物だ。

#import "MyHTTP.h"
#import "ASIHTTPRequest.h"
#import "SBJson.h"

// 今回は触れないけど通信するので圏外判定に使っている
#import "UIDevice-Reachability.h"
#import "Reachability.h"

#define URL @"http://example.org/api/data"

@implementation MyHTTP
@synthesize delegate;

// 通信して更新する
- (void)loadData
{
    if([[UIDevice currentDevice] networkAvailable] == YES){
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:URL]];
        [request setDelegate:self];
        [request setRequestMethod:@"GET"];
        [request startAsynchronous];// 非同期通信の開始
    }
    else{
        // 圏外の時 = cahceから再現させる
    }
}

#pragma mark -
#pragma mark ASIHTTPRequestDelegate
- (void)requestFinished:(ASIHTTPRequest *)request
{
    SBJsonParser *parser = [[SBJsonParser alloc] init];
    NSArray *json = [parser objectWithString:[request responseString] error:nil];
    if(json != nil && [json count] > 0){// success
        [delegate reflectData:json];
    }
    [parser release];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
    NSError *error = [request error];
}
@end

■テストクラス

MyHTTPTest.h

以下のようにGHAsyncTestCaseを継承する。

#import <GHUnitIOS/GHUnit.h>

@class MyHTTP;

@interface MyHTTPTest : GHAsyncTestCase {
    MyHTTP *http;
}
@property (nonatomic, retain) MyHTTP *http;
@end

MyHTTPTest.m

以下のように実装する。

#import "MyHTTPTest.h"
#import "MyHTTP.h"

@implementation MyHTTPTest
@synthesize http;

- (void)setUpClass
{
    http = [[MyHTTP alloc] init];
    [http setDelegate:self];
}
- (void)tearDownClass
{
    [http release];
}

- (void)testLoadData
{
    // 非同期処理が始まる前の準備
    [self prepare];
    
    // 非同期処理を実行する
    [self performSelector:@selector(startAsynchronous) withObject:nil afterDelay:0.1];
    
    // どのくらい[結果]が帰ってくるのを待つか指定する(ここでは3秒待つことにした)
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

- (void)startAsynchronous
{
    [http loadData];
}

#pragma mark -
#pragma mark MyHTTPDelegate
- (void)reflectData:(NSArray *)json
{
    NSLog(@"json:%d", [json count]);
    @try {
        GHAssertFalse(
                      !!([json count] == 0),
                      @"success to transfer for popular");
        [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
    @catch (NSException *exception) {// アサーションに失敗した場合
        [self notify:kGHUnitWaitStatusFailure forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
}
@end

GHAssert系のメソッドはテストが失敗した場合にExceptionを発生させるので、上述のようにcatchしないとユニットテスト自体がクラッシュする。ちなみに以下のように書いても問題なくテストできた。


#import "MyHTTPTest.h"
#import "MyHTTP.h"

@implementation MyHTTPTest
@synthesize http;

- (void)setUpClass
{
    http = [[MyHTTP alloc] init];
    [http setDelegate:self];
}
- (void)tearDownClass
{
    [http release];
}

- (void)testLoadData
{
    // 非同期処理が始まる前の準備
    [self prepare];
    
    // 非同期処理を実行する
    [http loadData];
    
    // どのくらい[結果]が帰ってくるのを待つか指定する(ここでは3秒待つことにした)
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

#pragma mark -
#pragma mark MyHTTPDelegate
- (void)reflectData:(NSArray *)json
{
    NSLog(@"json:%d", [json count]);
    @try {
        GHAssertFalse(
                      !!([json count] == 0),
                      @"success to transfer for popular");
        [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
    @catch (NSException *exception) {// アサーションに失敗した場合
        [self notify:kGHUnitWaitStatusFailure forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
}
@end

performSelectorメソッドの有用性が分からない。

■おまけ

実はMyHTTPクラスには通信ができなかった時の事を考慮して、キャッシュから呼び出す機能がある。以下のようにSELをプロパティに持たせる事で通知先を分岐するようにした。

#import "MyHTTPProxyTest.h"
#import "MyHTTPProxy.h"

@implementation MyHTTPProxyTest
@synthesize http;
@synthesize selector;

- (void)setUpClass
{
    http = [[MyHTTPProxy alloc] init];
    [http setDelegate:self];
}
- (void)tearDownClass
{
    [http release];
}

- (void)testLoadCache
{
    [self setSelector:@selector(testLoadCache)];
    [self prepare];
    [self performSelector:@selector(startLoadCache) withObject:nil afterDelay:0.01];
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

- (void)testLoadData
{
    [self setSelector:@selector(testLoadData)];
    [self prepare];
    
    // Do asynchronous task here
    [self performSelector:@selector(startAsynchronous) withObject:nil afterDelay:0.01];
    
    // Wait for notify
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

- (void)startLoadCache
{
    [http loadCache];
}

- (void)startAsynchronous
{
    [http loadData];
}

#pragma mark -
#pragma mark MyHTTPDelegate
- (void)reflectData:(NSArray *)json
{
    NSLog(@"json:%d", [json count]);
    @try {
        GHAssertFalse(
                      !!([json count] == 0),
                      @"success to transfer");
        [self notify:kGHUnitWaitStatusSuccess forSelector:[self selector]];
    }
    @catch (NSException *exception) {
        [self notify:kGHUnitWaitStatusFailure forSelector:[self selector]];
    }
}
@end

カスタマイズされたUITableViewCellを使う

UITableViewのCellは使い回しされているのでサブビューを加えたりカスタマイズする時は注意が必要って備忘録。

■実装

UITableViewCellCustomized.h

#import <UIKit/UIKit.h>
@interface UITableViewCellCustomized : UITableViewCell {
    UILabel *title;
}
@property (nonatomic, retain) UILabel *title;
@end

UITableViewCellCustomized.m

#import "UITableViewCellCustomized.h"
@implementation UITableViewCellCustomized
@synthesize title;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {// Initialization code
        // title
        title = [[[UILabel alloc] initWithFrame:CGRectMake(20, 5, 280, 20)] autorelease];
        [title setFont:[UIFont systemFontOfSize:18]];
        [self addSubview:title];
        // accessory
        [self setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];
}

- (void)dealloc
{
    [title release];
}
@end

クライアントコード

// create cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // call cell
    UITableViewCellCustomized *cell = (UITableViewCellCustomized *)[tableView dequeueReusableCellWithIdentifier:@"table_cell"];
    if (cell == nil) {// create cell
        cell = [[[UITableViewCellCustomized alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"table_cell"] autorelease];
    }
    [cell.title setText:@"なんらかの文字列"];
    return cell;
}

上述のようにUITableViewCellを継承したクラスを用意してセットアップする。以下の例のようにしてはならない。

悪い例

以下のように通常の使い回しされているUITableViewCellに直にaddSubviewする。

// create cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"table_cell"];
    cell = nil;
    if (cell == nil) {
        cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"table_cell"] autorelease];
    }
    [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
    // title
    UILabel *title = [[[UILabel alloc] initWithFrame:CGRectMake(20, 5, 280, 20)] autorelease];
    [title setText:@"なんらかの文字列"];
    [cell addSubview:title];
    return cell;
}