HTMLの基礎覚え書き - Style Sheet

あと2回くらいでHTMLの覚え書きは終了したいと思いますが、今回はStyle Sheet(CSS)に関して、またもや、ここを参照してます

Style Sheet

構造化された文書を装飾する方法。CSSはCascading Style Sheetの略

Styleの定義の仕方

p {color:red}

"p"要素がセレクタ、"color"がプロパティと呼ばれる

Styleの定義場所

1. 外部file

<link rel="stylesheet" href="default.css" type="text/css" />

*default.cssという名称の外部fileに定義

2. head要素内

<head>
    <style type="text/css">
        p {color: red}
        h1 {color: blue; font-size: 120%}
    </style>
</head>

3. 個々のタグにstyle属性として

<p style="color: red">これは段落です</p>

*この場合、style属性にはtypeが指定できないのでmeta要素を使用してtypeを前もって指定しておく必要がある

<meta http-equiv="Content-Style-Type" content="text/css" />

styleの局所指定

1. class属性やid属性を要素に指定することにより、styleの適用箇所を限定することができる

<head>
    <style type="text/css">
        p.hot {color: red}
	.cool {color: blue}
    </style>
<head>
<body>
    <p class="hot">ここは赤</p>
    <p>ここは普通</p>
</body>

hot classはp要素のclassとして限定されるが、cool classは全要素タイプを対象にしている

<li>んで<span class="cool">ここは青</span></li>

2. 同じclass属性を持つ要素は複数存在できるが、id属性は一意である必要がある。つまり、あるidセレクタの属性を持つ要素は一つに限定される

p#hot {color: red}
<p id="hot">ここだけが赤</p>

3. ある要素内に現れる時のみあるstyleを適用したい場合は親要素の後に空白を入れて定義する

<!-- これでli要素内のem要素のみに上記styleが適用される -->
li em {color: red}

Style適用の優先順位

    • 局所的が優先
    • 宣言が遅い方が優先

プロパティに関してはここを参照のこと

HashTag #HTML

HTMLの基礎覚え書き - ブロックレベル要素とインライン要素

またまたHTMLの覚え書きです

要素の種類

1. ブロックレベル要素

    • それ自体が文章を構成する単位

p dl ul ol h1 h2 h3 h4 h5 h6 address hr table div form blockquote fieldset pre

2. インライン要素

    • ブロックの一部に対して適用される修飾的な要素

下記は例

a abbr acronym b bdo big cite code dfn em i kbd label q samp small span strong sub sup tt var

ブロックレベル要素

1. hr要素

    • 横線
<hr />

2. blockquote要素

    • ブロック要素を引用したところとしてmarkup
<blockquote>
    <p>これは引用文です</p>
<blockquote>

*本文中で引用文を表したいならq要素

3. div要素

    • ブロック要素をグループ化
<div class="hobby" style="font-weight:bold">
    <h1>趣味</h1>
    <p>私の趣味は
        <ol>
            <li>Running</li>
            <li>Swmming</li>
        </ol>
    </p>
</div>

*インライン要素をグループ化したいならspan要素

4. pre要素

    • 整形済の文章としてmarkup。改行、空白がそのまま表示される
<pre>
    <p>
    これは、空白や
        改行が
    そのまま表示されます
    </p>
</pre>

インライン要素

1. br要素

    • 改行
<br />

2. em要素またはstrong要素

    • 強調
<strong>強調する</strong>
<em>強調する</em>

3. sup要素

    • 上付き文字
X<sup>2</sup>

4. sub要素

    • 下付き文字
H<sub>2</sub>O

5. dfn要素

    • 文中で重要な言葉が初めて出現して、それに関する説明がそこでなされているときなどに使う
    • dfn要素はdt要素と違い、paragraphの中で単語の定義を行う
    • ここを参照
<dfn>定義語</dfn>

