2017年11月21日火曜日

iPhoneX時代の画面設計

iPhoneXが発売され、いわゆるアスペクト比2:1の端末が増えてきました
AndroidでもGaraxy S8、LG G8などが2:1に近いアスペクト比となっており、GoogleもAndroidアプリ開発者に対し、アスペクト比2:1以上の画面に対応することを促しています

例えば横スクロールのアクションゲームで、タブレット端末でよくある4:3の端末と
2:1の両対応を単純にしようとするとこのような画面になってしまいます
これだとステージの先、見えている敵キャラが違うためゲームの難易度に大きな違いが生じてしまいます
4:3のアスペクト比を基本にして、それよりも横長なら両端黒帯ってのは簡単な対応ですが、それは避けたいところですので、いくつか対応案を考えてみました
1つ目は2:1の横幅を基本として天井(空)の拡縮で対応する横幅固定の下寄せ
こんな感じ
見た目にかなり差が出ますが、見えているステージの範囲は同じになるので難易度に違いは出なくなるかと
2つ目は横スクロールでも天井がある場合や、シューティングの場合ステージの縦幅を変えるのは都合が悪い時に対応する横幅固定の真ん中寄せ
横幅は2:1を基本にして上下の地面と天井部分を拡縮します
ものによっては地面と天井の比率を変えてもよいかと思います

3つ目は2:1端末が増えてきたとはいえ、主流はまだまだ16:9のワイドなので、それを基本として調整するバランス型
横幅は16:9のワイドを基本にして、2:1端末では影響が出にくい自キャラの後ろ部分を拡縮してやります
4:3では上部分を拡縮して対応

以上3つの手法を紹介しましたが、やり方はまだまだあると思います
要はメインとなるステージの縦と横幅で変えられないところ、アスペクト比が変えられるのかというところを決めて、基本とする端末のアスペクト比からサイズ差を吸収する調整枠を決定するという流れになるかと思います

2017年11月10日金曜日

XMLで作るアニメーションするメニュー

新作アプリオタマーズ(仮)作ってます タイトルメニューに階層があって、切り替えにアニメーションをつけてみたので紹介します
ほぼほぼXMLで済ませているので簡単です
画面レイアウトのXMLがこちら

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 <ImageView
 <FrameLayout
  <LinearLayout
  <ImageButton
   android:id="@+id/setting"
  <ImageButton
   android:id="@+id/mission"
・・・
  </LinearLayout>
 </FrameLayout>
</LinearLayout>
くっそ長いので全文は下のほうに追いやりました
メニュー部分はFrameLayoutで直下のLinearLayoutに1階層目のボタン3つ
id="@+id/stageの部分が2階層目になっててvisible=goneで隠してます
次にAnimation用のXMLがこちら
// 1階層目メニュースライドイン
// res/anim/menu_in.xml

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/decelerate_interpolator"
 android:fromXDelta="-100%p"
 android:toXDelta="0%p"
 android:fillAfter="true"
 android:duration="800" />

// 1階層目メニュースライドアウト
// res/anim/menu_out.xml

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/decelerate_interpolator"
 android:fromXDelta="0%p"
 android:toXDelta="120%p"
 android:fillAfter="true"
 android:duration="800" />

// 2階層目メニューズームイン
// res/anim/zoom_in.xml

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/decelerate_interpolator"
 android:fromXScale="0"
 android:toXScale="1.0"
 android:fromYScale="0.0"
 android:toYScale="1.0"
 android:pivotX="50%"
 android:pivotY="50%"
 android:fillAfter="true"
 android:duration="600" />
