SQLの窓

2016年08月21日


Google ドライブの WEBホスティングが無くなったので、Google の Firebase をとりあえず使う方法

もう、【2016/8/31で廃止】であることは解っていたのですが、Google からメールが届きました。

IMPORTANT: Drive web hosting is shutting down

We announced last year that we're deprecating web hosting in Google Drive for users and developers. Our records show that you might have used this feature to publish a webpage or serve other web assets.

On Aug 31, 2016, we will discontinue serving content via googledrive.com/host/[id] and the webpages will not be accessible anymore.

As an alternative to web hosting in Drive, we recommend:

Blogger−An easy and free way to host websites.
Firebase Hosting− An alternative if you're using the web-hosting feature to serve static webpages with items on Drive.
で、試してみました。確かに普通に使えます。インターネットの情報はそれなりにありますが、無料プランの説明はちょっと古いものしか見つかりませんでした。結論から言って、『無料プランで独自ドメインが使用できます』 技術者が使用するのであれば、DB や Android でPush 通知などがすぐいろいろ試してみれると思うので、特に問題は無いですが(ドキュメントが英語というのはいつものことですが)、一般の人にはちょっとハードルが高いです。ただ、ホスティングのみをする目的で、Node.js をインストールしてコマンドプロンプトを使ってデプロイするという事は、FTP クライアント使うのとそう違わないのかなぁ・・という気もします。それどころか、ローカルに http://localhost:5000 のウェブサーバを使用して表示を確かめる事ができるので、そんなに悪くも無いのかなぁとも思いました。 インストール等手順 二つのアカウントと、3つのPC から試しました。これでいけると思います。画像がやたらと多くなるので、NAVER のシステムを使ってまとめました。 Firebase の使い方 / Google の 1 GB まで無料のホスティングサービス プロジェクトの作成もコマンドプロンプトからできるようですが、プロジェクトはブラウザから作成したほうがいいでしょう。そして、ブラウザであらかじめログインしておく事になるので、そのブラウザは規定のブラウザである必要があると思います。ログイン時にたぶん、規定のブラウザを使っていると思われるからです。 カスタムドメインは、自分の管理するドメイン側で、二つ TXT レコードを書き込む必要があります。これは、以前から Google が使用していた方法のようです。ただ、説明としてはサブドメインが使えるとあるのですが、自分がゾーン編集できるのはメインのドメインのみなので、試してはいません。 入力したとたん、TXT レコード が表示されて、そのエントリをすぐに削除できませんでした。仕方ないので一晩明けて見に行ったら『削除可能』になっていました。 コマンドプロンプトについて コマンドプロンプトに慣れている人にはいまさらですが、『簡易編集モード』に変更しておくと、コピーペーストが楽になります。ただ、インストール場所をデフォルトの Program Files にした場合、昨今変更できないので、『簡易編集モード』へ変更する為にデスクトップにショートカットを作成してから『簡易編集モード』に変更する事になります。 ▼ Windows10 の設定画面 また、Node.js 用にインストール時に Path 環境変数の先頭に文字列が追加されるので、これも知ってる人はいいですが、知らない場合は後で『あれ?』と思うかもしれません。 デプロイ エラーが出る可能性があるので、その場合はしばらくしてから再度実行が必要です。
posted by lightbox at 2016-08-21 23:01 | Google | このブログの読者になる | 更新情報をチェックする

2016年08月08日


Android Studio で理解不能なエラーが出た時の対処方法 : Invalidate Caches / Restart



具体的には、Activity に当然あるはずのメソッドが『存在しません』等表示されて、赤い線でエラーが発生したりします。

その他にも、ありえないエラー等あった場合は、メニューから『Invalidate Caches / Restart』を実行します

復旧に数分かかる結構長い処理になりますが、過去4,5回発生したトラブルは皆この処理で解決しています。



posted by lightbox at 2016-08-08 18:57 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2016年08月01日


Android : TabHost のタブに下から上へのアニメーションを設定して、include で同一画面を使用するので 回転しないように AndroidMainfest で設定する

