2012年10月31日水曜日

Android ADB Shellを使えるようにする

Androidをインストールしたフォルダのandroid-sdk\platform-tools\adb.exeがあります。
ここへ環境変数を設定しパスを通します。

コンピュータのプロパティより「システムの詳細設定」を選択します。
「システムのプロパティ」ダイアログより「詳細設定」タブを選択し、「環境変数」ボタンをクリックします。

システム変数にANDROID_HOMEを追加します。
変数名:「ANDROID_HOME」
変数値:Androidをインストールしたパス¥android-sdk

環境変数のPathを編集し末尾に「;」がなければ追加し
「%ANDROID_HOME%¥platform-tools;」を追加します。

コマンドプロンプトを起動し「adb」を入力してコマンド一覧がでればOKです。

Android エミュレータで起動はキャンセルされました!の対処法

エミュレータで「起動はキャンセルされました!」とエラーになり実行できないときの対処法です。

eclipseのメニューより「ウィンドウ」→「設定」を選択し、表示されたダイアログより「Android」の「DDMS」を選択します

「ADB接続タイム・アウト(ミリ秒)」をデフォルトの5000から、もう少し長めに変更します。

2012年10月26日金曜日

.NET シリアライズしたファイルをデシリアライズした際、改行が読み込まれない

シリアライズしたファイルをデシリアライズで読み込んだ際に改行が読み込まれない問題

あほxのまとめwiki XmlSerializerの改行の扱いについてによると
改行がCRLFのStringをXmlSerializer.Serializeでシリアライズして保存すると、ファイルには改行がCRLFのまま保存されるが、
デシリアライズする際に
ファイルをStreamReaderでXmlSerializer.Deserializeに渡すと改行がLFになる。
ファイルをXmlTextReaderでXmlSerializer.Deserializeに渡すと改行がCRLFになる。

ほぉ~そ~なのか!

シリアライズ
Dim hoge As New Hoge
Using writer As New System.Xml.XmlTextWriter(path, System.Text.Encoding.GetEncoding("UTF-8"))
    Try
        Dim serializer As New System.Xml.Serialization.XmlSerializer(GetType(Hoge))
        serializer.Serialize(writer, hoge)
    Finally
        writer.Close()
    End Try
End Using
デシリアライズ
Dim hoge As Hoge
Using reader As New System.Xml.XmlTextReader(path)
    Try
        Dim serializer As New System.Xml.Serialization.XmlSerializer(GetType(Hoge))
        Dim obj As Object = serializer.Deserialize(reader)
        hoge = CType(obj, Hoge)
    Finally
        reader.Close()
    End Try
End Using

2012年10月16日火曜日

Android ActionBarとFragmentを使用してTab画面を表示する(Android 4.0以上)

以前Android TabActivityとTabHostを使用してTab画面を表示するで作成したTab画面はTabActivityを継承し作成していましたが
Android3.xよりTabActivityは非推奨になったようです。

TabActivityを使用しないでTab画面を作成する方法です。
Android3.0より導入されたActionBarとFragmentを使用します。

対象:
Build SDK:Android 4.0 (API 14) / Min SDK:Android 4.0 (API 14)

Android 2.x でActionBarとFragmentを使用してTab画面を表示する方法はコチラ
Android ActionBarとFragmentを使用してTab画面を表示する(Android 2.x)

まずメインとなるアクティビティを作成します。
android.app.Activityを継承して作成します。
import android.os.Bundle;
import android.view.Menu;
import android.app.Activity;

public class MainActivity extends Activity {

     @Override
     public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
     }

     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
          getMenuInflater().inflate(R.menu.activity_main, menu);
          return true;
     }

}

次に各タブのコンテンツとなるFragmentを作成します。
こちらはandroid.app.Fragmentを継承して作成します。
※android.support.v4.app.FragmentはAndroid2.xでFragmentを使用できるようにサポートされているパッケージです。
今回はandroid4.0以上が対象なのでandroid.app.Fragmentを使用します。
レイアウトはそれぞれの違いがわかるように適当に変更しておいてください。
import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Tab1Fragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 第3引数のbooleanは"container"にreturnするViewを追加するかどうか
        //trueにすると最終的なlayoutに再度、同じView groupが表示されてしまうのでfalseでOKらしい
        return inflater.inflate(R.layout.fragment_tab1, container, false);
    }
}
今回は面倒なので2タブにします。
同じように2タブめに表示するFragmentを作成します。
import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Tab2Fragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 第3引数のbooleanは"container"にreturnするViewを追加するかどうか
        //trueにすると最終的なlayoutに再度、同じView groupが表示されてしまうのでfalseでOKらしい
        return inflater.inflate(R.layout.fragment_tab2, container, false);
    }
}

