2014年12月25日木曜日

AppCompat v7 の version 21 では、Fragment 入れ替えに
android.R.id.content を使ってはいけない

FragmentTransaction.replace() で android.R.id.content に MainFragment を入れる処理です。

public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getSupportFragmentManager() .beginTransaction() .replace(android.R.id.content, new MainFragment(), "MainFragment") .commit(); } public static class MainFragment extends ListFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setListAdapter(new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, new String[]{"test1", "test2", "test2"})); } } }
AppCompat v7 の version 19 だと普通に MainFragment が表示されます。 apply plugin: 'com.android.application' android { compileSdkVersion 19 buildToolsVersion "21.1.1" defaultConfig { applicationId "net.yanzm.sample2" minSdkVersion 15 targetSdkVersion 19 versionCode 1 versionName "1.0" } ... } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:19.+' }



しかし、AppCompat v7 の version 21 にすると、でない! apply plugin: 'com.android.application' android { compileSdkVersion 21 ... defaultConfig { ... targetSdkVersion 21 ... } ... } dependencies { ... compile 'com.android.support:appcompat-v7:21.0.3' }

悲しみ。。。

レイアウトファイルを用意すれば表示されるけど。。。 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" /> public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getSupportFragmentManager() .beginTransaction() .replace(R.id.content, new MainFragment(), "MainFragment") .commit(); } ... }


バグなんだろうか。。。

↓多分同じこと言ってる http://stackoverflow.com/questions/27460502/appcompat-v7-android-5-0-actionbaractivity-textview-not-visible





追記: getSupportActionBar() を呼ぶと表示された。。。謎い。 public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // これを呼ぶと表示される!なぜだ! getSupportActionBar(); getSupportFragmentManager() .beginTransaction() .replace(android.R.id.content, new MainFragment(), "MainFragment") .commit(); } ... }


2014年12月19日金曜日

Android AlertDialog のボタンの色を変える Material Design 編

■ Lollipop

values/styles.xml <?xml version="1.0" encoding="utf-8"?> <resources> <style name="AppTheme" parent="Theme.Material.Light.DarkActionBar"> <item name="android:alertDialogTheme">@style/AlertDialogTheme</item> </style> <style name="AlertDialogTheme" parent="Theme.Material.Light.Dialog"> <item name="android:colorAccent">#61a00e</item> </style> </resources>


■ AppCompat

values/styles.xml <?xml version="1.0" encoding="utf-8"?> <resources> <style name="AppThemeBase" parent="Theme.AppCompat.Light.DarkActionBar"> </style> </resources> values-v21/styles.xml <?xml version="1.0" encoding="utf-8"?> <resources> <style name="AppTheme" parent="AppThemeBase"> <item name="android:alertDialogTheme">@style/AlertDialogTheme</item> </style> <style name="AlertDialogTheme" parent="Theme.AppCompat.Light.Dialog"> <item name="colorAccent">#61a00e</item> </style> </resources>


2014年12月15日月曜日

MQTT ハンズオンに参加しました。

MQTT ハンズオン http://connpass.com/event/9765/ に参加してきました。

会場はクラウド会計ソフトを開発されている freee さんでした。しゃれおつなオフィスでした。卓球台とダーツがありました。(GTUG Girlsの勉強会でも使えるといいな)

講師の @r_rudi さん、@voluntas さんありがとうございました。

MQTT については時雨堂さんの解説を読むのがいいと思います。


ハンズオンでは大きく分けて2つのことをやりました。
  • mqttcli を使ってみる
  • いろんな言語で MQTT クライアントを作ってみる
当日はハンズオン用のHost(Brokerともいう)が用意されていましたが、復習したい場合は sango の無料プランで試すのがおすすめです。 githubアカウントでログインできるので簡単。


■ mqttcli を使ってみる

mqttcliはシェルスクリプト用の MQTT クライアントです。
インストール方法は mqttcli のページを見てください。

MQTTサーバーに接続するための設定を指定する方法が4つあります。
  • ~/.mqttcli.cfg に設定を書く
  • --conf で設定ファイルを指定する
  • --host, --port, --user, --password で指定する
  • $MQTT_HOST, $MQTT_POST, $MQTT_USERNAME, $MQTT_PASSWORD を設定する
~/.mqttcli.cfg に設定を書くのが簡単です。

こんな感じの JSON を書きます。 { "host": "localhost", "port": 1883, "username": "yanzm", "password": "hogehoge" }
sango だとこんな感じ { "host": "lite.mqtt.shiguredo.jp", "port": 1883, "username": "yanzm@github", "password": "hogehoge" } *追記: port は int なので " で囲わないのが正しいそうです。

コンソールを2つ開いて

片方(subscribe側)で $ mqttcli sub -t 'yanzm@github/say' *-t でトピックを指定します。sangoではアクセス先トピックがユーザー毎に分かれています。

もう片方(publish側)で $ mqttcli pub -t 'yanzm@github/say' -m 'hello' とすると、subscribe側に hello と表示されます。

当日は subscribe したメッセージを say コマンドに流すPCが用意されていて、参加者がそこに publish して楽しみました。

自分のPCでやるには、

日本語の Voice がダウンロードされていないと、say コマンドで日本語を読んでくれないので、
[System Preferences...] → [Dictation & Speech] → [Text to Speech] タブ → [System Voice:] → [Customize...] → Kyoko と Otoya をチェックしてOK
で日本語の Voice をダウンロードしておきます。
ダウンロードが終ったら、[System Voice:] を Kyoko か Otoya にしておきます。

$ mqttcli sub -t 'yanzm@github/say' | while read line ; do echo $line | say ; done とすると、publish するたびにメッセージを読み上げてくれます。楽しい!


■ いろんな言語で MQTT クライアントを作ってみる

Sango MQTT ドキュメント の中から好きな言語でクライアントを作って、pub/sub して遊びました。


■ Github の MQTT publish に挑戦

Github のリポジトリの設定に Webhooks & Services がありますが、Services に MQTT publish が用意されています。これを使うと、commit や push があったときに publish されるようなのですが、自分のリポジトリで試してもうまくいきませんでした。。。なぜだろう。。。



2014年12月12日金曜日

【プログラマー向け】よく使うフォトショ機能

Tech Women Advent Calendar 2014 の12日目として書いてます。

私は自分でアイコンやゴーファーを描いたりもしますが、 Photshop は既存の画像を加工する(Android SDK に入ってる画像の色を変えて利用するとか)のに使うことが多いです。

イラストや写真加工などガチな用途ではないですが、ちょっとした方法を知っておくといろいろと便利です。
そこで、私がアプリ開発や執筆でよく使う機能を紹介します。