6. acronym要素、abbr要素

    • 省略文字があり、tooltipを表示したい時はacronym要素、abbr要素でtitle属性にtooltipに表示する文言を定義
<acronym title="North Atrantic Treaty Organization">NATO</acronym>

7. q要素

    • 本文中での引用
<p>夏目漱石の小説で<q>吾輩は猫である。名前はまだない。</q>という有名な書き出しがある</p>

8. cite要素

    • 引用もとを示す
<p>引用:<cite>夏目漱石『吾輩は猫である』</cite></p>

9. span要素

    • インライン要素をグループ化
<p>ここは普通だけど、<span class="color_red" style="font-weight:bold">ここは太文字</span></p>

HashTag #HTML

HTMLの基礎覚え書き - 概要

HTMLに関して勉強しようと思い立ったので覚え書き(ここを参考にしました)

HTML構造の概要

1. Root要素であるhtml要素はhead要素、body要素によって構成される

<html>
    <head>
    </head>
    <body>
    </body>
</html>
    • html要素には名前空間とそのHTML文書の記載言語を記す
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">

2. head要素にはtitle要素とmeta要素を書く

<head>
    <title>これはtitle要素です</title>
</head>
    • meta要素のhttp-equiv属性に"Content-TypeやContent-Script-Type"を記載する
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>

3. body要素はp要素とheading要素(h1..h6)からなる

<body>
    <h1>見出し語1</h1>
    <h2>subの見出し語</h2>
    <p>これはparagraphです</p>
</body>

4. HTML文書の先頭ではXML宣言をする

<?xml version="1.0" encoding="UTF-8"?>

list表示をしたい時

1. ol要素でList itemとして使用したいli要素を囲む
通し番号がふられる。olの意味はordered list。liの意味はlist item

<ol>
    <li>これはリストアイテムです</li>
</ol>

2. ul要素でList itemとして使用したいli要素を囲む
通し番号がふられない。ulの意味はunordered list

<ul>
    <li>これもリストアイテムです</li>
</ul>

3. dl要素ではdt要素、dd要素を入れ子しリスト表示する
辞書見出し語をdt要素でmarkupし説明文をdd要素でmarkupする

<dl>
    <dt>これは見出し語です</dt>
    <dd>これは説明文です</dd>
</dl>

Tableを表示したい時

1. 表を作成するにはtable要素でtr要素(Table row)とtd要素(Table data)を入れ子にする(tr要素はtd要素を入れ子する)

    • table要素にborder属性、cellspacing属性、cellpadding属性を指定することにより表のstyleを定義することが出来る
<table border="5" cellspacing="10" cellpadding="10">
    <tr>
        <td>Name</td>
        <td>Flavour</td>
        <td>Color</td>
    </tr>
    <tr>
        <td>Banana</td>
        <td>like banana</td>
        <td>Yellow</td>
    </tr>
</table>
    • table要素にsummary属性を付加することができる(ex. ハンディキャップがある人のためなど)
    • table開始タグの直後にcaption要素を付け加えることにより、表に題名をつけることができる
<table summary="This is a table of fruits">
    <caption>Fruit Tables</caption>
    <tr>
        <td>Name</td>
        <td>Flavour</td>
    </tr>
    <tr>
        <td>Orange</td>
        <td>Sour</td>
    </tr>
</table>
    • 項目名を示したい場合はtd要素の代わりにth要素でmarkupする
    • セル内の背景色を変更したい場合はstyle属性のbackgroundプロパティで色を指定する
    • セルの統合をしたい場合はrowspan属性、colspan属性で値を指定する
<table border="1" cellpadding="10" cellspacing="10">
  <caption>Table of fruits</caption>
  <tr>
    <th>Category</th>
    <th>Name</th>
    <th>Flavour</th>
    <th>Shape</th>
  </tr>
  <tr>
    <th rowspan="2" style="background:#FF0000">Red Fruits</th>
    <td>Apple</td>
    <td>Sweet</td>
    <td>Round</td>
  </tr>
  <tr>
    <td>Strawberry</td>
    <td>Sweet</td>
    <td>Pyramid</td>
  </tr>
  <tr>
    <th>Yellow Fruits</th>
    <td>Orange</td>
    <td>Sour</td>
    <td colspan="2">I don't know</td>
  </tr>
