Archive for the ‘Android apps’ Category

Paging(Swipe)するViewの作成

2013年1月26日

画面を指で左右にめくる操作・・・。最もスマホ的な操作かと思っていたのに、Androidでの実装には別のライブラリを追加するなど、少し面倒な作業が必要でした。(便利なテンプレートを用意してくれるAppleとの差はまだ大きい・・・ 訂正、新しいSDKではテンプレートが用意されていました。)

とはいえ、以下のページにサンプルコード付きで説明されているので、やり方は簡単。要はGallaryと同じようにAdapterを使うだけです。

http://developer.android.com/training/implementing-navigation/lateral.html#horizontal-paging
ここでは、ApiDemo のGallary を元にして、Swipeで操作できるように改造してみました。(ソース

1. FragmentActivity にする

  • Support Libraryをダウンロードする

http://developer.android.com/tools/extras/support-library.html

ここからダウンロードして、android-support-v4.jar を プロジェクトのlibフォルダにコピー。ライブラリのPathにも追加(Java build pathにAdd Jar…)します。(libにコピーじゃなくて、直接Add External Jar…でも大丈夫かもしれません。)

2. ソースに以下のimportを追加

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.app.FragmentActivity;

3. Activityの基本クラスをActivityからFragmentActivityに変更

public class SwipeViewActivity extends FragmentActivity {...}

4. Adapter を実装

FragmentStatePagerAdapter を継承するクラスを作成

Overrideする必要があるのは以下の2つ

Fragment getItem(int)

int getCount()

Fragmentは、めくられるページ(View)の内容を保持します。getItem(int) の引数としてページの番号が与えられるので、それをもとに、ページの内容を作成するのに必要な情報を持ったFragmentのオブジェクトを作成して返します。

例えば、現在のページ番号だけ分かればいいのであれば、引数のintを直接渡してしまえばOK

	public class CollectionPagerAdapter extends
 	FragmentStatePagerAdapter {
		public CollectionPagerAdapter(FragmentManager fm) {
			super(fm);
		}

		@Override
		public Fragment getItem(int i) {
		     Fragment fragment = new ObjectFragment();
		     Bundle args = new Bundle();
		     // Our object is just an integer :-P
		     args.putInt(ObjectFragment.ARG_OBJECT, i);
		     fragment.setArguments(args);
		     return fragment;
		}

		@Override
		public int getCount() {
		     return mImageIds.length;
		}
	}

5. Fragment を実装

Fragmentを継承するクラスを作成します

リソースにある画像のIDを配列で保持し、その画像をViewで表示する例です

画像のIDを配列はこのように定義(ApiDemoのGallaryのをコピーして使用)

    static private final Integer[] mImageIds = {
            R.drawable.gallery_photo_1,
            R.drawable.gallery_photo_2,
            R.drawable.gallery_photo_3,
            R.drawable.gallery_photo_4,
            R.drawable.gallery_photo_5,
            R.drawable.gallery_photo_6,
            R.drawable.gallery_photo_7,
            R.drawable.gallery_photo_8
    };

リソースの画像を表示するViewを作成して返します。

	public static class ObjectFragment extends Fragment {
		public static final String ARG_OBJECT = "object";
		private Bitmap mBmp = null;

		@Override
		 public View onCreateView(LayoutInflater inflater,
		         ViewGroup container, Bundle savedInstanceState) {
		     // The last two arguments ensure LayoutParams are inflated
		     // properly.
			ImageView imgView = (ImageView)inflater.inflate(
		             R.layout.imageview, container, false);
		    Bundle args = getArguments();
		    int position = (int) args.getInt(ARG_OBJECT);
		    imgView.setImageResource(mImageIds[position]);
		    imgView.setScaleType(ImageView.ScaleType.FIT_CENTER);
		    return imgView;
		 }
	}

6. 画面リソース(main.xml)を定義

android.support.v4.view.ViewPager を使います。

<?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" >

    <android.support.v4.view.ViewPager android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true" android:id="@+id/imageviewpager"/>

</LinearLayout>

7. Fragmentで作成するViewのxml定義。Layoutは要らない。(もちろん、XMLを使わず、動的に作成してもいいはず)

<?xml version="1.0" encoding="utf-8"?>
    <ImageView xmlns:android="http://schemas.android.com/apk/res/android" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
android:id="@+id/imageview"/>

8. ViewPagerにAdapterを設定

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ViewPager pager = (ViewPager) findViewById(R.id.imageviewpager);
        pager.setAdapter(new CollectionPagerAdapter(getSupportFragmentManager()));
    }

