2011/02/24

ListViewを使うとなると、行のコンポーネントをカスタマイズしたいところ。 iPhoneで言うと、UITableCellView、Flexで言うと(ry

参考ページ

ListViewとListActivity(3)-応用編

Android Adapter & ListView & ExpandableListView

ListViewの作法を知る

想像していたよりやっかいでした。 http://hoge/api/person/list にアクセスして人名リストデータをJSONで取得し、ListViewに人名と画像を表示するサンプル。 (サンプル用にオブジェクト名を変えているのでコンパイルエラーしているかも) ※画像はプロジェクトに埋め込み

layout/person_row.xml





    
        
            
            
            
        
    

src/xxxx/PersonList.java [java] public class PersonList extends ListActivity { DefaultHttpClient httpClient;

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    initHttp();
    loadPersons();
}

static class ViewHolder {
    TextView nameTextView;
    ImageView imageView1;
}

class Person {
    String name;
    String no;

    public Person() {

    }
}

public int personImageId(int no) {
    switch (no) {
        case 1:
             return R.drawable.image1;
        case 2:
             return R.drawable.image2;
        case 3:
             return R.drawable.image2;
    }
    return 0;
}

public class MyAdapter extends ArrayAdapter<Person> {
    private LayoutInflater inflater;
    private int layoutId;

    public MyAdapter(Context context, int layoutId, List<Person> objects) {
        super(context, 0, objects);
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.layoutId = layoutId;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = inflater.inflate(layoutId, parent, false);
            holder.nameTextView = (TextView) convertView.findViewById(R.id.nameTextView);
            holder.imageView1 = (ImageView) convertView.findViewById(R.id.imageView1);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        if(!isEnabled(position)) {
            convertView.setBackgroundColor(Color.BLACK);
        } else {
            Person person = getItem(position);
            holder.nameTextView.setText(person.name);   
            int resourceId1 = personImageId(Integer.parseInt(person.no));
            if (resourceId1 > 0) holder.imageView1.setImageResource(resourceId1);           
        }

        return convertView;
    }

}

public void initHttp() {
    //スキーマ登録
    SchemeRegistry schReg = new SchemeRegistry();
    schReg.register(
            new Scheme(
                    HttpHost.DEFAULT_SCHEME_NAME,
                    PlainSocketFactory.getSocketFactory(),
                    80
            )
    );

    //HTTPパラメータ設定
    HttpParams httpParams;
    httpParams= new BasicHttpParams();
    HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
    HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8);

    //HTTPクライアント生成
    httpClient = new DefaultHttpClient(
            new ThreadSafeClientConnManager(httpParams, schReg),
            httpParams
    );
}

public void loadPersons() {
    Uri.Builder uriBuilder = new Uri.Builder();
    uriBuilder.scheme("http");
    uriBuilder.authority("hoge");
    uriBuilder.path("/api/person/list");

    String uri = uriBuilder.toString();
    HttpUriRequest httpRequest = new HttpGet(uri);

    //HTTP Request送信
    HttpResponse response = null;
    try{
        response = httpClient.execute(httpRequest);
    } catch(Exception e) {
        return;
    }

    // レスポンスコードを確認
    if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK){
        AlertDialog.Builder diag = new AlertDialog.Builder(PersonList.this);
        diag.setTitle("ネットワークエラー");
        diag.setMessage("サーバに接続できませんでした");

        // ダイアログに表示するボタンの定義
        DialogInterface.OnClickListener listner = new DialogInterface.OnClickListener(){
        public void onClick(DialogInterface dialog, int which) {
                setResult(RESULT_OK);
            }
        };

        diag.setPositiveButton("OK",listner);
        diag.create();
        diag.show();
        return;
    }

    //JSON取得
    StringBuilder json = new StringBuilder();
    try{
        HttpEntity entity = response.getEntity();
        InputStream input = entity.getContent();
        InputStreamReader reader = new InputStreamReader(input);
        BufferedReader bufReader = new BufferedReader(reader);
        String line;
        while((line = bufReader.readLine()) != null) {
            json.append(line);
        }
    } catch(IOException e) {
        return;
    }

    items = new ArrayList<Person>();
    // JSON解析
    try{
        JSONObject jsonRoot = new JSONObject(json.toString());
        JSONArray jsonAreas = jsonRoot.getJSONArray("person");
        int i;
        for(i = 0; i < jsonAreas.length(); i++){
            JSONObject jsonRslt = jsonAreas.getJSONObject(i);

            Person person = new Person();
            person.name = jsonRslt.getString("name");
            person.image1 = jsonRslt.getString("image1");
            items.add(person);
        }

        MyAdapter adapter = new MyAdapter(this, R.layout.person_row, items);
        setListAdapter(adapter);
    } catch(JSONException e) {
        return;
    }
}