</table>
    • colgroup要素を最初のtr要素の前に記述することにより、列をgroup化し同じstyleを適用することが出来る
    • 同じstyleを連続した複数の列にまたがって適用したい場合はspan属性で数を指定する
    • col要素も同じ。col要素を意味的なまとまりにしたい場合はcolgroup要素の入れ子として扱ってもいい
<table>
    <colgroup style="background:#FF0000">
    </colgroup>
    <colgroup>
        <col span="2" style="background:#000000">
    </colgroup>
    <tr>
        <td>Swimming</td>
        <td>Running</td>
        <td>Jogging</td>
    </tr>
</table>
    • 行はthead要素、tbody要素、tfoot要素でgroup化することが出来る
    • tbody要素は複数のtr要素をgroup化出来るが、thead要素、tfoot要素は1つのtr要素しか出来ない
<table>
    <thead style="background:#FF0000">
        <tr>
            <th>Name</th>
            <th>Duration</th>
            <th>Time</th>
        </tr>
    </thead>
    <tfoot style="background:#00FFFF">
        <tr>
            <th>Sum</th>
            <td>100 min</td>
            <td>3 times</td>
        </tr>
     </tfoot>
     <tbody style="background:#EEEEEE">
         <tr>
            <th>Running</th>
            <td>50 min</td>
            <td>2 times</td>
        </tr>
        <tr>
            <th>Swimming</th>
            <td>50 min</td>
            <td>1 time</td>
        </tf>
     </tbody>
</table>        

画像を表示したい時

1. img要素のsrc属性に画像のpath、alt属性に画像が表示出来なかった時の代替え文言を指定する

    • width属性、height属性で画像の大きさを指定することが出来る
    • align属性でそれに続くtextの表示位置を指定出来る
<!-- alignがleftだとtextは画像の右側に回り込む -->
<img src="image.jpg" alt="ここにimageを表示" width="100" height="100" align="left">

Hyper linkをしたい時

1. a要素(Anchorの意味)のhref属性にリンク先のaddressを指定し、リンク表示文言をa要素で囲む()

    • Localに保存されているHTML文書へのリンクの場合は相対URLを使用できる
    • ハイパーリンクはweb pageだけじゃなくて"mailto:"URIでmailをリンクすることも出来る
<a href="http://www.google.com">Google</a>
    • HTML文書の特定の場所(フラグメント)にリンクを張りたい時は
      1. リンク先のa要素にid属性を定義する
      2. リンク元のhref属性で「file名#リンク先id」を指定する
<!-- リンク元 -->
<a href="file_b.html#linked">linkedへのリンク</a>
<!-- リンク先 -->
<a id="linked">これはlinkedです</a>

同file内へのlinkは「file名#リンク先id」の「file名」を省略することが出来る

作者の情報を記載する

1. address要素でHTML文書の作者情報を記載

<address>
  sei10sa10; 2011-08-20日更新;
  <a href="mailto:yourmail@domain.com">sei10sa10へのメッセージ</a>
</address>

HashTag #HTML

重要だよねSQLiteDatabase

さて今回はSQLiteDatabaseを使用してみたいと思います。下記の雑誌の記事を参考に、より簡単にしたものを作っています(ただ、DBにaccessするところはthread化してます)

日経ソフトウエア 2011年 09月号 [雑誌]

日経ソフトウエア 2011年 09月号 [雑誌]

やりたいこと

  1. SQLiteでDBを作成する
  2. DBにdata挿入
  3. DBからdataを読み込み
  4. SpinnerやListViewを使用してdataを表示