1. 切り抜きツール



切り抜くサイズ(幅・高さ)を指定すると、エリア指定がそのアスペクト比になります。
切り抜いたサイズはそのサイズに自動でリサイズされます。

切り抜くサイズは幅・高さのどちらかだけ指定することもできます。
もちろん指定しない(=フォームを空にしておく)こともできます。
これらの場合はエリア指定のアクペクト比は固定されません。






2. マジック消しゴムツール



連続している同じ色のエリアを消してくれます。
背景色がついている画像で背景を透明にしたいときに便利です。



背景の円にあててみると...






3. スポイトツール



クリックしたピクセルの色がわかります。




4. ものさしツール



ドラッグした長さがわかります。
長さは上部のバーに表示されます。







5. レイヤースタイル

レイヤーに効果をつけます。
レイヤーのサムネイルをダブルクリックするとレイヤースタイルダイアログが開きます。



よく使うのが「ドロップシャドウ」と「カラーオーバーレイ」。

ピクトグラムの色を変えたいときに「カラーオーバーレイ」が便利です。
Android SDK に入ってる画像の色をかえるときとか。

「カラーオーバーレイ」


「ドロップシャドウ」



6. モザイク

原稿に載せるアプリのスクリーンショットでメールアドレスを消すのとかによく使ってます。
モザイクにしたい部分を選択ツールで選択してから
フィルター → ピクセレート → モザイク... で適用します。







まとめ

PhotoShop といえば、色調補正とかマスクとか写真加工に使うものというイメージがあるかもしれませんが、 アイコンの修正や加工にも便利なので、プログラマーで使ってみるといいと思います!


2014年12月10日水曜日

MaterialTabHost つくりました。

Android Material Design のタブの仕様まとめ」の Fixed Tabs の3つの種類を簡単に実装できる MaterialTabHost を作りました。 github : yanzm/MaterialTabHost

MaterialTabHost.Type.FullScreenWidth





MaterialTabHost.Type.Centered





MaterialTabHost.Type.LeftOffset





2014年12月9日火曜日

Android Material Design な TabHost + ViewPager に移行する

前回のエントリ「Android Material Design のタブの仕様まとめ」で紹介した Material Design の Fixed Tabs full-screen width を TabHost で実装してみます。

Activity 生成ウィザードの Tabbed Activity で Swipe Views(ViewPager)を選択して作成される Activity に付け足していきます。

*以下の例は minSdkVersion = 21 としています。


0. テーマに色をセット

メインカラーとアクセントカラーをセットしておきます

res/values-v21/styles.xml <?xml version="1.0" encoding="utf-8"?> <resources> <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar"> <item name="android:colorPrimaryDark">#00695C</item> <item name="android:colorPrimary">#00897B</item> <item name="android:colorAccent">#FFD54F</item> </style> </resources>


1. 普通に TabHost を実装する <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TabHost android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/colorPrimary"> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" /> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="0dp" android:layout_height="0dp" /> </TabHost> <android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Create the adapter that will return a fragment for each of the three // primary sections of the activity. mSectionsPagerAdapter = new SectionsPagerAdapter(getFragmentManager()); // Set up the ViewPager with the sections adapter. mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(mSectionsPagerAdapter); TabHost tabHost = (TabHost) findViewById(android.R.id.tabhost); tabHost.setup(); for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) { tabHost.addTab(tabHost .newTabSpec(String.valueOf(i)) .setIndicator(mSectionsPagerAdapter.getPageTitle(i)) .setContent(android.R.id.tabcontent)); } tabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() { @Override public void onTabChanged(String tabId) { mViewPager.setCurrentItem(Integer.valueOf(tabId)); } }); mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener(){ @Override public void onPageSelected(int position) { super.onPageSelected(position); tabHost.setCurrentTab(position); } }); }

基本的なタブ機能が実装できましたが、Material Design に沿ったタブにするにはいくつか修正が必要です。
  • タブ間の区切り線を消す
  • Action Bar と タブの間の影を消す
  • タブの下に影を出す
  • タブの文字を仕様に合わせる
  • ViewPager のスクロールに応じてインディケータもスクロールさせる


