2015年2月28日土曜日

ToolbarでSearchViewを利用する

SYSTEM_KDです。

Android5.0より前のバージョンでMaterialDesignを利用するために、「Toolbar」が使えるという記事を以前書いたのですが、その「Toolbar」へ検索に利用できる「SearchView」を追加する方法になります。

 

やりたいこと

MaterialDesignを適用した、「Toolbar(ActionBar)」へ「SearchView」を追加

searchview01

searchview02

 

実現方法

Toolbarのメニューとして、「SerchView」を設定してやる。

 

ソース

ActionBarをToolbarで置き換えていることを前提としてます。
不明な場合は「こちら」を参照ください。

// 略
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
// 略

public class MainActivity extends ActionBarActivity {

private Toolbar toolbar;

private SearchView searchView;

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

toolbar = (Toolbar)findViewById(R.id.tool_bar);
setSupportActionBar(toolbar);

photoUri = null;
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);

getMenuInflater().inflate(R.menu.menu_search, menu);

MenuItem searchItem = menu.findItem(R.id.searchView);

searchView = (SearchView) MenuItemCompat.getActionView(searchItem);

return true;
}

// 以下略

}
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<item
android:id="@+id/searchView"
android:icon="@android:drawable/ic_menu_search"
android:orderInCategory="100"
app:showAsAction="always"
android:title="検索"
app:actionViewClass="android.support.v7.widget.SearchView"/>

<!-- android:actionViewClass はダメ app にする -->

</menu>

 


説明 & ハマったポイント


まず、「Activity側」についてですが、SearchViewを追加するために必要なコードは、32行目~36行目の部分になります。


メニューを追加して、そこから「SearchView」を取り出しているだけです。
あとは、この「searchView」をActionBarの時と同様に利用したのでOKです。


次に、「メニュー」の指定(というか追加)部分ですが、記述の通りなので、とくに説明はないのですが、気をつける点として9行目と11行目が「android:xxx」ではなく「app:xxx」になっている部分になります。


コメントでも記述していますが、「android:actionViewClass」にして動かそうとすると虫眼鏡アイコンは表示されるけど、動作しないという状態になります。


(私はここに気付かずしばらく、ハマってしまいました。。)


それと、なんかエラーが出る、上手く動かないという場合は、Activity部分のimportを見直してみると良いかいと思います。


それ以外は、ActionBarの際と同様の利用方法で問題ないと思います!!

2015年2月22日日曜日

Android4.0以降&4.4以降で、カメラとギャラリーから選択を1つのChooserへまとめて、パスを取得する

SYSTEM_KDです。
(タイトル長すぎですね。。)

Androidアプリで、「カメラ」「ギャラリー」から画像を取得する際に、1つのChooserへまとめてユーザへ選択させる方法です。

先日ググってサクッと実装しようとしたのですが、いざ実装してみると、OSのバージョンで若干処理を分けたりしないといけかったり、上手くいかなかったりしたりしたので、まとめ。

やりたいこと

アプリで画像を扱うために、「画像」&「画像のパス」を取得したい。

ようは、これ。

one_chooser

実現方法

ググったところ、カメラを呼び出すIntentと、ギャラリーを呼び出すIntentを作成して、Chooserへ設定してやれば良いらしい。

ハマったポイント

カメラを呼び出すIntentを用意する際に、撮影情報保存用のファイルをgetContentResolver経由で用意すると、キャンセルした際に空のファイルが消えてくれない。(消えないというか、ギャラリーを表示したのに空ファイルができてしまうような気がする)

ギャラリーから選択する際に、OSのバージョンによってパスの取得方法が違う。

実装

やることは、「実現方法」の内容通りですが、ちょこちょこ小細工してます。

まずは、画像(写真)の取得・設定から

private Uri photoUri;
private static final int REQUEST_CHOOSER = 1000;
private static final int KITKAT_API_LEVEL = 19;

private void showCameraGallery() {

// カメラ起動のIntent作成 */
File pathExternalPublicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
String filename = System.currentTimeMillis() + ".jpg";
File capturedFile = new File(pathExternalPublicDir, filename);
photoUri = Uri.fromFile(capturedFile);
Intent intentPhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intentPhoto.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);