TabHost のタブの切り替わりがあまりにもそっけないので、slide_from_bottom.xml と slide_up.xml を作成して、下から上へと移動するアニメーションを設定しました。左右にしてもいいのですが、それだともう二種類のアニメーションが必要になりそうなので下から上にしました。

Interpolators の値(Android developer)
※ 参考にした記事

TabHost 内の各 TabSpec 内にある TextView の 端末回転時における保存と復帰 では、tab 毎に違った画面を定義しましたが、同じ画面を使用して工数が減るパターンとしてサンプルを作成しました。

ただ、この場合ですと、端末の回転による Activity の再作成で、Tab 内の EditText の保存と復帰が正しくされないので、AndroidMainfest で『回転しない』ように縦固定にしました。
public class MainActivity extends AppCompatActivity {

	private TabHost tabhost;
	private View pview;
	private View tabView1;
	private View tabView2;

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

		// TabHost のインスタンスを取得
		tabhost = (TabHost)MainActivity.this.findViewById(R.id.tabHost);
		// タブ追加の前に必要な初期処理
		tabhost.setup();

		// タブ用のインスタンスを作成
		TabHost.TabSpec tab1 = tabhost.newTabSpec("tab1");
		// タイトル文字列を設定
		tab1.setIndicator("タブ1の\nタイトル");
		// このタブ内に表示するコンテンツを TabHost 画面内の FrameLayout
		// の中にあるうちのコンテンツのひとつを設定
		tab1.setContent(R.id.linearLayout1);
		// TabHost に このタブを追加
		tabhost.addTab(tab1);

		TabHost.TabSpec tab2 = tabhost.newTabSpec("tab2");
		tab2.setIndicator("タブ2の\nタイトル");
		tab2.setContent(R.id.linearLayout2);
		tabhost.addTab(tab2);

		TabHost.TabSpec tab3 = tabhost.newTabSpec("tab3");
		tab3.setIndicator("タブ3の\nタイトル");
		tab3.setContent(R.id.linearLayout3);
		tabhost.addTab(tab3);

		pview = tabhost.getCurrentView();

		tabhost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
			@Override
			public void onTabChanged(String tabId) {
				View currentView = tabhost.getCurrentView();
				int tab = tabhost.getCurrentTab();

				Animation a_out = AnimationUtils.loadAnimation(MainActivity.this, R.anim.slide_up);
				Animation a_in = AnimationUtils.loadAnimation(MainActivity.this, R.anim.slide_from_bottom);

				pview.setAnimation(a_out);
				currentView.setAnimation(a_in);

				pview = currentView;
			}
		});

		// tab1 内のボタン
		tabView1 = MainActivity.this.findViewById(R.id.linearLayout1);
		Button button1;
		button1 = (Button) tabView1.findViewById(R.id.button);
		button1.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				TextView tv;
				// 画面最上部の TextView
				tv = (TextView) MainActivity.this.findViewById(R.id.textView);

				// 最初のタブの EditText
				EditText editText = (EditText) tabView1.findViewById(R.id.editText);
				tv.setText(editText.getText().toString());

				// tab1 のコンテンツを取得
				tv = (TextView) tabView1.findViewById(R.id.textView2);
				tv.setText(editText.getText().toString());

			}
		});

		// tab2 内のボタン
		tabView2 = MainActivity.this.findViewById(R.id.linearLayout2);
		Button button2;
		button2 = (Button) tabView2.findViewById(R.id.button);
		button2.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				TextView tv;

				// 真ん中のタブの EditText
				EditText editText = (EditText) tabView2.findViewById(R.id.editText);

				// tab2 のコンテンツを取得
				tv = (TextView) tabView2.findViewById(R.id.textView2);
				tv.setText(editText.getText().toString());

			}
		});

	}

}


画面定義
<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="lightbox.july.tabhostapplication.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView"
        android:textSize="30dp"/>

    <TabHost
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/tabHost"
        android:layout_below="@+id/textView">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
            </TabWidget>

            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <include
                    android:id="@+id/linearLayout1"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    layout="@layout/tab_entry"/>

                <include
                    android:id="@+id/linearLayout2"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    layout="@layout/tab_entry"/>

                <include
                    android:id="@+id/linearLayout3"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    layout="@layout/tab_entry"/>

            </FrameLayout>
        </LinearLayout>
    </TabHost>