Spinnerで地域名を選ぶとそのAddressを持ったshop名をListViewに表示

実装の要点を下記にまとめます

1. SQLiteOpenHelper拡張classを作成

public class MySQLiteOpenHelper extends SQLiteOpenHelper {

	@Override
	public void onCreate(SQLiteDatabase db) {

	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

	}
}

2. SQLiteDatabase objectを使用し、SQLiteOpenHelper#onCreate()でTableを作成

private static final String CREATE_SHOP_TABLE = "create table " + MyDao.SHOP_TABLE_NAME + " (" + 
	MyDao.SHOP_ID + " integer primary key autoincrement, " + 
	MyDao.SHOP_NAME + " text not null, " + 
	MyDao.ADDRESS1 + " text not null, " +
	MyDao.ADDRESS2 + " text not null, " + 
	MyDao.ADDRESS3 + " text not null)";

@Override
public void onCreate(SQLiteDatabase db) {
	db.execSQL(CREATE_SHOP_TABLE);
}

3. DAO(Data Access Object) classにData挿入のmethodを定義

public class MyDao {
	
	public static void insert(SQLiteDatabase db, String shopName, String address1, String address2, String address3){
		ContentValues values = new ContentValues();
		values.put(SHOP_NAME, shopName);
		values.put(ADDRESS1, address1);
		values.put(ADDRESS2, address2);
		values.put(ADDRESS3, address3);
		db.insert(SHOP_TABLE_NAME, null, values);
	}
	
	public static Cursor getAll(SQLiteDatabase db){
		return db.rawQuery(GET_ALL, null);
	}
	
	public static Cursor getAllDistinctAddress2(SQLiteDatabase db){
		return db.rawQuery(GET_ALL_DISTINCT, null);
	}
	
	public static Cursor getAllByAddress2(SQLiteDatabase db, String address2){
		return db.rawQuery(GET_ALL_BY_ADDRESS2, new String[]{address2});
	}
}

4. DAO classにData読み込みのmethodを定義

public class MyDao {

	public static final String SHOP_TABLE_NAME = "shop";
	public static final String SHOP_ID = "_id";
	public static final String SHOP_NAME = "shop_name";
	public static final String ADDRESS1 = "address1";
	public static final String ADDRESS2 = "address2";
	public static final String ADDRESS3 = "address3";
	
	private static final String GET_ALL = "select * from " + SHOP_TABLE_NAME;
	private static final String GET_ALL_DISTINCT = "select distinct " + ADDRESS2 + " from " + SHOP_TABLE_NAME;
	private static final String GET_ALL_BY_ADDRESS2 = "select * from " + SHOP_TABLE_NAME + " where " + ADDRESS2 + " = ?";
	
	public static Cursor getAll(SQLiteDatabase db){
		return db.rawQuery(GET_ALL, null);
	}
	
	public static Cursor getAllDistinctAddress2(SQLiteDatabase db){
		return db.rawQuery(GET_ALL_DISTINCT, null);
	}
	
	public static Cursor getAllByAddress2(SQLiteDatabase db, String address2){
		return db.rawQuery(GET_ALL_BY_ADDRESS2, new String[]{address2});
	}
}

5. Adapterに取得したDataをbindしSpinnerやListViewにset

他、覚えておきたい点まとめます

  • SQLiteOpenHelper#onCreate()ははじめてDBを作成する時のみ呼ばれる
  • Activity#startManagingCursor()でCursorのlife cycleをActivityに任せられる
  • Transaction処理が多い場合SQLiteDatabase#beginTransaction()をcall、SQLiteDatabase#setTransactionSuccessful()でcommit、SQLiteDatabase#endTransaction()でTransaction処理を終了
  • DBにaccessする時はUI threadとは別のthreadで行うべき
  • Spinnerには定石がある(ここ参照)

んでは実装です

続きを読む

Parcelableを使ってみる

今回はParcelableを使用してみようと思います