次にタブを変更したときのリスナーを作成します。
ActionBar.TabListenerインターフェースを実装します。
このクラスの各メソッドの引数「FragmentTransaction」はバグっており常にnullになっているため使用できません。
2012/12/11追記
縦横を切り替えたときFragment#OnCreateが2回走る不具合があり、
コンストラクタでFragmentを探すように変更し
onTabSelectedでもdetachされていないときだけattachするよう変更しました。
詳しくは・・・
Android ActionBarとFragmentを利用したTab画面でFragment#OnCreateが2回走る
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.ActionBar.Tab;


public class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;
    
    /**
     * コンストラクタ
     * @param activity
     * @param tag
     * @param clz
     */
    public TabListener(Activity activity, String tag, Class clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
        //FragmentManagerからFragmentを探す。  2012/12/11 追記    
    mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);      
    }

    /**
     * @brief タブが選択されたときの処理
     */
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        //ftはnullなので使用できない        
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            FragmentManager fm = mActivity.getFragmentManager();
            fm.beginTransaction().add(R.id.container, mFragment, mTag).commit();
        } else {
             //detachされていないときだけattachするよう変更   2012/12/11 変更   
            //FragmentManager fm = mActivity.getFragmentManager();   
            //fm.beginTransaction().attach(mFragment).commit();   
            if (mFragment.isDetached()) {      
                FragmentManager fm = mActivity.getFragmentManager();      
                fm.beginTransaction().attach(mFragment).commit();      
             }    

        }
    }
    /**
     * @brief  タブの選択が解除されたときの処理
     */
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            FragmentManager fm = mActivity.getFragmentManager();
            fm.beginTransaction().detach(mFragment).commit();
       }    
    }
    /**
   * @brief タブが2度目以降に選択されたときの処理
     */
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
}

それではメインアクティビティに戻ります。
メインアクティビティではonCreateメソッドでActionBarを取得し、NavigationModeをタブにします。
後はActionBarにタブのコンテンツとなるFragmentを追加していくだけです。
import android.app.ActionBar;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Set up the action bar.
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        actionBar.addTab(actionBar.newTab() 
                .setText("ページ1") 
                .setTabListener(new TabListener( 
                        this, "tag1", Tab1Fragment.class))); 
        actionBar.addTab(actionBar.newTab() 
                .setText("ページ2") 
                .setTabListener(new TabListener( 
                        this, "tag2", Tab2Fragment.class))); 
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.activity_main, menu);
         return true;
    }

}

2012年10月11日木曜日

.NET RowFilterでEvaluateExceptionが発生した場合のエスケープ

DataViewのRowFilterを利用してデータを抽出するとき
特殊文字をエスケープしないとEvaluateExceptionが発生します。

エスケープは文字を角かっこ[]で囲みます。