で、ボタンを押したときの処理がこちら
動かすViewに上記のstartAnimationをセットしてやるだけです
@Override public void onClick(View v) {
  switch (v.getId()) {
    case R.id.mission : //2階層目表示
      menu_off = true;
      findViewById(R.id.stage).startAnimation(AnimationUtils.loadAnimation(this, R.anim.zoom_in));
      findViewById(R.id.setting).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_out));
      findViewById(R.id.mission).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_out));
      findViewById(R.id.challenge).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_out));
      break;
    case R.id.title1 : //1階層目に戻る
      menu_off = false;
      findViewById(R.id.stage).clearAnimation(); //これやらんとなぜか変に表示が残る
      findViewById(R.id.stage).setVisibility(View.GONE);
      findViewById(R.id.setting).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_in));
      findViewById(R.id.setting).setVisibility(View.VISIBLE);
      findViewById(R.id.mission).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_in));
      findViewById(R.id.mission).setVisibility(View.VISIBLE);
      findViewById(R.id.challenge).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_in));
      findViewById(R.id.challenge).setVisibility(View.VISIBLE);
      break;
  }
}
メニューを消すときにstartAnimation直後に.setVisibility(View.GONE)をやってしまうと
アニメーションする前に消えてしまうので、AnimationListenerをimplementsして
onAnimationEndで消してます
public class Menu extends Activity implements View.OnClickListener, Animation.AnimationListener {

@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
@Override public void onAnimationEnd(Animation animation) {
  if (menu_off) {
    findViewById(R.id.setting).setVisibility(View.GONE);
    findViewById(R.id.mission).setVisibility(View.GONE);
    findViewById(R.id.challenge).setVisibility(View.GONE);
  }
}
以上

res/layout/menu.xml
<?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:background="@drawable/title_back"
 android:gravity="center_horizontal"
 android:orientation="vertical" >
 <ImageView
  android:scaleType="fitStart"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_margin="32dp"
  android:src="@drawable/title" />
 <FrameLayout
  android:layout_width="fill_parent"
  android:layout_height="0dp"
  android:layout_weight="1"
  android:gravity="center">
  <LinearLayout
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:gravity="center"
   android:layout_gravity="center">
  <ImageButton
   android:id="@+id/setting"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_marginBottom="36dp"
   android:background="@null"
   android:onClick="onClick"
   android:src= "@drawable/menu_sg"/>
  <ImageButton
   android:id="@+id/mission"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_marginBottom="36dp"
   android:background="@null"
   android:onClick="onClick"
   android:src= "@drawable/menu_mi"/>
  <ImageButton
   android:id="@+id/challenge"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_marginBottom="36dp"
   android:background="@null"
   android:onClick="onClick"
   android:src= "@drawable/menu_ch"/>
  </LinearLayout>
  <LinearLayout
   android:id="@+id/stage"
   android:orientation="vertical"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:gravity="center"
   android:visibility="gone">
   <LinearLayout
    android:orientation="horizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal">
    <LinearLayout
     android:id="@+id/stage1"
     android:tag="1"
     android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_margin="12dp"
     android:onClick="onClick"
     android:gravity="center_horizontal">
     <ImageView
      android:scaleType="matrix"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/mission_u"/>
     <TextView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Stage1"
      android:gravity="center"
      android:textColor="#000"
      android:textSize="12sp"
      android:ellipsize="end"/>
    </LinearLayout>
    <LinearLayout
     android:id="@+id/stage2"
     android:tag="2"
     android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_margin="12dp"
     android:onClick="onClick"
     android:gravity="center_horizontal">
     <ImageView
      android:scaleType="matrix"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/mission_l"/>
     <TextView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Stage2"
      android:gravity="center"
      android:textColor="#000"
      android:textSize="12sp"
      android:ellipsize="end"/>
    </LinearLayout>
    <LinearLayout
     android:id="@+id/stage3"
     android:tag="3"
     android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_margin="12dp"
     android:onClick="onClick"
     android:gravity="center_horizontal">
     <ImageView
      android:scaleType="matrix"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/mission_u"/>
     <TextView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Stage3"
      android:gravity="center"
      android:textColor="#000"
      android:textSize="12sp"
      android:ellipsize="end"/>
    </LinearLayout>
   </LinearLayout>
   <LinearLayout
    android:orientation="horizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal">
    <LinearLayout
     android:id="@+id/stage4"
     android:tag="4"
     android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_margin="12dp"
     android:onClick="onClick"
     android:gravity="center_horizontal">
     <ImageView
      android:scaleType="matrix"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/mission_u"/>
     <TextView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Stage4"
      android:gravity="center"
      android:textColor="#000"
      android:textSize="12sp"
      android:ellipsize="end"/>
    </LinearLayout>
    <LinearLayout
     android:id="@+id/stage5"
     android:tag="5"
     android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_margin="12dp"
     android:onClick="onClick"
     android:gravity="center_horizontal">
     <ImageView
      android:scaleType="matrix"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/mission_u"/>
     <TextView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Stage5"
      android:gravity="center"
      android:textColor="#000"
      android:textSize="12sp"
      android:ellipsize="end"/>
    </LinearLayout>
    <LinearLayout
     android:id="@+id/stage6"
     android:tag="6"
     android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_margin="12dp"
     android:onClick="onClick"
     android:gravity="center_horizontal">
     <ImageView
      android:scaleType="matrix"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/mission_u"/>
     <TextView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Stage6"
      android:gravity="center"
      android:textColor="#000"
      android:textSize="12sp"
      android:ellipsize="end"/>
    </LinearLayout>
   </LinearLayout>
   <ImageButton
    android:id="@+id/title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="36dp"
    android:background="@null"
    android:onClick="onClick"
    android:src= "@drawable/menu_tt"/>
  </LinearLayout>
 </FrameLayout>
</LinearLayout>

2017年10月27日金曜日

TouchScreenBenchmark 実機計測(FUJITSU ARROWS Tab F-03G)

タッチスクリーンベンチマークアプリTouchScreenBenchmark
手持ちの実機を使って計測を行ってみました

今回は2014年発売の FUJITSUのタブレットARROWS Tab F-03Gです

トータルスコアは78474でした

3年前の端末ですが、前回のdtab 01と同じくらいのスコアとなりました。

タッチ感度テストでは1インチ中の平均イベント数が18.4、1秒あたりで83.91と
前回のdtab同様に近年の端末よりもタッチイベントが少ない印象ですが
タッチ平均遅延時間は23.8msと上々の反応速度でした
逆にタッチ位置精度の方は平均92.99%と少しぶれる結果となりました

2017年10月18日水曜日

TouchScreenBenchmark 実機計測(Huawei dtab 01)

タッチスクリーンベンチマークアプリTouchScreenBenchmark
手持ちの実機を使って計測を行ってみました

今回は2013年発売の Huaweiのタブレットdtab 01です

トータルスコアは78319でした

過去の3機種のテストではさほどスコアに差がなく、もしかしてどの機種でも変わらない説かと思われましたが、さすがに4年前の端末だと若干スコアが落ちました。

タッチ感度テストでは1インチ中の平均イベント数が16.45、1秒あたりで79.36
近年の端末よりもタッチイベントが少ない印象でした
タッチ平均遅延時間は55msと遅延が体感できる感じを受けました

2017年10月13日金曜日

TouchScreenBenchmark 実機計測(SONY Xperia Z3 )

タッチスクリーンベンチマークアプリTouchScreenBenchmark
手持ちの実機を使って計測を行ってみました

今回はSONY のスマートフォンXperia Z3(SO-01G)です

トータルスコアは81659でした


タッチ感度テストでは1インチ中の平均イベント数が38.83、1秒あたりで87.78
前回前々回のタブレットよりも1秒当たりのイベント数が少ないのに
1インチ中のイベント数が多いのは画面が小さいから、イベント密度が上がるためですね
タッチ平均遅延時間は7.8msかなり遅延が少ない印象でした

2017年10月11日水曜日

TouchScreenBenchmark 実機計測(HUAWEI MediaPad T2 8 Pro)

タッチスクリーンベンチマークアプリTouchScreenBenchmark
手持ちの実機を使って計測を行ってみました

今回はHUAWEI のタブレットMediaPad T2 8 Proです

トータルスコアは81792でした


タッチ感度テストでは1インチ中の平均イベント数が31.07、1秒あたりで111.45
タッチ平均遅延時間は37.5msとほぼ前回のASUS Zenpadと変わらない印象でした

2017年10月5日木曜日

TouchScreenBenchmark 実機計測(ASUS ZenPad 8 Z380KNL)

タッチスクリーンベンチマークアプリTouchScreenBenchmark
手持ちの実機を使って計測を行ってみました

最初はASUSのタブレットZenPad 8 Z380KNL

トータルスコアは81994でした


タッチ感度テストでは1インチ中の平均イベント数が30.77、1秒あたりで111.18
タッチ平均遅延時間は18.0msと良好でした

2017年9月25日月曜日

TouchScreenBenchmarkの計測項目 FreeMode

先日リリースした新作アプリTouchScreenBenchmarkには定型的な計測を行いスコアを測定するモードのほかに、自由にタッチスクリーンのテストを行うことができるFreeModeがあります

FreeModeではタッチよるイベントを表示し、タッチごとに割り振られるポインターIDに応じてポイントを色を変えて表示します

タッチ切れなどがあった場合は色が変わるのですぐにわかるかと思います。
マルチタッチにも対応してます。


対応するタッチイベントは以下

DOWN
POINTER_DOWN
MOVE
UP
POINTER_UP
LONGPRESS
SCROLL
FLING
SINGLETAPUP
SHOWPRESS
DOUBLETAP
DOUBLEEVENT
SINGLECONFIRMED

以上

2017年9月12日火曜日

TouchScreenBenchmarkの計測項目その3

先日リリースした新作アプリTouchScreenBenchmarkの計測項目

三つ目はPrecision

Precisionはタッチ位置精度について計測することができます

計測方法は以下のように表示に合わせてターゲットを順番にタップするだけ


注意点としては位置精度の計測なんで、なるべくターゲットの中心を押すこと


Average:平均中心一致率
MaxValue:最高中心一致率
MinValue:最低中心一致率
PhantomTap:ファントムタップ(ゴーストタップ)の数

以上

2017年9月4日月曜日

TouchScreenBenchmarkの計測項目その2

先日リリースした新作アプリTouchScreenBenchmarkの計測項目

二つ目はResponse

Responseはタッチレスポンス率とタッチ遅延時間について計測することができます

計測方法はレスポンス率と遅延に分かれており、以下のように
レスポンス率は表示に合わせてターゲットを順番にタップ
タッチ遅延計測はゲージの赤いラインに合わせてターゲットをタップ



注意点としてはレスポンス率のターゲットは1ターゲットにつき1タップ
失敗してもタップしなおさないことです
タッチ遅延計測はとにかくタイミングを合わせることです



TapResponse:タップ成功率
DelayAverage:平均タップ遅延時間
PhantomTap:ファントムタップ(ゴーストタップ)の数

以上

2017年8月24日木曜日

TouchScreenBenchmarkの計測項目その1

先日リリースした新作アプリTouchScreenBenchmarkの計測することができる、3つの項目を説明していきたいと思います

一つ目はSensitivity

Sensitivityはタッチ解像度やタッチ切れ、ファントムタップなどのタッチスクリーンの感度について計測することができます

計測方法は以下のようにスタートを押すとターゲットがらせん状に移動するのでそれに合わせてタッチします


注意点としては一度タッチしたら、指をスクリーンから離さないこと
タッチは指1本で行い、他の指が触れないようにすることです
計測は2回行い結果が表示されます
計測後にRecordA-RecordBのボタンでタッチした軌跡が表示され
Resultを押すとスコアの表示


TotalPoints:全タッチイベント数
RightPoints:ターゲットに沿ったタッチイベント数
Point/inch:1インチ中の平均タッチイベント数
Point/Second:1秒間の平均タッチイベント数
Wavering:タッチぶれの割合
WrongPoints:不正なタッチイベント数
No Response:計測中タッチがなかった時間
PhantomTap:ファントムタップ(ゴーストタップ)の数
Interupption:タッチ切れの数

以上

2017年8月17日木曜日

新作アプリTouchScreenBenchmark

2年ぶりに新作アプリをリリースしました

スマートフォンやタブレットのタッチスクリーンの性能を計測するベンチマークアプリです

計測項目はスクリーン感度・タッチレスポンス・タッチ位置精度の3つとなってます
あとフリーテストモードもあってイベントやマルチタッチのテストもできるツールとなってます

よかったら使ってみてください