Parcelableとは

あるclassのInstanceをParcelに保存したり、または、復元したりするために実装するInterface(原文ここ)。Orientation変更したりするとSystemによりActivityがonDestroy()まで呼ばれ、再度、Activityの生成Processが実行されるので、Orientation変更前の状態を保持したい場合にそのinstanceのclassにParcelable interfaceを実装する感じですね

実装の要点をまとめます

1. Parcelable interfaceを実装する

public class ParcelableData implements Parcelable{
	
	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {

	}
    }
}

2. Parcelable#writeToParcel()をoverrideし、引数に渡されるParcel objectに保存したいfieldを書き込む

@Override
public void writeToParcel(Parcel dest, int flags) {
	dest.writeString(mString);
	dest.writeInt(mInt);
	dest.writeBooleanArray(mBooleanArray);
	dest.writeTypedList(mAnotherParcelableDataList);
}

3. Parcelable.Createrを実装する

public static final Parcelable.Creator<ParcelableData> CREATOR
	
	= new Parcelable.Creator<ParcelableData>() {
		@Override
		public ParcelableData createFromParcel(Parcel in) {
			return new ParcelableData(in);
		}
		@Override
		public ParcelableData[] newArray(int size) {
			return new ParcelableData[size];
		}
	};
}

4. Parcelable.Creater#createFromParcel()で返すobejctを生成するためのprivate constructor内で引数で渡されるParcel objectからfieldの値を復元

private ParcelableData(Parcel in) {
       mString = in.readString();
       mInt = in.readInt();
       in.readBooleanArray(mBooleanArray);
       in.readTypedList(mAnotherParcelableDataList, AnotherParcelableData.CREATOR);
}

5. Activity#onSaveInstanceState()内で引数に渡されるBundle objectにParcelable objectを保存する

 @Override
protected void onSaveInstanceState(Bundle outState) {
	super.onSaveInstanceState(outState);
	outState.putParcelable(KEY_PARCELABLE, mData);
}

6. Activity#onCreate()内で引数に渡されるBundle objectからParcelable objectを復元する

@Override
public void onCreate(Bundle savedInstanceState) {
       if(savedInstanceState != null){
       	   mData = savedInstanceState.getParcelable(KEY_PARCELABLE);
       }else{
       	   mData = createParcelableData();
       }
}

気付いた点をまとめます

  • BooleanはArrayでしか保存出来ない

それでは実装です

続きを読む

一応、最後にしたいGoogle Map - Overlayの拡張

今回のPostで今までやってきたGoogle Maps APIを使用した実装の最後としたいと思います。ItemizedOverlayを実装した回のあとがきで少しふれましたが、ItemizedOverlayを使用した場合、Multi ThreadでReal timeにOverlayItemの数などを変更するとAPIの内部でcrashしてしまう場合がありAPIのuserがどうにも対処出来ない(?)問題があります。ということで今回はもう少しcustomizeが可能なOverlayを拡張してみたいと思います

今回やりたいこと

  1. Overlay拡張classを使用しmap上にiconを表示
  2. iconをtapするとiconに紐づけられたstringを表示

ちなみに前回やったこと

  1. MapViewを拡張しZoom level変更event、Center position変更eventを取得(Listenerに通知も)


こんな感じでiconを表示し、iconのtapでtoastの表示

実装の要点は下記です(前回実装したことに関しては省略)

1. Overlayを拡張したclassを作成

public class MyOverlay extends Overlay {
	
        public synchronized void setItems(ArrayList<MyOverlayItem> items){
		mItems = items;
	}

	@Override
	public synchronized void draw(Canvas canvas, MapView mapView, boolean shadow) {

	}
	
	@Override
	public synchronized boolean onTap(GeoPoint p, MapView mapView) {
		return super.onTap(p, mapView);
	}
}

2. Overlay#draw()をoverrideし保持しているMyOverlayItemのiconを描画する処理を記載

