Androidプログラムの基本から
by K.I
2014/09/19〜
Index
- Androidのプログラミングは、他のプログラム環境と較べて、
- 開発環境がGUIになってるので、その操作方法にばかり目が行ってしまい、
- 肝心のプログラムの構成が分かり辛く、なかなかイメージが湧かないためだと思う。
- まぁAndroidのプログラムについては、基本からよく分かっていなかったので、
- Eclipseの操作とか、そこらへんは出来るだけ省略して、
[top]
- Androidのプログラムは、どのように動いているのか、考えてみる。
- Cのコンソールプログラムなら、起動すると、main関数が実行される
- Windowsのプログラムならば、WinMain関数が最初に実行される
- では、Androidでは何が最初に実行されるんだろう?
- 最初に、EclipseでAndroidアプリケーションのProjectを作っておく
- 但し、Activityは作成しないでおくので、srcフォルダは空の状態から始める
- Androidでは、Activityという単位でプログラムが実行されるらしい
- Activityは、1つの画面に対応している様なので、Windowのようにも思えるが、
- 実行中のメインループを指しているのかもしれない
- とりあえず、1つだけのActivityを持つ javaプログラムを用意して、srcフォルダに入れてやる1
- そもそも、Activityを用意するだけでは、プログラムは実行されないようだ。
- では、最初に実装されるActivityは、どうやって決まるんだろう?
- 使用するActivityは、AndroidManifest.xmlで定義しておかなければならない。
- 特に、最初に実行されるActivityには、intent-filterの記述が必要3になる
- これは、アプリケーション実行のIntent(ACTION_MAIN)を受け付ける
- さらに、ランチャーで登録可能なアプリケーションであることを示しているらしい
- ちなみにintent-filterに、category.DEFAULTを指定しないと、暗黙的IntentにActivityが応答しないらしい。
参考: IntentFilterにDEFAULT_CATEGORYが必要な理由 →よく理解していないが、とりあえずメモ
- ここまでで、メインのActivityのjavaファイルを用意して、
- AndroidManifest.xmlに、Activityの定義をしたので、Androidでちゃんと起動するプログラムになる。
- でも、何もないActivityが表示されるだけのアプリケーションなので、
- とりあえず、Toastの記述を追加して、"Hello Android!"と表示されるようにしてみる。
package hm.orz.bluefish.helloandroid;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
public class HelloAndroid extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(this, "Hello Android!", Toast.LENGTH_SHORT).show();
}
}
- たぶん、これが最小限のAndroidアプリケーションじゃないかと思う。
- Activityの画面レイアウトは、res/layoutフォルダに入れる
- Eclipseでプロジェクトを作成する際に、Activityを作っていなかったので、layoutフォルダは空になっている
- でも、デフォルトのアプリケーションアイコンは、res/drawableフォルダに入っている
- また、アプリケーション名は、res/values/strings.xmlで、定義されている
- Androidでは、表示データはres/valueフォルダに入れることになっているらしい
- 文字列のリソースは、string.xmlに定義するようだ
- 前項で作成したアプリケーションは、なにもレイアウトの指定をしていないが、
- 何もないActivityの上の方に、アイコンと、アプリケーション名は表示されている。
- これが、デフォルトのLayoutなのかな
- 或いは、デフォルトのActivityってことなのかな?
- このデフォルトのレイアウト?の状態を変えることも出来るのかもしれない
Intent intent=new Intent(this,subActivity.class);
startActivity(intent);
Intent intent = new Intent();
intent.setClassName(getPackageName(),GDSlayer.class.getCanonicalName());
startActivity(intent);
- 転移先から戻るには、やはりIntentを使うのかと思ったら、
- なんと、これだけで良いらしい。
finish();
- Activityは、デフォルトでは順にスタックに積まれて行くので、
- 一つ前に戻るだけなら、Activityを終了させるだけで、Backボタンを押した時も同じ動作になる
- intent.setFlagsで、ここら辺は、いろいろ設定できるみたいだが、
- Activityの遷移で、引数を渡す
- Bundleで、データを束ねて送るらしい
final Intent intent = new Intent(this,subActivity.class);
intent.putExtra("str","ABC");
intent.putExtra("num",123);
startActivity(intent);
- 遷移先のActivityで、以下の様に引数を受け取ることが出来る
public class subActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
String str = getIntent().getStringExtra("str");
int num = getIntent().getIntExtra("num", -1);
}
}
- キーが無い時は、getStringExtraはnull、getIntExtraは第2引数の値を返す
- startActivityではなく、startActivityForResultで起動、
- 起動先で、setResultでIntentを返して、
- onActivityResultで受け取るということらしい
- 。。なんか、ちょっと面倒だ。
- でも、値を戻す方法は別として、受け取りはイベントでやるのは良いかもしれない。
1この例では、hm.orz.bluefish.helloandroidというパッケージを作って、その中に入れてやる必要がある。
2packageの記述は、パッケージを作ると自動的に追加されるようだ。
3Activityの名前は、大文字小文字含めて正しく指定しないと実行時に落ちる。
[top]
- Activityの上に、いろいろ表示するために、Layoutリソースを使う
- Layoutのリソースを用意して、表示してみる
- src/layoutフォルダに、main.xmlファイルを追加
- 試しに、簡単なレイアウトを作る
- main.xmlをダブルクリックして、グラフィカル・レイアウトでPaletteから、
- レイアウト→LinerLayout(Vertical)を配置
- フォーム・ウィジェット→LargeTextを配置
- テキスト・フィールド→プレーン・テキストを配置
- フォーム・ウィジェット→ボタンを配置
- 最初の行の、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="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="@+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" >
////green
<requestFocus />
</EditText>
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
- 下にあるタブで、main.xmlのテキストファイルを見てみると、
- ファイル→全て保存とすると、エラーが消えた。何故?4
- エラーじゃないけど、Infoのアイコンがいくつか表示されている
- Layoutリソースを定義して、gen/hm.orz.bluefish.helloandroid/R.javaを見ると、
- main.xmlに定義した内容が、Rクラスにも定義されているのが分かる
- R.javaは、自動生成されるので、これを直接編集する必要はない
- Activityに、Layoutをセットするには、
- 実行してみると、layout/main.xmlで定義したレイアウトが表示される
- でも、この状態ではLayoutを表示しているだけで、機能を記述していないので、ボタンを押しても何も反応しない。
- Layout定義で表示されていた、infoというよりWarningかな。
- これは、Textはリソースを使いなさいということか
[I18N] Hardcoded string "****", should use @string resource
- Androidでは、res/valuesで定義するのが流儀ということなんだろう。
- これは、inputTypeとhintが指定されていないと言っているのか?
- main.xmlの、EditTextに以下を追加しておく
- android:hint="EditTextに表示される文字列"
- android:inputTyte="text" //文字列入力の場合、textを指定
- Androidでは、文字列は基本的にstringリソースで定義するのが作法らしい
- res/values/string.xmlに以下を追加
- res/layout/main.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="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/textview_text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="@+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/edittext_hint"
android:inputType="text" >
<requestFocus />
</EditText>
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button_text" />
</LinearLayout>
- TextViewのラベル文字列の設定
- EcitTextのhint文字列と、inputTypeの設定
- Buttonの文字列の設定
- 定義したリソースは、Rクラスでは以下の様に参照する
- 別のリソースファイルからは、以下の様に参照する
- XMLファイルのlayout定義で、以下のような記述がある
android:id="@+id/textView1"
- これは、新規のIDを生成して、Rクラスに追加するという意味らしい
@id/textView1
- Layoutだけ定義しても、何も動かない。
- GUIの操作に対して、どのように動作するかを記述しなければならない。
- Androidではイベントに対する処理を、イベントリスナーと呼ばれる割り込み処理のようなもので登録する
- setOnClickListenerで、ボタンクリック時の処理を定義する
package hm.orz.bluefish.helloandroid;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
import android.text.Editable;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
public class HelloAndroid extends Activity {
private EditText edit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(this, "Hello Android!", Toast.LENGTH_SHORT).show();
setContentView(R.layout.main);
edit = (EditText)this.findViewById(R.id.editText1);
findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Editable name = edit.getText();
Toast.makeText(HelloAndroid.this, "こんにちは! "+ name +"さん", Toast.LENGTH_LONG).show();
}
});
}
}
- ボタンが押された時、Toastで挨拶を表示するようにした
→ボタンを押すと、しばらくの間、挨拶が表示される
4Eclipseは、よく分からないことが多い。。
[top]
- 最初に何をやるか考えたけど、Androidのインターフェースとして、よく使われる
- リスト表示して選択させる ListViewをやってみようと思う
- まず、セルというか、1行分のレイアウトをXMLで定義する。
- とりあえずActivityには、ListViewだけを配置したものを用意した(res/layout/main.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="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
- ArrayAdapterに、ActivityのContext、1行分のリソースとTextViewのID、配列データを渡して
- それを、ListViewにセットする
package hm.orz.bluefish.hellolistview;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class HelloListView extends Activity {
private ListView list;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String[] ldata = {"AAA","BBB","CCC","DDD","EEE","FFF"};
list = (ListView)this.findViewById(R.id.listView1);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list, R.id.list_textView1, ldata);
list.setAdapter(adapter);
}
}
- これで、ListViewにデータが表示される
- ListViewのアイテムをクリックした時のイベントリスナ
- OnItemClickListenerを実装する
package hm.orz.bluefish.hellolistview;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class HelloListView extends Activity {
private ListView list;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String[] ldata = {"AAA","BBB","CCC","DDD","EEE","FFF"};
list = (ListView)this.findViewById(R.id.listView1);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list, R.id.list_textView1, ldata);
list.setAdapter(adapter);
list.setSelection(1);
list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?>adapter, View view, int pos, long id) {
Toast.makeText(HelloListView.this, list.getAdapter().getItem(pos) +" が選択されました", Toast.LENGTH_LONG).show();
}
});
}
}
- ListViewをクリックすると、選択内容をToastで表示する
5wrap_contentだと、一度仮想的に描画してサイズを確認したあと、改めて描画するというような操作をしているらしい。
[top]
- ListViewのいろんな設定は、基本的にXMLでやるみたいなんだけど、
- 選択状態の表示とか、いろいろやろうとすると、カスタマイズする方が簡単に思えてきた。
- ようするに、左側からメニューがスルスルと出てくるように表示することができる
- Adapterというのは、ListViewにデータを割り当てるためのプログラムらしい(src/CustomAdapter.java)
- これをカスタマイズする
package hm.orz.bluefish.sidesliderlistview;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Color;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ArrayAdapter;
import android.widget.TextView;
public class CustomAdapter extends ArrayAdapter<String>{
static class ViewHolder {
TextView labelText;
}
private LayoutInflater inflater;
// コンストラクタ
public CustomAdapter(Context context,int textViewResourceId, ArrayList<String> labelList) {
super(context,textViewResourceId, labelList);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
View view = convertView;
// Viewを再利用している場合は新たにViewを作らない
if (view == null) {
inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.list, null);
TextView label = (TextView)view.findViewById(R.id.list_textView1);
holder = new ViewHolder();
holder.labelText = label;
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
// 特定の行のデータを取得
String str = getItem(position);
if (!TextUtils.isEmpty(str)) {
// テキストビューにラベルをセット
holder.labelText.setText(str);
}
// 行毎に背景色を変える
if(position%2==0){
holder.labelText.setBackgroundColor(Color.parseColor("#aa0000"));
}else{
holder.labelText.setBackgroundColor(Color.parseColor("#880000"));
}
// XMLで定義したアニメーションを読み込む
Animation anim = AnimationUtils.loadAnimation(getContext(), R.anim.list_motion);
// リストアイテムのアニメーションを開始
view.startAnimation(anim);
return view;
}
}
- Adapterで、肝心となる処理がgetViewで、
- データを「取ってきて描く」という感じのルーチンだと思う
- 特に、気をつけなければならないのが、引数のconvertView
- これがnullの時だけViewを生成すること
- nullじゃ無いときは、既に生成されたViewがあるので、データの入れ替えだけ行うということらしい。
- inflaterっていうのは、「膨らませる」というような意味みたいだが、
- まぁ、XMLから実際のViewを作るというようなものらしい
- holderっていうのは、生成したViewのデータを保持するキャッシュのようなもの?なのかな
- ここらへんは、決まり文句みたいなものらしいけど、ちょっと面倒7だなぁ。
- あとは、「行毎に背景色を変える」で、奇数行、偶数行で背景色を変えている
- これは、リストViewの表示方法をカスタマイズする参考になるだろう。
- さらに、「XMLで定義したアニメーションを読み込む」で、XMLで定義したアニメーションを実行している
- これだけでスライドの動きを出せるのは、簡単でいいなぁ
- あとは、標準のAdapterの代わりに、カスタムAdapterを使うようにするだけ(src/SideSliderListView.java)
package hm.orz.bluefish.sidesliderlistview;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
public class SideSliderListView extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// ListViewのインスタンスを取得
ListView list = (ListView)findViewById(R.id.listView1);
// リストアイテムのラベルを格納するArrayListをインスタンス化
final ArrayList<String> labelList = new ArrayList<String>();
// "List Item + ??"を20個リストに追加
for(int i=1; i<=20; i++){
labelList.add("List Item "+i);
}
CustomAdapter mAdapter = new CustomAdapter(this, R.layout.list, labelList);
list.setAdapter(mAdapter);
list.setSelection(1);
list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?>adapter, View view, int pos, long id) {
Toast.makeText(SideSliderListView.this, labelList.get(pos) +" が選択されました", Toast.LENGTH_LONG).show();
}
});
}
}
- このカスタムViewは、とても動きがとても面白いので、本当におススメです。 →元記事
- まぁ、説明がわかりやすいというのが、一番良いところなんだけれども。
6ListViewのカスタマイズということだけなら、このファイルは特に要らないけど。
7なんかシステムでやっても良い様なことまで、やらされてるんじゃないかなって気がする。
[top]
- LinearLayoutを継承して、CustomLayoutクラスを作る(src/CustomLayout.java)
- Itemとして、icon(ImageView)と、title(TextView)を持つようにする
package hm.orz.bluefish.sidesliderlistview;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class CustomLayout extends LinearLayout {
TextView mTitleView;
ImageView mIconView;
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTitleView = (TextView) findViewById(R.id.list_textView1);
mIconView = (ImageView) findViewById(R.id.imageView1);
}
public void bindView(Item item) {
mTitleView.setText(item.title);
mIconView.setImageResource(item.icon);
}
}
- onFinishInflateで、findViewByIdするのが肝心のようだ
- Handlerは、findViewByIdの呼び出し回数を減らす(生成済みなら使いまわす)ためなので、同様の効果があるらしい
- Itemクラスの定義(src/Item.java)
package hm.orz.bluefish.sidesliderlistview;
public class Item {
public String title;
public int icon;
}
- CustomLayoutクラスを使って、XMLでレイアウトを定義する(res/layout/list.xml)
- Holderを使わないと、Adapterの処理はかなりすっきりする(src/CustomAdapter.java)
package hm.orz.bluefish.sidesliderlistview;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ArrayAdapter;
public class CustomAdapter extends ArrayAdapter<Item>{
private LayoutInflater inflater;
private int mItemLayoutResource;
// コンストラクタ
public CustomAdapter(Context context,int resource, ArrayList<Item> object) {
super(context,resource, object);
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mItemLayoutResource = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final CustomLayout view;
// Viewを再利用している場合は新たにViewを作らない
if (convertView == null) {
view = (CustomLayout)inflater.inflate(mItemLayoutResource, null);
} else {
view = (CustomLayout)convertView;
}
view.bindView(getItem(position));
// 行毎に背景色を変える
if(position%2==0){
view.mTitleView.setBackgroundColor(Color.parseColor("#aa0000"));
}else{
view.mTitleView.setBackgroundColor(Color.parseColor("#880000"));
}
// XMLで定義したアニメーションを読み込む
Animation anim = AnimationUtils.loadAnimation(getContext(), R.anim.list_motion);
// リストアイテムのアニメーションを開始
view.startAnimation(anim);
return view;
}
}
- メインは基本的に変わらないが、ArrayListをItem単位に変更(src/SideSliderListView.java)
package hm.orz.bluefish.sidesliderlistview;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
public class SideSliderListView extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// ListViewのインスタンスを取得
ListView list = (ListView)findViewById(R.id.listView1);
// リストアイテムを格納するArrayListをインスタンス化
final ArrayList<Item> labelList = new ArrayList<Item>();
// Itemを20個リストに追加
for(int i=1; i<=20; i++){
Item item = new Item();
item.title = "List Item "+i;
item.icon = R.drawable.ic_launcher;
labelList.add(item);
}
CustomAdapter mAdapter = new CustomAdapter(this, R.layout.list, labelList);
list.setAdapter(mAdapter);
list.setSelection(1);
list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?>adapter, View view, int pos, long id) {
Toast.makeText(SideSliderListView.this, labelList.get(pos) +" が選択されました", Toast.LENGTH_LONG).show();
}
});
}
}
- Layoutのカスタマイズは、ちょっと面倒だけど、
- Holderの動作が、いまいち分かり辛かったので、むしろこの方が分かり易いかもしれない。
[top]
- ListViewをスクロールして、範囲外になった時、
- ミョーンと伸びてまた戻るという動作を、OverScrollというらしい
- これの実装の仕方が紹介されていたので、カスタムListViewを作ってみた。
- ListViewのカスタマイズ例として、ほとんどそのままやってみました。
- OverScroll用に、ListViewをカスタマイズする(src/CustomListView.java)
- といっても、元々その機能は持っているので、overScrollByメソッドの、maxOverScrollYを設定するだけで良い。
package hm.orz.bluefish.overscrolllistview;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;
public class CustomListView extends ListView {
private int maxOverScrollY = 0;
public CustomListView(Context context) {
super(context);
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.maxOverScrollY = attrs.getAttributeIntValue(null, "maxOverScrollY", 0); }
public CustomListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.maxOverScrollY = attrs.getAttributeIntValue(null, "maxOverScrollY", 0); }
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, this.maxOverScrollY, isTouchEvent);
}
}
- コンストラクタで使っている、attrs.getAttributeIntValueで、XMLの設定を読むことが出来るようだ。
- カスタマイズしたViewのパラメータ設定に、いろいろ使えそうな機能だ
- そして、カスタマイズしたListViewを使って、OverScroll設定するLayoutファイルを作成する(res/layout/main.xml)
- 1行分のリストは、前に使ったものの使いまわし(res/layout/list.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="fill_parent" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/list_textView1"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical|center_horizontal"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
- あとは普通に、ListViewを呼び出すだけ(OverScrollListView.java)
- 特に通常のListViewの呼び出しと変わりなし
package hm.orz.bluefish.overscrolllistview;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
public class OverScrollListView extends ListActivity {
String[] ldata = {"AAA","BBB","CCC","DDD","EEE","FFF","GGG","HHH"};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list, R.id.list_textView1, ldata);
setListAdapter(adapter);
}
}
- OverScrollは、たいしたことは無い機能だが、iPhoneでは標準的な動作であり、
- ListViewが全く動かないより、レスポンスがあった方が、やっぱりしっくりくる。
[top]
- Androidは、Viewと呼ばれる部品を配置して、画面を構成している
- TextViewや、ボタンは、それぞれViewになっている。
- AppleのiOSでは、Viewを階層的に作れたと思うけど、AndroidのViewは直接階層を持てないようだ。
- その代わりに、View GroupでViewを配置する
- View GroupもViewだが、これは階層を持てるので、複雑な構造を定義できる
- ActivityにLayoutをセットするときに、setContentViewで、リソースファイルのIDを指定したが、
- ちなみにViewの引数は Contextで、
- Contextというのは、アプリケーション環境の情報へアクセスするためのインターフェースらしい
- 余談になるが、thisは この場合はActivityのことだが、ActivityはContextクラスを継承したものなので、ActivityのContextを指す
- ApplicationのContextは、getApplicationContextで取得できるが、この場合はそれも使える
- ActivityのContextを使うか、ApplicationのContextを使うべきかは、場合によって違うらしい
- 動いていたとしても、メモリリークの原因になることもあるらしい
- 自分は、まだよく分かっていないので、現状は動けば良いという事で保留としておく。
- 以降、Application Contextを使った例が多いが、間違っているかもしれない。
- Viewクラスを継承した、DrawViewというクラスを作成する(src/HelloGraphics.java)
- onDrawイベントで、描画を行う
package hm.orz.bluefish.hellographics;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.View;
public class HelloGraphics extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DrawView view = new DrawView(getApplication());
setContentView(view);
}
class DrawView extends View {
public DrawView(Context context) {
super(context);
}
public void onDraw(Canvas c) {
c.drawColor(Color.BLACK);
Paint fill_paint = new Paint();
fill_paint.setStyle(Paint.Style.FILL);
fill_paint.setColor(Color.BLUE);
c.drawRect(500f, 500f, 1000f, 1000f, fill_paint);
fill_paint.setColor(Color.RED);
c.drawOval(new RectF(0f, 0f, 500f, 500f), fill_paint);
}
}
}
- これで、とりあえず Activityに図形を描画することが出来た
- でも実際、アプリケーションを作るとしたら、Viewが1つだけというのは、ちょっと困る
- View Groupを使って、Viewを並べることになると思う。
- しかし例えば、TextViewと、DrawViewと、Buttonを並べて、
- DrawViewに描画するのは、どうすれば良いんだろう?
- ActivityにViewGroupを、プログラムで配置する例はいろいろ見つけられたんだけど、
- その配置したViewに、描画する例を見つけることが出来なかった。
- それに部品の配置は、xmlファイルでやった方が楽なので、
- プログラムで配置することは、そもそもあまり考えていない。
- 例えば、Windowsのプログラムであれば、
- Dialogリソースに、Textコントロール、Pictureコントロール、Buttonを配置して、
- Windowに、Dialogリソースを貼り付けて、Dialogリソース用のWindowProcedureを定義するという感じになると思う
- Androidでも、同じようにやりたいんだけどなぁ。
- Viewクラスを継承して作成した描画用のViewを、XMLで配置することが出来れば良いんじゃないか?
- それを基に、カスタムのViewクラスを作成してみる(src/CustomView.java)
package hm.orz.bluefish.hellographics;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
public class CustomView extends View {
public CustomView(Context cont) {
super(cont);
}
public CustomView(Context cont, AttributeSet attr) {
super(cont,attr);
}
@Override
protected void onDraw(Canvas c){
c.drawColor(Color.BLACK);
Paint fill_paint = new Paint();
fill_paint.setStyle(Paint.Style.FILL);
fill_paint.setColor(Color.BLUE);
c.drawRect(500f, 500f, 1000f, 1000f, fill_paint);
fill_paint.setColor(Color.RED);
c.drawOval(new RectF(0f, 0f, 500f, 500f), fill_paint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize,heightSize);
}
}
XMLファイルで、Layout可能にするためには、
- XML用のコンストラクタと、大きさを返すonMeasureメソッドを追加する必要があるらしい
- XMLファイルは普通にGUIで、TextViewとButtonをLinerLayoutで配置した後、
- Activityは、Layoutリソースを参照するだけで良い。(src/HelloGraphics.java)
package hm.orz.bluefish.hellographics;
import android.app.Activity;
import android.os.Bundle;
public class HelloGraphics extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
- これでやりたいことが、少し出来るようになってきたかな。
8これって、けっこう凄いことだよね。
[top]
- アクションバーは、画面の一番上のタイトルバーに、いろいろなウィジェットを仕込むことが出来る
- デフォルトでは、アプリケーションのアイコンとアプリケーション名が入っている
- ActionBarにメニューを付ける
- onCreateOptionsMenuをオーバーライドすれば良い
@Override
public boolean onCreateOptionsMenu( Menu menu ) {
super.onCreateOptionsMenu( menu );
menu.add( 0, MENUID_FILE, 0, "Select Open File..." );
menu.add( 0, MENUID_ABOUT, 0, "About Application..." );
return true;
}
- 選択時の処理のために、IDを付けておく
- リソースで追加する場合は、メニューリソースファイル
値 | 表示方法 |
ifRoom | Barに表示できない場合は、Overflowメニューに表示 |
withText | Textを表示 |
never | 常にOverflawメニューに表示する |
always | 常にBar上に表示 |
collapseActionView | android:actionViewLayoutで指定したActionViewと関連付ける |
@Override
public boolean onCreateOptionsMenu( Menu menu ) {
super.onCreateOptionsMenu( menu );
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.xxx_menu, menu);
return true;
}
- メニューの表示前に実行されるonPrepareOptionsMenuをオーバーライドして、
- なんか最初だけ呼ばれるんだけど、それ以降は呼ばれないみたいだ。
- ググって見ると、ActivityでinvalidateOptionsMenuを呼ぶと、onPrepareOptionsMenuが呼ばれてメニューが更新されるらしい。
参考: stackOverflow
- 設定変更する度に、いちいちinvalidateしなきゃいけないのは、なんか面倒だ。
- 設定とか別クラスでやってたりするので、その度にActivityで更新しなおすのは嫌だし
- 名前からして動的に設定するためのonPrepareだと思うんだけど、どうしてこんな仕様に9したんだろう?
- うーん、これだと最初だけ更新されないなぁ。onPrepareはちゃんと呼ばれてるんだけど。間に合わないのかな。
- 続けてやると、ちゃんと次からは反映されるようになるから単純に間に合ってないわけじゃなさそうなんだけど。。
- 結局は、仕方ないので、メニューを変更しているクラスで、いちいちinvalidateするようにした。
- もっと良い方法がありそうなんだけど。これじゃ馬鹿みたいじゃないか。。
- オーバーフローメニューにアイコンを付ける方法を調べたけど、分からなかった。
- サブメニューにアイコンを付けることは出来るので、サブメニューをオーバーフローのアイコンにすると見た目はそれらしくなる。
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/item1"
android:icon="@drawable/item1"
android:showAsAction="always"
android:title="@string/item1"/>
<item
android:id="@+id/item2"
android:icon="@drawable/item2"
android:showAsAction="always"
android:title="@string/item2"/>
<item
android:id="@+id/item3"
android:icon="@drawable/item3"
android:showAsAction="always"
android:title="@string/item3"/>
<item
android:id="@+id/overflow"
android:icon="@drawable/overflow"
android:showAsAction="always"
android:title="@string/overflow">
<menu>
<item
android:id="@+id/sub1"
android:icon="@drawable/sub1"
android:showAsAction="never"
android:title="@string/sub1"/>
<item
android:id="@+id/sub2"
android:icon="@drawable/sub2"
android:showAsAction="never"
android:title="@string/sub2"/>
<item
android:id="@+id/sub3"
android:icon="@drawable/sub3"
android:showAsAction="never"
android:title="@string/sub3"/>
</menu>
</item>
</menu>
- Spinnerは、メニューというか、ComboBoxのようなインターフェース
- アクションバー上の表示用リソース(res/layout/spinner.xml)
- ドロップダウン時の表示用リソース(res/layout/spinner_down,xml)
- Spinner用のリソースを用意したら、ActionBarにSpinnerを設定する
public class CanvasTest extends Activity
implements OnNavigationListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getActionBar().setDisplayShowTitleEnabled(false);
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.color_list, R.layout.spinner);
adapter.setDropDownViewResource( R.layout.spinner_down);
getActionBar().setListNavigationCallbacks(adapter, this);
getActionBar().setSelectedNavigationItem(pos) //初期位置
}
@Override
public boolean onNavigationItemSelected(int pos, long id) {
Toast.makeText(this, "pos = " + pos, Toast.LENGTH_SHORT).show();
return true;
}
}
- まず、ActionBarのタイトル表示をしない様にして、モードを設定
- Spinnerのデータと表示方法をリソースで設定
- さらに、DropDown時の表示方法もリソースで設定
- あとは、リスト選択時のコールバックを設定?して、
- onNavigationItemSelectedメソッドで、選択時の動作を記述する
- 初期位置は無指定の場合、0(一番上)になる
9もしかすると、Theme使ってる所為かもしれないけど。
10MacとかWindowsでは、Menuは動的に変化することが前提なので、メニュー準備のイベントは必ず呼ばれるけど、Androidは何でこうなってるんだろう。。
[top]
- Mouseの動きで描画するにはどうすれば良いんだろう
- Mouseで描画可能なカスタムViewを作成する(src/CustomVew.java)
- Mouseの動きで、線(Path)を描画する
package hm.orz.bluefish.canvastest;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff.Mode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CustomView extends View {
private Paint paint;
private Path path;
public CustomView(Context cont) {
super(cont);
initView();
}
public CustomView(Context cont, AttributeSet attr) {
super(cont,attr);
initView();
}
private void initView() {
path = new Path();
paint = new Paint();
paint.setColor(0xFF008800);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(10);
}
@Override
protected void onDraw(Canvas c){
c.drawPath(path, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
path.moveTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
path.lineTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
path.lineTo(x, y);
invalidate();
break;
}
return true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize,heightSize);
}
}
- コンストラクタで、Pathを生成して、色と線幅を設定する
- Mouseをクリックしたところに、線の開始ポイントを移動して、
- Mouseの動きに合わせて、線を描画する
- 作成したCustomViewを、Activityに直接置くだけなら、以下のようにすれば良い
public class CanvasTest extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CustomView(this));
}
}
- せっかくなので、レイアウトして置きたいんだけど、どうすれば良いんだろうか?
- プログラムでレイアウトすれば、それぞれの部品のViewを求めることが出来るんだけど。。
LinearLayout layout = new LinearLayout(this);
setContentView(layout);
final TextView tv = new TextView(this);
final CustomView cv = new CustomView(this);
final Button bt = new Button(this);
tv.setHeight(150);
tv.setText("Title");
tv.setTextSize(50);
tv.setGravity(1);
bt.setHeight(200);
bt.setText("Button");
bt.setTextSize(50);
layout.addView(tv);
layout.addView(btn);
layout.addView(cv);
- この方法では、レイアウトの設定がかなり面倒だ。。というか、やってられない。。。
- あとから考えてみると、単に以下のように記述すれば出来そうな気がするんだけど、(141005追記)
public class CanvasTest extends Activity {
CustomView cv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
cv = (CustomView)findViewById(R.id.customView1);
}
}
- Inflaterを使う必要は無いんじゃないか? なんで出来ないと思ったんだろう。。
- いろいろ調べると、LayoutInflaterを使うとViewの設定を個別に出来る様だ(src/CanvasTest.java)
- これで、TextView,CustomView,ButtonのViewを、それぞれ tv,cv,btとして取得できる
package hm.orz.bluefish.canvastest;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class CanvasTest extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.main, null);
setContentView(layout);
TextView tv = (TextView)layout.findViewById(R.id.textView1);
CustomView cv = (CustomView)layout.findViewById(R.id.customView1);
Button bt = (Button)layout.findViewById(R.id.button1);
bt.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast.makeText(CanvasTest.this, "Click!", Toast.LENGTH_LONG).show();
}
});
}
}
- Inflaterの仕組みが、いまいち分かってないんだけど。。
- Layoutは、XMLファイルで指定すれば良い(res/layout/main.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="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center_vertical|center_horizontal"
android:text="Title"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="50sp"
android:layout_weight="1"/>
<hm.orz.bluefish.canvastest.CustomView
android:id="@+id/customView1"
android:layout_width="wrap_content"
android:layout_height="500dp"
android:layout_weight="10"/>
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="Claer"
android:textSize="50sp"
android:layout_weight="1"/>
</LinearLayout>
- LayoutInflaterは、基本的にはXMLのレイアウトを動的にセットするためのものらしい。
- 普通はsetContentViewしていないと、findViewByIdは使えないっぽい。
- でもLayoutInflaterを使えば、セットしていなくてもfindViewByIdが使えるみたいだ。(ここら辺は、ちょっと未確認だけど)
- 今のところ、動的にセットするために使っていないので、ViewGroupをnullにして使ってるけど、
[top]
- 画面に直接描画するんじゃなくて、表示されないCanvasに描いておいて、
- 表示画面に転送するようなやり方を、オフスクリーンとかダブルバッファとか言う
- 表示用のCanvasとは別に、同じ大きさのCanvasを用意する。
- Bitmap.config.ARGB_8888というのは、Bitmapの1ピクセルあたりのデータを定義している
- これ以外にも、以下のようなBitmapを定義することが出来るらしい
Bitmap.config | 1ピクセルあたりの、byte数/定義内容 |
ARGB_8888 | 4byte/αチャネル(8bit)、R(8bit)、G(8bit)、B(8bit) |
ARGB_4444 | 2byte/αチャネル(4bit)、R(4bit)、G(4bit)、B(4bit) |
RGB_565 | 2byte/αチャネルなし、R(5bit)、G(6bit)、B(5bit) |
ALPHA_8 | 1byte/αチャネル(8bit)のみ |
- View生成時にCanvasを生成すれば良いが、
- とりあえずは、onSizeChanged(Viewのサイズ変更時)で、オフスクリーンを生成する
- これだと、画面の向きを変えた時も再生成されてしまうが、今回はそれでよしとする
- いままでは、Mouseの動きに合わせて、Pathを記録しながら描画していた
- Pathは1つしかないので、色を変えても全部の色が変わってしまう。
- 今度は、Pathを描き終わったら、オフスクリーンへも描画するようにする
- で、描き始める前に、オフスクリーンを表示画面にコピーしてから、
- その上に、追加で描画するようにする
- 最後に、やはりオフスクリーンに、表示画面が保存されることになる
- こうすると、オフスクリーンには、いままで描いた絵が保存され、
- Mouseで描いている間は、最後のPathのみ描くことになる
- 描いているPathの色を変えても、今まで描いた絵には影響しないし、
- onDrawで描くのは、最後のPathのみになるので、処理も軽くなる
- オフスクリーンを使って、カスタムViewを作り直してみる
package hm.orz.bluefish.canvastest;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CustomView extends View {
private Canvas canvas;
private Bitmap bitmap;
private Paint paint;
private Path path;
private Context appCont;
public CustomView(Context cont) {
super(cont);
initView(cont);
}
public CustomView(Context cont, AttributeSet attr) {
super(cont,attr);
initView(cont);
}
private void initView(Context cont) {
appCont = cont.getApplicationContext();
path = new Path();
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(10);
}
@Override
protected void onDraw(Canvas c){
c.drawBitmap(bitmap, 0, 0,null);
c.drawPath(path, paint);
}
@Override
protected void onSizeChanged(int w,int h,int oldw,int oldh) {
bitmap = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
path.reset();
path.moveTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
path.lineTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
path.lineTo(x, y);
canvas.drawPath(path, paint);
path.reset();
invalidate();
break;
}
return true;
}
public void setColor(int color) {
paint.setColor(color);
}
public void clearView() {
canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize,heightSize);
}
}
- clearViewメソッドは、Canvasをクリアするコマンドを調べて定義したんだけど、
- この呪文のようなModeは何なんだろう? ちゃんと動くんだけどね。。
- オフスクリーンを使ったViewを呼び出すActivityのプログラム
- Buttonで、Viewのメソッドを呼び出すようにしてみた
package hm.orz.bluefish.canvastest;
import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class CanvasTest extends Activity
implements OnNavigationListener {
TextView tv;
CustomView cv;
Button bt;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.main, null);
setContentView(layout);
tv = (TextView)layout.findViewById(R.id.textView1);
cv = (CustomView)layout.findViewById(R.id.customView1);
bt = (Button)layout.findViewById(R.id.button1);
bt.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast.makeText(CanvasTest.this, "Click!", Toast.LENGTH_LONG).show();
cv.clearView();
}
});
getActionBar().setDisplayShowTitleEnabled(false);
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.color_list, R.layout.spinner);
adapter.setDropDownViewResource( R.layout.spinner_down);
getActionBar().setListNavigationCallbacks(adapter, this);
}
@Override
public boolean onNavigationItemSelected(int pos, long id) {
Toast.makeText(this, "pos=" + pos, Toast.LENGTH_SHORT).show();
int color = Color.WHITE;
switch(pos){
case 0: color = Color.BLACK; break;
case 1: color = Color.RED; break;
case 2: color = Color.GREEN; break;
case 3: color = Color.BLUE; break;
case 4: color = Color.CYAN; break;
case 5: color = Color.MAGENTA; break;
case 6: color = Color.YELLOW; break;
}
cv.setColor(color);
return true;
}
}
- CustomViewのメソッドにアクセスするために、inflaterを使って、CustomViewのViewを個別に取得している
- Buttonで、画面クリアするために、clearViewを呼び出している
- 色の設定は、ActionBar上にSpinnerDropDownリストで選択して、setColorを呼び出す
- ViewをPNGファイルとして保存する saveAsPngImageメソッドを追加する
public void saveAsPngImage(){
try {
File extStrageDir =
Environment.getExternalStorageDirectory();
File file = new File(
extStrageDir.getAbsolutePath() + "/" + Environment.DIRECTORY_DCIM,
getFileName());
FileOutputStream outStream = new FileOutputStream(file);
bitmap.compress(CompressFormat.PNG, 100, outStream);
outStream.close();
Toast.makeText(appCont,"Image saved",Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
Toast.makeText(appCont,"FileNotFoundException!!",Toast.LENGTH_SHORT).show();
e.printStackTrace();
} catch (IOException e) {
Toast.makeText(appCont,"IOException!!",Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
protected String getFileName(){
Calendar c = Calendar.getInstance();
String s = c.get(Calendar.YEAR)
+ "_" + (c.get(Calendar.MONTH)+1)
+ "_" + c.get(Calendar.DAY_OF_MONTH)
+ "_" + c.get(Calendar.HOUR_OF_DAY)
+ "_" + c.get(Calendar.MINUTE)
+ "_" + c.get(Calendar.SECOND)
+ "_" + c.get(Calendar.MILLISECOND)
+ ".png";
return s;
}
- StrageDir(普通は/sdrom/)の下のDCIMフォルダに日付を表すファイル名で保存する
- でも、FileNotFoundExceptionになってしまい、保存出来ない。
[top]
- Androidは、いろいろ面倒な手続きが多い気がする
- リソースをXMLで書かなきゃ11いけないし、デフォルトの設定だと、ちょっと見づらかったり
- Inflaterとか、Intentとか、Adapterとか、Holderとか、独自の用語、仕組みが多すぎる
- マルチウィンドウでも無いわけなので、プログラム構造的にはWindowsより簡単で良いと思うんだけど。。
- もうちょっと、シンプルなものにして欲しかったなぁ。
- でも、オフスクリーンが作れることが分かったので、自分の作りたいプログラムが出来そうな気がしてきた。
- まぁ、わかんないことは、まだまだ、いろいろあるんだけど。
11XMLって、冗長な感じがして、どうもあまり好きじゃない。
[top]
- リソース定義の、XMLファイルのフォーマットに誤りがあるというエラー
- このエラーが出て、実際、xmlファイルのフォーマットが間違っていたんだけど、
- フォーマットが正しいのに、このエラーになる場合、
- 一旦、エラー部分を全部Cutして、エラーが無いことを確認してから、もう一度、Pasteすると直るみたいだ。
12一度、このエラーが出ると、Eclipseの状態が少しおかしくなるのかもしれない。
[top]
[top]
[プログラムの部屋に戻る]