Dim findChar As String() = {vbTab, vbCr, vbLf, "~", "(", ")", "#", "\", "/", "=", ">", "<", "+", "-", "*", "%", "&", "|", "^", "'", "[", "]", """"}

Dim lstPattern As New List(Of String)
For Each s As String In findChar
    lstPattern.Add(System.Text.RegularExpressions.Regex.Escape(s))
Next
Dim sPattern As String = String.Join("|", lstPattern.ToArray)

Dim sInput As String = Me.TextBox1.Text
sInput = System.Text.RegularExpressions.Regex.Replace(sInput, sPattern, "[$&]")

Dim tbl As DataTable = ごにょごにょ
Dim viw As New DataView(tbl)
viw.RowFilter = "Column1 Like '%" & sInput & "%'"

2012年10月2日火曜日

Android Spinnerで表示する値と実際に使用する内部的な値

Spinnerで表示する値と、実際に使用する内部的な値を変えたい場合の方法です。
いろいろな方法があるようですが、ArrayAdapterを拡張する方法です。

まず内部的な値(Key)と表示する値(Value)のペアとなるクラスを作成しました。
Androidにはキーと値のペアを表すPair<T,T>クラスがあるのですが、 キーはInteger、値はStringという場合がほとんどで、Pair<Integer,String>とイチイチ書くのが面倒なので作っておきます。
このクラスはなくてもいいです。
その場合はKeyValuePairを使用している箇所はPair<Integer,String>と読み替えてください。
public class KeyValuePair extends Pair<Integer,String> {

    public KeyValuePair(Integer key, String value) {
        super(key, value);
    }

    public Integer getKey(){
        return super.first;
    }
    
    public String getValue(){
        return super.second;
    }
}

次にArrayAdapterを継承しKeyValuePairArrayAdapterクラスを作成します。
getViewメソッドでスピナー部分に表示する値を返すようにします。
getDropDownViewメソッドでスピナーのドロップダウンに表示する値を返すようにします。
getPositionメソッドでスピナーに設定した値リストから、内部で使用する値(キー)の要素番号を返すようにします。
    public class KeyValuePairArrayAdapter extends ArrayAdapter<KeyValuePair> {
    
    /**
     * @brief コンストラクタ
     * @param context
     * @param textViewResourceId
     */
    public KeyValuePairArrayAdapter(Context context, int textViewResourceId) {
        super(context, textViewResourceId);
    }
    /**
     * @brief コンストラクタ
     * @param context
     * @param textViewResourceId
     * @param list
     */
    public KeyValuePairArrayAdapter(Context context, int textViewResourceId, List<KeyValuePair> list) {
        super(context, textViewResourceId, list);    
    }
    
    /**
     * @brief Spinerに表示するViewを取得します。
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView view = (TextView) super.getView(position, convertView, parent);
        view.setText(getItem(position).getValue());
        return view;

    }
    /**
     * @brief Spinerのドロップダウンアイテムに表示するViewを取得します。
     */
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        TextView view = (TextView) super.getDropDownView(position, convertView, parent);
        view.setText(getItem(position).getValue());
        return view;
    }

    /**
     * @brief keyに一致するインデックスを取得します。
     * @param key
     * @return
     */
    public int getPosition(int key){
        int position = -1;
        for (int i = 0 ; i < this.getCount(); i++){
            if (this.getItem(i).getKey() == key) {
                position = i;
                break;
            }
        }
        return position;
        
    }
    
}


実際に使用する際のコードです。

まずはベタっとした場合
public class MainActivity extends Activity {
    
    private Spinner _spinner = null;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //スピナー
        _spinner =  (Spinner)this.findViewById(R.id.spinner1); 
        _spinner.setOnItemSelectedListener(Spinner1_OnItemSelectedListener);
        //スピナーのドロップダウンアイテムを設定
        KeyValuePairArrayAdapter adapter = new KeyValuePairArrayAdapter(this, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        adapter.add(new KeyValuePair(1,"One"));
        adapter.add(new KeyValuePair(2,"Two"));
        adapter.add(new KeyValuePair(3,"Three"));
        _spinner.setAdapter(adapter);
        //キーが2の値を選択する
        Integer selectKey = 2;
        _spinner.setSelection(adapter.getPosition(selectKey));
    }
    
    /**
     * @brief スピナーのOnItemSelectedListener
     */
    private OnItemSelectedListener Spinner1_OnItemSelectedListener = new OnItemSelectedListener() {
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            KeyValuePair item = (KeyValuePair)_spinner.getSelectedItem();   
            Toast.makeText(MainActivity.this, item.getKey().toString(), Toast.LENGTH_LONG).show();       
        }
        public void onNothingSelected(AdapterView<?> arg0) {            
        }  
    };  

}

次はデータベースのデータの場合
public class MainActivity extends Activity {
    
