Android ListViewでなんちゃってフィルター
ListViewでフィルターぽい処理をしてみました。
フィルタするデータのレイアウトを非表示にするパターンなのでデータの再読み込みは発生しません。
もしかしたら、デフォルトでフィルタ機能があるかもしれませんが・・・
MainActivity
public class MainActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // filterButton ((Button)findViewById(R.id.filterButton)).setOnClickListener(new View.OnClickListener() { public void onClick(View view) { String keyword = ((EditText)findViewById(R.id.keywordText)).getText().toString(); ListView listView = (ListView)findViewById(R.id.listView); AppAdapter appAdapter = ((AppAdapter)listView.getAdapter()); //adapter取得 appAdapter.setFilter(keyword); // filter設定 appAdapter.notifyDataSetInvalidated(); // listView更新 listView.setSelectionAfterHeaderView(); // listViewのTOPに移動 // ソフトキーボード消す InputMethodManager inputMethodManager = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); } }); // データ取得 new AppTask(getApplicationContext(), (ListView)findViewById(R.id.listView)).execute(); } class AppTask extends AsyncTask<Void, Void, List<ApplicationInfo>> { private Context context; private ListView listView; public AppTask(Context context, ListView listView) { this.context = context; this.listView = listView; } @Override protected void onPreExecute() { // progress表示 } @Override protected List<ApplicationInfo> doInBackground(Void... params) { // アプリ情報を取得 PackageManager pm = getPackageManager(); return pm.getInstalledApplications(BIND_AUTO_CREATE); } @Override protected void onPostExecute(List<ApplicationInfo> appList) { // progress非表示 AppAdapter appAdapter = new AppAdapter(context, appList); listView.setAdapter(appAdapter); } } }
adapter
public class AppAdapter extends ArrayAdapter<ApplicationInfo>{ private Context context; private LayoutInflater inflater; private String keyword; // フィルタ用キーワード public AppAdapter(Context context, List<ApplicationInfo> items) { super(context, 0, items); this.context = context; inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); keyword = ""; } public View getView(final int position, View convertView, ViewGroup parent) { Holder holder = null; View view = convertView; if (view == null) { view = inflater.inflate(R.layout.item, null); RelativeLayout mainLayout = (RelativeLayout)view.findViewById(R.id.mainLayout); ImageView iconImage = (ImageView)view.findViewById(R.id.iconImage); TextView nameText = (TextView)view.findViewById(R.id.nameText); holder = new Holder(mainLayout, iconImage, nameText); view.setTag(holder); } else { holder = (Holder)view.getTag(); } ApplicationInfo appInfo = getItem(position); if (appInfo != null) { try { // アプリ名 String name = appInfo.loadLabel(context.getPackageManager()).toString(); holder.nameText.setText(name); // アイコン holder.iconImage.setImageDrawable(null); // クリア holder.iconImage.setTag(name); // 非同期だとアイコンが一致しないため // フィルタ処理 if (TextUtils.isEmpty(keyword)) { // フィルタなし holder.mainLayout.setVisibility(View.VISIBLE); new BitmapTask(holder.iconImage).execute(appInfo); //非同期 } else if (name.toLowerCase().indexOf(keyword.toLowerCase()) >= 0) { // 一致していれば表示 holder.mainLayout.setVisibility(View.VISIBLE); new BitmapTask(holder.iconImage).execute(appInfo); //非同期 } else { // 不一致なのでlayout非表示 holder.mainLayout.setVisibility(View.GONE); } } catch (Exception e) { } } return view; } /** * フィルタする文字列設定 * @param keyword */ public void setFilter(String keyword) { this.keyword = keyword; } static class Holder { RelativeLayout mainLayout; ImageView iconImage; TextView nameText; Holder(RelativeLayout mainLayout, ImageView iconImage, TextView nameText) { this.mainLayout = mainLayout; this.iconImage = iconImage; this.nameText = nameText; } } // アイコン取得 class BitmapTask extends AsyncTask<Object, Void, Drawable> { private ImageView iconImage; private String tag; public BitmapTask(ImageView iconImage) { this.iconImage = iconImage; tag = iconImage.getTag().toString(); } @Override protected void onPreExecute() {} @Override protected Drawable doInBackground(Object... params) { // 本来ならHashMap等にキャッシュとして保持して再利用 try { ApplicationInfo appInfo = (ApplicationInfo)params[0]; return appInfo.loadIcon(context.getPackageManager()); } catch (Exception e) { return null; } } @Override protected void onPostExecute(Drawable icon) { if (tag.equals(iconImage.getTag().toString())) { // tagが一致したらセット iconImage.setImageDrawable(icon); } } } }
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:layout_height="wrap_content" android:id="@+id/linearLayout1" android:layout_width="fill_parent"> <EditText android:layout_height="wrap_content" android:id="@+id/keywordText" android:layout_width="fill_parent" android:layout_weight="1"></EditText> <Button android:layout_height="wrap_content" android:id="@+id/filterButton" android:text="filter" android:layout_width="wrap_content"></Button> </LinearLayout> <ListView android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/listView" android:divider="#00000000"></ListView> </LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:id="@+id/layout" android:layout_height="wrap_content" android:orientation="vertical"> <!-- フィルタ時はこのRelativeLayoutを非表示 --> <RelativeLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/mainLayout"> <LinearLayout android:layout_height="fill_parent" android:layout_width="wrap_content" android:id="@+id/item_layout"> <ImageView android:id="@+id/iconImage" android:layout_height="72px" android:layout_gravity="center_vertical" android:layout_width="72px"></ImageView> <TextView android:id="@+id/nameText" android:text="TextView" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_width="fill_parent"></TextView> </LinearLayout> <!-- 擬似的にラインを引いているが、画像などできれいなラインにしたほうがいいかも --> <ImageView android:id="@+id/lineImage" android:background="#ffffff" android:layout_width="fill_parent" android:layout_below="@+id/iconImage" android:layout_alignParentBottom="true" android:layout_height="1px"></ImageView> </RelativeLayout> <!-- このダミーがないと非表示の時にレイアウトがつぶれる --> <View android:id="@+id/dummy" android:layout_width="wrap_content" android:layout_height="1px"></View> </LinearLayout>
Androidアプリ 漢字Check 50,000ダウンロード達成
友人のiPhoneアプリからの移植ですが、Android版の漢字Checkが50,000ダウンロード達成しました。
まさにツールという感じのシンプルアプリですが、場面によっては重宝するかと思います。
今後ともよろしくお願いいたします。
https://market.android.com/details?id=it.kakeru.kanji_check
AndroidでAdMobとAdMakerの切り替え
AdMakerのSDKもバージョンが上がって、広告の成否が判断できるようになってました。
優先度を設定して表示するサンプル。
・AdMobのSDKは4.1.1
・AdMakerのSDKは1.1
広告クラス
public class ComAd { private static final int STATE_OK = 1; // 成功 private static final int STATE_EXEC = 0; // 実行中 private static final int STATE_NG = -1; // 失敗 private Activity activuty; private RelativeLayout adLayout; private AdView adMob; private libAdMaker adMaker; private boolean priorityAdMobFlg; // AdMob優先フラグ private int stateAdMob; // AdMob状態 private int stateAdMaker; // AdMaker状態 /** * * @param activuty */ public ComAd(Activity activity, RelativeLayout adLayout) { this.activuty = activity; this.adLayout = adLayout; this.adLayout.setVisibility(LinearLayout.INVISIBLE); if (Locale.getDefault().equals(Locale.JAPAN) || Locale.getDefault().equals(Locale.JAPANESE)) { // 日本のみAdMaker優先とか priorityAdMobFlg = false; } else { priorityAdMobFlg = true; } } // 広告取得 public void loadAd() { // 実行中に設定 stateAdMob = STATE_EXEC; stateAdMaker = STATE_EXEC; // 実行 loadAdMob(); loadAdMaker(); } // AdMob取得 private void loadAdMob() { adMob = new AdView(activuty, AdSize.BANNER, "xxxxxxx"); adLayout.addView(adMob); adMob.loadAd(new AdRequest()); adMob.setAdListener(new AdListener() { public void onReceiveAd(Ad arg0) { // 成功 stateAdMob = STATE_OK; setAd(); } public void onFailedToReceiveAd(Ad arg0, ErrorCode arg1) { // 失敗 stateAdMob = STATE_NG; setAd(); } public void onPresentScreen(Ad arg0) {} public void onLeaveApplication(Ad arg0) {} public void onDismissScreen(Ad arg0) {} }); } // AdMaker取得 private void loadAdMaker() { LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); adMaker = new libAdMaker(activuty); adMaker.setLayoutParams(lp); adMaker.setBackgroundColor(Color.WHITE); adLayout.addView(adMaker); adMaker.siteId = "XXX"; // siteId adMaker.zoneId = "XXXX"; // zoneId adMaker.setUrl("http://images.ad-maker.info/apps/xxxx.html"); // URL adMaker.start(); adMaker.setAdMakerListener(new AdMakerListener() { public void onReceiveAdMaker() { // 成功 stateAdMaker = STATE_OK; setAd(); } public void onFailedToReceiveAdMaker(String arg0) { // 失敗 stateAdMaker = STATE_NG; setAd(); } }); } // Ad private void setAd() { // メモのためif分は分かりやすく記述 // ここのif分を変更すれば、先に表示された広告を優先で表示等にできる if (stateAdMob == STATE_OK && priorityAdMobFlg) { //AdMobかつAdMob優先ならAdMob表示(AdMaker処理は待たない) setAdMob(); return; } if (stateAdMaker == STATE_OK && !priorityAdMobFlg) { //AdMakerかつAdMob優先ではないならAdMaker表示(AdMob処理は待たない) setAdMaker(); return; } if (stateAdMob == STATE_OK && stateAdMaker == STATE_NG) { //adMobのみ成功 setAdMob(); return; } if (stateAdMob == STATE_NG && stateAdMaker == STATE_OK) { //adMakerのみ成功 setAdMaker(); return; } if (stateAdMob == STATE_NG && stateAdMaker == STATE_NG) { // 両方失敗ならやりなおし loadAd(); } } // AdMob private void setAdMob() { // AdMob表示 adLayout.setVisibility(LinearLayout.VISIBLE); adMob.setVisibility(AdView.VISIBLE); // AdMaker非表示 adMaker.setVisibility(AdView.GONE); adMaker.stop(); } // AdMaker private void setAdMaker() { // AdMaker表示 adLayout.setVisibility(LinearLayout.VISIBLE); adMaker.setVisibility(AdView.VISIBLE); // AdMob非表示 adMob.setVisibility(AdView.GONE); adMob.stopLoading(); } }
呼び出し元ActivityのOnCreateあたりで
//広告表示 new ComAd(this, (RelativeLayout)findViewById(R.id.ad_layout)).loadAd();
呼び出し元Activityのレイアウトの上部か下部に
<RelativeLayout android:id="@+id/ad_layout" android:layout_width="fill_parent" android:layout_height="50dp"></RelativeLayout>
AndroidManifestに以下を忘れずに
<activity android:name="com.google.ads.AdActivity" android:configChanges="orientation|keyboard|keyboardHidden"></activity> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
AndroidでTwitter4jを使用した、OAuth認証と認証解除
OAuth認証のサンプルはよく見かけるのですが、解除がなかったのでメモ。
(正しいかどうかは不明です・・・)
以下、ソース(エラー処理とか省く)
定数
public final class Const { public static final String CONSUMER_KEY = "xxxxx"; public static final String CONSUMER_SECRET = "xxxxx"; public static final String OAUTH_CALLBACK_URL = "myapp://callback"; // コールバック public static final String OAUTH_VERIFIER = "oauth_verifier"; public static final String OAUTH_URL = "oauth_url"; public static final String OAUTH_CHECK_TEXT = "https://api.twitter.com/oauth/authorize"; // Twitterのウェブサイトで認証or解除をしたかの確認文字列 }
認証開始のActivity
public class OAuthActivity extends Activity { private Twitter twitter = null; private RequestToken requestToken = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.oauth); // button Button oauthButton = (Button)findViewById(R.id.oauth_button); oauthButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { doOAuth(); } }); setView(); } // 画面処理 private void setView(){ TextView nameText = (TextView)findViewById(R.id.nameTextView); ImageView iconImage = (ImageView)findViewById(R.id.iconImageView); Button oauthButton = (Button)findViewById(R.id.oauth_button); OAuth oAuth = loadOAuth(); if (oAuth.isEmpty()) { oauthButton.setText("アプリ認証"); nameText.setText(""); iconImage.setImageBitmap(null); } else { oauthButton.setText("アプリ認証解除"); nameText.setText(oAuth.name); new ImageTask(iconImage).execute(oAuth.imageUrl); // Twitterアイコン読み込み } } // Twitter認証 private void doOAuth() { ConfigurationBuilder confbuilder = new ConfigurationBuilder(); confbuilder.setOAuthConsumerKey(CONSUMER_KEY).setOAuthConsumerSecret(CONSUMER_SECRET); twitter = new TwitterFactory(confbuilder.build()).getInstance(); try { requestToken = twitter.getOAuthRequestToken(OAUTH_CALLBACK_URL); } catch (TwitterException e) { // エラー } // ブラウザ起動 Intent intent = new Intent(this, OAuthWebActivity.class); intent.putExtra(OAUTH_URL, requestToken.getAuthorizationURL()); startActivityForResult(intent, 0); } // ブラウザからの戻り protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); // キャンセル if (resultCode == RESULT_CANCELED) { Toast.makeText(this, "キャンセル", Toast.LENGTH_LONG).show(); return; } // 認証or認証解除 if (resultCode == RESULT_OK){ String verifier = intent.getExtras().getString(OAUTH_VERIFIER); if (!TextUtils.isEmpty(verifier)) { // verifierがあれば認証 try { AccessToken accessToken = twitter.getOAuthAccessToken(requestToken, verifier); // user取得 User user = twitter.verifyCredentials(); saveOAuth(new OAuth(user.getScreenName(), user.getProfileImageURL().toString(), accessToken.getToken(), accessToken.getTokenSecret())); setView(); Toast.makeText(this, "認証OK", Toast.LENGTH_LONG).show(); } catch (TwitterException e) { // エラー } } else { // 認証解除 if (!loadOAuth().isEmpty()) { Toast.makeText(this, "認証解除", Toast.LENGTH_LONG).show(); saveOAuth(new OAuth()); // OAuthクリア } } } } // icon取得 class ImageTask extends AsyncTask<String, Void, Bitmap> { private ImageView iconView; public ImageTask(ImageView iconView) { this.iconView = iconView; } @Override protected void onPreExecute() { // progress表示 } @Override protected Bitmap doInBackground(String... params) { try { String url = params[0].toString(); URL imageUrl = new URL(url); Bitmap bitmap = BitmapFactory.decodeStream(imageUrl.openStream()); return bitmap; } catch (Exception e) { return null; } } @Override protected void onPostExecute(Bitmap bitmap) { // progress非表示 iconView.setImageBitmap(bitmap); } } // OAuthの保存と取得はPreference用の別クラス等に実装がよい // OAuth保存 private void saveOAuth(OAuth oAuth) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); Editor editor = prefs.edit(); editor.putString(OAuth.NAME, oAuth.name); editor.putString(OAuth.IMAGE_URL, oAuth.imageUrl); editor.putString(OAuth.TOKEN, oAuth.token); editor.putString(OAuth.TOKEN_SECRET, oAuth.tokenSecret); editor.commit(); } // OAuth取得 private OAuth loadOAuth() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); return new OAuth(prefs.getString(OAuth.NAME, ""), prefs.getString(OAuth.IMAGE_URL, ""), prefs.getString(OAuth.TOKEN, ""), prefs.getString(OAuth.TOKEN_SECRET, "")); } }
認証ブラウザActivity
public class OAuthWebActivity extends Activity { private boolean checkFlg = false; protected void onCreate(Bundle bundle) { super.onCreate(bundle); // layout setContentView(R.layout.oauth_web); // webview WebView webView = (WebView)findViewById(R.id.oauth_web_view); webView.getSettings().setJavaScriptEnabled(true); // JavaScript有効 webView.setScrollBarStyle(WebView.SCROLLBARS_INSIDE_OVERLAY); // スクロールバー設定 webView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { // ページ読み込み開始 super.onPageStarted(view, url, favicon); if (url.startsWith(OAUTH_CALLBACK_URL)) { Uri uri = Uri.parse(url); // verifier取り出し String oauthVerifier = uri.getQueryParameter(OAUTH_VERIFIER); Intent intent = getIntent(); intent.putExtra(OAUTH_VERIFIER, oauthVerifier); if (checkFlg) { // 認証or認証解除 setResult(RESULT_OK, intent); } else { // キャンセルの場合 setResult(RESULT_CANCELED, null); } finish(); } } // ページ読み込み終了 public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // 認証or認証解除をしたか確認 checkFlg |= url.equals(OAUTH_CHECK_TEXT); } }); // ページ読み込み webView.loadUrl(getIntent().getExtras().getString(OAUTH_URL)); } }
OAuth保持Entity
public class OAuth { public static final String NAME = "oauth_name"; public static final String IMAGE_URL = "oauth_image_url"; public static final String TOKEN = "oauth_token"; public static final String TOKEN_SECRET = "oauth_token_secret"; public String name; public String imageUrl; public String token; public String tokenSecret; public OAuth() { this.name = ""; this.imageUrl = ""; this.token = ""; this.tokenSecret = ""; } public OAuth(String name, String imageUrl, String token, String tokenSecret) { this.name = name; this.imageUrl = imageUrl; this.token = token; this.tokenSecret = tokenSecret; } public boolean isEmpty() { return TextUtils.isEmpty(name); } }
レイアウト:R.layout.oauth_web
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/main_layout"> <Button android:layout_width="wrap_content" android:id="@+id/oauth_button" android:layout_height="wrap_content"></Button> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/nameTextView"></TextView> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/iconImageView"></ImageView> </LinearLayout>
レイアウト:R.layout.oauth_web
<?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:id="@+id/oauth_web_layout"> <android.webkit.WebView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/oauth_web_view"> </android.webkit.WebView> </LinearLayout>
AndroidManifest
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="me.undigital.test" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="4" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name="OAuthActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="OAuthWebActivity" android:label="@string/app_name"> </activity> </application> <uses-permission android:name="android.permission.INTERNET" /> </manifest>
wwwtter
wwwtterという、Twitter関連のアプリをAndroidMarketへリリースしました。
Twitterでつぶやかれている、WEBページをランキング形式でリアルタイムにチェックできるアプリです。
ランキングからWEBページへの遷移とそのWEBページに対してのツイートも簡単にできます。
※TwitterのStreamingAPIを使用してサーバーで集計してます。
宣伝もしてないので、スタートダッシュは大失敗な感じですが・・・気長にメンテナンスとかしていきます。
無料ですので、よかったら使ってみてください。
GalaxySをAndroid2.3にアップデートしてみた
準備
・SamsungのサイトからKiesをダウンロードしてインストール
http://jp.samsungmobile.com/pc/lineup/SC-02Bdownload.html
Windowsのみ・・・Macな方はドコモに行ったら期間限定らしいですがアップデートしてくれるらしいです。
・Android端末のUSB設定をSamsung Kiesモードに変更する(設定>無線とネットワーク>USB設定)
ここでプチはまりモード・・・
デバックモードのままでは「デバイスをSamsung Kies(PC Studio)モードで接続してください。」と
表示が出てPCが認識してくれません。
まあ、SamsungのサイトにUSBモード変更が記載されていたので、よく説明を読まないと・・・
更新
・Android端末が認識されたらファームウェアのアップデートと出るのでポチる
男らしく!?バックアップはさくっとキャンセルしたので、更新時間は5〜6分程度。
最後に「USBを抜いて端末を再起動してください」的なメッセージがでましたが、どうみても端末の画面は
まだアップデートの真っ最中で、ここでUSB抜いたら危険な予感がw
迷っていたら、USBを繋いだままでも問題なく端末再起動されて無事完了しました。
まあ、アップデート後は少し早くなった気もしますが、プラシーボ効果かも。
ListView等の高さや幅を動的に変更する方法
AndroidのListView等はメソッドにsetHeight(int)等がありません。
アプリ開発で動的に変更する必要があったので調査してみました。
ListView listView = (ListView)findViewById(R.id.list_view); listView.getLayoutParams().width = 100; listView.getLayoutParams().height = 100; listView.requestLayout();
ListViewからLayoutParamsを取得して、そこに高さや幅をセット。
最後にrequestLayoutを呼び出して設定を反映。
もっといい方法があるかもしれないけど、お約束の実現できたのでOKと・・・