[/java]

ポイントやハマったところ。

ListActivityを使う

普通にActivityを使ってもできますが、手続きを多少簡略化できるListActivityを使いました。 Activityの場合、ListViewレイアウトを作ったり、Adapterのセットの仕方が異なってくるかと。

2011/08/11 追記 ListActivity を継承しない場合は、 [java] .... MyAdapter adapter = new MyAdapter(this, R.layout.list_row, values); (ListView) listView = (ListView) findViewById(R.id.listView); listView.setAdapter(adapter); [/java] のように、setAdapter() メソッドで Adapter を直接指定します。 ListActivity は1画面1リスト(だと思う)なので、複数リストを利用したい場合は、このやり方でよいかと。

ArrayAdapter

JSONデータ取得&Listデータ生成後、ArrayAdapterを利用してListViewにバインドしてやります。 [java] MyAdapter adapter = new MyAdapter(this, R.layout.person_row, items); setListAdapter(adapter); [/java] 2番目の引数が、行レイアウトになります。

getView

リストを描画しようとする度に呼ばれます(多分)。 iOSで言うと、TableView:cellForRowAtIndexPath にあたる部分と思ってよいかも。

Androidでは何やらif文で分岐してまどろっこしい書き方だけど、 既に行のコンポーネントが作成されていれば再利用します。

つまりは、ListViewの行のキャッシュ処理

行のキャッシュは、静的クラス(ViewHolder)として定義しています。

positionは行のインデックスにあたり、データであるListからgetItemで取得します。 またisEnabledで行がない場合は、何も表示されないので非表示。

レイアウト作成でイライラ

これはどうにかして欲しいかも。 ちょっと、画面を作る気にならないほど面倒くさいです。

と言う訳で、リスト表示に関してはコーディング的にもツール的にAppleの方が軍配が上がるかな?

  2011/02/24

画像ファイルなど、リソースファイル名が数字にすると、 コンパイルでgen(R.java)がエラーになります。

仕様でファイル名が、プロパティとして自動生成されるからのようです。 [java] public static final class drawable { public static final int icon=0x7f020000; public static final int player1=0x7f020001; public static final int player2=0x7f020002; public static final int player3=0x7f020003; public static final int player4=0x7f020004; public static final int player5=0x7f020005; public static final int player6=0x7f020006; } [java]

プロパティからリソースIDを取得して、画像を生成。 [java] ImageView imageView; imageView = (ImageView) convertView.findViewById(R.id.imageView); imageView.setImageResource(R.drawable.player1);
[/java]

  2011/02/24

だそうです。 Titanium Mobile 1.6.0 is Released!

既に、日本語訳も。 Titanium Mobile 1.6.0がリリースされました(速報→追記しました)

iphoneもAndroidoもFacebook Graph SDKに対応とのこと。

Android NDK

Android NDKを使う(アプリの高速化)

Android NDKのメリットとして、 (1) DalvikVMはJava言語を利用するので、NativeのC/C++のコードより遅くなる (2) C/C++のライブラリを組み込める

ただ、Android 2.2以降では、JITコンパイラの採用により高速化されているとのこと。 自分もC/C++はやらないので、必要ないかな?

  2011/02/24

Android + Eclipseでアプリを起動すると、エミュレータのエラーがちょくちょくある。

ActivityManager: Warning: Activity not started, its current task has been brought to the front

既にActivityが起動していて、新たにActivityを起動できない。 【esc】キーでActivityを終了してやる必要がある。

the user data image is used by another emulator. aborting

エミュレータを複数起動しようとしている。

Titaniumと同時で起動してると、エミュレータの権限を奪われてしまうのか?おかしくなってしまう。 解決としては、単にTitaniumとエミュレータのプロセスを完全に落として、Eclipseを再起動。

また、Titaniumをインストールしてしまうと、Titanium用のエミュレータも追加されるので、 Runする時にエミュレータを明確に指定する必要がある。 android eclipse

  2011/02/24

EclipseのWindow > Show View > Other...でLog Catを選択する。 android

android

  2011/02/23

Objective-Cを勉強し始めた頃、Objective-C 基礎編 Vol2で、@synthesizeについてメモをした。

この時は実践してなかったので、チンプンカンプン。 で、数ヶ月経った今、本当に基本的な機能を知らなかった事が判明・・・。

@synthesize で定義したプロパティはselfでないとアクセッサが機能しない!

ちょっと脱力です(^^;)

今まではselfなしで・・・