@Override
public synchronized void draw(Canvas canvas, MapView mapView, boolean shadow) {

	//shadowがtrueの場合、Iconの影を描くためのdrawなのでIconのみを描く場合は無視
	if(!shadow){
		Projection pj = mapView.getProjection();
		Point point = new Point();
		for(MyOverlayItem item: mItems){
			pj.toPixels(item.getGeoPoint(), point);
			Drawable icon = item.getIcon();
			
			Rect bounds = new Rect();
			int halfWidth = icon.getIntrinsicWidth() / 2;
				
			bounds.left = point.x - halfWidth;
			bounds.right = point.x + halfWidth;
			bounds.top = point.y - icon.getIntrinsicHeight();
			bounds.bottom = point.y;
				
			icon.setBounds(bounds);
			icon.draw(canvas);
		}
	}
}

3. Overlay#onTap()をoverrideしMyOverlayItemをtapした場合はToastを表示する処理を記載

@Override
public synchronized boolean onTap(GeoPoint p, MapView mapView) {
	Projection pj = mapView.getProjection();
	int hitIndex = hitTest(pj, p);
		
	if(hitIndex != -1){
		MyOverlayItem item = mItems.get(hitIndex);
		Toast.makeText(mContext, item.getString(), Toast.LENGTH_SHORT).show();
	}
		
	return super.onTap(p, mapView);
}

4. Overlay拡張classで表示する(要素を保持する)classを作成

public class MyOverlayItem {
	
}

5. 独自のOverlayを表示するためOverlayを拡張したclassのobjectを取得

mMyOverlay = new MyOverlay(this);


6. MyOverlayItem(表示要素を保持するclass)のobject listを取得しOverlay拡張classにset

ArrayList<MyOverlayItem> items = getMyOverlayItems();
mMyOverlay.setItems(items);

7. overlayのlistにOverlayを拡張したclassを登録

List<Overlay> overlays = mMapView.getOverlays();
overlays.add(mMyOverlay);

気付いた点

  • Overlay#onTap()はOverlay#onTouch()が呼ばれた後に呼ばれる

んで実装です

続きを読む

Google MapでZoom event/Scroll event取得 - MapViewの拡張

再度、前回に続いてGoogle Maps APIを使用した実装に関するPostになります。Google Maps APIを使用していて、Zoom level変更event、或はMapのCenter position変更event(Scroll event)を取得したいと思い、調べてみたところ残念ながらAPIは提供されていない模様。ということで更に調べているとStackoverflowでこういうPostを見かけました。このPostを参考にUser interaction以外でもMapView#animateTo()といったCenter position変更eventを取得出来るように改良してみました(このPostのままだと自分がやりたい事をやった際に上手くいきませんでした)

今回やってみたいこと

  1. MapViewを拡張しZoom level変更event、Center position変更eventを取得(Listenerに通知も)

ちなみに前回やったこと

  1. ItemizedOverlayを使用しtouchされたmapの場所にOverlayItemを表示
  2. OverlayItemがtapされた場合は、それが保持するLocation情報を表示


こんな感じでZoom levelが変更されるとToastを表示


こんな感じでScrollされるとToastを表示

実装の要点は下記です(前回実装したことに関しては省略)

1. MapViewを拡張したclassを作成

public class MyMapView extends MapView {
        @Override
	public boolean onTouchEvent(MotionEvent ev) {
		return super.onTouchEvent(ev);
	}
        @Override
	public void computeScroll() {
		super.computeScroll();
	}
}

2. MapViewを拡張したclassをlayout xmlに定義

<com.android.practice.map.MyMapView 
		android:id="@+id/map_view"
		android:layout_width="fill_parent"
		android:layout_height="fill_parent"
		android:clickable="true"
		android:apiKey="自分のGoogle Maps API key" />

3. Zoom level変更/Center position変更event listenerを作成

public interface ZoomListener{
	public void onZoomChange(MapView mapView, int newZoom, int oldZoom);
}