// ギャラリー用のIntent作成 */
Intent intentGallery;
if (Build.VERSION.SDK_INT < KITKAT_API_LEVEL) {
intentGallery = new Intent(Intent.ACTION_GET_CONTENT);
intentGallery.setType("image/*");
} else {
intentGallery = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intentGallery.addCategory(Intent.CATEGORY_OPENABLE);
intentGallery.setType("image/jpeg");
}

Intent chooserIntent = Intent.createChooser(intentGallery, "Pickup");
// カメラ起動用のIntent を追加
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {intentPhoto});

startActivityForResult(chooserIntent, REQUEST_CHOOSER);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if(requestCode == REQUEST_CHOOSER) {

if(resultCode != RESULT_OK) {
// キャンセル時
return ;
}

Uri resultUri = (data != null ? data.getData() : photoUri);

if(resultUri == null) {
// 取得失敗
return;
}

// ギャラリーへスキャンを促す
MediaScannerConnection.scanFile(
this,
new String[]{resultUri.getPath()},
new String[]{"image/jpeg"},
null
);

// 画像を設定
ImageView imageView = (ImageView)findViewById(R.id.imageView);
imageView.setImageURI(resultUri);
}
}

Chooserの表示から画像の取得・表示までの抜粋ですが、こんな感じになるかと思います。

ハマったポイントで上げた通り、カメラ画像を保存するファイルを「getContentResolver」経由で作成すると、空ファイルが残ってしまったので、「File」で用意して、「onActivityResult」で「ギャラリーへスキャンを促す」ようにしてます。

ギャラリーから選択する部分は、Kitkat以降とそれより前で、Actionを「Intent.ACTION_GET_CONTENT」「Intent.ACTION_OPEN_DOCUMENT」とわけてます。

画像のパスを取得する

続いて、画像のパスを取得する方法です。

public static String getImagePath(ContentResolver contentResolver, Uri uri) {

String retPath = "";

if(uri == null) return retPath;

File file = new File(uri.getPath());
if(file != null && file.exists()) {
return uri.getPath();
}

if(Build.VERSION.SDK_INT < 19) {

String[] columns = { MediaStore.Images.Media.DATA };
Cursor cursor = contentResolver.query(uri, columns, null, null, null);
cursor.moveToFirst();
retPath = cursor.getString(0);
cursor.close();

} else {

String id = DocumentsContract.getDocumentId(uri);
String selection = "_id=?";
String[] selectionArgs = new String[]{id.split(":")[1]};

Cursor cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.MediaColumns.DATA},
selection, selectionArgs, null);

if (cursor.moveToFirst()) {
retPath = cursor.getString(0);
}
cursor.close();
}

return retPath;
}

ContentResolver」と「Uri」を渡せば、パスを返却するようにしてます。

写真の場合は、「UriのgetPath()」でそのままとれるので、そのパスを利用。

ギャラリーから取得した画像の場合は、OSのバージョンによって呼び出すものを変えているので、取得方法をそれに沿って違ってます。


まとめ


一応、いまのところはこれで画像とパスはとれてそうなので、大丈夫かと思います。

今回、とってきた画像をそのままImageViewへ設定しており、写真の向きに合わせて回転するとかは含めてません。

2015年2月21日土曜日

Androidからベーシック認証がかかっているサイトへファイルをアップする方法

SYSTEM_KDです。

AndroidからWEBサイトに対して、ファイルをアップしようとした際に、普通にファイルをアップする処理は、ググれば見つかったのですが、ベーシック認証(基本認証)がかかっているサイトに対してファイルをアップする方法が見つからなかったので、メモをかねてまとめ。

 

Androidアプリからベーシック認証があるサイトへのファイルアップ

では、早速ソース。

    DefaultHttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("POST先のURL");

// 基本認証
credentials = new UsernamePasswordCredentials(
"ベーシック認証ID",
"ベーシック認証パスワード"
);

final int DEFAULT_PORT = 80;
scope = new AuthScope("アップ先のドメイン", DEFAULT_PORT);
client.getCredentialsProvider().setCredentials(scope, credentials);

MultipartEntityBuilder entity = MultipartEntityBuilder.create();
entity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

// POST 値設定
entity.addTextBody("post項目のname", "post項目のvalue");

// アップするファイルを設定
String fileName = "ファイル名";
File upFile = new File("ファイルパス");
InputStream is = new FileInputStream(upFile);

byte[] filebyte;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

final int READ_BYTE = 5120;
byte[] buffer = new byte[READ_BYTE];