今までは、プロパティには self をつけずにコーディングしてました。 で、deallocの処理で律儀に全部 release していたのです。

@synthesizeで定義されたプロパティは以下のsetter、getterを処理してくれます。

- (id) foo {
    return foo;
}
- (void) setFoo:(id) aFoo {
    if (aFoo != foo) {
        [aFoo retain];
        [foo release];
        foo = aFoo;
    }
}

よって、dealloc() で release する必要がなく nil にするだけです。

@property (nonatomic, retain) Person person;

@synthesize person;

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person = [[Person alloc] init];
}

- (void)walk {
    self.person = ....
}

- (void)dealloc {
    person = nil;
}

[[Person alloc] init] しないケース(他の画面遷移からオブジェクトを受ける等)場合は、 deallocのperson = nil さえ要らないケースもあるかも知れませんが、とりあえず nilにしておいた方が安全ですかね?

@synthesize は selfを絶対に利用すべき?

はっきり言って、自分はわかりません(賢い人教えてw)。 selfを利用するかどうかは人それぞれかも知れませんし、 現に自分もdealloc→releaseで取りあえず何とかなっていたみたいなので。

ただ、releaseを書くくらいなら、selfでアクセスする癖を付けた方が良いのかなぁ?とも思いました。 あとNSIntegerとかポインターでないオブジェクトは別に関係ないかも知れません。

2011/10/17 追記

カスタムクラスのインスタンスの扱いでも記載したように、init する場合は明示的に

Person _person = [[Person alloc] init];
self.person = _person;
[_person release];

とrelease した方がAnalyze で警告を防げます。 また、以下のコードでも問題はないかも。

person = [[Person alloc] init];

ただし init 以降の person のアクセスは、self.person で行うべきです。

  2011/02/23

Android 3.0 Platform Android 3.0 Platform Highlights Android API Differences Report

SDKはHoneycombからAPI Level 11のアップです。 Android 3.0

現状はタブレット向けにしかのらないと思いますが、animation framework、Hardware-accelerated 2D、Renderscript 3Dなどグラフィック周りが進化してるみたい。

早速、一部日本語訳してます。 Android 3.0 Platform - 1 -

あぁ、タブレット買っちゃおうかな?

  2011/02/23

簡単なアプリならあまりハマる事はないと思うんですけど、 オブジェクトが増大するとメモリ管理が本当に大変です。

今作ってるアプリが、100ファイル以上あるので大ハマり! 現状iOSではretainCount方式しかないので自分の脳ではちょっと整理ができません(T_T) あとでいいや!とか半ば諦めていましたが、ついに糸口が見えてきました。

実機とInstrumentsの挙動が違う事がある

シミュレータやInstruentsで一見うまくいっているように見えても、実機で落ちる事があります。 これは環境の速度やタイミングの問題でしょうか? オブジェクトの定義ミス(getterやreleaseなど)を正確に処理してないことが原因が多かったです。

<a href="

実機でメモリ使用量の確認

自分は「iMemoryGraph」ってソフトで各画面で処理して確認しました。

はじめは、

特定の処理をする毎に5M消費

というループにハマって、あっという間に100Mを消費してしまいます。 iOS4はマルチタスク(バックグランド待機)なので、メモリは消費したままで解放できない。 これじゃぁ・・・とずっと悩んで、Instrumentsと睨めっこする日々。

とにかくInstrumentsと睨めっこ

だいたい自分のコーディングの問題なので、自分のアプリ名のタスクを探します。 iPhone で、よく増加するオブジェクトタイプを見つけて、メモリ解放されていないオブジェクトを発見します。

そして、どのセンテンスがどれくらい影響してるか?がおよその見当がつけられます。 iPhone

という事で、ちょっと出口が見えてきました!

  2011/02/21

Android、iPhoneを取りあえず手っ取り早く作りたい為のツール。 Corona、Titanium、Unity 3D

Coronaの私見

Coronaをちょっと触ってみたんだけど、ゲームなどのアニメーション系をサクッと作るのには向いてると思う。 日本コロナの会にも行って来ましたが、Flasherぽい人が多数感じで。 だけど、

Luaを覚えなきゃいけないし、開発環境がイマイチ

Flasherの人には良いかもしれないが、開発環境的にちょっと非効率的な気もする。 またバリバリのゲームだったら、試してはいないけどUnity 3Dの選択肢もあるかも知れない。

とりあえずCoronaもアンテナだけは張っておくとして、今月のWeb+DBでも紹介されていたTitanium Developerを試してみた。

Titaniumをインストール

まずTitanuimとは?

Titanium Mobileで始める iPhone/Androidアプリ開発※注)iWorks資料Titanium Mobileで作る! iPhone/Androidアプリ

