うるう年の判定ってこんなんじゃまずいのかな?
( (y % 4 == 0 && y % 100 != 0) || y % 400 == 0 )
久しぶりにRailsなんて触っちゃってみる
sudo gem install rails
こんなエラーが出るかもしれない。
Installing ri documentation for rails-3.1.3... File not found: lib
Rdocを新しいやついれれば解決。
sudo gem install rdoc
プロジェクト作成&サーバー起動!
rails new hogehoge
cd hogehoge
rails server
こんなエラーが出るかもしれない。
/usr/local/lib/ruby/gems/1.9.1/gems/execjs-1.2.9/lib/execjs/runtimes.rb:47:in `autodetect': Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable) from /usr/local/lib/ruby/gems/1.9.1/gems/execjs-1.2.9/lib/execjs.rb:5:in `<module:ExecJS>' from /usr/local/lib/ruby/gems/1.9.1/gems/execjs-1.2.9/lib/execjs.rb:4:in `<top (required)>' from /usr/local/lib/ruby/gems/1.9.1/gems/coffee-script-2.2.0/lib/coffee_script.rb:1:in `require' from /usr/local/lib/ruby/gems/1.9.1/gems/coffee-script-2.2.0/lib/coffee_script.rb:1:in `<top (required)>' from /usr/local/lib/ruby/gems/1.9.1/gems/coffee-script-2.2.0/lib/coffee-script.rb:1:in `require' from /usr/local/lib/ruby/gems/1.9.1/gems/coffee-script-2.2.0/lib/coffee-script.rb:1:in `<top (required)>' from /usr/local/lib/ruby/gems/1.9.1/gems/coffee-rails-3.1.1/lib/coffee-rails.rb:1:in `require' from /usr/local/lib/ruby/gems/1.9.1/gems/coffee-rails-3.1.1/lib/coffee-rails.rb:1:in `<top (required)>' from /usr/local/lib/ruby/gems/1.9.1/gems/bundler-1.0.21/lib/bundler/runtime.rb:68:in `require' from /usr/local/lib/ruby/gems/1.9.1/gems/bundler-1.0.21/lib/bundler/runtime.rb:68:in `block (2 levels) in require' from /usr/local/lib/ruby/gems/1.9.1/gems/bundler-1.0.21/lib/bundler/runtime.rb:66:in `each' from /usr/local/lib/ruby/gems/1.9.1/gems/bundler-1.0.21/lib/bundler/runtime.rb:66:in `block in require' from /usr/local/lib/ruby/gems/1.9.1/gems/bundler-1.0.21/lib/bundler/runtime.rb:55:in `each' from /usr/local/lib/ruby/gems/1.9.1/gems/bundler-1.0.21/lib/bundler/runtime.rb:55:in `require' from /usr/local/lib/ruby/gems/1.9.1/gems/bundler-1.0.21/lib/bundler.rb:122:in `require' from /hogehoge/config/application.rb:7:in `<top (required)>' from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.1.3/lib/rails/commands.rb:52:in `require' from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.1.3/lib/rails/commands.rb:52:in `block in <top (required)>' from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.1.3/lib/rails/commands.rb:49:in `tap' from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.1.3/lib/rails/commands.rb:49:in `<top (required)>' from script/rails:6:in `require' from script/rails:6:in `<main>'
https://github.com/sstephenson/execjs
のリストにあるJSエンジン入れろと言っているので
sudo apt-get install nodejs
再度実行!
rails server => Booting WEBrick => Rails 3.1.3 application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server [2011-11-29 22:04:02] INFO WEBrick 1.3.1 [2011-11-29 22:04:02] INFO ruby 1.9.3 (2011-10-30) [i686-linux] [2011-11-29 22:04:02] INFO WEBrick::HTTPServer#start: pid=23096 port=3000
でとりあえず起動できたよ。
ProgressBarに画像を使う
ProgressBarってバージョンごとに色が違ったり色合いがダサかったりでヤダ!
ってことで独自で定義するやり方。
背景画像(progress_back.png)
メーター画像(progress_front.png)
どちらも幅1pxの画像。
プログレスバーのdrawable
custom_progress.xml
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background" android:drawable="@drawable/progress_back"/> <item android:id="@android:id/progress"> <clip android:drawable="@drawable/progress_front"/> </item> </layer-list>
レイアウト
main.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#AAAAAAAA"> <ProgressBar android:id="@+id/progress_bar" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_gravity="center" android:progressDrawable="@drawable/custom_progress" style="?android:attr/progressBarStyleHorizontal" /> </FrameLayout>
アクティビティ
MainActivity.java
package com.sample; import android.app.Activity; import android.os.Bundle; import android.widget.ProgressBar; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar); progressBar.setMax(100); progressBar.setProgress(50); } }
ただし、現行のADT 10.0.1では、これ(main.xml)をグラフィカルエディタで表示した場合に、
java.lang.UnsupportedOperationException Exception details are logged in Window > Show View > Error Log
となり、
android:progressDrawable="@drawable/custom_progress"
の部分でエラーが起きてエディタが使えない。
なので、プログラム上で
progressBar.setProgressDrawable(getResources().getDrawable(R.drawable.custom_progress));
って設定するほうが良いかも。
タブレット時代のレイアウト FragmentLayout
Androidは解像度の異なる端末がいろいろ出てきて悩みどころ。
タブレット型に関してはFragments API使えばiPadっぽいレイアウトが組めるらしいけど現状3.0のみだし。
iPadとiPhoneのようにマーケットが分かれていれば片方のみ対応とかできるけどAndroidはそうもいかないし...
とか、いろいろ困ってる人たちに朗報が!
Google、Android 3.0の「Fragments API」で非互換性問題を解消へ
http://japan.internet.com/allnet/20110309/8.html
Android Developers Blog「Fragments For ALL」
http://android-developers.blogspot.com/2011/03/fragments-for-all.html
なんとFragments APIが1.6までの後方互換が効くようになった。
ってことでやってみよう。
まず、「Android SDK and AVD Manager」の「Available Packages」に
Android Compatibility package
が増えているのでインストール。
そうするとSDKのディレクトリにandroid-compatibilityフォルダができていて、
その下にv4/android-support-v4.jarがあるのでライブラリに追加する。
これがFragmentsの後方互換ライブラリ。
ではアプリ作成。
作るのはリストのアイテムを選択すると詳細画面が表示される、というシンプルなもの。
レイアウトファイルの作成
まず、縦向きと横向き用のレイアウトを用意
縦:layout/fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <fragment class="com.sample.MainActivity$TitlesFragment" android:id="@+id/titles" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </FrameLayout>
横:layout-land/fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <fragment class="com.sample.MainActivity$TitlesFragment" android:id="@+id/titles" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> <FrameLayout android:id="@+id/details" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> </LinearLayout>
詳細画面のレイアウト:layout/details.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/detail_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp"/> </FrameLayout>
※リスト画面のレイアウトはプログラム内で作ってしまうのでいらない
アクティビティの作成
これがなかなか面倒くさい。ApiDemosにあるソースを拝借して必要な部分のみ記述した。
package com.sample; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.ListFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; public class MainActivity extends FragmentActivity { private static final String[] ITEMS = { "A", "B", "C" }; private static final String[] DETAILS = { "ラテン文字(アルファベット)の1番目の文字。小文字は a 。ギリシャ文字のΑ(アルファ)に由来し、キリル文字のАに相当する。", "ラテン文字(アルファベット)の2番目の文字。ギリシャ文字のΒ(ベータ)に由来する。小文字は b 。キリル文字のБ、Вと同系である。", "C は、ラテン文字(アルファベット)の3番目の文字。小文字は c 。" }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_layout); } /** * ポートレイト(縦向き)のアクティビティ */ public static class DetailsActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { // ランドスケープ(横向き)モードの場合、このアクティビティはいらない finish(); return; } if (savedInstanceState == null) { // フラグメント(右ペイン)を作成 DetailsFragment details = new DetailsFragment(); details.setArguments(getIntent().getExtras()); getSupportFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); } } } /** * 左のペイン */ public static class TitlesFragment extends ListFragment { boolean mDualPane; int mCurCheckPosition = 0; int mShownCheckPosition = -1; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // リストの作成 setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_checked, ITEMS)); // デュアルモードかどうかを確認 View detailsFrame = getActivity().findViewById(R.id.details); mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; if (savedInstanceState != null) { // 前回の選択位置を取得 mCurCheckPosition = savedInstanceState.getInt("curChoice", 0); mShownCheckPosition = savedInstanceState.getInt("shownChoice", -1); } if (mDualPane) { getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); showDetails(mCurCheckPosition); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("curChoice", mCurCheckPosition); outState.putInt("shownChoice", mShownCheckPosition); } @Override public void onListItemClick(ListView l, View v, int position, long id) { showDetails(position); } void showDetails(int index) { mCurCheckPosition = index; if (mDualPane) { // デュアルモードの場合は選択されているアイテムをハイライト getListView().setItemChecked(index, true); if (mShownCheckPosition != mCurCheckPosition) { // 別のアイテムが選択されたら対応する画面を作成 DetailsFragment df = DetailsFragment.newInstance(index); // トランザクションを開始して既存のフラグメントと入れ替える FragmentTransaction ft = getFragmentManager() .beginTransaction(); ft.replace(R.id.details, df); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.commit(); mShownCheckPosition = index; } } else { // デュアルモードではない場合、詳細画面を起動 Intent intent = new Intent(); intent.setClass(getActivity(), DetailsActivity.class); intent.putExtra("index", index); startActivity(intent); } } } /** * 右のペイン */ public static class DetailsFragment extends Fragment { public static DetailsFragment newInstance(int index) { DetailsFragment f = new DetailsFragment(); // リストに対応するインデックスを仕込む Bundle args = new Bundle(); args.putInt("index", index); f.setArguments(args); return f; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container == null) { // ポートレイトでリスト画面の場合は詳細画面を作らない return null; } View view = inflater.inflate(R.layout.details, null); TextView textView = (TextView) view.findViewById(R.id.detail_text); textView.setText(DETAILS[getArguments().getInt("index", 0)]); return view; } } }
ExpandableListViewっぽいものを独自で実装してみる
ExpandableListViewの使い方覚えるが面倒で、更にカスタマイズしてみたけど開閉ボタンの消し方が分からなかったので、結局それっぽものを自作してみた。
※drawableのxmlは省略
ヘッダーのレイアウト
list_header.xml
<?xml version="1.0" encoding="utf-8"?> <com.sample.RowHeader xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/header"> <TextView android:id="@+id/header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textColor="#FFFFFFFF" android:textSize="20sp"/> </com.sample.RowHeader>
列のレイアウト
list_row.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/row"> <TextView android:id="@+id/row" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textColor="#FF000000" android:textSize="15sp"/> </LinearLayout>
メイン
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/virtual_listview" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > </LinearLayout>
ヘッダークラス
RowHeader.java
package com.sample; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; public class RowHeader extends LinearLayout { public boolean mIsExpanded; public List<View> mRows; public RowHeader(Context context, AttributeSet attr) { super(context, attr); mIsExpanded = false; mRows = new ArrayList<View>(); } public boolean isExpanded() { return mIsExpanded; } public void expand() { mIsExpanded = true; for (final View v : mRows) { v.setVisibility(View.VISIBLE); } } public void collapse() { mIsExpanded = false; for (final View v : mRows) { v.setVisibility(View.GONE); } } public List<View> getRows() { return mRows; } public int getRowCount() { return mRows.size(); } public void addRow(View row) { mRows.add(row); } }
メイン
package com.sample; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.LinearLayout; import android.widget.TextView; public class MainActivity extends Activity { private Map<String, List<String>> map; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); map = new HashMap<String, List<String>>() { { put("哺乳類", new ArrayList<String>() {{ add("イヌ"); add("ネコ"); add("イルカ"); }} ); } { put("鳥類", new ArrayList<String>() {{ add("スズメ"); add("ペンギン"); }} ); } { put("魚類", new ArrayList<String>() {{ add("ブリ"); }} ); } }; // 擬似リストビュー final LinearLayout listView = (LinearLayout) findViewById(R.id.virtual_listview); for (final Entry<String, List<String>> set : map.entrySet()) { // ヘッダーのレイアウトを取得してテキストを設定 RowHeader headerLayout = (RowHeader) View.inflate(this, R.layout.list_header, null); TextView headerText = (TextView) headerLayout.findViewById(R.id.header); headerText.setText(set.getKey()); // ヘッダークリックイベント headerLayout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { RowHeader headerView = (RowHeader) v; // ヘッダーが親から見て何番目にあるかを取得 int index = listView.indexOfChild(headerView); if (headerView.getRowCount() == 0) { // 0の場合は子どもがまだ生成されていない List<String> rows = set.getValue(); for (String row : rows) { LinearLayout rowLayout = (LinearLayout) View.inflate(MainActivity.this, R.layout.list_row, null); TextView rowText = (TextView) rowLayout.findViewById(R.id.row); rowText.setText(row); // ヘッダーの次のインデックスにどんどん追加 listView.addView(rowLayout, ++index); headerView.addRow(rowLayout); } } if (headerView.isExpanded()) { headerView.collapse(); // 閉じる } else { headerView.expand(); // 開く } } }); listView.addView(headerLayout); } } }
アニメーションがうまくつかねー
>>2011/07/25 追記
expandableListView.setGroupIndicator(null)
でインジケーター消せた<<2011/07/25
node.jsに触れてみた
node.js公式
インストール
wget http://nodejs.org/dist/node-v0.4.1.tar.gz tar zxvf node-v0.4.1.tar.gz cd node-v0.4.1 ./configure make make install
Hello node.js!!
公式にあるサンプルを実行してみる。
var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(8124, "127.0.0.1"); console.log('Server running at http://127.0.0.1:8124/');
サーバーにブラウザでアクセスすると「Hello World」が表示された!
なんだかオラわくわくしてきたぞ!
仕事で使うことはないけどな!!
初心者に厳しいAndroid開発環境構築
Androidネタがたまってきたので、まず開発環境構築から。
よくWindowsで…とかMacで…とかってあるけど、基本的にそんなの関係ねぇ、ということを認識して欲しいです。
eclipseのダウンロード
Eclipse公式(英語)
http://www.eclipse.org/downloads/
*1
デフォルトで日本語がいい!
MergeDoc
http://mergedoc.sourceforge.jp/
*2
Androidプラットフォームのインストールとエミュレータ起動
%SDK_HOME%tools/android を実行してAndroid SDK and AVD Managerを起動。
[Available packages]を選択し[Packages available for download]の[Android Repository]を展開。
・Android SDK Tools
・SDK Platform Android 3.0
にチェックをいれてインストール。(結構時間がかかる)
完了したら[Virtual devices]を選択し[New]。
NameとTarget(Android3.0)を設定して[Create AVD]。
作成したAVDを選択して[Start]で[Launch Options]が開くので[Scale display to real size]にチェックを入れて
Screen Size: 68
Monitor dip: 160 *3
で[Launch]。
*4
なぜかMOTOROLA XOOMが立ち上がります。(これまた結構時間がかかる)
MOTOROLA XOOM公式
http://www.motorola.com/Consumers/US-EN/Consumer-Product-and-Services/Tablets/ci.MOTOROLA-XOOM-US-EN.overview
>>2011/07/25 追記
XOOMじゃなくてこれがAndroid3.0のUIだったのね。。。<<2011/07/25
KDDI公式
http://www.kddi.com/corporate/news_release/2011/0228a/index.html
Android ADT Pluginのインストール
Name: Android ADT Plugin (任意)
Location: https://dl-ssl.google.com/android/eclipse/
Developer Toolが出てくるのでチェックしてすべてインストール・再起動。
eclipseにAndroid SDKのパスを設定
設定画面に[Android]の項目が増えてるので選択。
※ここでレポートを送信するみたいなダイアログが出るので消す。これ重要。
SDK LocationにAndroid SDKへのパスを設定しApply。
一覧にプラットフォーム名等ずらっとでれば設定完了。
Androidプロジェクトの作成
eclipseツールバーのこれクリック(たぶんデフォルトは左から5つ目)
Contents: Create project from existing sample
Build Target: Android 3.0
を選択して[finish]。
※最初はSamplesのプルダウンをApiDemosがおすすめ
生成されたプロジェクトを選択して[Run As]→[Android Application]でアプリケーションのインストールが開始・起動。
※コンソールにこんなエラーが出ちゃった場合
You must perform a full uninstall of the application. WARNING: This will remove the application data!
Please execute 'adb uninstall com.example.android.apis' in a shell.
%SDK_HOME%/platform-toolsにadbってのがあるので、書いてあるとおりに
adb uninstall com.example.android.apis
を実行し、再度Run As Android Applicationしましょう。