int n = is.read(buffer);
while(n >= 0) {
byteArrayOutputStream.write(buffer, 0, n);
n = is.read(buffer);
}
is.close();

filebyte = byteArrayOutputStream.toByteArray();

ByteArrayBody byteArrayBody = new ByteArrayBody(
filebyte ,
ContentType.create("application/x-gzip"), // MIME タイプ設定
fileName);

entity.addPart("Post(ファイル)項目のname", byteArrayBody);

// post値生成
post.setEntity(entity.build());


// Cookieの設定が必要な場合 --ここから--
BasicClientCookie basicCookie
= new BasicClientCookie(cookie.getKey(), cookie.getValue());

// ドメイン、パス設定
basicCookie.setDomain("ドメイン");
basicCookie.setPath("パス");

client.getCookieStore().addCookie(basicCookie);
// Cookieの設定が必要な場合 --ここまで--

// ファイルを含めてPOST
HttpResponse response = client.execute(post);

// レスポンスコード取得
int status = response.getStatusLine().getStatusCode();
if(status == HttpStatus.SC_OK) {
// 成功
else {
// 失敗
}

ざっくり説明


5行目~12行目:
ベーシック認証のIDとパスを設定。

14行目~15行目:
POST値を設定するためのEntity作成。

18行目:
POST値を設定、必要であれば増やします。

21行目~46行目:
アップするファイルを設定している部分。
39行目までで、byteで読み込み、41行目でアップする値を生成。
※今回は「tar.gz」をアップするMIMEタイプを設定。

49行目:
Entityからpostを生成。

52行目~61行目:
Cookieの設定が必要な場合は、ここにあるような形で設定。

64行目~:
サーバへpostを行って、結果を確認。

※以下のjarを利用してます
・httpcore-4.3.3.jar
・httpmime-4.3.6.jar


まとめ


ということで、Androidから基本認証がかかっている状態のサイトに対して、ファイルをアップする方法でした。
まぁ、あんまり必要になることはない気がしますが、これでできるかと思います。

2015年2月17日火曜日

Azureを利用して無料でECサイトを構築する

SYSTEM_KDです。
なんやかんやで、久々の投稿です。

久々のネタは、「無料でECサイトを構築するには」について書きたいと思います。

無料でECサイトを用意したいといった場合、最近では「BASE」や「STORES.jp」といったサービスを利用することにより、無料でECサイトの出店を行うことが可能です。

 

お手軽にECショップを出店できますが、自分で色々好きにできるといったわけでなないというデメリットはあるかと思います。

 

とはいえ、上記の様なサービスを利用せず独自で行う場合、サーバを借りて、ECサイトを構築して、SSLを取得して・・・と色々やらないといけないことがあり、その分お金がかかりますが、これを全て無料で行う方法があります。

 

それは・・・

 

Azureを利用することです!

Azureとは何かと簡単に説明しますと、マイクロソフトが提供しているクラウドサービスになります。
(別にマイクロソフトの回し者ではありませんよ^^;

http://azure.microsoft.com/ja-jp/
です。

 

上記のURLを見てみると、「無料で試す」という表記があるので、「なんだお試しか」と思われるかもしれませんが、お試しだけでなく料金プランとして無料プランがあるのです。

 

どこにあるかと言いますと、「Websites」というものになります。

 

無料ですので、もちろんアクセス量など制限はありますが、とりあえずECショップを出してみようという方には良いと思います。これを利用すれば、無料で始めて、ショップの売れ行きが好調になってきた時に有料プランへ変更といったことが簡単に行えます。

 

これで、サーバは無料で確保です。
さらに、AzureのWebsitesは独自ドメインを利用せずサブドメインでの運用を行えばSSLも無料で利用することができます。

ECサイトは個人情報を扱いますので、SSLは必須ですが、それも網羅できます。

 

あとは、オープンソース(無料)のECサイトパッケージ、EC-CUBEとデータを保存するためのデータベース、ClearDB(の無料プラン)を利用すればECサイトが構築できます。

 

メールサーバもないという方は、SendGrid(の無料プラン)を利用すれば、メールの送信まで網羅できます。

 

いやーなんでも無料でそろってしまうって、凄いですね。

(需要があれば、AzureへEC-CUBEを導入する方法についてまとめたいと思います。まぁググればかなり出てきますが)