まず、iOSとAndroid SDKはインストールしておく必要があります。 次に、AppCeleratorのTitanium MobileからダウンロードしてTitanium Developerをアプリケーションにインストール。 ※iPhoneもあるのでOSX

Titanium Mobileを初回時に起動すると、以下のディレクトリにSDKがインストールされる。

/Library/Application\ Support/Titanium

また、自動的に最新アップデータするか?と聞いてくるのでアップデートする。

次に、AndroidSDKのパスを選択する必要がある(iPhoneは自動認識)。 Android SDK 2.3.1以前では

/Developer/android-sdk-mac_x86/platform-tools/adb

のパスでは起動しないので、

/Developer/android-sdk-mac_x86/tools/

へadbをコピーしてから選択する。

サンプルプロジェクトKitchenSink

とりあえずの動作確認として「KitchenSink」が用意されているのでダウンロード。 Titanium Developerで【Project Import】し、Test&Package>Run EmulatorでLaunchするとエミュレータが起動する。 titanium

titanium

Androidはうまくインストールされない・・・。 titanium どうやら、選択するSDKによって挙動が違うらしい。

Android SDK & AVD Managerを以下の通り更新。 ※Google APIは必須 titanium Titaniumの起動SDKを「APIs 2.3」にしてみると起動した。 titanium

プロジェクトを作成してみる

【New Project】をクリックして新規プロジェクトを作成。 Project type:Mobileすると「iPhone SDK 」「Android SDK」が検知されるので、必要項目を入力して【Create Project】する。 titanium

  2011/02/21

removeFromSuperview でアニメーションそのまま記述すると、 アニメーションが実行される前に、viewが取り除かれてしまう。

そこで、setAnimationDidStopSelector を利用してアニメーション実行後に処理をしてやることで実現できる。

- (IBAction)closeViewPressed:(id)sender {
    [UIView beginAnimations:@"transition2" context:NULL];
    [UIView setAnimationDuration:0.5f];
    sampleView.alpha = 0.85f;
    [UIView setAnimationDidStopSelector:@selector(closeLyricView)];
    lyricsSelectView.alpha = 0;
    [UIView commitAnimations];
}

- (void)closeView {
    [sampleView removeFromSuperview];   
}
pngファイルの軽量化
Google DriveのIconを再起的に削除
php-markdownでバニラPHPなコードブロック処理
laravel-ffmpeg を使う
2021年版 Ubuntu + certbot + Let's Encrypt でサーバ証明書設定
GihHub のデフォルトでない master ブランチを checkout する
マルチログインで未認証のリダイレクト
Homebrew で Redis をインストール
CSS だけでスムーズスクロール
EC-CUBE4 で Gmail の smtp を利用する
Amazon Linux 2 の amazon-linux-extras とは
UNIQUE カラムのバリデーションで自分自身を除外して更新
フォーム有効期限切れで Page Expired をリダイレクト
ログを日付でローテーションやクリアや削除
Homebrew で PHP8.0 から PHP7.4 にダウングレード
Big sur で zsh 移行と Homebrew アップグレード
Mac に minikube をインストール
途中から .gitignore に追加する
Larevel 6.x から Laravel 8.x にバージョンアップ
Composer で Allowed memory size (メモリ不足)エラー
Blade でカスタムクラスを利用する
git push git pull にブランチ指定せずに実行する
git pull や git push できなくなったとき
Docker のコンテナからホストOS に接続
Mac で ローカル IP アドレス(ipv4)のみを表示する
ホストOS から Docker の MySQLコンテナに接続
caching_sha2_password のエラー
node-config で環境設定ファイルを利用する
rootパスワードを初期化(再設定)する
Git から clone したときのエラー対処
Mac に MySQL をインストール
Mac に PostgreSQL をインストール
Laravel 環境構築 - Mac ネイティブ編
Firebase 入門 - Firebase とは
Firebase 入門 - CLI インストールとデータベースの設定
AWS 無料枠(t2.micro)で容量とメモリエラー
Cloud9 を起動する -初心者編-
gcloud で GCEインスタンスを起動してみる
AWS CLI と jq でインスタンス一覧を整形して表示
React と Laravel7 のプロジェクトを作成する
Homebrewインストール-2020年版
3直線で囲まれた範囲塗りつぶし
PuLP で線形最適化問題を解く
カスタムのペジネーションを作る
node-sass を使って sass をコンパイルする
Log ファサードでSQLログを分離して書き出す
いちから始める Docker - 複数のコンテナを使う - (2020年)
いちから始める Docker - docker-compose を使う - (2020年)
AWS ECR を使ってみる
Laravel7 でマルチ認証