2. タブ間の区切り線を消す @Override protected void onCreate(Bundle savedInstanceState) { ... TabWidget tabWidget = (TabWidget) findViewById(android.R.id.tabs); // タブ間の区切り線を消す tabWidget.setStripEnabled(false); tabWidget.setShowDividers(LinearLayout.SHOW_DIVIDER_NONE); }


3. Action Bar と タブの間の影を消す @Override protected void onCreate(Bundle savedInstanceState) { ... // Action Bar と タブの間の影を消す getActionBar().setElevation(0); }


4. タブの下に影を出す @Override protected void onCreate(Bundle savedInstanceState) { ... // タブの下に影を出す // App bar の elevation は 4dp // http://www.google.com/design/spec/what-is-material/objects-in-3d-space.html#objects-in-3d-space-elevation float elevation = 4 * getResources().getDisplayMetrics().density; tabHost.setElevation(elevation); }


5. タブの文字を仕様に合わせる

res/color/tab_widget_text.xml <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="#ffffffff" android:state_selected="true" /> <item android:color="#99ffffff" /> </selector> res/layout/tab_widget.xml <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="bottom|center_horizontal" android:paddingBottom="16dp" android:textColor="@color/tab_widget_text" android:textSize="14sp" tools:layout_width="match_parent" tools:text="group 1 ITEM TWO" /> @Override protected void onCreate(Bundle savedInstanceState) { ... LayoutInflater inflater = LayoutInflater.from(this); for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) { TextView tv = (TextView) inflater.inflate(R.layout.tab_widget, tabWidget, false); tv.setText(mSectionsPagerAdapter.getPageTitle(i)); tabHost.addTab(tabHost .newTabSpec(String.valueOf(i)) .setIndicator(tv) .setContent(android.R.id.tabcontent)); } ... }


6. ViewPager のスクロールに応じてインディケータもスクロールさせる <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TabHost android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/colorPrimary"> ... <View android:id="@+id/indicator" android:layout_width="match_parent" android:layout_height="2dp" android:layout_gravity="bottom" android:background="?android:attr/colorControlActivated" /> </TabHost> ... </LinearLayout> View indicator; TabWidget tabWidget; @Override protected void onCreate(Bundle savedInstanceState) { ... tabWidget = (TabWidget) findViewById(android.R.id.tabs); ... indicator = findViewById(R.id.indicator); mViewPager.setOnPageChangeListener(new PageChangeListener()); } private class PageChangeListener implements ViewPager.OnPageChangeListener { private int scrollingState = ViewPager.SCROLL_STATE_IDLE; @Override public void onPageSelected(int position) { // スクロール中はonPageScrolled()で描画するのでここではしない if (scrollingState == ViewPager.SCROLL_STATE_IDLE) { updateIndicatorPosition(position, 0); } tabWidget.setCurrentTab(position); } @Override public void onPageScrollStateChanged(int state) { scrollingState = state; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { updateIndicatorPosition(position, positionOffset); } private void updateIndicatorPosition(int position, float positionOffset) { View tabView = tabWidget.getChildTabViewAt(position); int indicatorWidth = tabView.getWidth(); int indicatorLeft = (int) ((position + positionOffset) * indicatorWidth); final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) indicator.getLayoutParams(); layoutParams.width = indicatorWidth; layoutParams.setMargins(indicatorLeft, 0, 0, 0); indicator.setLayoutParams(layoutParams); } }


Android Material Design のタブの仕様まとめ

Tabs - Components - Google design guidelines

共通の仕様

  • タブの高さ : 48dp
  • indicatorの高さ : 2dp
  • タブ間に区切り線なし
  • 文字サイズ : 14sp Roboto Medium
  • 文字位置 : ベースライン = タブ下端から20dp
  • 文字色 :
    • 選択・非選択で同じ色 : 選択 = #ffffff、非選択 = #99ffffff
    • 選択・非選択で別の色 : 選択 = indicatorの色、非選択 = 無彩色

http://material-design.storage.googleapis.com/publish/v_2/material_ext_publish/0Bx4BSt6jniD7OWxWU0JiNjA4Vzg/components_tabs_usage_specs1.png

Fixed Tabs

■ full-screen width
  • タブ幅 : 画面幅 / タブ数
  • タブ数 : せいぜい4つまで。タブレットではタブが横に広がりすぎるので centered か left offset の方がよい


■ centered
  • タブ幅 : 12dp + 最も長いラベル幅 + 12dp(タブレットでは24dp)
  • タブ数 : タブ幅 x タブ数が画面幅を超えない数まで可能
  • タブは中央に寄せる


■ left offset
  • タブ幅 : 12dp + ラベル幅 + 12dp(タブレットでは24dp)
  • タブ数 : オフセット + タブ幅の和が画面幅を超えない数まで可能
  • 画面左端から選択されているタブのラベル左端までのオフセット = 72dp(タブレットでは 80dp)


Types of tabs の Fixed tabs に「Fixed tabs have equal width, based on the widest tab label.」とあるので、それに従ったのが centered、しかし隣の画像をみると Scrollable と同じようにタブによって幅が変わっているのでそれに従ったのが left offset とした。

Scrollable

  • タブ幅 : 12dp + ラベル幅 + 12dp(タブレットでは24dp)
  • 画面左端から選択されているタブのラベル左端までのオフセット = 72dp(タブレットでは 80dp)


2014年12月4日木曜日

Android Activity Transitions の対象から、Navigation Bar と Status Bar を外す(Activity Transitions を実装する その2)

Android Activity Transitions を実装する」の続きです。

Activity Transitions の対象から Navigation Bar と Status Bar を外すには、Transition.excludeTarget() を使います。

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); Transition transition = new Fade(); // Navigation Bar の背景を対象から外す transition.excludeTarget(android.R.id.navigationBarBackground, true); // Status Bar の背景を対象から外す transition.excludeTarget(android.R.id.statusBarBackground, true); getWindow().setExitTransition(transition); getWindow().setEnterTransition(transition);

↓ 開発者オプションでアニメーションの速度をx5にしています。



Android Activity Transitions を実装する

Activity Transitions のドキュメントを読んだのですが、よくわからなかったので実際に試してみました。

1. window content transitions を有効にする

Activity Transitions を使うには、window content transitions を有効にする必要があります。

コードで有効にする場合 getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); テーマで有効にする場合 <style name="BaseAppTheme" parent="android:Theme.Material"> <item name="android:windowContentTransitions">true</item> </style>

2. transition を指定する

切り替え時のアニメーションを指定します。用意されているクラスは Explode, Fade, Slide です。
自分で transition を作るときは、これらの親クラスの Visibility を継承するのがよさそうです。

コードで指定する場合 getWindow().setExitTransition(new Explode()); Explode, Fade, Slide に対応するリソースは
@android:transition/explode
@android:transition/fade
@android:transition/slide
です。

テーマで指定する場合 <style name="BaseAppTheme" parent="android:Theme.Material"> <item name="android:windowEnterTransition">@android:transition/explode</item> <item name="android:windowExitTransition">@android:transition/explode</item> </style>

3. ActivityOptions を指定して実行

ActivityOptions.makeSceneTransitionAnimation() を使います。
Android Developers のドキュメントでは、引数に this しか渡していませんが、引数が1つのメソッドはありません!(2014年12月4日現在)
次のように第2引数に null を指定します。 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, null).toBundle()); 全体としてこんな感じになります。 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); getWindow().setExitTransition(new Explode()); setContentView(R.layout.activity_main); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { gotoSub(); } }); } private void gotoSub() { Intent intent = new Intent(this, SubActivity.class); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, null).toBundle()); } } Explode


Fade


Slide




4. sharedElement を指定する

遷移前の画面の一部が拡大などして、遷移後の画面の一部になるような transition が可能です。
そのためには、どの View が共通として見なせるのか指定する必要があります。

遷移前後両方のActivityで、一意の名前を指定します。
レイアウトで指定する場合は android:transitionName を使います。

以下では、それぞれの Activity のレイアウトで ImageView に android:transitionName="image" を指定しています。

遷移前(activity_main.xml) <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" ...> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#cccccc" android:src="@drawable/ic_launcher" android:transitionName="image" /> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="go to SubActivity" /> </RelativeLayout> 遷移後(activity_sub.xml) <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" ...> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="200dp" android:background="#cccccc" android:src="@drawable/gopher" android:transitionName="image" /> </RelativeLayout> * わかりやすさのために、遷移前後の ImageView の画像を変えています。


android:transitionName に指定した名前を makeSceneTransitionAnimation() でも指定する必要があります。 public class MainActivity extends Activity { ... private void gotoSub() { ImageView iv = (ImageView) findViewById(R.id.image); Intent intent = new Intent(this, SubActivity.class); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, iv, "image").toBundle()); } }




android:transitionName を使わずにコードで指定することもできます。 public class MainActivity extends Activity { ... private void gotoSub() { ImageView iv = (ImageView) findViewById(R.id.image); String sharedElementName = "image"; iv.setTransitionName(sharedElementName); Intent intent = new Intent(this, SubActivity.class); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, iv, sharedElementName) .toBundle()); } } public class SubActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sub); String sharedElementName = "image"; findViewById(R.id.image).setTransitionName(sharedElementName); } }