動かしてみるとこんな感じです(ページをめくろうとしているところ)

swipe

ローカライゼーション(多言語対応)(Android)

2013年1月21日

res フォルダの下にある各リソースのフォルダをコピーして、フォルダ名の後ろにハイフン(”-”)に続けてロケールを指定する。例えば ”values”フォルダの日本語なら”values-ja”。このフォルダの内容を日本語のもので置き換える。リソースのIDは変更しない。

なお、リソースを編集した後は、Javaのソースに一部変更を加えて(例えばスペースや改行を追加するなど)ソースがコンパイルされるようにしないと、xml.outという拡張子のファイルが作成されてしまい、エラーになることがあるので注意。

ライブラリ参照の追加でエラー’setting build path has encountered a problem’

2012年11月18日

Eclipse でライブラリのJarを追加しようとしても、’setting build path has encountered a problem’とのエラーで追加できない場合。

⇒プロジェクトの”.classpath”ファイルのHidden属性を外すと直ります。

<参照>

http://stackoverflow.com/questions/1888075/eclipse-error-setting-build-path-has-encountered-error

GPSの位置情報を取得する (Android)

2012年10月27日

AndroidでGPSの位置情報を取得してみます。

位置情報は、主にGPSと携帯基地局の情報から取得できます。

手順としては、まずLocationManagerを取得し

LocationManager lm =

(LocationManager) getSystemService(Context.LOCATION_SERVICE);

最後に取得した情報を取得するには LocationManagerのgetLastKnownLocation()を使用します。

そして、最新の情報を取得するには、LocationManagerのrequestLocationUpdates() を呼び出します。このとき、Location Provider(GPSまたは携帯基地局)を指定します。また、位置を取得した後の動作として、 LocationListenerを定義して通知を受ける方法と、Intentを指定する方法があります。

位置情報が更新されるとLocation Listener の onLocationChanged(Location location) が呼び出されます。ここで引数として与えられる Location が位置情報です。

通知を受け取らないようにする(位置情報の取得を停止する)には removeUpdates()を呼び出します。

参考資料

画面の向きが変わったときにメンバ変数の値もクリアされてしまう(Android)

2012年7月7日

画面の向きが変わったときにメンバ変数の値もクリアされてしまう

なぜかAndroidでは画面の向きを変えたときに、Activity.onCreate() が呼ばれる、つまりActivityが再作成されている。このとき、クラスのメンバ変数の値もクリアされてしまう。この問題は以下の3つの方法で回避できた。

1)画面の向きを固定にする

Activity.onCreate()で setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) を呼び出す。画面の向きを変える必要がないときは、これが一番簡単で、スムーズで、安定している。

2)Activityの終了時に値を保存して、新しいActivityの開始時に再設定する

SharedPreferences を使う。面倒だが、これが一番推薦されているように思える。

3)変数を static にする

正しいやり方なのかわからないが、これで回避できた。新しいActivityも同一プロセス内にあるということらしい。

画面の向きを変える

なお、2)と3)では、画面の向きにあわせてデザインを変えた方が使いやすいはず。

これも正しいやり方なのかわからないが、Displayの縦横比で方向を判断してレイアウトを切り替える方法がある。

Activity.onCreate()内でhorizontal と landscape レイアウトを使い分けるサンプル

        int orientation = getWindowManager().getDefaultDisplay().getOrientation();
        if(orientation == 0) {
            setContentView(R.layout.main);        
        }
        else {
            setContentView(R.layout.main_l);                
        }

Foregroundにする(Android)

2012年6月23日

一度BackgroundになってしまったアプリをForegroundにするには

1.Activity の launchMode を “singleTask” に設定する

AndroidManufest.xml の <activity> に以下を追加
            android:launchMode="singleTask" 

2.startActivityで、そのActivityを呼び出す
       Intent intent = new Intent(getApplicationContext(), XXXXActivity.class);
       startActivity(intent);

Ringtone Picker を使う(Android)

2012年6月23日

Ringtoneを選択する画面(Ringtone Picker)を表示する

    	Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
    	startActivityForResult(intent, 0);