public interface PanListener{
	public void onCenterChange(MapView mapView, GeoPoint newGeoPoint, GeoPoint oldGeoPoint);
}

private class MyMapViewListener implements ZoomListener, PanListener{
	@Override
	public void onZoomChange(MapView mapView, int newZoom, int oldZoom) {
		Toast.makeText(MyMapActivity.this, "New zoom lv:" + newZoom + "\nOld zoom lv:" + oldZoom, Toast.LENGTH_SHORT).show();
	}

	@Override
	public void onCenterChange(MapView mapView, GeoPoint newGeoPoint,
			GeoPoint oldGeoPoint) {
		Toast.makeText(MyMapActivity.this, 
				"New center latitude:" + newGeoPoint.getLatitudeE6() + "\nNew center longitude:" + newGeoPoint.getLongitudeE6(), 
				Toast.LENGTH_SHORT).show();
	}
}

4. 3で作成したMapView event listener classをlistenerとしてMapView拡張classに登録

mMyMapViewListener = new MyMapViewListener();
if(mMapView != null){
	mMapView.setZoomListener(mMyMapViewListener);
	mMapView.setPanListener(mMyMapViewListener);
}

5. Zoom level変更用、Center position変更用の通知Runnableをそれぞれ作成

mZoomChangeNotifier = new Runnable(){
	@Override
	public void run() {
		mHandler.removeCallbacks(this);
		notifyZoomChange();
	}
};

mCenterChangeNotifier = new Runnable(){
	@Override
	public void run() {
		mHandler.removeCallbacks(this);
		notifyCenterChange();
	}
};

6. 移動Animation終了check用のRunnableを作成(User interaction以外でもZoom level/Center positionの変更を確認するため)

mAnimationFinishChecker = new Runnable(){
	@Override
	public void run() {
		mHandler.removeCallbacks(this);
		if(isAnimationFinished()){
			isDuringAnimation = false;
			mHandler.post(new Runnable(){
				@Override
				public void run() {
					mHandler.removeCallbacks(mZoomChangeNotifier);
					mHandler.removeCallbacks(mCenterChangeNotifier);
					if(mLastZoomLevel != getZoomLevel()){
						notifyZoomChange();
					}
					if(isCenterChanged()){
						notifyCenterChange();
					}
				}
			});
		}else{
			mHandler.postDelayed(this, EVENT_DELAY);
		}
	}
};

7. MapView#onTouchEvent()をoverrideしtouch中か移動Animation中かのflagを設定

@Override
public boolean onTouchEvent(MotionEvent ev) {
	
	int action = ev.getAction();
		
	switch(action & MotionEvent.ACTION_MASK){
	case MotionEvent.ACTION_DOWN:
		isTouched = true;
		isDuringAnimation = false;
		break;
	case MotionEvent.ACTION_UP:
		isTouched = false;
		break;
	}
	return super.onTouchEvent(ev);
}

8. MapView#computeScroll()をoverrideしZoom level変更/Center position変更の確認。確認には6で作成したRunnableをHandlerでpost

@Override
public void computeScroll() {
        super.computeScroll();

	if(!isDuringAnimation){
		checkAndNotifyChanges(EVENT_DELAY);
	}
}

private void checkAndNotifyChanges(long delay){
	if(getZoomLevel() != mLastZoomLevel){
		mHandler.postDelayed(mZoomChangeNotifier, delay);
	}

	if(isCenterChanged() && !isTouched){
		mHandler.postDelayed(mCenterChangeNotifier, delay);
	}
}

長々となりましたが、こんな感じです

気付いた点をまとめます

  • UI上移動Animationは終了しているが、移動距離が長い場合MapVeiw#computeScroll()が実行されている場合がある(実装を気をつけないとcomputeScroll()でevent通知を行っている為、変なtimingでevent通知がされる)

それでは実装です

続きを読む