5. 複数の sharedElement を指定する

複数の View を共通のエレメントとして指定することができます。 public class MainActivity extends Activity { ... private void gotoSub() { ImageView iv = (ImageView) findViewById(R.id.image); String sharedElementName = "image"; iv.setTransitionName(sharedElementName); ImageView iv2 = (ImageView) findViewById(R.id.image2); String sharedElementName2 = "image2"; iv2.setTransitionName(sharedElementName2); Intent intent = new Intent(this, SubActivity.class); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, new Pair<View, String>(iv, sharedElementName), new Pair<View, String>(iv2, sharedElementName2)) .toBundle()); } } public class SubActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sub); String sharedElementName = "image"; findViewById(R.id.image).setTransitionName(sharedElementName); String sharedElementName2 = "image2"; findViewById(R.id.image2).setTransitionName(sharedElementName2); } }




6. sharedElement の transition を指定する

sharedElement の transition は setSharedElementEnterTransition()setSharedElementExitTransition() で指定します。その他の transition には setExitTransition() や setEnterTransition() で指定したものが適用されます。

コードで指定する場合 Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.custom); getWindow().setSharedElementExitTransition(transition); getWindow().setSharedElementEnterTransition(transition); XMLで用意した transition リソースから Transition オブジェクトを作るには TransitionInflater を使います。

テーマで指定する場合 sharedElement のデフォルトの transition は Theme.Material で指定されており、@android:transition/move になっています。
このファイルの中身は次のようになっています。

@android:transition/move <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> <changeBounds/> <changeTransform/> <changeClipBounds/> <changeImageTransform/> </transitionSet> changeBounds は View の大きさをアニメーションさせ、changeImageTransform は ImageView にセットされている画像サイズをアニメーションさせます。

transition リソースの書き方は Transition クラスに概要があります。

例えば、@android:transition/move の <transitionSet> に android:transitionOrdering="sequential" を指定すると、各アニメーションが順番に行われます。
res/transition/custom.xml <?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:transitionOrdering="sequential"> <changeBounds /> <changeTransform /> <changeClipBounds /> <changeImageTransform /> </transitionSet>





おまけ: デフォルト設定

2014年11月18日火曜日

Android Enter キーでフォーカスを移動するのは android:nextFocusDown ? android:nextFocusForward ?

singleLine の EditText で、Enter キーが押されたときに次のフィールドにフォーカスが移動すると使いやすいですよね。
ある程度は勝手にフォーカスが移動するようになるのですが、細かい部分は自分で指定しないと思い通りに移動してくれません。

例えば、次のように EditText が LinearLayout で縦に並んでいる場合、edit_text1 で Enter キーを押すと、edit_text2 へ移動してくれます。 <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <EditText android:id="@+id/edit_text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="text" /> <EditText android:id="@+id/edit_text2" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="text" /> <EditText android:id="@+id/edit_text3" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="text" /> </LinearLayout>

一方、次のように、edit_text1 と edit_text2 が横に並び、その下に edit_text3 がある場合、edit_text1 で Enter キーを押すと edit_text3 に移動します。 <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/edit_text1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="text" /> <EditText android:id="@+id/edit_text2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="text" /> </LinearLayout> <EditText android:id="@+id/edit_text3" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="text" /> </LinearLayout>

edit_text1 で Enter キーを押したときに edit_text2 に移動させるには、方法が2つあります。

1. android:nextFocusDown を指定する <EditText android:id="@+id/edit_text1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="text" android:nextFocusDown="@+id/edit_text2" />

2. android:nextFocusForward と android:imeOptions を指定する <EditText android:id="@+id/edit_text1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="text" android:nextFocusForward="@+id/edit_text2" android:imeOptions="actionNext" />

次へ、なんだから android:nextFocusForward だろうと思いきや、こちらは android:imeOptions(他にも android:imeActionLabel とか android:imeActionId などの指定でも可)を指定するという workaround が必要になります。バグじゃないかと思うんだけど。。。
ちなみに、Tab を押したときには android:nextFocusForward で指定された id の View に移動します。


解説

キモのメソッドは TextView.onCreateInputConnection() と TextView.onEditorAction() です。
4.x はそれぞれ微妙にコードが違うのですが、4.4.2 (API Level 19)のコードで説明します。

ちょっと長いですが、android:singleLine="true" や android:inputType="text" が指定されている EditText では、focusSearch(FOCUS_DOWN) で次に移動できる View を探します(ここでは FOCUS_DOWN なのよね...)。