選択されたRingtoneのUriを取得する

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 0) {
            if (resultCode == RESULT_OK) {
            	//If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap object in the extra field. 
            	Bundle b = data.getExtras();
                Set keys = b.keySet();
                Iterator k = keys.iterator();
                while(k.hasNext()) {
                    String key = k.next();
                    if(key.equals(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)) {
                        Object o = b.get(key);                		
                        mRtUri = (Uri)o;
                        if(mRtUri != null) {

                        }
                        break;
                     }
                }
            }
        }
    }

Beep音を出す(Android)

2012年6月23日

Beep音を鳴らす。

Ringtone を使うには、RingtoneのURIを指定して、RingtoneManager.getRingtone() を呼び出し、Ringtone を取得。

取得したRingtone の start() と stop() を呼び出す。

※このコードはエミュレータではエラーになる。(Settings.System.DEFAULT_RINGTONE_URI=null になってしまう)

    private void beep(int ms) {
        Uri rturi = Settings.System.DEFAULT_RINGTONE_URI;
        if(rturi != null) {
            Ringtone rt = RingtoneManager.getRingtone(this.getContext(), rturi);
            if(rt != null) {
                rt.play();                
                try {
                    Thread.sleep(ms);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                rt.stop();                
            }
        }        
    }

他にも以下のようなものを使って音を出せる。

Custom Dialog を作成 (Android)

2012年6月22日

別にMainのViewをもつActivityの中で使用するCustom Dialog を作成してみる。

Dev Guideにある以下のページを参照。丁寧に書いてあるので、ほぼこのページとそこのリンクだけの情報で作成できる。
http://developer.android.com/guide/topics/ui/dialogs.html#CustomDialog
今回は、タイマーの時間をセットするだけの簡単なものなので、AlertDialogを使用する。
引っかかってしまった点が1つだけあった。Dev GuideにあるAlertDialogを作成するコード例では
OnCreatedDialog() でDialogをcreateするコードの中で、LayoutInflater を取得するところが

 
    	Context mContext = getApplicationContext();
    	LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);

となっていたが、Activityクラスで作成するときは、Context は getApplicationContext() で取得するものでは例外が発生してしまった。API Demo のソースなどを参照し、下のように this つまりActivity クラス自身を指定すると例外はなくなった。

 
    	LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);

このあとは、DevGuideのサンプル通りに、以下のようなコードで実装できた。
なお、setView() でカスタムのレイアウトを指定しても、setNegativeButton()、setPositiveButton() を指定すると、ボタンはそのレイアウトの下に表示された。つまり、カスタムのレイアウトのなかにはOKやCancelのボタンは不要ということになる。

 
    	AlertDialog.Builder builder;
    	AlertDialog dialog;
    	LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
    	final View layout = inflater.inflate(R.layout.timer_setting_dialog, null)
        //Custom Dialog 内のコントロールの初期化処理
        Spinner spn1 = (Spinner)layout.findViewById(R.id.spinner1);
        ArrayAdapter adapter1 = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
        adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        for(int i = 0; i<100; i++) {
        	adapter1.add(String.format("%02d", i));
        }
        spn1.setAdapter(adapter1);
        //(省略)

        //AlertDialog.Builderを使う
    	builder = new AlertDialog.Builder(this);
    	builder.setView(layout);
    	builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
            }
        });
    	builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                //(省略)
            }
        });
    	dialog = builder.create();
Custom Dialog

Custom Dialog

Custum View をXMLのレイアウトに加える(Android)

2012年3月3日

タイマーの残り時間を表示する簡単なCustom View を作成し、既存のGUIコンポーネントと一緒にXMLのレイアウトに組み込む。

参照ページ

Custom View を実装

android.view.Viewを継承したクラスを作成し、onDraw(Canvas canvas) をオーバーライドして描画ロジックを実装するだけ。但し、XMLのレイアウトに組み込むには、さらに以下の2つの形式のコンストラクタも実装する必要がある。

public View (Context context)

public View (Context context, AttributeSet attrs)

Layoutに組み込む

このCustomViewをXMLのレイアウトに組み込むには、ただ、CustomViewのクラス名をフルパスで指定するだけでよい。

    <TextView
        android:id="@+id/textView1"
        android:layout_alignParentTop="true"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <stndstn.intervaltimer.TimerView
        android:id="@+id/timerview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView1"
        android:layout_centerHorizontal="true"
    />

これはLayout.xmlの一部で、上は通常のTextView、下が今回作成したCustomView。

クラス名をフルパスで設定する以外は、手でXMLのレイアウトを編集するときと同じ要領。