</RelativeLayout>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:id="@+id/textView2"
        android:textSize="40dp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="入力を表示"
        android:id="@+id/button"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editText"/>

</LinearLayout>

※ 便宜的に二つの画面をまとめて表示しています

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="lightbox.july.tabhostapplication"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>


アニメーション
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
    <translate android:fromYDelta="50%p" android:toYDelta="0" android:duration="160"/>
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="160" />

</set>

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromYDelta="0" android:toYDelta="-50%p" android:duration="@android:integer/config_mediumAnimTime"/>
    <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="200" />
</set>
※ 便宜上ひとつにして表示しています
※ 上が、slide_from_bottom.xml です


posted by lightbox at 2016-08-01 20:22 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

Android : TabHost 内の各 TabSpec 内にある TextView の 端末回転時における保存と復帰

TabHost そのものは、ひとつの Activity で処理する場合とても簡単ですが、Android Studio の デザインで各タブの画面を確認できるように、xml で3つの画面定義をして activity_main.xml で include で読み込んでいます。

include タグにはそれぞれ id を設定して、TabSpec のコンテンツとして渡せるようにもしてあります。

端末回転時の保存と復帰に関しても、一般的な onSaveInstanceState と onRestoreInstanceState を使用していますが、画面内のコンテンツの id のパターンを以下のようにして、サンプルコードを作成しています。

1) EditText と Button には全て違った id を設定する
2) TextView には同じ id を設定する

通常、EditText は内部で自動的に内容が保存されて復帰されますが、3つの inclide 内の id を同じにしてしまうと( つまり、ひとつの画面定義で3つの include を使いまわししてしまうと )、保存と復帰がうまくいかないようなので、3つの画面を作成して、別々の id を設定しています。

しかし、同じ id を持った画面を使いまわす事も想定して、TextView には同じ id を設定して、include の id でまず view を取得して、その中の TextView として取得するサンプルコードです。


ここでは、使用していませんが最近の Drawable の取得方法として
Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.□□□, null);

そして、Tab にアイコンを表示させる方法について
Icon in Tab is not showing up (StackOverflow)
※ 要するにカスタムなインジケータを作成するそうです
public class MainActivity extends AppCompatActivity {

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

		// TabHost のインスタンスを取得
		TabHost tabhost = (TabHost)MainActivity.this.findViewById(R.id.tabHost);
		// タブ追加の前に必要な初期処理
		tabhost.setup();

		// タブ用のインスタンスを作成
		TabHost.TabSpec tab1 = tabhost.newTabSpec("tab1");
		// タイトル文字列を設定
		tab1.setIndicator("タブ1の\nタイトル");
		// このタブ内に表示するコンテンツを TabHost 画面内の FrameLayout
		// の中にあるうちのコンテンツのひとつを設定
		tab1.setContent(R.id.linearLayout1);
		// TabHost に このタブを追加
		tabhost.addTab(tab1);

		TabHost.TabSpec tab2 = tabhost.newTabSpec("tab2");
		tab2.setIndicator("タブ2の\nタイトル");
		tab2.setContent(R.id.linearLayout2);
		tabhost.addTab(tab2);

		TabHost.TabSpec tab3 = tabhost.newTabSpec("tab3");
		tab3.setIndicator("タブ3の\nタイトル");
		tab3.setContent(R.id.linearLayout3);
		tabhost.addTab(tab3);

		// tab1 内のボタン
		Button button1;
		button1 = (Button) MainActivity.this.findViewById(R.id.button);
		button1.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				TextView tv;
				// 画面最上部の TextView
				tv = (TextView) MainActivity.this.findViewById(R.id.textView);

				// 最初のタブの EditText
				EditText editText = (EditText) MainActivity.this.findViewById(R.id.editText);
				tv.setText(editText.getText().toString());