移動できる View があって、IME_ACTION が明示的に指定されていない場合、IME_ACTION として EditorInfo.IME_ACTION_NEXT が指定されます。 @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { if (onCheckIsTextEditor() && isEnabled()) { mEditor.createInputMethodStateIfNeeded(); outAttrs.inputType = getInputType(); if (mEditor.mInputContentType != null) { outAttrs.imeOptions = mEditor.mInputContentType.imeOptions; outAttrs.privateImeOptions = mEditor.mInputContentType.privateImeOptions; outAttrs.actionLabel = mEditor.mInputContentType.imeActionLabel; outAttrs.actionId = mEditor.mInputContentType.imeActionId; outAttrs.extras = mEditor.mInputContentType.extras; } else { outAttrs.imeOptions = EditorInfo.IME_NULL; } if (focusSearch(FOCUS_DOWN) != null) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT; } if (focusSearch(FOCUS_UP) != null) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS; } if ((outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_UNSPECIFIED) { if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0) { // An action has not been set, but the enter key will move to // the next focus, so set the action to that. outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT; } else { // An action has not been set, and there is no focus to move // to, so let's just supply a "done" action. outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE; } if (!shouldAdvanceFocusOnEnter()) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION; } } if (isMultilineInputType(outAttrs.inputType)) { // Multi-line text editors should always show an enter key. outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION; } outAttrs.hintText = mHint; if (mText instanceof Editable) { InputConnection ic = new EditableInputConnection(this); outAttrs.initialSelStart = getSelectionStart(); outAttrs.initialSelEnd = getSelectionEnd(); outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType()); return ic; } } return null; }

次に onEditorAction() です。

ict という変数の null チェックをしているのがポイントです。この変数が null じゃない場合、まず EditorActionListener の処理をします。
EditorActionListener がセットされているかをチェックし、セットされている場合は EditorActionListener.onEditorAction() を呼び、戻り値が true ならそこで処理を終了します。

EditorActionListener がセットされていない、またはセットされていても戻り値が false の場合は、actionCode に応じた処理をします。
actionCode が IME_ACTION_NEXT の場合 focusSearch(FOCUS_FORWARD) で View を探して、対応する View があればそれに対して requestFocus() を呼びます(ここは FOCUS_FORWARD)。
IME_ACTION_DONE の場合はキーボードを閉じます。

ict が null のときや、上記に対応する View がない場合などは、その下の処理が行われます。つまり、KeyEvent.KEYCODE_ENTER の KeyEvent.ACTION_DOWN と KeyEvent.ACTION_UP を ViewRootImpl.dispatchKeyFromIme() で割り当てます。
これにより、onKeyDown() と onKeyUp() が呼ばれることになります。

ict が null かどうかによらず actionCode に対応した処理部分を呼ぶようにするべきなんじゃないかなと思いました。。。 public void onEditorAction(int actionCode) { final Editor.InputContentType ict = mEditor == null ? null : mEditor.mInputContentType; if (ict != null) { if (ict.onEditorActionListener != null) { if (ict.onEditorActionListener.onEditorAction(this, actionCode, null)) { return; } } // This is the handling for some default action. // Note that for backwards compatibility we don't do this // default handling if explicit ime options have not been given, // instead turning this into the normal enter key codes that an // app may be expecting. if (actionCode == EditorInfo.IME_ACTION_NEXT) { View v = focusSearch(FOCUS_FORWARD); if (v != null) { if (!v.requestFocus(FOCUS_FORWARD)) { throw new IllegalStateException("focus search returned a view " + "that wasn't able to take focus!"); } } return; } else if (actionCode == EditorInfo.IME_ACTION_PREVIOUS) { View v = focusSearch(FOCUS_BACKWARD); if (v != null) { if (!v.requestFocus(FOCUS_BACKWARD)) { throw new IllegalStateException("focus search returned a view " + "that wasn't able to take focus!"); } } return; } else if (actionCode == EditorInfo.IME_ACTION_DONE) { InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null && imm.isActive(this)) { imm.hideSoftInputFromWindow(getWindowToken(), 0); } return; } } ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { long eventTime = SystemClock.uptimeMillis(); viewRootImpl.dispatchKeyFromIme( new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE | KeyEvent.FLAG_EDITOR_ACTION)); viewRootImpl.dispatchKeyFromIme( new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE | KeyEvent.FLAG_EDITOR_ACTION)); } }

onKeyUp() での KeyEvent.KEYCODE_ENTER の処理は onEditorAction() と同じような感じなのですが、focusSearch() & requestFocus() する対象が FOCUS_DOWN です(こっちは FOCUS_DOWN !) @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (!isEnabled()) { return super.onKeyUp(keyCode, event); } if (!KeyEvent.isModifierKey(keyCode)) { mPreventDefaultMovement = false; } switch (keyCode) { ... case KeyEvent.KEYCODE_ENTER: if (event.hasNoModifiers()) { if (mEditor != null && mEditor.mInputContentType != null && mEditor.mInputContentType.onEditorActionListener != null && mEditor.mInputContentType.enterDown) { mEditor.mInputContentType.enterDown = false; if (mEditor.mInputContentType.onEditorActionListener.onEditorAction( this, EditorInfo.IME_NULL, event)) { return true; } } if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 || shouldAdvanceFocusOnEnter()) { /* * If there is a click listener, just call through to * super, which will invoke it. * * If there isn't a click listener, try to advance focus, * but still call through to super, which will reset the * pressed state and longpress state. (It will also * call performClick(), but that won't do anything in * this case.) */ if (!hasOnClickListeners()) { View v = focusSearch(FOCUS_DOWN); if (v != null) { if (!v.requestFocus(FOCUS_DOWN)) { throw new IllegalStateException( "focus search returned a view " + "that wasn't able to take focus!"); } /* * Return true because we handled the key; super * will return false because there was no click * listener. */ super.onKeyUp(keyCode, event); return true; } else if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0) { // No target for next focus, but make sure the IME // if this came from it. InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null && imm.isActive(this)) { imm.hideSoftInputFromWindow(getWindowToken(), 0); } } } } return super.onKeyUp(keyCode, event); } break; } ... }

つまり、ict が null になってしまうときは android:nextFocusDown に指定したものに、null にならないときは android:nextFocusForward に指定したものに移動します。
ややこし。。。
明示的に android:imeOptions="actionNext" を指定するのは、この ict が null にならないようにするためです。 (ict が null にならないようにする方法は他にもあって、android:imeActionLabel を指定するとかいくつかあります。)


2014年11月17日月曜日

Android CheckBox 画像の正しいカスタマイズ方法

各テーマでのチェックボックスのスタイルは android:checkboxStyle で指定されています。
以下は 4.0.3 のコードですが、この部分は 3.0 で Theme.Holo が追加されて以降特に変わっていません。

http://tools.oesf.biz/android-4.0.3_r1.0/xref/frameworks/base/core/res/res/values/themes.xml ... ... ...

Widget.Holo.CompoundButton.CheckBox と Widget.Holo.Light.CompoundButton.CheckBox を見ると、Widget.CompoundButton.CheckBox をそのまま継承しているだけです。

http://tools.oesf.biz/android-4.0.3_r1.0/xref/frameworks/base/core/res/res/values/styles.xml#1003 つまり、Theme であろうが Theme.Holo であろうが Widget.CompoundButton.CheckBox が使われるということです。


このスタイルですが、4.1 と 4.2 で微妙に変わります。

4.1まで

http://tools.oesf.biz/android-4.1.2_r1.0/xref/frameworks/base/core/res/res/values/styles.xml#345
4.2以降

http://tools.oesf.biz/android-4.2.0_r1.0/xref/frameworks/base/core/res/res/values/styles.xml#345 4.1 までは Widget.CompoundButton.CheckBox で android:background が指定されていますが、4.2 以降ではなくなっています。


実は 4.2 で paddingLeft の使い方が変わりました。Rtl に対応するためだと思われます。

4.1 までは左端からテキストの間が paddingLeft でした。そのため、チェックボックス画像分の paddingLeft を指定するために、背景画像が使われていました。
一方、4.2 からはチェックボックス画像とテキストの間が paddingLeft になりました。



チェックボックスをカスタマイズする場合

この違いのため、チェックボックスをカスタマイズする場合は注意が必要です。

例えばチェックボックス画像を 32 x 32 dp で作成し、画像とテキストの間を 4dp あけたいとしたら、次のように values/dimens.xml と values-v17/dimens.xml を用意する必要があります。

values/dimens.xml 36dp values-v17/dimens.xml 4dp


チェックボックスの画像を変えるには、checkboxStyle に指定するスタイルで android:button に drawable を指定するか、テーマで android:listChoiceIndicatorMultiple に drawable を指定します。

values/styles.xml or




おまけ : CompoundButton.java の変更について

onDraw() については、Rtl のとき右端に描画されるようになっているだけで、paddingLeft の扱いは変わっていません。

4.1.2

http://tools.oesf.biz/android-4.1.2_r1.0/xref/frameworks/base/core/java/android/widget/CompoundButton.java#228 227 @Override 228 protected void onDraw(Canvas canvas) { 229 super.onDraw(canvas); 230 231 final Drawable buttonDrawable = mButtonDrawable; 232 if (buttonDrawable != null) { 233 final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; 234 final int height = buttonDrawable.getIntrinsicHeight(); 235 236 int y = 0; 237 238 switch (verticalGravity) { 239 case Gravity.BOTTOM: 240 y = getHeight() - height; 241 break; 242 case Gravity.CENTER_VERTICAL: 243 y = (getHeight() - height) / 2; 244 break; 245 } 246 247 buttonDrawable.setBounds(0, y, buttonDrawable.getIntrinsicWidth(), y + height); 248 buttonDrawable.draw(canvas); 249 } 250 }
4.2.0

http://tools.oesf.biz/android-4.2.0_r1.0/xref/frameworks/base/core/java/android/widget/CompoundButton.java#252 251 @Override 252 protected void onDraw(Canvas canvas) { 253 super.onDraw(canvas); 254 255 final Drawable buttonDrawable = mButtonDrawable; 256 if (buttonDrawable != null) { 257 final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; 258 final int drawableHeight = buttonDrawable.getIntrinsicHeight(); 259 final int drawableWidth = buttonDrawable.getIntrinsicWidth(); 260 261 int top = 0; 262 switch (verticalGravity) { 263 case Gravity.BOTTOM: 264 top = getHeight() - drawableHeight; 265 break; 266 case Gravity.CENTER_VERTICAL: 267 top = (getHeight() - drawableHeight) / 2; 268 break; 269 } 270 int bottom = top + drawableHeight; 271 int left = isLayoutRtl() ? getWidth() - drawableWidth : 0; 272 int right = isLayoutRtl() ? getWidth() : drawableWidth; 273 274 buttonDrawable.setBounds(left, top, right, bottom); 275 buttonDrawable.draw(canvas); 276 } 277 } 4.2 で padding の扱いが変わった理由は、getCompoundPaddingLeft() と getCompoundPaddingRight() を Override して padding を上書きするようになったからです。 227 @Override 228 public int getCompoundPaddingLeft() { 229 int padding = super.getCompoundPaddingLeft(); 230 if (!isLayoutRtl()) { 231 final Drawable buttonDrawable = mButtonDrawable; 232 if (buttonDrawable != null) { 233 padding += buttonDrawable.getIntrinsicWidth(); 234 } 235 } 236 return padding; 237 } 238 239 @Override 240 public int getCompoundPaddingRight() { 241 int padding = super.getCompoundPaddingRight(); 242 if (isLayoutRtl()) { 243 final Drawable buttonDrawable = mButtonDrawable; 244 if (buttonDrawable != null) { 245 padding += buttonDrawable.getIntrinsicWidth(); 246 } 247 } 248 return padding; 249 } このメソッドは TextView の onMeasure() などから呼ばれています。


2014年11月3日月曜日

Android Studio でよく使うコマンド

command + o : クラス検索で開く
command + shif + o : ファイル名検索で開く
command + e : Recent Files
command + n : Generate...
option + F7 : find usage
option + command + l : 整形
option + command + v : 左辺を挿入
ctrl + option + o : optimize imports
shift + command + @ : 左のタブに移動
shift + command + [ : 右のタブに移動
command + l : Go to Line
command + shift + delete : 前回の編集位置に飛ぶ



2014年10月26日日曜日

GAE Go で static なサイトをホストする

0. python 2.7 が必要

1. https://cloud.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go から SDK をダウンロードして解凍する

2. PATH に解凍したディレクトリを設定する

export PATH=/path/to/go_appengine:$PATH

3. フォルダ構成

myapp/ app.yaml index.html img/ css/ js/ src/ main.go

4. app.yaml

application: myapp-application-id version: 1 runtime: go api_version: go1 handlers: - url: /(.*\.html)$ static_files: \1 upload: .*\.html$ - url: /img static_dir: img - url: /css static_dir: css - url: /js static_dir: js - url: /.* script: _go_app

5. main.go

static dir / static files 以外の全ての URL を /index.html にリダイレクトする package myapp import ( "net/http" ) func init() { http.HandleFunc("/", handler) } func handler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "index.html", http.StatusFound) }

6. ローカルでテスト

$ cd myapp $ goapp serve localhost:8080 で実行される

7. Deploy

$ cd myapp $ goapp deploy --oauth


2014年9月3日水曜日

Android の Chrome をリモートデバッグ

メモ

1. [デバイス] 開発者オプションを有効にする
2. [PC] Chrome で chrome://inspect にアクセスし、Discover USB devices にチェックを入れる
3. デバイスとPCをUSBで接続する
4. デバッグしたいタグの inspect をタップする

https://developer.chrome.com/devtools/docs/remote-debugging


vulcanize を使ってみた

vulcanize は Polymer 用の最適化ツールで、Web Components を1つのファイルにできる。
「Concatenate a set of Web Components into one file」

Polymer/vulcanize : github

GTUG Girls のサイトに使ってみた。

$ vulcanize -o index.html index_raw.html

Web Components が index.html に結合され、platform.js と polymer.js だけ残る。


Google の PageSpeed Insights にかけてみた
  • vulcanize 前 : モバイル 81/100、パソコン 89/100
  • vulcanize 後 : モバイル 74/100、パソコン 86/100
「HTML を縮小する」が増えたため点数は下がった。

だいたい3000行くらい(index_raw.html は125行)になったので、-s オプションを付けてコメントと空行を削ったら1000行くらいになった。

$ vulcanize -s -o index.html index_raw.html


Request数
  • vulcanize 前 : 68 requests
  • vulcanize 後 : 42 requests
実際ブラウザで表示されるまでの時間が短くなっているような気もする。


2014年8月6日水曜日

ゴーファーのイラストを描きました。

Illustratorの練習でゴーファーのイラストを描きました。

















Tシャツほしいという方がいたので、suzuri.jp を初めて使ってみました。便利ですね。

https://suzuri.jp/yanzm







もともとの Go gopherRenee French さんによってデザインされています。
(上のはぬいぐるみのゴーファーがベースなのでオリジナルにはあんまり似ていないです。ぬいぐるみのゴーファー好きなのです。)


2014年7月28日月曜日

Material Design のハンバーガーアイコン

ハンバーガーアイコンは Navigation Drawer を使うときに左上に表示する三本線のアイコンのことです。



Icons - Style - Google design guidelines にも Resources - Google design guidelines にもアイコンリソースが無かったので、Icons のページにあった絵と線の太さが2dpであるという情報からハンバーガーアイコンをaiファイルに起こしました。

ちなみに設計図は次のようになります。1マスが1dpです。



画像のサイズは 48dp x 48dp で、アイコン部分は 24dp x 24dp に収まるようにします。

aiファイルも置いておきます。



2014年7月23日水曜日

Polymer <a> 内の <content> は Chrome ではリンクにならない

リンクテキストの右側にアイコンを付けるだけのコンポーネントがあるとします。 <link rel="import" href="../core-icon/core-icon.html"> <polymer-element name="icon-link" attributes="href"> <template> <style> a { color: #0277bd; text-decoration: none; } </style> <p> <a href="{{href}}" target="new"> <content></content> <core-icon icon="launch"></core-icon> </a> </p> </template> <script> Polymer({ }); </script> </polymer-element> これを、こんな感じで使うと <icon-link href="http://y-anz-m.blogspot.com">Y.A.Mの雑記帳</icon-link> Chrome(Version 36.0.1985.125)では <content></content> に挿入される文字がリンクになりません。




一方、Firefox(31.0)ではリンクになります。





次のように <content> ではなく、attributes を使うと Chrome でも文字部分がリンクになります。 <link rel="import" href="../core-icon/core-icon.html"> <polymer-element name="icon-link" attributes="href label"> <template> <style> a { color: #0277bd; text-decoration: none; } </style> <p> <a href="{{href}}" target="new"> {{label}} <core-icon icon="launch"></core-icon> </a> </p> </template> <script> Polymer({ }); </script> </polymer-element> <icon-link href="http://y-anz-m.blogspot.com" label="Y.A.Mの雑記帳"></icon-link>


Web Components に詳しくないので、よくわからないのですが Chrome の挙動は仕様通りなのかしら?


追記:実際に見れるリンクないの?と言われたのでつくりました。http://www.yanzm.net/sample1.html

追記2:Chrome 38(Chrome Canary)だと直っているそうだ。

追記3:以下のように<span>で囲むと Chrome 36 でもリンクとして動作する。 <icon-link href="http://y-anz-m.blogspot.com"><span>Y.A.Mの雑記帳</span></icon-link> しかし、マウスカーソルの形はホバーしても変わらない。。。たぶん、https://code.google.com/p/chromium/issues/detail?id=314488 の問題。


2014年7月18日金曜日

Google I/O 2014 Polymer セッションまとめ

Google I/O 2014 - Polymer and the Web Components revolution



Topeka というサンプルアプリを作った。
Polymer と Material Design をテストするため。
http://polymer-project.org/apps/topeka
トリビアアプリ
Leaderboardがある

PCのChromeブラウザ上でのデモ。画面サイズを変えても動く。
Nexus5のChrome Betaでも動く。

"Polymer is a library that makes building applications easier"

"Polymer is different than what has come before"

"Polymer was built differently"

Polymer チームは Chrome チームの一部。2年前の今日(I/Oの日)プロジェクトを始めた。

Polymer は Web Components で作られている。
Web Components はすべてを変える。
Custom elements を作ることができる。Shadow DOM を使っている。
HTML の再利用ができる。

Chrome 36 からネイティブ対応。(I/Oのときは)beta channel。2、3週間で stable に入る。
Chrome だけ?そんなことない。 polyfills のおかげで思っているよりも多くのブラウザで動く。

Polymer は boilerplate を減らす。
Polymer は宣言的なシンタックス。

どうやって Poymer を使うのか?
1. エレメントを使う
2. エレメントを作る

エレメントを使う
1. 使いたいエレメントを見つける
2. Import する
<link rel="import" href="my-button.html">
3. 使う
<my-button label="Press Me!"></my-button>

Polymer のエレメントはただのHTML

エレメントを作る
1. 新しいタグとプロトタイプを登録
2. ビューを定義
3. イベントを処理
4. データとビューを紐づける
5. 属性の変更に対応する

Polymer Core Elements
ベーシックなエレメント。あまりスタイリングされていない。
<core-icon>
<core-ajax>
<core-localstorage>
<core-style>
<core-tooltip>

ajax や localstorage のタグもある


Polyme Paper Elements
Material Design っぽいエレメント
Button, Input, Tab, Card, Panel など...

mozilla は というのを出しているが、Polymer と対立するものではない。

デザイナーツールを用意した。
Polymer Designer

最適化
Polymer Vulcanizer
すべての import をまとめて CSS, JS, HTML にする

テストには Karma, Chai, Mocha を使っている。


QA

Q : Window を縮めたとき、エレメントがリサイズされませんでした。これは、いつ Window のリサイズに応じるかという Polymer paper のビジョンのよるものですか? media queries を行き来するよりも、エレメントはその場に残りますか?
(あんまり質問の意味がわからない)

A : 見ているスクリーンによると思います。つまり、どのようにセットアップされているかによると思います。ほとんどのウィジェットはコンテナーの幅に依存します。


Q : Polymer はとても cool ですが、まだまだプロトタイプですよね。Angular とかのライブラリを使う必要がある場合、Polymer paper とかを取り入れることはできますか? それとも Polymer で最初からやり直さないといけない?

A : custom elements はただの HTML エレメントです。どのように HTML を扱うか知っていれば、Polymer elements も使うことができます。element の外側と内側の世界があります。外側の世界では Polymer は他の HTML エレメントと同じようなものです。プロパティ、メソッド、属性があります。内側の世界では Polymer が提供する利点があります。Polymer の内側をフレームワークで作ろうとした場合にトラブルが起きることがあるかもしれません。なぜなら、それらは Shadow DOM などの扱い方を知らないからです。


Q : Polymer components と x-tagas components 両方含むような component directory はありますか?

A : customelements.io のような Polymer かどうかによらないレジストリがあります。Polymer かどうかによらず、書く web component を使うことができます。


Q : Safari と他のブラウザのサポートはどうですか?

A : native support のことですよね? Mozilla はかなり進んでいます。custom elements はリリースされています。Shadow DOM はまだです。でも活発に進んでいます。Safari は正直わかりません。彼らは計画を公開していないので。Salesforce.com のような polyfill を使っている人たちによると、mobile Safari では、かなり許容できるパフォーマンスがあるようです。


Q : IE については?

A : IE は最近少しだけ計画について公開するようになってきました。web components について興味があるようです。計画について誰にも言わないので、正直わかりませんが。ときどき私たちのMLにでできます。WinJS と Polymer を合わせた実験を内部では完了しているようです。Polymer と web components に興味がありそうですが、様子を見る必要があります。


Q : Chromeでの実行パフォーマンスは素晴らしかったです。どのくらいが Polymer の影響で、どのくらいが Chrome のパフォーマンス改善によるものですか?

A : 実際、それらは混在しています。我々は Chrome チームの一部ですし。両方のコンビネーションです。


Q : long-term のパフォーマンスについて。DOM elements + DOM 操作と、DOM を最小化しようとする React(これでよい?)のようなフレームワークとを比較するとどうですか?

A : それは別の世界ですよね? polyfills を使った現状から考えると、Polymer は基本的に他の JavaScript フレームワークと同じくらいよいです。Chrome ならなおよいです。native support がありますから。
その他のこととして、数年前 Polymer チームは webOS の Enyo(これでよい?)と呼ばれるフレームワークに関わっていました。哲学的に React、Polymer、Enyo は component のマインドセットが似ています。ただ、アイディアとして、DOM にタッチしません。DOM は遅い、DOM にさわりたくないでしょう。そこで、大きい HTML を吐いていました。それが速かったですから。Chrome チームに移ったとき、Chrome の人たちはそれはよくないと言いました。そこから時間をかけていろいろ変えていきました。DOM APIs はすごく速くなりました。なので、その領域は改善の余地があるということです。Chrome がそうできたように。





Google I/O 2014 - Polymer and Web Components change everything you know about Web development



(現状、字幕のタイミングがスピーチとずれています。。。)

1. State of the union
2. Problems solved by web components
3. "Thinking in components"

2010年に Web Components の旅は始まった。

2014年のサポート状況



platform.js を使ったときの polyfills のサポート状況



みんな IE のこと質問するよね。希望はあるよ!



・Chrome OS の Keyboard は Polymer を使ってる。
・Chrome OS の Media Player は Polymer を使ってる。

Web Components はどんな問題を解決するのか。
・Gmail は div soup
・tab strip を作ろうとしたら、標準的な構成やパターンもないし、APIはいろんなフレームワークで違うし。。。



Custom elements を使うと、宣言的で読みやすいHTMLになる。



タグの継承もできる



HTML Template と比較してどうか





Polymer 内でデータをバインディングしたりできる



Polymer はデフォルトで Shadow DOM を使う



Polymer のエレメントは HTML imports で使う



Polymer の Core elements



Material Design elements



<google-map> タグを用意したよ!便利!
他のタグもチェック!

googlewebcomponents.github.io







Google I/O 2014 - Unlock the next era of UI development with Polymer



HTML は document-centric model なので、アプリっぽいものを作るのは苦手。
Polymer はアプリの作成に適している。


core-elements : ベースになるブロックにようなもの。あまりスタイル化されていない。

Visual
・<core-toolbar>
・<core-header-panel>
・<core-drawer-panel>
・<core-menu>
・<core-icon>
・<core-overlay>
...

Non-visual
・<core-ajax>
・<core-localstorage>
・<core-range>
・<core-shared-lib>
・<core-media-query>
・<core-iconset>



paper-elements : よりスタイル化されている。Material Design システムの一部

・<core-toolbar>

タブやボタン、タイトルを置くためのコンテナ
Android の ActionBar みたいなもの
core-icon-button でハンバーガーアイコンを表示したり、class="tall" で高さをかえたり




・<core-header-panel>

header セクション用のコンテナ
core-toolbar を上に pin させたり、一緒にスクロールさせたり




・<core-scroll-header-panel>

スクロール時のエフェクトができる




・<core-drawer-panel>

Navigation Drawer みたいなの





flexbox CSS

Polymer 定義の中でも外(=Polymerを使っているページ)でも使える。

Android の layout_weight みたいなことができる。










中央揃えも楽々。








Material controls

<paper-checkbox> : Material Design にそった、アニメーション付きのチェックボックス

<paper-toggle-button> : Material Design にそった、アニメーション付きのスイッチ

<paper-input> : Material Design にそった、アニメーション付きの入力フォーム
validate="^[0-9]*$" のように正規表現でバリデーションを指定できる

<paper-tabs> & <paper-tab> : Material Design にそった、アニメーション付きのタブ

<paper-ripple> : タッチ&クリックしたときに ripple エフェクトがおこる

<paper-shadow> : z="5" のように elevation を指定することで影の量を変えられる



Theming

::shadow

エレメントの shadow dom 内部のノードのスタイルを指定できる



/deep/

shadow 境界に穴をあけ、shadow dom 内のすべてにスタイルを指定できる



<core-style>

(まだ experimental)
style をエレメント間で共有する



style binding





Transitions

<core-animated-pages>

あるビューから次のビューへのスムーズなアニメーションを作れる



QA

Q : attributes の順番に依存はありますか?

A : 順番は関係ないです。


Q : jQuery とかと一緒に使うのはどうですか?

A : 外側から利用する場合は、これまでと一緒です。custom element を作るだけです。内部で利用したい場合は、慎重になる必要があります。


Q : shadow dom の内部では JavaScript はサンドボックスになりますか?

A : いいえ、JavaScript はページの一部です。


Q : Angular JS と一緒に使うのはどうですか?

A : 一般的には、ちゃんと動きそうです。複雑なデータを binding するときに問題が起こるかもしれません。ただし、HTML と同じように扱う分においては、問題ないです。


Q : Accessibility はどうですか?チェックボックスは canvas を使っているようですが。タブをキーボードで移動しようとしてもできませんでした。なにかロードマップはありますか?

A : はい。Accessibility は我々にとっても大きな懸念事項です。Alice Boxhall が DevByte で詳しく説明しています。ロードマップがありますし、サポートする予定があります。


Q : レガシーな Android の WebView ではどうですか?

A : 私の理解では、KitKat の WebView は Polymer で動くようです。Chromium ベースではにものは動かないでしょう。


Q : IE ではどうですか?

A : 他のブラウザでも動きますよ。他のすべてのブラウザでテストしています。Firefox、Safari、IE。。。各ブラウザの最新の2バージョンはサポートしています。それがターゲットです。


Q : Bootstrap に比べてどうですか?

A : Bootstrap は素晴らしいです。でもたくさんマークアップが必要です。nav bar とか。将来的には Bootstrap はエレメントのコレクションになるかもしれません。