    private Spinner _spinner = null;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //スピナー
        _spinner =  (Spinner)this.findViewById(R.id.spinner1); 
        _spinner.setOnItemSelectedListener(Spinner1_OnItemSelectedListener);
        //スピナーのドロップダウンアイテムを設定
        List<KeyValuePair> list = getSpinnerData();
        KeyValuePairArrayAdapter adapter = new KeyValuePairArrayAdapter(this, android.R.layout.simple_spinner_item, list);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        _spinner.setAdapter(adapter);
        //キーが2の値を選択する
        Integer selectKey = 2;
        _spinner.setSelection(adapter.getPosition(selectKey));
    }

    /**
     * @brief スピナーデータを取得します。
     * @return
     */
    private List<KeyValuePair> getSpinnerData(){
        StringBuilder sql = new StringBuilder(); 
        sql.append(" SELECT Code, Name"); 
        sql.append(" FROM MYTABLE");
                
        List<KeyValuePair> list = new ArrayList<KeyValuePair>();
        DatabaseHelper dbhelper = new DatabaseHelper(this);
        SQLiteDatabase db = dbhelper.getReadableDatabase();
        try {
            Cursor cursor = db.rawQuery(sql.toString(), null);
            cursor.moveToFirst();
            try {
                while (cursor.moveToNext()){   
                    list.add(new KeyValuePair(cursor.getInt(0),cursor.getString(1)));   
                }   
            } finally {
                cursor.close();
            }
        } finally {
            db.close();
        }
        return list;
    }
    /**
     * @brief スピナーのOnItemSelectedListener
     */
    private OnItemSelectedListener Spinner1_OnItemSelectedListener = new OnItemSelectedListener() {
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            KeyValuePair item = (KeyValuePair)_spinner.getSelectedItem();   
            Toast.makeText(MainActivity.this, item.getKey().toString(), Toast.LENGTH_LONG).show();       
        }
        public void onNothingSelected(AdapterView<?> arg0) {            
        }  
    };  

}

最後にリソースで定義したデータの場合
リソース
<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <!-- Spinner1のドロップダウンアイテム -->
    <string-array name="spinner1_item1"> 
       <item name="key">1</item> 
       <item name="value">One</item> 
    </string-array> 
    <string-array name="spinner1_item2"> 
       <item name="key">2</item> 
       <item name="value">Two</item> 
    </string-array> 
    <string-array name="spinner1_item3"> 
       <item name="key">3</item> 
       <item name="value">Three</item> 
    </string-array> 
    
    <array name="spinner1_data"> 
       <item>@array/spinner1_item1</item> 
        <item>@array/spinner1_item2</item>
        <item>@array/spinner1_item3</item>  
    </array> 

</resources>
アクティビティ
public class MainActivity extends Activity {
    
    private Spinner _spinner = null;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //スピナー
        _spinner =  (Spinner)this.findViewById(R.id.spinner1); 
        _spinner.setOnItemSelectedListener(Spinner1_OnItemSelectedListener);
        //スピナーのドロップダウンアイテムを設定
        List<KeyValuePair> list = getSpinnerData();
        KeyValuePairArrayAdapter adapter = new KeyValuePairArrayAdapter(this, android.R.layout.simple_spinner_item, list);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        _spinner.setAdapter(adapter);
        //キーが2の値を選択する
        Integer selectKey = 2;
        _spinner.setSelection(adapter.getPosition(selectKey));
    }

    /**
     * @brief スピナーデータを取得します。
     * @return
     */
    private List<KeyValuePair> getSpinnerData(){
        List<KeyValuePair> list = new ArrayList<KeyValuePair>();
        Resources res = getResources();
        TypedArray spinner1_data = res.obtainTypedArray(R.array.spinner1_data);         
        for (int i = 0; i < spinner1_data.length(); ++i) { 
            int id = spinner1_data.getResourceId(i, -1); 
            if (id > -1) { 
                String[] item = res.getStringArray(id); 
                list.add(new KeyValuePair(Integer.valueOf(item[0]), item[1]));
            } 
        } 
        spinner1_data.recycle(); 
        return list;
    }
    /**
     * @brief スピナーのOnItemSelectedListener
     */
    private OnItemSelectedListener Spinner1_OnItemSelectedListener = new OnItemSelectedListener() {
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            KeyValuePair item = (KeyValuePair)_spinner.getSelectedItem();   
            Toast.makeText(MainActivity.this, item.getKey().toString(), Toast.LENGTH_LONG).show();       
        }
        public void onNothingSelected(AdapterView<?> arg0) {            
        }  
    };  

}