				// tab1 のコンテンツを取得
				View tab1 = MainActivity.this.findViewById(R.id.linearLayout1);
				tv = (TextView) tab1.findViewById(R.id.textView2);
				tv.setText(editText.getText().toString());

			}
		});

		// tab2 内のボタン
		Button button2;
		button2 = (Button) MainActivity.this.findViewById(R.id.button2);
		button2.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				TextView tv;

				// 真ん中のタブの EditText
				EditText editText = (EditText) MainActivity.this.findViewById(R.id.editText2);

				// tab2 のコンテンツを取得
				View tab2 = MainActivity.this.findViewById(R.id.linearLayout2);
				tv = (TextView) tab2.findViewById(R.id.textView2);
				tv.setText(editText.getText().toString());

			}
		});


	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);

		TabHost tabhost = (TabHost)findViewById(R.id.tabHost);

		// タブ位置の復帰
		int currentTab =  savedInstanceState.getInt("CurrentTab");
		if ( currentTab != 0l ) {
			tabhost.setCurrentTab(currentTab);
		}
		else {
			tabhost.setCurrentTab(0);
		}

		// 一番上の TextView を復帰
		String textTop = savedInstanceState.getString("TextViewTop");
		if (textTop != null ) {
			TextView tv;
			tv = (TextView) MainActivity.this.findViewById(R.id.textView);
			tv.setText(textTop);
		}

		// tab1 内の TextView を復帰
		String text1 = savedInstanceState.getString("TextView1");
		if (text1 != null ) {
			View tab1 = MainActivity.this.findViewById(R.id.linearLayout1);
			TextView tv = (TextView) tab1.findViewById(R.id.textView2);
			tv.setText(text1);
		}

		// tab2 内の TextView を復帰
		String text2 = savedInstanceState.getString("TextView2");
		if (text2 != null ) {
			View tab2 = MainActivity.this.findViewById(R.id.linearLayout2);
			TextView tv = (TextView) tab2.findViewById(R.id.textView2);
			tv.setText(text2);
		}

	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);

		// タブ位置の保存
		TabHost tabhost = (TabHost)MainActivity.this.findViewById(R.id.tabHost);
		int currentTab = tabhost.getCurrentTab();
		outState.putInt("CurrentTab", currentTab);

		// 一番上の TextView を保存
		TextView tvTop = (TextView) MainActivity.this.findViewById(R.id.textView);
		outState.putString("TextViewTop", tvTop.getText().toString());

		// tab1 のコンテンツを取得
		View tab1 = MainActivity.this.findViewById(R.id.linearLayout1);
		TextView tv1 = (TextView) tab1.findViewById(R.id.textView2);
		outState.putString("TextView1", tv1.getText().toString());

		// tab2 のコンテンツを取得
		View tab2 = MainActivity.this.findViewById(R.id.linearLayout2);
		TextView tv2 = (TextView) tab2.findViewById(R.id.textView2);
		outState.putString("TextView2", tv2.getText().toString());

	}
}


画面定義
<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="lightbox.july.tabhostapplication.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView"
        android:textSize="30dp"/>

    <TabHost
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/tabHost"
        android:layout_below="@+id/textView">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
            </TabWidget>

            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <include
                    android:id="@+id/linearLayout1"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    layout="@layout/tab_entry"/>

                <include
                    android:id="@+id/linearLayout2"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    layout="@layout/tab_entry2"/>

                <include
                    android:id="@+id/linearLayout3"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    layout="@layout/tab_entry3"/>

            </FrameLayout>
        </LinearLayout>
    </TabHost>
</RelativeLayout>


3つの tab 毎の定義
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:id="@+id/textView2"
        android:textSize="40dp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="入力を表示"
        android:id="@+id/button"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editText"/>

</LinearLayout>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:id="@+id/textView2"
        android:textSize="40dp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="入力を表示"
        android:id="@+id/button2"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editText2"/>

</LinearLayout>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:id="@+id/textView2"
        android:textSize="40dp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="なにもしない"
        android:id="@+id/button3"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editText3"/>

</LinearLayout>
※ 便宜上3つにまとめていますが、xml ファイルはそれぞれ 作成しています



posted by lightbox at 2016-08-01 13:40 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり