トップ・ページの表示 注意書きの表示 掲示板に書き込む前に必ず この ”注意書き”を お読み下さい.

"伊邪那"

   
   

ページの表示順:{ 新しい順/ 古い順}.
初期・ページの表示・位置:{ 先頭ページ/ 末尾ページ}.
1ページ内のスレッド表示数:











<Number>: [000009CA]  <Date>: 2016/03/22 05:39:54
<Title>: Tetraz
<Name>: amanojaku@管理人



中々プレイしやすいテトリスが無いので自分で作ってみる。
ちなみに「Tetris(R)」は登録商標「(R)」になっているようなので、名前は「Tetraz」としています。

「Medium」モードの場合、「黄色(yellow)、紫(purple)」の図形の出現率を少し多めに、「Easy」モードの場合、「水色(cyan)、黄色(yellow)、紫(purple)」の図形の出現率を少し多めにしています。
プレイ中の「メニュー・キー、戻る・キー」の誤操作 対策として「メニュー・キー、戻る・キー」が押された場合はデフォルトの処理は無効化され、(2秒間) Toast が表示され、その間(2秒間)ゲームの Thread も Sleep します。
以前 作成した「SurfaceView 028」を元に やっつけ仕事でプログラムを作成したので、Class 設計としてはチョット ダメな感じ。

とりあえず、[New Project]で(1ページ目)[Application name]:「Tetraz」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<ユーザー名>\AndroidStudioProjects\SurfaceView」と設定、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
ただし、「<プロジェクト名>→app→build.gradle」内の"targetSdkVersion"がデフォルト値だと「Android 2.2(API Level 8)」の端末にインストールできない場合があるようだ。
「<プロジェクト名>→app→build.gradle」内の"targetSdkVersion"を(当方は SDK に「API Level 10:Android OS 2.3.3」をインストールしているので)「10」(API Level 10:Android OS 2.3.3)に設定してやると「Android 2.2(API Level 8)」の端末にインストールできるようになる場合があるようだ。
低いバージョンの端末まで対応したい場合は、 SDK に「API Level 10:Android OS 2.3.3」を(「Android Studio」ではなくオリジナルの「Android SDK」の「SDK Manager.exe」で)インストールしておくことをオススメする。
なお、「targetSdkVersion」はアプリ側が(OS 側の)どのAPIレベルまでサポートしているかと言うことらしい。

このスレの下の方にに"drawable-hdpi"フォルダー用のアイコン・ファイル(「48x48」Pixel)をアップしてあるので、「drawable-hdpi」フォルダー(存在しない場合は作成してやる)内にコピペして下さい。
一応、アプリの Logo マークを Android のマスコット・キャラの「Droid君」にしている。
"drawable-mdpi"フォルダー用は手抜きしているが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。
その場合、当然 画質は悪くなるので画質を綺麗にしたい場合は手抜きせずにチャント"drawable-mdpi"フォルダー用の画像ファイル(「32x32」Pixel)も用意してやる。
「Toolbar」の UI はスタンダードな「ActionBarActivity」のような感じになっている。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。



『<Project名>→app→main→java→com.example.tetraz→MainActivity』(MainActivity.java)


package com.example.tetraz;

import android.os.Bundle;
import android.util.Log;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
// import android.widget.Toolbar;
import android.content.Context;

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;

import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import android.view.Gravity;
import android.view.View;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.widget.ImageButton;
import android.view.KeyEvent;
import android.view.MotionEvent;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Bitmap;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener, View.OnTouchListener, SurfaceHolder.Callback {
    static final String fsDebugCrest = " 021";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = false; // true; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。
    MainActivity oMainApp;
    Context oBaseContext;
    Context oAppContext;
    Context oContext;
    ActionBar oActionBar;
    Menu oMainMenu;

    String vsPackageName;
    String vsVersionName;
    int iVersionCode;
    String vsToolbarTitle;

    enum AppLaunchMode_Enum { feCreate, feRestart };
    static AppLaunchMode_Enum eAppLaunchMode;

    // String vsMessage = "";
    // TextView ctvMessage;
    RunningSurfaceView oRunningSurfaceView;

    static final int[] ONTOUCH_WIDGETS = new int[]{
            R.id.cibBlockRight, R.id.cibBlockLeft,
            R.id.cibBlockDown, R.id.cibBlockRotate, };
    ImageButton cibBlockRight, cibBlockLeft, cibBlockDown;

    enum TetrazBlocks_Enum { feConflictCheck, feDraw, feErase, feDeposit,
        feSqueezeCheck, feSqueeze };
    static final int [][][] d3sTetrazBlockColorRID = new int[][][]{
            {
                    { R.color.cyan, R.color.cyan, R.color.cyan, R.color.cyan, },
            },
            {
                    { R.color.blue, R.color.empty, R.color.empty, },
                    { R.color.blue, R.color.blue, R.color.blue, },
            },
            {
                    { R.color.empty, R.color.empty, R.color.orange, },
                    { R.color.orange, R.color.orange, R.color.orange, },
            },
            {
                    { R.color.yellow, R.color.yellow, },
                    { R.color.yellow, R.color.yellow, },
            },
            {
                    { R.color.empty, R.color.lawngreen, R.color.lawngreen, },
                    { R.color.lawngreen, R.color.lawngreen, R.color.empty, },
            },
            {
                    { R.color.empty, R.color.purple, R.color.empty, },
                    { R.color.purple, R.color.purple, R.color.purple, },
            },
            {
                    { R.color.red, R.color.red, R.color.empty, },
                    { R.color.empty, R.color.red, R.color.red, },
            },
    };
    static final int [] d1iTetrazBlockRoll_Default = new int[]{
            1, 3, 1, 0, 1, 1, 1,
    };
    static volatile ArrayList<RunnerBlocks> dl1oRunnerBlocksDepot; // = new ArrayList<RunnerBlocks>( );

    public abstract class RunnerPrimitive {

        public void run() {
        }
    }

    public abstract class RunnerFigure extends RunnerPrimitive {
        // volatile:最適化の抑制.
        volatile RunningSurfaceView oRunningSurfaceView;
        volatile Paint oPaint = new Paint();
        volatile double vdPX, vdPY, vdDX, vdDY;
    }

    public class RunnerBlocks extends RunnerFigure {

        final int [][][] d3sBlockColorRID = d3sTetrazBlockColorRID;

        // volatile:最適化の抑制.
        // volatile Paint oPSurface = new Paint();
        // volatile int iBlockPattern;
        volatile int iCBlockPattern = 0;
        // volatile int iOBlockPattern = -1;
        volatile int iEBlockRoll, iCBlockRoll, iOBlockRoll;
        volatile int iSCX, iSCY;
        volatile int iSOX, iSOY;
        volatile int iSEDX, iSEDY;
        volatile int iSCDX, iSCDY;
        volatile int iSNX, iSNY;
        // volatile boolean lEHorizonActivated = false; // true; //
        volatile int iBlocksAreaSWMin, iBlocksAreaSWMax;
        volatile int iBlocksAreaSHMin, iBlocksAreaSHMax;
        volatile int iBlocksTouchSXMin, iBlocksTouchSXMax;
        volatile int iBlocksUnTouchSXMin, iBlocksUnTouchSXMax;
        volatile boolean lBlocksConflict = false; // true; //
        volatile boolean lShowSW = true; // false; //
        volatile boolean lFirstStrike = true; // false; //
        // volatile boolean lDepositSW = false; // true; //
        volatile boolean lDestroy = false; // true; //
        // volatile boolean lCBlockRoll = false; // true; //
        volatile boolean lEBlockRoll = false; // true; //
        // volatile boolean lEDrawSW, lCDrawSW;

        int iBlockRoll_Default;
        boolean[] d1lConflict = new boolean[5];

        RunnerBlocks(RunningSurfaceView oRSV, int iCBP, int iCBR) {
            super( );
            oRunningSurfaceView = oRSV;
            if( 0<=iCBP ) {
                iCBlockPattern = iCBP;
            }else {
                // ???
                iCBlockPattern = (int)Math.floor(
                        Math.random() * d3sBlockColorRID.length);
            }
            Initialize(iCBR);
        }

        public void Initialize(int iCBR){
            RunningSurfaceView oRSV = oRunningSurfaceView;
            // ???
            if(DEBUG)Log.i(tag, "RunnerBlocks( ) : "+
                    "iCBlockPattern="+iCBlockPattern+"; "+
                    "d3sBlockColorRID.length="+d3sBlockColorRID.length+"; "+
                    "");
            if( 0<=iCBR ) {
                iEBlockRoll = iCBR;
                iCBlockRoll = iCBR;
                iOBlockRoll = iCBR;
            }else{
                iEBlockRoll = d1iTetrazBlockRoll_Default[iCBlockPattern];
                iCBlockRoll = iEBlockRoll;
                iOBlockRoll = iEBlockRoll;
            }
            lBlocksConflict = false; // true; //
            lShowSW = true; // false; //
            lFirstStrike = true; // false; //
            lDestroy = false; // true; //
            // lCBlockRoll = false; // true; //
            lEBlockRoll = false; // true; //
            iSCX = oRSV.iWallSWQnt / 2;
            iSCY = 0;
            iSOX = iSCX ;
            iSOY = iSCY;
            iSEDX = 0;
            iSEDY = 0;
            iSCDX = iSEDX;
            iSCDY = iSEDY;
        }

        void BlockRotate( ){
            iEBlockRoll++;
            if( 4<=iEBlockRoll ){
                iEBlockRoll = 0;
            }
        }

        @Override
        public void run() {
            RunningSurfaceView oRSV = oRunningSurfaceView;
            RunnerBlocks  oNewRunnerBlocks = null;
            TetrazBlocks_Enum eTB;
            int iSAX, iSAY, iNBlockPattern;
            int iSRX, iSRY, iSBX, iSBY, iABlockRoll;
            boolean lABlockRoll = false; // true; //
            lDestroy = false; // true; //
            boolean lBlockRoll = false; // true; //
            // int iSCDX, iSCDY;
            if( ! lFirstStrike ){
                FigureDraw( oRSV.iWallScreenBiasX, oRSV.iWallScreenBiasY,
                        oRSV.iWallSWQnt, oRSV.iWallSHQnt,
                        iCBlockPattern, iSOX, iSOY, iOBlockRoll,
                        TetrazBlocks_Enum.feErase );
            }
            if( lEBlockRoll ){
                BlockRotate();
            }
            lABlockRoll = lEBlockRoll;
            lEBlockRoll = false; // true; //
            // lCBlockRoll = false; // true; //
            iABlockRoll = iEBlockRoll;
            lBlockRoll = false; // true; //
            iSCDX = iSEDX;
            iSCDY = iSEDY;
            iSAX = iSCX+iSCDX;
            iSAY = iSCY+iSCDY;
            GetBlockArea(iCBlockPattern, iABlockRoll);
            FigureDraw( oRSV.iWallScreenBiasX, oRSV.iWallScreenBiasY,
                    oRSV.iWallSWQnt, oRSV.iWallSHQnt,
                    iCBlockPattern, iSAX, iSAY, iABlockRoll,
                    TetrazBlocks_Enum.feConflictCheck );
            if(DEBUG)Log.i(tag, "run( ) : "+
                    "lEBlockRoll="+lEBlockRoll+"; "+
                    "lABlockRoll="+lABlockRoll+"; "+
                    "iEBlockRoll="+iEBlockRoll+"; "+
                    "iABlockRoll="+iABlockRoll+"; "+
                    "iCBlockRoll="+iCBlockRoll+"; "+
                    "");
            if( ! oRSV.lDepositSW ) {
                if (lBlocksConflict) {
                    if( lABlockRoll ){
                        if( 0<iBlocksTouchSXMin && iBlocksTouchSXMax<0 ){
                            lBlockRoll = true; // false; //
                        }
                        if( iBlocksTouchSXMin<0 && iBlocksTouchSXMax<0 ){
                            iSAX = iSAX - (iBlocksAreaSWMin - iBlocksTouchSXMin) + 1;
                        }
                        if( 0<iBlocksTouchSXMin && 0<iBlocksTouchSXMax ){
                            if(DEBUG)Log.i(tag, "run( ) : "+
                                    "iSAX="+iSAX+"; "+
                                    "iBlocksAreaSWMax="+iBlocksAreaSWMax+"; "+
                                    "iBlocksTouchSXMax="+iBlocksTouchSXMax+"; "+
                                    "(iBlocksAreaSWMax-iBlocksTouchSXMax)="+(iBlocksAreaSWMax - iBlocksTouchSXMax)+"; "+
                                    "");
                            iSAX = iSAX - (iBlocksAreaSWMax - iBlocksTouchSXMax) - 1;
                        }
                        if( ! lBlockRoll ){
                            FigureDraw( oRSV.iWallScreenBiasX, oRSV.iWallScreenBiasY,
                                    oRSV.iWallSWQnt, oRSV.iWallSHQnt,
                                    iCBlockPattern, iSAX, iSAY, iABlockRoll,
                                    TetrazBlocks_Enum.feConflictCheck );
                        }
                        if( ! lBlocksConflict ||
                                0<iBlocksTouchSXMin && iBlocksTouchSXMax<0 ){
                            lBlockRoll = true; // false; //
                        }else {
                            lBlockRoll = false; // true; //
                            iEBlockRoll = iCBlockRoll;
                            iABlockRoll = iCBlockRoll;
                            iSAX = iSCX + iSCDX;
                            iSAY = iSCY + iSCDY;
                            if ( lFirstStrike ) {
                                lDestroy = true; // false; //
                                // oRSV.EngineDestroy();
                            }
                            // lCBlockRoll = lBlockRoll;
                        }
                    }else{
                        if (0 != iSCDX) {
                            iSAX = iSCX;
                        }
                        if ( lFirstStrike ) {
                            lDestroy = true; // false; //
                            // oRSV.EngineDestroy( );
                        } else if (0 != iSCDY) {
                            oRSV.lDepositSW = true; // false; //
                            oRSV.lRepaintSW = true; // false; //
                            iSAY = iSCY;
                            GetBlockArea(iCBlockPattern, iABlockRoll);
                            if( 0>(iSAY + iBlocksAreaSHMin) ){
                                lDestroy = true; // false; //
                            }else {
                                oNewRunnerBlocks = oRSV.dl1oRunnerBlocksStock.get(0);
                                oNewRunnerBlocks.Initialize(0);
                                oRSV.dl1oRunnerBlocksStock.remove(0);
                                oRSV.dl1oRunnerBlocksStock.add(oRSV.oBlockDepotDelivery(-1));
                            }
                        }
                    }
                }
            }
            iSCX = iSAX;
            iSCY = iSAY;
            iCBlockRoll = iABlockRoll;
            if(DEBUG)Log.i(tag, "run( ) : "+
                    "iCBlockPattern="+iCBlockPattern+"; "+
                    "iSCDX="+iSCDX+"; "+
                    "iSCDY="+iSCDY+"; "+
                    "iSAY="+iSAY+"; "+
                    "iSCX="+iSCX+"; "+
                    "iSCY="+iSCY+"; "+
                    "iBlocksAreaSWMin="+iBlocksAreaSWMin+"; "+
                    "iBlocksAreaSWMax="+iBlocksAreaSWMax+"; "+
                    "");
            eTB = TetrazBlocks_Enum.feDraw;
            if( oRSV.lDepositSW ){
                eTB = TetrazBlocks_Enum.feDeposit;
            }
            FigureDraw( oRSV.iWallScreenBiasX, oRSV.iWallScreenBiasY,
                    oRSV.iWallSWQnt, oRSV.iWallSHQnt,
                    iCBlockPattern, iSCX, iSCY, iCBlockRoll,
                    eTB );
            iOBlockRoll = iCBlockRoll;
            iSOX = iSCX;
            iSOY = iSCY;
            if( null!=oNewRunnerBlocks ){
                oRSV.oRunnerBlocks.Initialize(-1);
                dl1oRunnerBlocksDepot.add(oRSV.oRunnerBlocks);
                oRSV.oRunnerBlocks = oNewRunnerBlocks;
            }
            // lCBlockRoll = false; // true; //
            lFirstStrike = false; // true; //
            if(lDestroy){
                // Toast.makeText(oMainApp, "GAME OVER", Toast.LENGTH_SHORT).show();
                oRSV.EngineDestroy( );
            }
        }

        public void BlockBullet(int iBiasX, int iBiasY, int iSAX, int iSAY, Paint oPaint) {
            RunningSurfaceView oRSV = oRunningSurfaceView;
            int iRX, iRY;
            iRX = iBiasX + iSAX * oRSV.iBlockDistance;
            iRY = iBiasY + iSAY * oRSV.iBlockDistance;
            oRSV.oCOffScreen.drawRect(
                    iRX, iRY,
                    iRX + oRSV.iBlockDistance - 1, iRY + oRSV.iBlockDistance - 1,
                    oPaint);
        }

        public void GetBlockArea( int iBlockPattern, int iBRoll ){
            int iAX, iAY, iSAX, iSAY, iSRX, iSRY, iSBX, iSBY, iFOdd, iFXSign, iFYSign;
            double vdFBX, vdFBY, vdSBX, vdSBY ,vdSNX, vdSNY, vdSRX, vdSRY;
            double vdBASIWMin, vdBASIWMax, vdBASIHMin, vdBASIHMax;
            double vdBASFWMin, vdBASFWMax, vdBASFHMin, vdBASFHMax;
            int iBASFWMin, iBASFWMax, iBASFHMin, iBASFHMax;
            iFOdd = iBRoll % 2;
            iFXSign = 1; iFYSign = 1;
            if( 0!=iBRoll / 2 ){
                iFXSign = -1;
                iFYSign = -1;
            }
            if( 0!=iFOdd ){
                iFXSign = -iFXSign;
            }
            vdSBX = (double)-(d3sBlockColorRID[iBlockPattern][0].length - 1) / 2;
            vdSBY = (double)-(d3sBlockColorRID[iBlockPattern].length - 1) / 2;

            vdBASIWMin = vdSBX;
            vdBASIWMax = vdSBX + d3sBlockColorRID[iBlockPattern][0].length - 1;
            vdBASIHMin = vdSBY;
            vdBASIHMax = vdSBY + d3sBlockColorRID[iBlockPattern].length - 1;
            if( 0==iFOdd ){
                vdBASFWMin = iFXSign * vdBASIWMin;
                vdBASFWMax = iFXSign * vdBASIWMax;
                vdBASFHMin = iFYSign * vdBASIHMin;
                vdBASFHMax = iFYSign * vdBASIHMax;
            }else{
                vdBASFWMin = iFXSign * vdBASIHMin;
                vdBASFWMax = iFXSign * vdBASIHMax;
                vdBASFHMin = iFYSign * vdBASIWMin;
                vdBASFHMax = iFYSign * vdBASIWMax;
            }
            iBASFWMin = (int)Math.round(vdBASFWMin + .3);
            iBASFWMax = (int)Math.round(vdBASFWMax + .3);
            iBASFHMin = (int)Math.round(vdBASFHMin + .3);
            iBASFHMax = (int)Math.round(vdBASFHMax + .3);
            iBlocksAreaSWMin = Math.min(iBASFWMin, iBASFWMax);
            iBlocksAreaSWMax = Math.max(iBASFWMin, iBASFWMax);
            iBlocksAreaSHMin = Math.min(iBASFHMin, iBASFHMax);
            iBlocksAreaSHMax = Math.max(iBASFHMin, iBASFHMax);
        }

        public void FigureDraw(int iBiasX, int iBiasY, int iSWidth, int iSHeight,
                               int iBlockPattern, int iSPX, int iSPY, int iBRoll,
                               TetrazBlocks_Enum oTetrazBlocks) {
            RunningSurfaceView oRSV = oRunningSurfaceView;
            Paint oP = null;
            int iAX, iAY, iSRX, iSRY, iSBX, iSBY, iFOdd, iFXSign, iFYSign;
            double vdFBX, vdFBY, vdSBX, vdSBY ,vdSIX, vdSIY, vdSFX, vdSFY;
            double vdBASNWMin, vdBASNWMax, vdBASNHMin, vdBASNHMax;
            double vdBASRWMin, vdBASRWMax, vdBASRHMin, vdBASRHMax;
            int iBASRWMin, iBASRWMax, iBASRHMin, iBASRHMax;
            int iSFX, iSFY;
            boolean lConflict = false; // true; //
            boolean lConflictCheck = false; // true; //
            int iBTSXMin,iBTSXMax;
            lBlocksConflict = false; // true; //
            for( int i = 0; i<d1lConflict.length; i++ ){
                d1lConflict[i] = false; // true; //
            }

            iFOdd = iBRoll % 2;
            iFXSign = 1; iFYSign = 1;
            if( 0!=iBRoll / 2 ){
                iFXSign = -1;
                iFYSign = -1;
            }
            if( 0!=iFOdd ){
                iFXSign = -iFXSign;
            }
            vdSBX = (double)-(d3sBlockColorRID[iBlockPattern][0].length - 1) / 2;
            vdSBY = (double)-(d3sBlockColorRID[iBlockPattern].length - 1) / 2;

            iBTSXMin = 1;
            iBTSXMax = -1;
            iBlocksTouchSXMin = 1;
            iBlocksTouchSXMax = -1;
            GetBlockArea(iBlockPattern, iBRoll);
            BlockPattern : for(int iStepY = 0; iStepY < d3sBlockColorRID[iBlockPattern].length; iStepY++) {
                for(int iStepX = 0; iStepX < d3sBlockColorRID[iBlockPattern][iStepY].length; iStepX++) {
                    if( R.color.empty!=d3sBlockColorRID[iBlockPattern][iStepY][iStepX] ) {
                        vdSIX = vdSBX + iStepX;
                        vdSIY = vdSBY + iStepY;
                        if( 0==iFOdd ){
                            vdSFX = iFXSign * vdSIX;
                            vdSFY = iFYSign * vdSIY;
                        }else{
                            vdSFX = iFXSign * vdSIY;
                            vdSFY = iFYSign * vdSIX;
                        }
                        iSRX = (int)Math.round(iSPX + vdSFX + .3);
                        iSRY = (int)Math.round(iSPY + vdSFY + .3);
                        if( TetrazBlocks_Enum.feDraw==oTetrazBlocks ||
                                TetrazBlocks_Enum.feDeposit==oTetrazBlocks ) {
                            oP = oPaint;
                            oP.setColor(ContextCompat.getColor(oAppContext,
                                    d3sBlockColorRID[iBlockPattern][iStepY][iStepX]));
                            oP.setStyle(Style.FILL);
                        }
                        if( TetrazBlocks_Enum.feErase==oTetrazBlocks ) {
                            oP = oRSV.oPWall;
                        }
                        if( TetrazBlocks_Enum.feConflictCheck==oTetrazBlocks ){
                            lConflict = (iSRX<0 || iSWidth<=iSRX) ||
                                    (iSHeight<=iSRY);
                            lConflict = lConflict || (0<=iSRX && iSRX<iSWidth) &&
                                    (0<=iSRY && iSRY<iSHeight) &&
                                    (oRSV.oPWall.getColor()!=oRSV.d2iBlockPoolColorARGB[iSRX][iSRY]);
                            if( lConflict ){
                                lBlocksConflict = true; // false; //
                                iSFX = (int)Math.round(vdSFX + .3);
                                // iSFY;
                                d1lConflict[d1lConflict.length / 2 + iSFX] = true;
                            }
                        }else if( (0<=iSRX && iSRX<iSWidth) &&
                                (0<=iSRY && iSRY<iSHeight) ) {
                            if( TetrazBlocks_Enum.feConflictCheck==oTetrazBlocks ) {
                                if( oRSV.oPWall.getColor()!=oRSV.d2iBlockPoolColorARGB[iSRX][iSRY] ) {
                                    lBlocksConflict = true; // false; //
                                    // break BlockPattern;
                                }
                            }else{
                                BlockBullet(iBiasX, iBiasY, iSRX, iSRY, oP);
                                if( TetrazBlocks_Enum.feDeposit==oTetrazBlocks ) {
                                    oRSV.d2iBlockPoolColorARGB[iSRX][iSRY] = oP.getColor();
                                }
                            }
                        }else if(0<=iSRY){
                            if( TetrazBlocks_Enum.feConflictCheck==oTetrazBlocks ) {
                                lBlocksConflict = true; // false; //
                                break BlockPattern;
                            }
                        }
                    }
                }
            }
            if(lBlocksConflict){
                for( int i = 0; i<d1lConflict.length; i++ ){
                    iSFX = i - d1lConflict.length / 2;
                    if(d1lConflict[i]){
                        if( iSFX<=0 ){
                            iBlocksTouchSXMin = iSFX;
                        }
                        if( 0<=iSFX ){
                            if( -1==iBlocksTouchSXMax ){
                                iBlocksTouchSXMax = iSFX;
                            }
                        }
                    }
                    if(DEBUG)Log.i(tag, "FigureDraw( ) : "+
                            "i="+i+"; "+
                            "iSFX="+iSFX+"; "+
                            "iBlocksTouchSXMin="+iBlocksTouchSXMin+"; "+
                            "iBlocksTouchSXMax="+iBlocksTouchSXMax+"; "+
                            "");
                }
            }
        }
    }

    public abstract class RunningDriver
            implements Runnable {

        // volatile:最適化の抑制.
        // volatile ArrayList<RunnerFigure> dl1oRunnerMembers = new ArrayList<RunnerFigure>( );
        // volatile ArrayList<RunnerBlocks> dl1oRunnerBlocks = new ArrayList<RunnerBlocks>( );
        volatile RunnerBlocks oRunnerBlocks;
        // volatile ArrayList<RunnerBlocks> dl1oRunnerBlocksDeposit = new ArrayList<RunnerBlocks>( );
        volatile ArrayList<RunnerBlocks> dl1oRunnerBlocksStock; // = new ArrayList<RunnerBlocks>( );

        // volatile int iRunnerNumber;
        volatile boolean lLaunchActivated = false; // true; //
        volatile boolean lEngineSW = false; // true; //
        volatile boolean lWaitSW = false; // true; //
        volatile boolean lVirginGround = true; // false; //
        volatile boolean lClearSW = true; // false; //
        volatile boolean lDepositSW = false; // true; //
        volatile boolean lRepaintSW = false; // true; //
        volatile Thread oEngine = null;
        volatile long interval = 0;
        int iInterval_HorizonOrder = 225;
        int iInterval_SqueezeOrder = 600;
        int iInterval_DefaultDropOrder = 700;
        int iInterval_RollOrder = 550;
        int iInterval_DepositOrder = 50;
        int iInterval_FastDropOrder = 80;
        volatile int iESleepInterval = -1;
        volatile int iCSleepInterval = -1;
        int iInterval_ReleaseOrder = 300;
        int iHorizonMinuteOrderMax = 0;
        int iDropMinuteOrderMax = 0;
        // int iDefaultMinuteOrderMax = iDropMinuteOrderMax;
        int iEventMinuteOrderMax = -1;

        volatile int iMinuteOrderDCnt = -1;
        volatile int iDropMinuteOrderCnt = 0;
        volatile int iDropStep = 0;
        // volatile boolean lEHorizonActivated = false; // true; //
        // volatile boolean lEHorizonListening = false; // true; //
        // volatile boolean lEHorizonReceive = false; // true; //
        // volatile boolean lEHorizonOrder = false; // true; //
        // volatile boolean lCHorizonAction = false; // true; //
        // volatile boolean lHorizonOrder = false; // true; //
        volatile int iETouchAction_BlockRightPress = -1;
        volatile int iCTouchAction_BlockRightPress = -1;
        volatile int iETouchAction_BlockRightRelease = -1;
        volatile int iCTouchAction_BlockRightRelease = -1;
        volatile int iETouchAction_BlockLeftPress = -1;
        volatile int iCTouchAction_BlockLeftPress = -1;
        volatile int iETouchAction_BlockLeftRelease = -1;
        volatile int iCTouchAction_BlockLeftRelease = -1;
        volatile int iETouchAction_BlockDown = -1;
        volatile int iCTouchAction_BlockDown = -1;
        int iWallScreenBiasX, iWallScreenBiasY;
        int iWallSWQnt = 11;
        int iWallSHQnt = 19;

        public RunningDriver( ) {
            super( );
        }

        // abstract public boolean lPoolBottomCheck( );
        abstract public boolean lBlockPoolBufSqueeze( TetrazBlocks_Enum oTB );

        public void Launch(AppLaunchMode_Enum eLaunchMode) {
            lLaunchActivated = true; // false; //
            if( AppLaunchMode_Enum.feCreate==eLaunchMode ){
                Initialize( );
                EngineCreate(true); // false; //
            }
            if( AppLaunchMode_Enum.feRestart==eLaunchMode ){
                EngineCreate(true); // false; //
            }
        }

        public void Initialize( ){
            lLaunchActivated = false; // true; //
            lEngineSW = true; // false; //
            lWaitSW = false; // true; //
            lClearSW = true; // false; //
            lVirginGround = true; // false; //
            lRepaintSW = false; // true; //
            lDepositSW = false; // true; //
            iETouchAction_BlockRightPress = -1;
            iCTouchAction_BlockRightPress = -1;
            iETouchAction_BlockRightRelease = -1;
            iCTouchAction_BlockRightRelease = -1;
            iETouchAction_BlockLeftPress = -1;
            iCTouchAction_BlockLeftPress = -1;
            iETouchAction_BlockLeftRelease = -1;
            iCTouchAction_BlockLeftRelease = -1;
            iETouchAction_BlockDown = -1;
            iCTouchAction_BlockDown = -1;
            iESleepInterval = -1;
            iCSleepInterval = -1;
        }

        public abstract void RunningHeader( );

        public abstract void RunningFooter( );

        public void RunnerWait(boolean lWSW) {
            lWaitSW = lWSW;
            if (DEBUG) Log.i(tag,
                    "lWaitSW="+String.valueOf(lWaitSW)+"; "+
                            "; ");
            if( null!=oEngine ) {
// NEW:起動していないスレッド、
// RUNNABLE:実行されているスレッド、
// BLOCKED:ブロックされモニターロックを待機しているスレッド、
// WAITING:特定のアクションを実行するのを無期限に待機しているスレッド、
// TIMED_WAITING:指定された待機時間、ほかのスレッドがアクションを実行するのを待機しているスレッド、
// TERMINATED:終了したスレッドの状態、
                if (DEBUG) Log.i(tag,
                        "oEngine.getState="+String.valueOf(oEngine.getState( ))+
                                "; ");
                if( ! lWaitSW ){
                    synchronized(this) {
                        try {
// 「notify( )」は synchronized で囲わないと正常に機構しないようだ。
// (「wait( )」などで)待機中の場合は お互いに synchronized ブロックでも synchronized ブロックを通過できるらしい。
// 「notify( )」は この synchronized ブロック内では実行されず、
// この synchronized ブロック通過後にイベント的に「notify( )」コマンドが発行される。
                            notify( );
                        }catch(IllegalMonitorStateException e){
                            if(DEBUG)Log.i(tag, "RunningDriver#RunnerWait( ) : IllegalMonitorStateException; "+
                                    "");
                        }
                    }
                }
            }
        }

        public void EngineCreate( boolean lWSW ) {
            lEngineSW = true; // false; //
            lWaitSW = lWSW; // false; // true; //
            lClearSW = true; // false; //
            lVirginGround = true; // false; //
            lRepaintSW = false; // true; //
            if (DEBUG) Log.i(tag, "oEngine = new Thread(this)");
            oEngine = new Thread(this);
            oEngine.start();
        }

        public void EngineDestroy( ) {
            if( null!=oEngine ){
                oEngine.interrupt( );
            }
        }

        public void run() {
            if (DEBUG) Log.i(tag, "RunningDriver:run( );");
            RunnerBlocks oRB;
            boolean lDrawSW = true; // false; //
            boolean lSqueeze, lASqueeze; // false; // true; //
            boolean lPress = false; // true; //

            while ( oEngine!=null && lEngineSW ) {
                // lCHorizonAction = lEHorizonAction;
                iCTouchAction_BlockRightPress = iETouchAction_BlockRightPress;
                iCTouchAction_BlockLeftPress = iETouchAction_BlockLeftPress;
                iCTouchAction_BlockRightRelease = iETouchAction_BlockRightRelease;
                iCTouchAction_BlockLeftRelease = iETouchAction_BlockLeftRelease;
                iCTouchAction_BlockDown = iETouchAction_BlockDown;
                iCSleepInterval = iESleepInterval;
                // oRunnerBlocks.lCBlockRoll = oRunnerBlocks.lEBlockRoll;
                // oRunnerBlocks.lEBlockRoll = false;

                oRunnerBlocks.iSEDX = 0;
                oRunnerBlocks.iSEDY = 0;
                lASqueeze = false; // true; //
                lSqueeze = false; // true; //
                lDrawSW = false; // true; //
                interval = iInterval_DefaultDropOrder;
                lASqueeze = lBlockPoolBufSqueeze( TetrazBlocks_Enum.feSqueezeCheck );;
                if( 0<=iCSleepInterval ) {
                    interval = iCSleepInterval;
                    iESleepInterval = -1;
                }else if( lASqueeze ) {
                    lSqueeze = lASqueeze;
                    oRunnerBlocks.lEBlockRoll = false; // true; //
                    interval = iInterval_SqueezeOrder;
                }else {
                    lDrawSW = true; // false; //
                    if ( ! oRunnerBlocks.lFirstStrike ) {
                        oRunnerBlocks.iSEDY = 1;
                    }
                    if(oRunnerBlocks.lEBlockRoll){
                        oRunnerBlocks.iSEDY = 0;
                        // oRunnerBlocks.BlockRotate( );
                        interval = iInterval_RollOrder;
                    }
                    if ( lPress && MotionEvent.ACTION_UP==iCTouchAction_BlockRightRelease ) {
                        if (DEBUG) Log.i(tag, "if ( MotionEvent.ACTION_UP==iCTouchAction_BlockRightRelease );");
                        iETouchAction_BlockRightRelease = -1;
                        oRunnerBlocks.iSEDY = 0;
                        interval = iInterval_ReleaseOrder;
                    }
                    if ( lPress && MotionEvent.ACTION_UP==iCTouchAction_BlockLeftRelease ) {
                        if (DEBUG) Log.i(tag, "if( MotionEvent.ACTION_UP==iCTouchAction_BlockLeftRelease );");
                        iETouchAction_BlockLeftRelease = -1;
                        oRunnerBlocks.iSEDY = 0;
                        interval = iInterval_ReleaseOrder;
                    }
                    lPress = false; // true; //
                    if ( MotionEvent.ACTION_DOWN==iCTouchAction_BlockRightPress ||
                            cibBlockRight.isPressed( ) ) {
                        if (DEBUG) Log.i(tag, "if( MotionEvent.ACTION_DOWN==iCTouchAction_BlockRight );");
                        iETouchAction_BlockRightPress = -1;
                        lPress = true; // false; //
                        oRunnerBlocks.iSEDX = 1;
                        oRunnerBlocks.iSEDY = 0;
                        interval = iInterval_HorizonOrder;
                    }
                    if ( MotionEvent.ACTION_DOWN==iCTouchAction_BlockLeftPress ||
                            cibBlockLeft.isPressed()) {
                        if (DEBUG) Log.i(tag, "if( MotionEvent.ACTION_DOWN==iCTouchAction_BlockLeft );");
                        iETouchAction_BlockLeftPress = -1;
                        lPress = true; // false; //
                        oRunnerBlocks.iSEDX = -1;
                        oRunnerBlocks.iSEDY = 0;
                        interval = iInterval_HorizonOrder;
                    }
                    if (MotionEvent.ACTION_DOWN == iCTouchAction_BlockDown ||
                            cibBlockDown.isPressed()) {
                        if (DEBUG) Log.i(tag, "if(cibBlockDown.isPressed( ));");
                        iETouchAction_BlockDown = -1;
                        interval = iInterval_FastDropOrder;
                        if ( oRunnerBlocks.lFirstStrike ) {
                            lDrawSW = false; // true; //
                            // oRunnerBlocks.iSEDY = 0;
                        }
                    }
                }
                if(DEBUG)Log.i(tag, "run( ) : "+
                        "interval="+interval+"; "+
                        "");
                if( 0!=oRunnerBlocks.iSEDX ){
                    if(DEBUG)Log.i(tag, "if( 0!=oRunnerBlocks.iSEDX ) : "+
                            "oRunnerBlocks.iSEDX="+oRunnerBlocks.iSEDX+"; "+
                            "");
                }
                // lWaitSW = true; // false; //
                try {
                    if( lSqueeze ){
                        if(DEBUG)Log.i(tag, "run( ) : lBlockPoolBufSqueeze( ) : "+
                                "interval="+interval+"; "+
                                "");
                        RunningHeader();
                        lBlockPoolBufSqueeze(TetrazBlocks_Enum.feSqueeze);
                        RunningFooter();
                    }else if( lDrawSW ) {
                        RunningHeader();
                        // for (int iRunner = 0; iRunner < dl1oRunnerMembers.size(); iRunner++) {
                        // if ( null!=dl1oRunnerMembers.get(iRunner) ) {
                        // dl1oRunnerMembers.get(iRunner).run();
                        // }
                        // }
                        if( ! lVirginGround ){
                            lVirginGround = false; // true; //
                            oRunnerBlocks.run();
                            if ( lDepositSW ) {
                                lDepositSW = false; // true; //
                                // lDrawSW = true; // false; //
                                interval = iInterval_DepositOrder;
                                if(DEBUG)Log.i(tag, "run( ) : if ( lDepositSW )"+
                                        "interval="+interval+"; "+
                                        "");
                            }
                        }
                        RunningFooter();
                    }
                } catch (NullPointerException e) {
                    // ????
                    // e.printStackTrace( ); // ???
                    if (DEBUG) Log.i(tag, "RunningDriver:run( ):NullPointerException;");
                    lEngineSW = false; // true; //
                }
                lVirginGround = false; // true; //
                if(DEBUG)Log.i(tag, "run( ) :  "+
                        "interval="+interval+"; "+
                        "lSqueeze="+lSqueeze+"; "+
                        "");
                synchronized (this) {
                    // ↑ この this は oEngine では無く RunningDriver を継承している オブジェクトのインスタンス。
                    if (lWaitSW) {
                        try {
                            // 「wait( )」は synchronized で囲わないと正常に機構しない。
                            this.wait( );
                            if (DEBUG) Log.i(tag, "RunningDriver:wait( ):End.");
                            // ↑ この this は oEngine では無く RunningDriver を継承している オブジェクトのインスタンス。
                        } catch (InterruptedException e) {
                            // e.printStackTrace( );
                            if (DEBUG) Log.i(tag, "RunningDriver:run( ):InterruptedException;");
                            lEngineSW = false; // true; //
                        }
                    }
                    if (lEngineSW) {
                        if(DEBUG)Log.i(tag, "run( ) :  Thread.sleep( ) : "+
                                "interval="+interval+"; "+
                                "");
                        try {
                            Thread.sleep(interval);
                            // ↑ 「sleep(interval)」は指定された時間以上に待たされる場合があるようなので、
                            // 1回のループは「プログラムの処理時間+sleep(interval+α)」 となる。
                        } catch (InterruptedException e) {
                            // e.printStackTrace( );
                            if (DEBUG) Log.i(tag, "RunningDriver:run( ):InterruptedException;");
                            lEngineSW = false; // true; //
                        }
                    }
                }
            }
            // ???
            if( oRunnerBlocks.lDestroy ){
// Thread から Toast を表示する場合の定型処理。
                runOnUiThread(new Runnable() {
                    public void run() {
// (デフォルトでは Toast は画面の下側に表示されるが) Toast を画面の中央に表示させる。
// Toast の表示時間は、LENGTH_SHORT は2秒間、LENGTH_LONG は4秒間表示されるらしいので、
// 意図的に LENGTH_LONG を指定し、4秒間表示させている。
                        Toast toast = Toast.makeText(oMainApp, "GAME OVER", Toast.LENGTH_LONG);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();
                    }
                });
            }
            oEngine = null;
        }

    }

    public class RunningSurfaceView extends RunningDriver {
        // volatile:最適化の抑制.
        volatile Canvas oCanvas;
        volatile SurfaceHolder oSurfaceHolder;
        volatile Bitmap oBOffScreenBuffer;
        volatile Canvas oCOffScreen;
        volatile Paint oPBackground = new Paint();
        volatile Paint oPWall = new Paint();
        // Paint oPOffScreen = new Paint();
        // Paint oPBlack = new Paint();

        // boolean lLaunchActivated = false; // true; //
        // Manufacturing
        int iScreenManufactWidth = 480;
        int iScreenManufactHeight = 590;
        // Physical
        volatile int iScreenPhysWidth, iScreenPhysHeight;
        // volatile int iScreenBiasX, iScreenBiasY;
        volatile double vdScreenRate, vdScreenRateMin, vdScreenRateMax;
        volatile int iScreenWidth, iScreenHeight;

        int iBlockDivideX = iWallSWQnt + 3;
        int iBlockDivideY = iWallSHQnt;
        int iBlockDistance;
        int iStockScreenBiasX, iStockScreenBiasY;
        int iStockSWQnt = 2;
        int iStockSHQnt ;
        int [][] d2iBlockPoolColorARGB;
        int [] d1iWaitingWeightIndex;
        int [] d1iDeviationPatternIndex = null;
        double vdDeviationPatternAdvent;
        RunnerBlocks[] d1oPatternIndex;

        public RunningSurfaceView( ) {
            super( );
            // black // white
            oPBackground.setColor(ContextCompat.getColor(oAppContext, R.color.white));
            oPBackground.setStyle(Paint.Style.FILL);
            oPWall.setColor(ContextCompat.getColor(oAppContext, R.color.brass));
            oPWall.setStyle(Paint.Style.FILL);
        }

        public void SurfaceCreate( ){
            lLaunchActivated = false; // true; //
            SurfaceView oSV = (SurfaceView) findViewById(R.id.surfaceview);
            SurfaceHolder oSH = oSV.getHolder();
            oSH.addCallback(oMainApp);
            oSurfaceHolder = oSH;
        }

        public void ScreenSetting(int width, int height) {
            int iBlockDistanceX, iBlockDistanceY;
            iScreenPhysWidth = width;
            iScreenPhysHeight = height;
            iScreenWidth = iScreenPhysWidth;
            iScreenHeight = iScreenPhysHeight;
            double vdSWRate = (double)iScreenWidth/iScreenManufactWidth;
            double vdSHRate = (double)iScreenHeight/iScreenManufactHeight;
            vdScreenRateMin = Math.min(vdSWRate, vdSHRate);
            vdScreenRateMax = Math.max(vdSWRate, vdSHRate);
// 開発時の解像度「iScreenManufactWidth、iScreenManufactHeight」と
// 実機の解像度「iScreenWidth、iScreenHeight」の違いを「vdScreenRate」で表わしている。
// 大抵は「vdScreenRate = vdScreenRateMin;」で良いと思われる。
// ただし、これでは対応できない場合もある。
            vdScreenRate = vdScreenRateMin;
            if(DEBUG)Log.i(tag,
                    "surfaceChanged( );"+"\n"+
                            "iScreenPhysWidth="+iScreenPhysWidth+";\n"+
                            "iScreenPhysHeight="+iScreenHeight+";\n"+
                            "iScreenWidth="+iScreenWidth+";\n"+
                            "iScreenHeight="+iScreenHeight+";\n"+
                            "vdScreenRateMin="+vdScreenRateMin+";\n"+
                            "vdScreenRateMax="+vdScreenRateMax+";\n"+
                            "vdScreenRate="+vdScreenRate+";\n"+
                            "");
            OffScreenCreate( );
        }

        public void OffScreenCreate( ) {
            oBOffScreenBuffer = Bitmap.createBitmap(
                    iScreenPhysWidth, iScreenPhysHeight,
                    Bitmap.Config.ARGB_8888);
            oCOffScreen = new Canvas(oBOffScreenBuffer);
        }

        @Override
        public void Initialize( ) {
            super.Initialize( );
            int iBlockDistanceX, iBlockDistanceY, iBiasX;

            iWallSHQnt = (iWallSWQnt + 3) * 19 / (11 + 3);
            int iBlockDivideX = iWallSWQnt + 3;
            int iBlockDivideY = iWallSHQnt;
            iBlockDistanceX = iScreenPhysWidth / iBlockDivideX;
            iBlockDistanceY = iScreenPhysHeight / iBlockDivideY;
            iBlockDistance = Math.min(iBlockDistanceX, iBlockDistanceY);
            // iBiasX = (iScreenPhysWidth - iBlockDivideX * iBlockDistance) / 2;
            iWallScreenBiasX = (iScreenPhysWidth - iBlockDivideX * (iBlockDistance - 1)) / 2;
            iWallScreenBiasY = iScreenPhysHeight - iBlockDivideY * iBlockDistance;
            iStockScreenBiasX = iWallScreenBiasX + iWallSWQnt * iBlockDistance + iBlockDistance / 2;
            iStockScreenBiasY = iWallScreenBiasY;
            iStockSHQnt = iWallSHQnt;
            dl1oRunnerBlocksDepot = new ArrayList<RunnerBlocks>( );

            vdDeviationPatternAdvent = 0;
            d1iDeviationPatternIndex = null;
            if( 13==iWallSWQnt ){
                vdDeviationPatternAdvent = .12d; // 12%
                d1iDeviationPatternIndex = new int [] {
                        3, 5, 3, 5,
                        3, 5, 3, 5,
                        3, 5, 3, 5,
                };
            }else if( 14==iWallSWQnt ){
                vdDeviationPatternAdvent = .18d; // 18%
                d1iDeviationPatternIndex = new int [] {
                        0, 3, 5,
                        0, 3, 5,
                        0, 3, 5,
                        0, 3, 5,
                };
            }
// 下記の for の条件を「i<d3sTetrazBlockColorRID.length」にすれば、
// 「oRunnerBlocks、 dl1oRunnerBlocksStock」において同じパターンのブロックは出現しない。
// 下記の for の条件が「i<d3sTetrazBlockColorRID.length * 2」の場合は、
// 「oRunnerBlocks、 dl1oRunnerBlocksStock」において同じパターンのブロックは2つまで出現する。
            // d1iDeviationPatternIndex = null;
            d1iWaitingWeightIndex = null;
            int n = 2;
            d1oPatternIndex = new RunnerBlocks[d3sTetrazBlockColorRID.length * n];
            for(int i = 0; i<d3sTetrazBlockColorRID.length * n; i++) {
                d1oPatternIndex[i] = new RunnerBlocks(
                        this, i % d3sTetrazBlockColorRID.length, -1);
                dl1oRunnerBlocksDepot.add(d1oPatternIndex[i]);
            }
            oRunnerBlocks = oBlockDepotDelivery(0);
            dl1oRunnerBlocksStock = new ArrayList<RunnerBlocks>( );
            for(int i = 0; i<4; i++) {
                dl1oRunnerBlocksStock.add(oBlockDepotDelivery(-1));
            }
            d1iWaitingWeightIndex = new int[(int)Math.pow(2, dl1oRunnerBlocksDepot.size()) - 1];
            if(DEBUG)Log.i(tag, "Initialize( ) : "+
                    "dl1oRunnerBlocksDepot.size( )="+dl1oRunnerBlocksDepot.size()+"; "+
                    "d1iWaitingWeightIndex.lengt="+d1iWaitingWeightIndex.length+"; "+
                    "");
            int l = 0;
            for(int i = 0; i<dl1oRunnerBlocksDepot.size(); i++) {
                int k = dl1oRunnerBlocksDepot.size() - 1 - i;
                if(DEBUG)Log.i(tag, "Initialize( ) : "+
                        "l="+l+"; "+
                        "i="+i+"; "+
                        "k="+k+"; "+
                        "");
                for(int j = 0; j<(int)Math.pow(2, i); j++) {
                    d1iWaitingWeightIndex[l] = k;
                    l++;
                }
            }
            BlockPoolBufClear( );
        }

        public RunnerBlocks oBlockDepotDelivery(int iCBR){
            RunnerBlocks oRB = null;
            double vdRandom;
            int iPattern;
            int iRandom = 0;
            int iIndex = -1;
            // ???
            if( null!=d1iDeviationPatternIndex &&
                    vdDeviationPatternAdvent>Math.random() ){
// 意図的に図形の出現率に少し偏りを出している。
                iRandom = (int)Math.floor(
                        Math.random() * d1iDeviationPatternIndex.length );
                iPattern = d1iDeviationPatternIndex[iRandom];
                if(DEBUG)Log.i(tag, "oBlockDepotDelivery( ) : "+
                        "d3sTetrazBlockColorRID.length="+d3sTetrazBlockColorRID.length+"; "+
                        "d1oPatternIndex.length="+d1oPatternIndex.length+"; "+
                        "iRandom="+iRandom+"; "+
                        "iPattern="+iPattern+"; "+
                        "");
                int i = iPattern;
                while( i<d1oPatternIndex.length && 0>iIndex ){
                    iIndex = dl1oRunnerBlocksDepot.indexOf(d1oPatternIndex[i]);
                    i = i + d3sTetrazBlockColorRID.length;
                }
                if(DEBUG)Log.i(tag, "oBlockDepotDelivery( ) : "+
                        "iIndex="+iIndex+"; "+
                        "");
            }
            if(0>iIndex){
                if( null==d1iWaitingWeightIndex ){
                    iRandom = (int)Math.floor(
                            Math.random() * dl1oRunnerBlocksDepot.size( ) );
                    iIndex = iRandom;
                }else{
// 普通に Random だけだと特定の図形が全然出て来ない場合があるので、
// d1iWaitingWeightIndex によって待っている順番(時間)に応じ
// (長く待ってるほど)選ばれる確率が上がるようにしている。
                    iRandom = (int)Math.floor(
                            Math.random() * d1iWaitingWeightIndex.length );
                    iIndex = d1iWaitingWeightIndex[iRandom];
                }
            }
            if(DEBUG)Log.i(tag, "oBlockDepotDelivery( ) : "+
                    "iWallSWQnt="+iWallSWQnt+"; "+
                    "iRandom="+iRandom+"; "+
                    "iIndex="+iIndex+"; "+
                    "");
            oRB = dl1oRunnerBlocksDepot.get(iIndex);
            oRB.Initialize(iCBR);
            dl1oRunnerBlocksDepot.remove(iIndex);
            return oRB;
        }

        public void BlockPoolBufClear( ){
            d2iBlockPoolColorARGB = new int[iWallSWQnt][iWallSHQnt];
            for( int iSIY = 0; iSIY<iWallSHQnt; iSIY++) {
                for( int iSIX = 0; iSIX<iWallSWQnt; iSIX++) {
                    d2iBlockPoolColorARGB[iSIX][iSIY] = oPWall.getColor( );
                }
            }
        }

        public void BlockStockDisplay( ){
            for( int iSIY = 0; iSIY<iStockSHQnt; iSIY++) {
                for( int iSIX = 0; iSIX<iStockSWQnt; iSIX++) {
                    oRunnerBlocks.BlockBullet(
                            iStockScreenBiasX, iStockScreenBiasY, iSIX, iSIY, oPWall);
                }
            }
            if( ! lVirginGround ) {
                int h, j;
                for (int i = 0; i < dl1oRunnerBlocksStock.size(); i++) {
                    RunnerBlocks oRBS = dl1oRunnerBlocksStock.get(i);
                    // ???
                    oRBS.GetBlockArea(oRBS.iCBlockPattern, oRBS.iCBlockRoll);
                    h = - oRBS.iBlocksAreaSHMin + oRBS.iBlocksAreaSHMax + 1;
                    oRBS.iSCY = iStockSHQnt - 1 - (i * 5) - oRBS.iBlocksAreaSHMax
                            - (int)Math.ceil( (double) (4 - h) / 2 ) ;
                    if(DEBUG)Log.i(tag, "BlockStockDisplay( ) : "+
                            "i="+i+"; "+
                            "h="+h+"; "+
                            "(4-h)/2="+((4 - h) / 2)+"; "+
                            "");
                    oRBS.iSCX = - oRBS.iBlocksAreaSWMin;
                    oRBS.FigureDraw(iStockScreenBiasX, iStockScreenBiasY,
                            iStockSWQnt, iStockSHQnt,
                            oRBS.iCBlockPattern, oRBS.iSCX, oRBS.iSCY, oRBS.iCBlockRoll,
                            TetrazBlocks_Enum.feDraw);
                }
            }
        }

        @Override
        public void RunningHeader() {
            // oPBackground.setColor(ContextCompat.getColor(oAppContext, R.color.sienna));
            // oPBackground.setStyle(Paint.Style.FILL);
            Paint oP = new Paint( );
            oP.setStyle(Paint.Style.FILL);
            oCanvas = oSurfaceHolder.lockCanvas();
// 実は Canvas は描画時のチラツキ防止のために内部的に OffScreen Buffer を持っているのだが、
// この Canvas の Double Buffering が返ってアダとなる場合があり、
// その場合は自分で OffScreen Buffer を用意する必要がある。
            if(lClearSW) {
                // ???
                if( ! lVirginGround ) {
                    lClearSW = false; // true; //
                }
                oCOffScreen.drawRect(
                        0, 0, iScreenPhysWidth, iScreenPhysHeight,
                        oPBackground); // oPWall // oPBackground
                // d2iBlockPoolColorARGB = new int[iWallSWQnt][iWallSHQnt];
                for( int iSIY = 0; iSIY<iWallSHQnt; iSIY++) {
                    for( int iSIX = 0; iSIX<iWallSWQnt; iSIX++) {
                        oP.setColor(d2iBlockPoolColorARGB[iSIX][iSIY]);
                        oRunnerBlocks.BlockBullet(
                                iWallScreenBiasX, iWallScreenBiasY, iSIX, iSIY, oP);
                    }
                }
                BlockStockDisplay( );
            }
        }

        @Override
        public void RunningFooter() {
            // Debug
            Paint oP = new Paint( );
            oP.setStyle(Paint.Style.FILL);
            if( lRepaintSW ) {
                // ???
                lRepaintSW = false; // true; //
                for (int iSIY = 0; iSIY < iWallSHQnt; iSIY++) {
                    for (int iSIX = 0; iSIX < iWallSWQnt; iSIX++) {
                        oP.setColor(d2iBlockPoolColorARGB[iSIX][iSIY]);
                        oRunnerBlocks.BlockBullet(
                                iWallScreenBiasX, iWallScreenBiasY, iSIX, iSIY, oP);
                    }
                }
                BlockStockDisplay( );
            }
            // Bitmap OffScreen Buffer を描画する
            oCanvas.drawBitmap(oBOffScreenBuffer, 0, 0, null);
            oSurfaceHolder.unlockCanvasAndPost(oCanvas);
        }

        @Override
        public boolean lBlockPoolBufSqueeze( TetrazBlocks_Enum oTB ){
            boolean lSqueeze = false; // true; //
            boolean lSqueezeLine = true; // false; //
            Paint oP = new Paint( );
            for( int iSIY = iWallSHQnt - 1; 0<=iSIY; iSIY--) {
                lSqueezeLine = true; // false; //
                for (int iSIX = 0; iSIX < iWallSWQnt; iSIX++) {
                    if ( oPWall.getColor( )==d2iBlockPoolColorARGB[iSIX][iSIY] ) {
                        lSqueezeLine = false; // true; //
                        break;
                    }
                }
                lSqueeze = lSqueeze | lSqueezeLine;
                if( lSqueeze ){
                    lRepaintSW = true; // false; //
                    if( oTB.feSqueeze==oTB ){
                        for (int iSIX = 0; iSIX < iWallSWQnt; iSIX++) {
                            int iSLY = iSIY - 1;
                            if( 0<=iSLY ){
                                d2iBlockPoolColorARGB[iSIX][iSIY]
                                        = d2iBlockPoolColorARGB[iSIX][iSLY];
                            }else{
                                d2iBlockPoolColorARGB[iSIX][iSIY] = oPWall.getColor( );
                            }
                        }
                    }
                }
            }
            return lSqueeze;
        }

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(DEBUG)Log.i(tag, "Activity.onCreate( );");
        setContentView(R.layout.activity_main);
        oMainApp = this;
// System 共通の Context:別の Application とやりとりするとき。
        oBaseContext = getBaseContext( );
// Application 固有の Context(Application ごとに Context が変化する):Application 共通のモノが対象。
        oAppContext = getApplicationContext( );
// Activity 固有の Context(Activity ごとに Context が変化する):Activity に依存モノが対象。
        oContext = this;

        vsPackageName = getPackageName( );
        try {
            PackageInfo packageInfo = this.getPackageManager( ).getPackageInfo(
                    this.getPackageName( ), PackageManager.GET_META_DATA );
            // 「<Project名>→app→build.gradle」ファイル内の
            // 「versionName、versionCode」の設定値を取得。
            vsVersionName = packageInfo.versionName;
            iVersionCode = packageInfo.versionCode;
        } catch (NameNotFoundException e) {
            // 例外処理
            e.printStackTrace( );
        }

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        oActionBar = getSupportActionBar();

        // oActionBar.setIcon(R.drawable.ic_menu_info_details);
        oActionBar.setLogo(R.drawable.x_logo_android022);
        // ↑ 実際は Logo マークはアプリ固有の Icon にすべきモノです。
        // アプリ固有の Logo マークと言われてもイメージが湧かない方は
        // ブラウザの Icon(Logo マーク)をイメージしてみて下さい。
        // 下記サイトに主要ブラウザの Icon(Logo マーク)が掲載されています。
        // http://freesoft-100.com/pasokon/browser.html
        // ちなみに Toolbar に表示する Icon サイズのスタンダードは
        // "drawable-mdpi"フォルダー用は「32x32」、
        // "drawable-hdpi"フォルダー用は「48x48」だと思われます。

        vsToolbarTitle = "  "+getString(R.string.app_name)+
                fsDebugCrest+
                " V"+vsVersionName+ // Version.
                " R"+String.valueOf(iVersionCode); // Revision.
// ↑ Toolbar に Logo マークを表示させるとタイトルと Logo マークとの間に隙間がないので、
// タイトルの先頭に半角スペースを2つ入れている。
        oActionBar.setTitle(vsToolbarTitle);

        eAppLaunchMode = AppLaunchMode_Enum.feCreate;
        oRunningSurfaceView = new RunningSurfaceView();
// 「MainActivity」クラスの この「onCreate( )」コンストラクターでは
// まだ"SurfaceView"の生成が完了してない場合が有るようで、
// その時点で"Thread"が実行された場合
// 「oSurfaceHolder.lockCanvas( )」による"Canvas"の生成が"null"になってしまうので、
// 「RunningSurfaceView( )」コンストラクターには
// 「EngineCreate( )」("Thread"の生成・実行)は内包しない。
// 又、「RunnerCircle( )」 コンストラクター実行時には
// 「iScreenWidth、iScreenHeight」が設定されていなければならないので
// 「RunningSurfaceView( )」コンストラクターには内包せず、別途「Initialize( )」に内包する。
// それら「Initialize( )、EngineCreate( )」は「launch( )」に内包し「surfaceChanged( )」で実行する。
// ただし「launch( )」に内包された それら(「Initialize( )、EngineCreate( )」)は"appLaunchMode"で実行の可否が決定される。
        if (DEBUG) Log.i(tag, "oRunningSurfaceView = new RunningSurfaceView( );");

        for (int iWId : ONTOUCH_WIDGETS) {
            findViewById(iWId).setOnTouchListener(this);
        }
        cibBlockRight = (ImageButton)findViewById(R.id.cibBlockRight);
        cibBlockLeft = (ImageButton)findViewById(R.id.cibBlockLeft);
        cibBlockDown = (ImageButton)findViewById(R.id.cibBlockDown);
    }

    @Override
    protected void onRestart( ) {
        super.onRestart();
        if(DEBUG)Log.i(tag, "Activity.onRestart( );");

        eAppLaunchMode = AppLaunchMode_Enum.feRestart;
    }

    @Override
    protected void onStart( ) {
        super.onStart();
        if(DEBUG)Log.i(tag, "Activity.onStart( );");

        oRunningSurfaceView.SurfaceCreate( );
    }

    @Override
    protected void onResume( ) {
        super.onResume();
        if(DEBUG)Log.i(tag, "Activity.onResume( );");
    }

    @Override
    protected void onPause( ) {
        super.onPause();
        if(DEBUG)Log.i(tag, "Activity.onPause( );");
    }

    @Override
    protected void onStop() {
        super.onStop();
        if(DEBUG)Log.i(tag, "Activity.onStop( );");

        oRunningSurfaceView.EngineDestroy( );
        // oRunningSurfaceView.oSurfaceHolder.removeCallback(oMainApp);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(DEBUG)Log.i(tag, "Activity.onDestroy( );");
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // super.onTouch(v, event);
        if(DEBUG) Log.i(tag,"Activity.onTouch( );");

        int action = event.getAction();
        switch(v.getId()) {
            case R.id.cibBlockRotate :
                if (DEBUG) Log.i(tag, "R.id.cibBlockRotate");
                if (action == MotionEvent.ACTION_DOWN) {
                    if(DEBUG) Log.i(tag,"MotionEvent.ACTION_DOWN;");
                    oRunningSurfaceView.oRunnerBlocks.lEBlockRoll = true; // false; //
                }
                if ((action == MotionEvent.ACTION_UP)) {
                    if(DEBUG) Log.i(tag,"MotionEvent.ACTION_UP;");

                }
                break;

            case R.id.cibBlockRight :
                if (DEBUG) Log.i(tag, "R.id.cibBlockRight");
                if (action == MotionEvent.ACTION_DOWN) {
                    if(DEBUG) Log.i(tag,"MotionEvent.ACTION_DOWN;");
                    oRunningSurfaceView.iETouchAction_BlockRightPress = MotionEvent.ACTION_DOWN;
                }
                if ((action == MotionEvent.ACTION_UP)) {
                    if(DEBUG) Log.i(tag,"MotionEvent.ACTION_UP;");
                    oRunningSurfaceView.iETouchAction_BlockRightRelease = MotionEvent.ACTION_UP;
                }
// 意図的に戻り値に false を返し、他の Event Listener の処理を継続させている。
                return false; // true; //
            // break;

            case R.id.cibBlockLeft :
                if (DEBUG) Log.i(tag, "R.id.cibBlockLeft");
                if (action == MotionEvent.ACTION_DOWN) {
                    if(DEBUG) Log.i(tag,"MotionEvent.ACTION_DOWN;");
                    oRunningSurfaceView.iETouchAction_BlockLeftPress = MotionEvent.ACTION_DOWN;
                }
                if ((action == MotionEvent.ACTION_UP)) {
                    if(DEBUG) Log.i(tag,"MotionEvent.ACTION_UP;");
                    oRunningSurfaceView.iETouchAction_BlockLeftRelease = MotionEvent.ACTION_UP;
                }
// 意図的に戻り値に false を返し、他の Event Listener の処理を継続させている。
                return false; // true; //
            // break;

            case R.id.cibBlockDown :
                if (DEBUG) Log.i(tag, "R.id.cibBlockDown");
                if (action == MotionEvent.ACTION_DOWN) {
                    if(DEBUG) Log.i(tag,"MotionEvent.ACTION_DOWN;");
                    oRunningSurfaceView.iETouchAction_BlockDown = MotionEvent.ACTION_DOWN;
                }
                if ((action == MotionEvent.ACTION_UP)) {
                    if(DEBUG) Log.i(tag,"MotionEvent.ACTION_UP;");

                }
// 意図的に戻り値に false を返し、他の Event Listener の処理を継続させている。
                return false; // true; //
            // break;

            default :
                if (DEBUG) Log.i(tag, "Default");
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
                return false; // true; //
        }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public void onClick(View v) {
        // super.onClick(v);
        if(DEBUG) Log.i(tag,"Activity.onClick( );");
        switch(v.getId()) {
            case R.id.cibBlockRotate :
                if (DEBUG) Log.i(tag, "R.id.cibBlockRotate");
                // oRunningSurfaceView.oRunnerBlocks.BlockRotate();
                break;

            default :
                if (DEBUG) Log.i(tag, "Default");
                return;
        }
        return;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        // アクションバー内で使用する為のメニューアイテムを実体化
        MenuInflater inflater = getMenuInflater();
        // MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        // 「R.menu.main」の「menu.main」は 恐らく「res」からの「フォルダー名+ファイル名」
        oMainMenu = menu;
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if(DEBUG) Log.i(tag,"Activity.onOptionsItemSelected( );");
        switch(item.getItemId()) {
            case R.id.menu_main_settings :
                if (DEBUG) Log.i(tag, "menu_main_settings");
                oRunningSurfaceView.RunnerWait(true); // false;
                break;

            case R.id.menu_main_settings_play :
                if (DEBUG) Log.i(tag, "menu_main_settings_play");
                if( oRunningSurfaceView.lEngineSW ) {
                    oRunningSurfaceView.RunnerWait(false); // true;
                }else{
                    oRunningSurfaceView.Initialize();
                    oRunningSurfaceView.EngineCreate(false); // true; //
                }
                break;

            case R.id.menu_main_settings_wait :
                if (DEBUG) Log.i(tag, "menu_main_settings_wait");
                // oRunningSurfaceView.RunnerWait(true); // false;
                break;

            case R.id.menu_main_settings_renew_impossible :
                if (DEBUG) Log.i(tag, "menu_main_settings_renew_impossible");
                oRunningSurfaceView.iWallSWQnt = 11;
                // oRunningSurfaceView.iWallSHQnt = 19;
                oRunningSurfaceView.Initialize();
                oRunningSurfaceView.EngineCreate(false); // true; //
                break;

            case R.id.menu_main_settings_renew_tight :
                if (DEBUG) Log.i(tag, "menu_main_settings_renew_tight");
                oRunningSurfaceView.iWallSWQnt = 12;
                oRunningSurfaceView.Initialize();
                oRunningSurfaceView.EngineCreate(false); // true; //
                break;

            case R.id.menu_main_settings_renew_medium :
                if (DEBUG) Log.i(tag, "menu_main_settings_renew_medium");
                oRunningSurfaceView.iWallSWQnt = 13;
                oRunningSurfaceView.Initialize();
                oRunningSurfaceView.EngineCreate(false); // true; //
                break;

            case R.id.menu_main_settings_renew_easy :
                if (DEBUG) Log.i(tag, "menu_main_settings_renew_easy");
                oRunningSurfaceView.iWallSWQnt = 14;
                oRunningSurfaceView.Initialize();
                oRunningSurfaceView.EngineCreate(false); // true; //
                break;

            case R.id.menu_main_settings_exit :
                if (DEBUG) Log.i(tag, "menu_main_settings_exit");
                // ???
                oMainApp.finish( );
                break;

            default :
                if (DEBUG) Log.i(tag, "Default");
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
                return false; // true; //
        }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        // super.dispatchKeyEvent(event);
        if(DEBUG) Log.i(tag,"Activity.dispatchKeyEvent( );");
        final int action = event.getAction();
        final int keyCode = event.getKeyCode();
        switch(keyCode) {
            case KeyEvent.KEYCODE_MENU :
                if(DEBUG) Log.i(tag,"KeyEvent.KEYCODE_MENU;");
                if ( action==KeyEvent.ACTION_UP ) {
                    if(DEBUG) Log.i(tag,"KeyEvent.ACTION_UP;");
                    if ( oMainMenu!=null ) {
                        if(DEBUG) Log.i(tag,"oMainMenu.performIdentifierAction( );");
// Toast の表示時間は、LENGTH_SHORT は2秒間、LENGTH_LONG は4秒間表示されるらしいので、
// LENGTH_SHORT の表示期間のに合わせて2秒間 Sleep させる。
                        oRunningSurfaceView.iESleepInterval = 2000; // 2000ms
// Super Class を呼び出さずに帰り値に true を返して Menu Key を無効化する。
                        // oMainMenu.performIdentifierAction(R.id.menu_main_settings, 0);
                        Toast.makeText(oMainApp, "Please Command this Application from a \"Three Dots\" Icon.", Toast.LENGTH_SHORT).show();
                    }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
                    return true; // false; //
                }
                break;

            case KeyEvent.KEYCODE_BACK :
                if(DEBUG) Log.i(tag,"KeyEvent.KEYCODE_BACK;");
                if ( action==KeyEvent.ACTION_UP ) {
                    if(DEBUG) Log.i(tag,"KeyEvent.ACTION_UP;");
// Toast の表示時間は、LENGTH_SHORT は2秒間、LENGTH_LONG は4秒間表示されるらしいので、
// LENGTH_SHORT の表示期間のに合わせて2秒間 Sleep させる。
                    oRunningSurfaceView.iESleepInterval = 2000; // 2000ms
// Super Class を呼び出さずに帰り値に true を返して Back Key を無効化する。
                    Toast.makeText(oMainApp, "Please Command this Application from a \"Three Dots\" Icon.", Toast.LENGTH_SHORT).show();
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
                    return true; // false; //
                }
                break;

            default :
                super.dispatchKeyEvent(event);
                if (DEBUG) Log.i(tag, "Default");

                break;
        }
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
        return false; // true; //
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) { }

    @Override
    public void surfaceChanged(SurfaceHolder holder,
        int format, int width, int height) {
        RunningSurfaceView oRSV = oRunningSurfaceView;
        oRSV.ScreenSetting(width, height);
        if( ! oRSV.lLaunchActivated ){
// lLaunchActivated がクリアーされない限りは、「surfaceChanged( )」 が2回以上呼ばれても、ここは1回しか実行されない。
            oRSV.Launch(eAppLaunchMode);
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) { }

}



『<Project名>→app→main→res→layout→activity_main.xml』


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    tools:context="com.example.surfaceview.MainActivity" >

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
        android:minHeight="?attr/actionBarSize"
        android:background="#9b7fff"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <SurfaceView
        android:id="@+id/surfaceview"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="7dp"
        android:layout_marginLeft="7dp"
        android:layout_marginRight="7dp"
        android:layout_marginBottom="0dp"
        android:weightSum="1">

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/cibBlockLeft"
            android:src="@drawable/xic_menu_reverse_clip"/>

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="0.5666"
            android:gravity="center_horizontal">

            <ImageButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/cibBlockDown"
                android:src="@drawable/xic_menu_down_clip"/>

        </LinearLayout>

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/cibBlockRight"
            android:src="@drawable/ic_menu_play_clip"/>

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="0.4334"
            android:gravity="right">

            <ImageButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/cibBlockRotate"
                android:src="@drawable/ic_menu_rotate"/>
        </LinearLayout>

    </LinearLayout>

</LinearLayout>



『<Project名>→app→src→main→res→menu→main.xml(フォルダーとファイルを作成)』


<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.tetraz.MainActivity">

    <item android:id="@+id/menu_main_settings"
        android:title="Settings"
        android:icon="@drawable/ic_menu_moreoverflow_normal_holo_light"
        app:showAsAction="always">
        <!-- ↑ showAsAction:Toolbar に対する表示設定。 -->
        <!-- "always":常に表示、"never":常に表示しない、"ifRoom":表示する余裕があれば表示。 -->
        <menu>
            <item android:id="@+id/menu_main_settings_play"
                android:title="Play">
            </item>

            <item android:id="@+id/menu_main_settings_wait"
                android:title="Wait">
            </item>

            <item android:id="@+id/menu_main_settings_renew_impossible"
                android:title="Renew Impossible">
            </item>

            <item android:id="@+id/menu_main_settings_renew_tight"
                android:title="Renew Tight">
            </item>

            <item android:id="@+id/menu_main_settings_renew_medium"
                android:title="Renew Medium">
            </item>

            <item android:id="@+id/menu_main_settings_renew_easy"
                android:title="Renew Easy">
            </item>

            <item android:id="@+id/menu_main_settings_exit"
                android:title="Exit">
            </item>
        </menu>
    </item>

</menu>



『<Project名>→app→src→main→res→values→colors.xml』


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>

    <!-- 文字列(16進値)の4バイト指定時の場合は(最上位の)4バイト目が不透明度の指定になるようだ。 -->
    <color name="empty">#00000000</color>
    <color name="black">#000000</color>
    <color name="gray">#808080</color>
    <color name="silver">#c0c0c0</color>
    <color name="white">#ffffff</color>
    <color name="maroon">#800000</color>
    <color name="red">#ff0000</color>
    <color name="purple">#800080</color>
    <color name="fuchsia">#ff00ff</color>
    <color name="magenta">#ff00ff</color>
    <color name="green">#008000</color>
    <color name="lime">#00ff00</color>
    <color name="olive">#808000</color>
    <color name="yellow">#ffff00</color>
    <color name="navy">#000080</color>
    <color name="blue">#0000ff</color>
    <color name="teal">#008080</color>
    <color name="aqua">#00ffff</color>
    <color name="cyan">#00ffff</color>

    <color name="orange">#ffa500</color>
    <color name="pink">#ffc0cb</color>
    <color name="violet">#ee82ee</color>
    <color name="hotpink">#ff69b4</color>
    <color name="blueviolet">#8a2be2</color>
    <color name="darkviolet">#9400d3</color>
    <color name="greenyellow">#adff2f</color>
    <color name="lawngreen">#7cfc00</color>
    <color name="yellowgreen">#9acd32</color>

    <color name="lightgrey">#d3d3d3</color>
    <color name="darkgray">#a9a9a9</color>
    <color name="lightslategray">#778899</color>
    <color name="slategray">#708090</color>
    <color name="dimgray">#696969</color>

    <color name="khaki">#f0e68c</color>
    <color name="tan">#d2b48c</color>
    <color name="darkkhaki">#bdb76b</color>
    <color name="brass">#b5a642</color>
    <color name="darkgoldenrod">#b8860b</color>
    <color name="peru">#cd853f</color>
    <color name="goldenrod">#daa520</color>
    <color name="chocolate">#d2691e</color>
    <color name="sienna">#a0522d</color>
    <color name="saddlebrown">#8b4513</color>
    <color name="darkred">#8b0000</color>
    <color name="brown">#a52a2a</color>
    <color name="firebrick">#b22222</color>
    <color name="darkbrown">#da0b00</color>
    <color name="orangered">#ff4500</color>
</resources>



『<Project名>→app→src→main→res→values→styles.xml』


<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>



『<Project名>→app→main→AndroidManifest.xml』


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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <!-- screenOrientation="portrait":縦表示・固定 -->
        <activity android:name="com.example.tetraz.MainActivity"
            android:screenOrientation="portrait"
            android:launchMode="singleInstance" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>


<Number>: [000009D3]  <Date>: 2016/05/08 16:46:57
<Title>: AppCompat 012
<Name>: amanojaku@管理人



ActionBarActivity が非推奨になったので、チャントした(プログラム的に) Empty(空(から)) な AppCompatActivity を作ってみる。

以前は良くワカランかったが、「getSupportActionBar( )」で1歩前進したが、それでは Toolbar の「Logo(アイコン)」の表示ができなかったので、下記「OKWAVE」で「Logo(アイコン)、Icon」の表示方法を ご教授いただきました(詳細は下記「OKWAVE」ページ参照)。
Toolbar の Icon のイベントの取得は そんなに難しくないだろうと思って、イロイロ検索しても全然ダメでした。

OKWAVE
http://okwave.jp/qa/q9115191.html


《参考》

TextViewに枠線を付ける&背景色を変更する - yokkongの日記
http://d.hatena.ne.jp/yokkong/20111116/1321425474

【Android開発】スタイルにborderがないけど枠線を表示したい
http://se-suganuma.blogspot.jp/2010/02/androidborder.html


とりあえず、[New Project]で(1ページ目)[Application name]:「AppCompat」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。

『<Project名>→app→main→res→drawable-hdpi』フォルダー(存在しない場合は作成してやる)内に「Android SDK」のデフォルト・アイコン「ic_menu_moreoverflow_normal_holo_light.png」をコピーする。
一応、「アプリの Logo マーク」を Android のマスコット・キャラの「Droid君」にしている。
下の方に『<Project名>→app→main→res→drawable-hdpi』フォルダー用の画像ファイル「アプリの Logo マーク:「48x48」Pixel、その他"TextView"用アイコンをアップしてあります。
"drawable-mdpi"フォルダー用は手抜きして作ってないが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。
その場合、当然 画質は悪くなるので画質を綺麗にしたい場合は手抜きせずにチャント"drawable-mdpi"フォルダー用の画像ファイルを作成してやれば良い。
「Toolbar」の UI はスタンダードな「ActionBarActivity」のような感じになっている。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。


『<Project名>→app→main→java→com.example.appcompat→MainActivity(MainActivity.java)』


package com.example.appcompat;

import android.os.Bundle;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.KeyEvent;
import android.os.Environment;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.Context;
import android.content.res.Resources;
import android.content.Intent;
import android.app.PendingIntent;
import android.app.Activity;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
// import android.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.util.Log;

import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener {
    static final String fsDebugCrest = " 012";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    DisplayMetrics oDisplayMetrics;
    public static int iPhysicalWidth, iPhysicalHeight;
    float vfDisplayMetricsDensity;

    MainActivity oMainApp;
    Context oBaseContext;
    Context oAppContext; // Application Context.
    Context oContext; // Activity Context.
    String vsToolbarTitle;
    Menu oMainMenu;

    String vsPackageName;
    String vsVersionName;
    int iVersionCode;

    TextView wtvMessage;
    static final int[] WIDGETS =  new int[]{
            R.id.textView1, R.id.textView2,
            R.id.button1, R.id.checkBox1, R.id.toggleButton1, R.id.radioButton1 };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (DEBUG) Log.i(tag, "Activity.onCreate( );");
        setContentView(R.layout.activity_main);
        oMainApp = this;
// System 共通の Context:別の Application とやりとりするとき。
        oBaseContext = getBaseContext();
// Application 固有の Context(Application ごとに Context が変化する):Application 共通のモノが対象。
        oAppContext = getApplicationContext();
// Activity 固有の Context(Activity ごとに Context が変化する):Activity に依存モノが対象。
        oContext = this;
        oDisplayMetrics = oAppContext.getResources( ).getDisplayMetrics( );
        vfDisplayMetricsDensity = oDisplayMetrics.density;
        iPhysicalWidth = oDisplayMetrics.widthPixels;
        iPhysicalHeight = oDisplayMetrics.heightPixels;
        if (DEBUG) Log.i(tag, "Activity.onCreate( ) : " +
                "vfDisplayMetricsDensity=" + vfDisplayMetricsDensity + "; " +
                "iPhysicalWidth=" + iPhysicalWidth + "; " +
                "iPhysicalHeight=" + iPhysicalHeight + "; " +
                "");

        vsPackageName = getPackageName();
        try {
            PackageInfo packageInfo = getPackageManager().getPackageInfo(
                    getPackageName(), PackageManager.GET_META_DATA);
            // 「<Project名>→app→build.gradle」ファイル内の
            // 「versionName、versionCode」の設定値を取得。
            vsVersionName = packageInfo.versionName;
            iVersionCode = packageInfo.versionCode;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }

        wtvMessage = (TextView) findViewById(R.id.wtvMessage);
        wtvMessage.append("fsDebugCrest=" + fsDebugCrest + ";\n" +
                "PackageName=" + vsPackageName + ";\n" +
                "VersionName=" + vsVersionName + ";\n" +
                "RevisionNo=" + String.valueOf(iVersionCode) + ";\n" +
                "vfDisplayMetricsDensity=" + vfDisplayMetricsDensity + ";\n" +
                "iPhysicalWidth=" + iPhysicalWidth + ";\n" +
                "iPhysicalHeight=" + iPhysicalHeight + "\n" +
                "");

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        ActionBar actionbar = getSupportActionBar();

        // actionbar.setIcon(R.drawable.ic_menu_info_details);
        actionbar.setLogo(R.drawable.x_logo_android022);
        // ↑ 実際は Logo マークはアプリ固有の Icon にすべきモノです。
        // アプリ固有の Logo マークと言われてもイメージが湧かない方は
        // ブラウザの Icon(Logo マーク)をイメージしてみて下さい。
        // 下記サイトに主要ブラウザの Icon(Logo マーク)が掲載されています。
        // http://freesoft-100.com/pasokon/browser.html
        // ちなみに Toolbar に表示する Icon サイズのスタンダードは
        // "drawable-mdpi"フォルダー用は「32x32」、
        // "drawable-hdpi"フォルダー用は「48x48」だと思われます。

        vsToolbarTitle = "  " + getString(R.string.app_name) +
                fsDebugCrest +
                " V" + vsVersionName + // Version.
                " R" + String.valueOf(iVersionCode); // Revision.
// ↑ Toolbar に Logo マークを表示させるとタイトルと Logo マークとの間に隙間がないので、
// タイトルの先頭に半角スペースを2つ入れている。
        actionbar.setTitle(vsToolbarTitle);

        for (int iWId : WIDGETS) {
            View wvw = findViewById(iWId);
            wvw.setOnClickListener(this);
        }
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        if (DEBUG) Log.i(tag, "Activity.onRestart( );");
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (DEBUG) Log.i(tag, "Activity.onStart( );");
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (DEBUG) Log.i(tag, "Activity.onResume( );");
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (DEBUG) Log.i(tag, "Activity.onPause( );");
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (DEBUG) Log.i(tag, "Activity.onStop( );");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (DEBUG) Log.i(tag, "Activity.onDestroy( );");
    }

    @Override
    public void onClick(View v) {
        // super.onClick(v);
        if(DEBUG) Log.i(tag,"Activity.onClick( );");
        switch(v.getId()) {
            case  R.id.textView1 :
                if (DEBUG) Log.i(tag, "textView1");

                break;
            case  R.id.textView2 :
                if (DEBUG) Log.i(tag, "textView2");

                break;
            case R.id.button1 :
                if (DEBUG) Log.i(tag, "button1");

                break;
            case R.id.checkBox1 :
                if (DEBUG) Log.i(tag, "checkBox1");

                break;
            case R.id.toggleButton1 :
                if (DEBUG) Log.i(tag, "toggleButton1");

                break;
            case R.id.radioButton1 :
                if (DEBUG) Log.i(tag, "radioButton1");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
                return;
        }
        return;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        // アクションバー内で使用する為のメニューアイテムを実体化
        MenuInflater inflater = getMenuInflater();
        // MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        // 「R.menu.main」の「menu.main」は 恐らく「res」からの「フォルダー名+ファイル名」
        oMainMenu = menu;
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if(DEBUG) Log.i(tag,"Activity.onOptionsItemSelected( );");
        switch(item.getItemId()) {
            case R.id.menu_main_settings :
                if (DEBUG) Log.i(tag, "menu_main_settings");

                break;
            case R.id.menu_main_settings_item1 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item1");

                break;
            case R.id.menu_main_settings_item2 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item2");

                break;
            case R.id.menu_main_settings_item3 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item3");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
                return false; // true; //
        }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        super.dispatchKeyEvent(event);
        if (DEBUG) Log.i(tag, "Activity.dispatchKeyEvent( );");
        final int action = event.getAction();
        final int keyCode = event.getKeyCode();
        switch (keyCode) {
            case KeyEvent.KEYCODE_MENU:
                if (DEBUG) Log.i(tag, "KeyEvent.KEYCODE_MENU;");
                if (action == KeyEvent.ACTION_UP) {
                    if (DEBUG) Log.i(tag, "KeyEvent.ACTION_UP;");
                    if (oMainMenu != null) {
                        if (DEBUG) Log.i(tag, "oMainMenu.performIdentifierAction( );");
                        oMainMenu.performIdentifierAction(R.id.menu_main_settings, 0);
                    }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
                    return true; // false; //
                }
                break;

            default:
                if (DEBUG) Log.i(tag, "KeyEvent:Default");

                break;
        }
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
        return false; // true; //
    }

}



『<Project名>→app→main→res→drawable→border.xml』(ファイルを作成)
※「UI Widget」の枠として使用する。


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff" />
    <padding android:top="3dp" android:bottom="3dp"
        android:left="7dp" android:right="7dp" />
    <stroke android:width="1px" android:color="#000000" />
    <corners android:radius="5dp" />
</shape>



『<Project名>→app→main→res→drawable→button_style.xml』(ファイルを作成)
※「TextView」をボタン・スタイルで表示させている。


<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/ButtonBackground"/>
            <corners android:radius="3dp" />
            <stroke android:width="2dp" android:color="@color/ButtonShadow" />
            <!-- 「stroke android:width=」は ずらす幅より少し大きな値にする。 -->
        </shape>
    </item>
    <item android:bottom="1.45dp" android:right="1.47dp">
        <shape android:shape="rectangle">
            <solid android:color="@color/ButtonBackground"/>
            <padding android:top="4.4dp" android:bottom="4.4dp"
                android:left="5dp" android:right="5dp" />
            <corners android:radius="3dp" />
        </shape>
    </item>
</layer-list>



『<Project名>→app→main→res→layout→activity_main.xml』


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="@color/Background_Cornflowerblue" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_margin = "7dp"
        android:background="@drawable/border" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="top"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:id="@+id/wtvMessage" />

    </ScrollView>

<!-- ボタンは左右にムダなスペースがあるので、表示幅が Tight でボタンが表示できない場合、-->
<!-- TextView をボタン・スタイルで表示させれば良い。 -->
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textSize="22dp"
            android:textColor="@color/black"
            android:layout_marginTop = "6dp"
            android:layout_marginBottom = "6dp"
            android:layout_marginLeft = "3.5dp"
            android:layout_marginRight = "3.5dp"
            android:background="@drawable/button_style"
            android:text="TextView1"
            android:id="@+id/textView1" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textSize="22dp"
            android:textColor="@color/black"
            android:layout_marginTop = "6dp"
            android:layout_marginBottom = "6dp"
            android:layout_marginLeft = "3.5dp"
            android:layout_marginRight = "3.5dp"
            android:background="@drawable/button_style"
            android:drawableRight="@drawable/xic_menu_moreoverflow_focused_holo_light0272"
            android:text="TextView2"
            android:id="@+id/textView2" />
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textSize="22dp"
            android:text="Button1"
            android:id="@+id/button1" />

        <ToggleButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textSize="22dp"
            android:text="ToggleButton1"
            android:id="@+id/toggleButton1" />
    </LinearLayout>

    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="22dp"
        android:text="CheckBox1"
        android:id="@+id/checkBox1" />

    <RadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="22dp"
        android:layout_gravity="center_horizontal"
        android:text="RadioButton1"
        android:id="@+id/radioButton1" />

</LinearLayout>



『<Project名>→app→src→main→res→menu→main.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <item android:id="@+id/menu_main_settings"
        android:title="Settings"
        android:icon="@drawable/ic_menu_moreoverflow_normal_holo_light"
        app:showAsAction="always">
        <!-- ↑ showAsAction:Toolbar に対する表示設定。 -->
        <!-- "always":常に表示、"never":常に表示しない、"ifRoom":表示する余裕があれば表示。 -->
        <menu>
            <item android:id="@+id/menu_main_settings_item1"
                android:title="item1">
            </item>

            <item android:id="@+id/menu_main_settings_item2"
                android:title="item2">
            </item>

            <item android:id="@+id/menu_main_settings_item3"
                android:title="item3">
            </item>
        </menu>
    </item>

</menu>



『<Project名>→app→src→main→res→values→colors.xml』


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>

    <color name="holo_blue_light">#33b5e5</color>
    <color name="Background_Cornflowerblue">#6495ED</color>
    <color name="Background_MediumslateblueLike">#9b7fff</color>

    <color name="ButtonBackground">#d7d7d7</color>
    <color name="ButtonShadow">#c6c6c6</color>
    <color name="GrayOutText001">#909090</color>
    <color name="GrayOutText002">#888888</color>
    <color name="GrayOutText003">#777777</color>
    <color name="SettingMenuBackground">#eeeeee</color>

    <color name="black">#000000</color>
    <color name="gray">#808080</color>
    <color name="silver">#c0c0c0</color>
    <color name="white">#ffffff</color>
    <color name="maroon">#800000</color>
    <color name="red">#ff0000</color>
    <color name="purple">#800080</color>
    <color name="fuchsia">#ff00ff</color>
    <color name="magenta">#ff00ff</color>
    <color name="green">#008000</color>
    <color name="lime">#00ff00</color>
    <color name="olive">#808000</color>
    <color name="yellow">#ffff00</color>
    <color name="navy">#000080</color>
    <color name="blue">#0000ff</color>
    <color name="teal">#008080</color>
    <color name="aqua">#00ffff</color>
    <color name="cyan">#00ffff</color>

    <color name="pink">#ffc0cb</color>
    <color name="orange">#ffa500</color>
    <color name="greenyellow">#adff2f</color>
    <color name="yellowgreen">#9acd32</color>
</resources>



『<Project名>→app→src→main→res→values→styles.xml』


<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.enter.appcompat">

    <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:launchMode="singleInstance" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


<Number>: [000009E1]  <Date>: 2016/03/20 06:31:02
<Title>: WidgetELSProperty 009
<Name>: amanojaku@管理人



Widget(今回は Service 内)で View コンポーネントの Property を動的に変更する。
クリックにより(Service の) onStartCommand が実行されたら、TextView の Background Color を Random に変更する。
Widget のメソッド実行時に設定された(Widget Class 内の) Static 変数は、そのメソッドが実行中なら、当然 その値は保持されているだろうが、その後 その(Widget Class 内の) Static 変数の値が消失しているような感じなので、Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無いようだ、当然 Service 起動時に その(Widget Class 内の) Static 変数の値が残っている保障など無い。
よって Widget から Service へのデータの受け渡しには Wgeidt Class 内の Static 変数は使えない(Widget → Service の一方通行のデータの受け渡しに限定するなら、Intent により非常に簡単にデータを受け渡せるようだ、それに関しては次の WidgetILSToast にて記す)。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetELSProperty」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/


『<Project名>→app→main→java→com.example.widgetelsproperty→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetelsproperty;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

// WidgetELSProperty(Widget:{(Event Launch Service)(click)[Property]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 009";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    // Android Java の場合、カラーの実態は int。
    static final int[] d1iCPalette = new int[]{
            Color.BLUE,
            Color.CYAN,
            Color.GREEN,
            Color.MAGENTA,
            Color.RED,
            Color.YELLOW,
    };

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName( ), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        Intent si = new Intent(ctx, MyService.class);
        PendingIntent pi = PendingIntent.getService(ctx, 0, si, 0);
        rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

        // awm.updateAppWidget により前述の2つの「rv.set〜」の設定が Update される。
        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");
            // 前述の Widget の場合の記述とビミョウに違っている部分も有るので要注意。
            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName( ), R.layout.widget_main);

            // Android Java の場合、カラーの実態は int。
            int iColor = d1iCPalette[(int)(Math.random( ) * d1iCPalette.length)];
            rv.setInt(R.id.ctvTextView, "setBackgroundColor", iColor);
            String vsMessage = "MyService.onStartCommand( );\n"+
                    "iColor="+iColor+";\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( ) : "+
                    "iColor="+iColor+"; "+
                    "");

            awm.updateAppWidget(cn, rv);

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#c0c0c0"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#000000"
    android:editable="false" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


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

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
    </application>

</manifest>


<Number>: [000009E3]  <Date>: 2016/03/20 06:33:05
<Title>: WidgetILSToast 020
<Name>: amanojaku@管理人



どうも Widget から直接 Toast を使えないようで、Widget で Toast を表示したい場合は Service から Toast を表示させてやれば良いようだ。
また Widget のメソッド実行時に設定された(Widget Class 内の) Static 変数は、そのメソッドが実行中なら、当然 その値は保持されているだろうが、その後 その(Widget Class 内の) Static 変数の値が消失しているような感じなので、Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無いようだ、当然 Service 起動時に その(Widget Class 内の) Static 変数の値が残っている保障など無い。
よって Widget から Service へのデータの受け渡しには Wgeidt Class 内の Static 変数は使えない(Widget → Service の一方通行のデータの受け渡しに限定するなら、Intent により非常に簡単にデータを受け渡せる。
このプログラムで Intent によって Widget から Service にデータを受け渡すサンプルも記してみる。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetILSToast」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&


『<Project名>→app→main→java→com.example.widgetilstoast→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetilstoast;

import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.view.Gravity;
import android.widget.RemoteViews;
import android.util.Log;
import android.widget.Toast;

// WidgetILSToast(Widget:{(Instant Launch Service)[Toast]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 020";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");

        Intent si = new Intent(ctx, MyService.class);
        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "AppWidget.onUpdate( );\n";
        si.putExtra("MessageKey", vsMessage);
        si.putExtra("ToastKey", "Hello, world!");
        ctx.startService(si);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = si.getStringExtra("MessageKey");
            if( null==vsMessage ){
                vsMessage =  "fsDebugCrest="+fsDebugCrest+";\n";
            }
            vsMessage = vsMessage+
                    "MyService.onStartCommand( );\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            String vsToast = si.getStringExtra("ToastKey");
            if( null==vsToast ){
                vsToast =  "onStartCommand( )";
            }
            Toast.makeText(getApplicationContext( ), vsToast, Toast.LENGTH_SHORT).show();

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


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

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
    </application>

</manifest>


<Number>: [000009F0]  <Date>: 2016/03/20 06:35:13
<Title>: WidgetELActivity 003
<Name>: amanojaku@管理人



Widget から Activity を起動。
以前 作ったモノを元に AppCompat を起動してみる。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetELActivity」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
「drawable-mdpi、drawable-hdpi」フォルダー(存在しない場合は作成してやる)内に「Android SDK」のデフォルト・アイコン「ic_menu_info_details.png、ic_menu_moreoverflow_normal_holo_light.png」をコピーする。
「Toolbar」の UI はスタンダードな「ActionBarActivity」のような感じになっている。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

「drawable-mdpi、drawable-hdpi」フォルダー(存在しない場合は作成してやる)内に「Android SDK」のデフォルト・アイコン「ic_menu_moreoverflow_normal_holo_light.png」をコピーする。
一応、アプリの Logo マークを Android のマスコット・キャラの「Droid君」にしている。
下の方に"drawable-hdpi"フォルダー用の画像ファイル(「48x48」Pixel)をアップしてあります。
"drawable-mdpi"フォルダー用は手抜きして作ってないが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。
その場合、当然 画質は悪くなるので画質を綺麗にしたい場合は手抜きせずにチャント"drawable-mdpi"フォルダー用の画像ファイル(「32x32」Pixel)を作成してやれば良い。
「Toolbar」の UI はスタンダードな「ActionBarActivity」のような感じになっている。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&


『<Project名>→app→main→java→com.example.widgeteactivity→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetelactivity;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

// WidgetELActivity(Widget:{(Event Launch Activity)(click)}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 003";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName(), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        // 起動するアクティビティの指定
        Intent si = new Intent(ctx, MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(ctx, 0, si, 0);
        rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

        // awm.updateAppWidget により前述の2つの「rv.set〜」の設定が Update される。
        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent intent, int f, int sid) {
            super.onStartCommand(intent, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");
            // 前述の Widget の場合の記述とビミョウに違っている部分も有るので要注意。
            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "MyService.onStartCommand( );\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent intent) {
            // super.onBind(intent);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→java→com.example.widgetelactivity→MainActivity(MainActivity.java)』


package com.example.widgetelactivity;

import android.os.Bundle;
import android.util.Log;
import android.app.Activity;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
// import android.widget.Toolbar;
import android.content.Context;

import android.view.View;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import android.widget.TextView;
import android.view.KeyEvent;

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener {
    static final String fsDebugCrest = " 008";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。
    MainActivity oMainApp;
    Context oBaseContext;
    Context oAppContext;
    Context oContext;
    ActionBar oActionBar;
    Menu oMainMenu;

    String vsPackageName;
    String vsVersionName;
    int iVersionCode;
    String vsToolbarTitle;
    static final int[] WIDGETS =
            new int[]{R.id.button1, R.id.checkBox1, R.id.toggleButton1, R.id.radioButton1};
    TextView ctvMessage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(DEBUG)Log.i(tag, "Activity.onCreate( );");
        setContentView(R.layout.activity_main);
        oMainApp = this;
// System 共通の Context:別の Application とやりとりするとき。
        oBaseContext = getBaseContext( );
// Application 固有の Context(Application ごとに Context が変化する):Application 共通のモノが対象。
        oAppContext = getApplicationContext( );
// Activity 固有の Context(Activity ごとに Context が変化する):Activity に依存モノが対象。
        oContext = this;

        vsPackageName = getPackageName( );
        try {
            PackageInfo packageInfo = this.getPackageManager( ).getPackageInfo(
                    this.getPackageName( ), PackageManager.GET_META_DATA );
            // 「<Project名>→app→build.gradle」ファイル内の
            // 「versionName、versionCode」の設定値を取得。
            vsVersionName = packageInfo.versionName;
            iVersionCode = packageInfo.versionCode;
        } catch (NameNotFoundException e) {
            // 例外処理
            e.printStackTrace( );
        }

        ctvMessage = (TextView)findViewById(R.id.message);
        ctvMessage.append(
                "PackageName=" + vsPackageName + ";\n" +
                        "VersionName=" + vsVersionName + ";\n" +
                        "BuildNo=" + String.valueOf(iVersionCode) + ";\n" +
                        "");

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        oActionBar = getSupportActionBar();

        // oActionBar.setIcon(R.drawable.ic_menu_info_details);
        oActionBar.setLogo(R.drawable.x_logo_android022);
        // ↑ 実際は Logo マークはアプリ固有の Icon にすべきモノです。
        // アプリ固有の Logo マークと言われてもイメージが湧かない方は
        // ブラウザの Icon(Logo マーク)をイメージしてみて下さい。
        // 下記サイトに主要ブラウザの Icon(Logo マーク)が掲載されています。
        // http://freesoft-100.com/pasokon/browser.html
        // ちなみに Toolbar に表示する Icon サイズのスタンダードは
        // "drawable-mdpi"フォルダー用は「32x32」、
        // "drawable-hdpi"フォルダー用は「48x48」だと思われます。

        vsToolbarTitle = "  "+getString(R.string.app_name)+
                fsDebugCrest+
                " V"+vsVersionName+ // Version.
                " R"+String.valueOf(iVersionCode); // Revision.
// ↑ Toolbar に Logo マークを表示させるとタイトルと Logo マークとの間に隙間がないので、
// タイトルの先頭に半角スペースを2つ入れている。
        oActionBar.setTitle(vsToolbarTitle);

        for (int iWgId : WIDGETS) {
            View cvw = findViewById(iWgId);
            cvw.setOnClickListener(this);
        }
    }

    @Override
    protected void onRestart( ) {
        super.onRestart();
        if(DEBUG)Log.i(tag, "Activity.onRestart( );");
    }

    @Override
    protected void onStart( ) {
        super.onStart();
        if(DEBUG)Log.i(tag, "Activity.onStart( );");
    }

    @Override
    protected void onResume( ) {
        super.onResume();
        if(DEBUG)Log.i(tag, "Activity.onResume( );");
    }

    @Override
    protected void onPause( ) {
        super.onPause();
        if(DEBUG)Log.i(tag, "Activity.onPause( );");
    }

    @Override
    protected void onStop( ) {
        super.onStop();
        if(DEBUG)Log.i(tag, "Activity.onStop( );");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(DEBUG)Log.i(tag, "Activity.onDestroy( );");
    }

    @Override
    public void onClick(View v) {
        // super.onClick(v);
        if(DEBUG) Log.i(tag,"Activity.onClick( );");
        switch(v.getId()) {
            case R.id.button1 :
                if (DEBUG) Log.i(tag, "button1");

                break;
            case R.id.checkBox1 :
                if (DEBUG) Log.i(tag, "checkBox1");

                break;
            case R.id.toggleButton1 :
                if (DEBUG) Log.i(tag, "toggleButton1");

                break;
            case R.id.radioButton1 :
                if (DEBUG) Log.i(tag, "radioButton1");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
                return;
        }
        return;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        // アクションバー内で使用する為のメニューアイテムを実体化
        MenuInflater inflater = getMenuInflater();
        // MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        // 「R.menu.main」の「menu.main」は 恐らく「res」からの「フォルダー名+ファイル名」
        oMainMenu = menu;
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if(DEBUG) Log.i(tag,"Activity.onOptionsItemSelected( );");
        switch(item.getItemId()) {
            case R.id.menu_main_settings :
                if (DEBUG) Log.i(tag, "menu_main_settings");

                break;
            case R.id.menu_main_settings_item1 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item1");

                break;
            case R.id.menu_main_settings_item2 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item2");

                break;
            case R.id.menu_main_settings_item3 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item3");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
                return false; // true; //
        }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        super.dispatchKeyEvent(event);
        if(DEBUG) Log.i(tag,"Activity.dispatchKeyEvent( );");
        final int action = event.getAction();
        final int keyCode = event.getKeyCode();
        switch(keyCode) {
            case KeyEvent.KEYCODE_MENU :
                if(DEBUG) Log.i(tag,"KeyEvent.KEYCODE_MENU;");
                if ( action==KeyEvent.ACTION_UP ) {
                    if(DEBUG) Log.i(tag,"KeyEvent.ACTION_UP;");
                    if ( oMainMenu!=null ) {
                        if(DEBUG) Log.i(tag,"oMainMenu.performIdentifierAction( );");
                        oMainMenu.performIdentifierAction(R.id.menu_main_settings, 0);
                    }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
                    return true; // false; //
                }
                break;

            default :
                if (DEBUG) Log.i(tag, "KeyEvent:Default");

                break;
        }
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
        return false; // true; //
    }

}



『<Project名>→app→main→res→drawable→border.xml』(ファイルを作成)
※「TextView」の枠として使っている。


<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff" />
    <padding android:top="3dp" android:bottom="3dp"
        android:left="7dp" android:right="7dp" />
    <stroke android:width="1px" android:color="#000000" />
    <corners android:radius="5dp" />
</shape>



『<Project名>→app→main→res→layout→activity_main.xml』


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ffffff"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="#9b7fff"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <TextView
        android:id="@+id/message"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_margin = "7dp"
        android:background="@drawable/border"
        android:textSize="@dimen/abc_text_size_medium_material"
        android:layout_weight="1" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 1"
        android:id="@+id/button1"
        android:layout_gravity="center_horizontal" />

    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ToggleButton 1"
        android:id="@+id/toggleButton1"
        android:layout_gravity="center_horizontal" />

    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox 1"
        android:id="@+id/checkBox1"
        android:layout_gravity="center_horizontal" />

    <RadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RadioButton 1"
        android:id="@+id/radioButton1"
        android:layout_gravity="center_horizontal" />

</LinearLayout>



『<Project名>→app→src→main→res→menu→main.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <item android:id="@+id/menu_main_settings"
        android:title="Settings"
        android:icon="@drawable/ic_menu_moreoverflow_normal_holo_light"
        app:showAsAction="always">
        <!-- ↑ showAsAction:Toolbar に対する表示設定。 -->
        <!-- "always":常に表示、"never":常に表示しない、"ifRoom":表示する余裕があれば表示。 -->
        <menu>
            <item android:id="@+id/menu_main_settings_item1"
                android:title="item1">
            </item>

            <item android:id="@+id/menu_main_settings_item2"
                android:title="item2">
            </item>

            <item android:id="@+id/menu_main_settings_item3"
                android:title="item3">
            </item>
        </menu>
    </item>

</menu>



『<Project名>→app→src→main→res→values→styles.xml』


<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>



『<Project名>→app→main→AndroidManifest.xml』
※この場合 <activity>タグに"launchMode"は設定できないようだ、この場合 「launchMode="singleInstance"」を設定しなくても Activity のインスタンスは1つだけに限定されるようだ。


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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <receiver
            android:name=".AppWidget"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <activity android:name=".MainActivity" >
        </activity>
    </application>

</manifest>


<Number>: [000009FA]  <Date>: 2016/04/30 15:22:37
<Title>: WidgetELSStorage 012
<Name>: amanojaku@管理人



「Widget、Service」から Storage に読み書きする。
今回は「内部ストレージのアプリ固有の領域」に対して Stream を読み書きしてみる。
Widget の onEnabled 実行時に Stream のデータを消去。
Widget の onUpdate 実行時に、onUpdate イベントのログを Stream に追加する。
クリックによる Service の onStartCommand 実行時に、onStartCommand イベントのログを Stream に追加する。
なお、「内部ストレージのアプリ固有の領域」は(セキュリティー上)ファイラーからは見えないようになってるらしい。
ちなみに「外部ストレージ領域」はファイラーから見えるので、「パスワード、個人情報」などのようなデータは「外部ストレージ領域」に保存してはならない。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetELSStorage」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

アプリからデータを内部&外部ストレージへの保存
http://androidadvent.blog.shinobi.jp/2012/internal-external-storage-save

AndroidのContextについて
http://qiita.com/tyfkda/items/bbb77a0e5a97eda9d4b6

> getApplicationContext()
> getBaseContext()

> どれを使うべきか、はたまたgetApplicationで取得できるApplicationもContextを継承していて、どれをつかったらいいの?と悩む。

> Activityはアクティビティ単位なので、別のアクティビティに遷移するとContextも変わる
> Applicationは起動中のアプリに対して1個存在して、Contextも1つ。アプリが生きている間存在するアクティビティが変わっても同じ内容
> getBaseContextは別のアプリとやりとりするとき用

> OSからみてアプリケーションの機能として見ることができるモノ(SharedPreferencesやDB,リソースへのアクセスなど)はApplicationContextを用い,Activityに依存するモノ(BroadcastReceiverやViewのインスタンス生成など)はActivityを用いるもの

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&


『<Project名>→app→main→java→com.example.widgetelsstorage→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetelsstorage;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

import java.io.File;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.io.IOException;

import java.util.List;
import java.util.ArrayList;

import java.text.SimpleDateFormat;
import java.util.Date;

// WidgetELSStorage(Widget:{(Event Launch Service)(click)[Storage]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 012";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    static final String fvsSerialFile = "Storage.dat";
    static final SimpleDateFormat oDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    // Android Java の場合、カラーの実態は int。
    static final int[] d1iCPalette = new int[]{
            Color.BLUE,
            Color.CYAN,
            Color.GREEN,
            Color.MAGENTA,
            Color.RED,
            Color.YELLOW,
    };

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");

        Context oAppContext = ctx.getApplicationContext( );
        // 初期化。
        List<String> dl1Event = new ArrayList<String>( );
        Write(oAppContext, dl1Event);
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        Context oAppContext = ctx.getApplicationContext( );
        List<String> dl1Event = new ArrayList<String>( );
        Read(oAppContext, dl1Event);
        dl1Event.add("AppWidget.onUpdate");
        Write(oAppContext, dl1Event);

        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName( ), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        Intent si = new Intent(ctx, MyService.class);
        PendingIntent pi = PendingIntent.getService(ctx, 0, si, 0);
        rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

        // awm.updateAppWidget により前述の2つの「rv.set〜」の設定が Update される。
        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");
    }

    static void Read(Context oAppContext, List<String> dl1Event) {
        if(DEBUG)Log.i(tag, "AppWidget.Read( );");
        try {
            File vsFFDir = oAppContext.getFilesDir();
            if(DEBUG)Log.i(tag,"AppWidget.Read( ) : "+
                    "vsFFDir.getCanonicalPath()="+vsFFDir.getCanonicalPath()+"; "+
                    "");
            File vsFSPath = oAppContext.getFileStreamPath(fvsSerialFile);
            if(DEBUG)Log.i(tag,"AppWidget.Read( ) : "+
                    "vsFSPath.getCanonicalPath()="+vsFSPath.getCanonicalPath()+"; "+
                    "");
            FileInputStream oFIS = oAppContext.openFileInput(fvsSerialFile);
            BufferedInputStream oBIS = new BufferedInputStream(oFIS);
            ObjectInputStream oOISSerial = new ObjectInputStream(oBIS);
            int iListSize = ((Integer)oOISSerial.readObject( )).intValue( );
            if(DEBUG)Log.i(tag,"AppWidget.Read( ) : "+
                    "iListSize="+iListSize+"; "+
                    "");
            for( int i= 0; i<iListSize; i++ ){
                dl1Event.add((String)oOISSerial.readObject( ));
                if(DEBUG)Log.i(tag,"AppWidget.Read( ) : "+
                        "dl1Event.get(i)="+dl1Event.get(i)+"; "+
                        "");
            }
            oOISSerial.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            // e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static void Write(Context oAppContext, List<String> dl1Event) {
        if(DEBUG)Log.i(tag, "AppWidget.Write( );");
        try {
            if(DEBUG)Log.i(tag,"AppWidget.Write( ) : "+
                    "dl1Event.size( )="+dl1Event.size( )+"; "+
                    "");
            File vsFFDir = oAppContext.getFilesDir();
            if(DEBUG)Log.i(tag,"AppWidget.Write( ) : "+
                    "vsFFDir.getCanonicalPath()="+vsFFDir.getCanonicalPath()+"; "+
                    "");
            // Context の openFileOutput は内部ストレージのアプリ固有の領域にアクセスされるらしい。
            // Context.MODE_PRIVATE:PRIVATE 出力モード:他のアプリからはアクセスできない。
            FileOutputStream oFOS = oAppContext.openFileOutput(fvsSerialFile, Context.MODE_PRIVATE);
            BufferedOutputStream oBOS = new BufferedOutputStream(oFOS);
            ObjectOutputStream oOOSSerial = new ObjectOutputStream(oBOS);
            oOOSSerial.writeObject(new Integer(dl1Event.size()));
            for( int i = 0; i<dl1Event.size( ); i++ ){
                oOOSSerial.writeObject(dl1Event.get(i));
            }
            oOOSSerial.close();
            File vsFSPath = oAppContext.getFileStreamPath(fvsSerialFile);
            if(DEBUG)Log.i(tag,"AppWidget.Write( ) : "+
                    "vsFSPath.getCanonicalPath()="+vsFSPath.getCanonicalPath()+"; "+
                    "");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

            String vsDateTime = oDateFormat.format(
                    new Date(System.currentTimeMillis()));
            if (DEBUG) Log.i(tag, "AppWidget : " +
                    "vsDateTime=" +vsDateTime+ "; " +
                    "");
            Context oAppContext = getApplicationContext();
            List<String> dl1Event = new ArrayList<String>( );
            Read(oAppContext, dl1Event);
            dl1Event.add("MyService.onStartCommand : "+
                    "vsDateTime=" +vsDateTime);
            Write(oAppContext, dl1Event);

            // 前述の Widget の場合の記述とビミョウに違っている部分も有るので要注意。
            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName( ), R.layout.widget_main);

            // Android Java の場合、カラーの実態は int。
            int iColor = d1iCPalette[(int)(Math.random( ) * d1iCPalette.length)];
            rv.setInt(R.id.ctvTextView, "setBackgroundColor", iColor);
            String vsMessage = "MyService.onStartCommand( );\n"+
                    "iColor="+iColor+";\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( ) : "+
                    "iColor="+iColor+"; "+
                    "");

            awm.updateAppWidget(cn, rv);

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#c0c0c0"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#000000"
    android:editable="false" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


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

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
    </application>

</manifest>


<Number>: [00000A05]  <Date>: 2016/04/16 00:43:37
<Title>: WidgetILSTimerILSBKill 006
<Name>: amanojaku@管理人



Widget で(Service から) Timer を起動、Broadcast による通知で Timer を停止する。
秒単位での短い間隔の繰り返しには Timer オブジェクトが適してるらしいので、Timer オブジェクトを使用する。
Widget Class 内のフィールド変数ではデータが保持されないので、Timer オブジェクトを Service Class 内のフィールド変数として定義し、Service から起動する。
Broadcast で Timer に通知して停止させる、良く分からないが Widget だと「getBaseContext( )」が使えないようで、それだと Broadcast できないと言うことになるから、結局 (Broadcast に関しては) Service から実行する必要があるようだ。
問題点としては このプログラムでは Timer を1つしか作れないので、複数の Timer が必要な場合は何らかの工夫が必要となる。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetILSTimerILSBKill」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&
WidgetELSStorage
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009FA.+&


『<Project名>→app→main→java→com.example.widgetilstimerilsbkill→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetilstimerilsbkill;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

import java.util.Timer;

// WidgetILSTimerILSBKill(Widget:{(Instant Launch Service)[Timer],(Instant Launch Service)[Broadcast Kill]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 006";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

// (世界中で一意の「Intent Filter 名」とするため)「Intent Filter」に設定する文字列は
// パッケージ名「自分のサイトのドメイン名を逆転させた名前+アプリケーション名」を設定する事が推奨されている。
// ここではパッケージ名 自体が手抜きなので とりあえず
// 「国名+自分の大まかな出生地+ハンドル・ネーム+アプリケーション名」としている
// (出生地にしておけば引っ越しても変更する必要がない)。
// これは あくまで暫定的な一例であり、実際の「Intent Filter 名」は もっとチャントしたモノが良いだろう。
// もし、ここの「Intent Filter」を変更した場合は
// 「AndroidManifest.xml」の「Intent Filter 名」も変更しなければならない。
// 本来、「パッケージ名」自体が もっとチャントしたモノになっていれば、
// 「getPackageName( )」を使えば良いだけなだが…。
// このプログラムでは、この「fsIntentFilter_Action」変数に「null」を設定すれば、
// 「getPackageName( )」の値が使用されるようになっている。
// その場合も 当然「AndroidManifest.xml」の「Intent Filter 名」を変更しなければならない。
// ただイキナリ、世界中で一意の「Intent Filter 名」を考え出すのも難しいかもしれないので、
// とりあえず このような変数を使って試行錯誤すると言う手もある
// (試行錯誤する場合、いちいち「パッケージ名」自体を変更するのは面倒)。
    static final String fsIntentFilter_Action = "jp.kanagawa.amanojaku.WidgetILSTimerILSBKill";

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");

// サービスの起動を1回だけに限定したい場合は、サービスの起動を onEnabled メソッド内に記述する。
        Intent si = new Intent(ctx, MyService.class);
        ctx.startService(si);
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName(), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");

        Intent si = new Intent(ctx, MyService.class);
        si.putExtra("KillKey", Boolean.valueOf(true)); // false; //
        ctx.startService(si);
        /*
        Intent si = new Intent(ctx, MyService.class);
        ctx.stopService(si);
        */
    }

    public static class MyService extends Service {

        Timer timer; // = new Timer( );

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "MyService.onStartCommand( );\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            Boolean oKill = si.getBooleanExtra("KillKey", false);
            if( ! oKill.booleanValue( ) ){
                if(DEBUG)Log.i(tag, "MyTimerTask( );");
                long delay = 0; // 0ms
                long interval = 1000; // 1000ms
                timer = new Timer( );
                timer.scheduleAtFixedRate(new MyTimerTask(timer), delay, interval);
            }else{
                if(DEBUG)Log.i(tag, "Kill;");
                String ifa = fsIntentFilter_Action;
                if( null==ifa ){
                    ifa = getPackageName( );
                }
                Intent bi = new Intent( );
                bi.setAction(ifa);
                getBaseContext( ).sendBroadcast(bi);
                stopSelf( );
            }

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→java→com.example.widgetilstimerilsbkill→MyTimerTask(MyTimerTask.java)』(ファイルを作成)


package com.example.widgetilstimerilsbkill;

import java.util.Timer;
import java.util.TimerTask;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyTimerTask extends TimerTask {
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = AppWidget.DEBUG;
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。

    static final String fsIntentFilter_Action = AppWidget.fsIntentFilter_Action;

    static Timer timer; // = new Timer();
    SimpleDateFormat oDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    public MyTimerTask(Timer t){
        super();

        timer = t;
    }

    public static class MyReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context ctx, Intent ri) {
            if (DEBUG) Log.i(tag, "Receiver.onReceive( );");

            String ifa = fsIntentFilter_Action;
            if( null==ifa ){
                ifa = ctx.getPackageName();
            }
// プログラム側と「AndroidManifest.xml」の「Intent Filter 名」が一致してないような
// ヒューマン・エラー対策として Action を比較する。
            if( ifa.equals(ri.getAction( )) ) {
                if (DEBUG) Log.i(tag, "Action;");
                if(timer != null){
                    timer.cancel( );
                    timer = null;
                }
            }
        }

    }

    @Override
    public void run( ) {
        String vsDateTime = oDateFormat.format(
                new Date(System.currentTimeMillis()));
        if (DEBUG) Log.i(tag, "TimerTask : " +
                "vsDateTime=" +vsDateTime+ "; " +
                "");
    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


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

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
        <receiver android:name=".MyTimerTask$MyReceiver" >
            <intent-filter>
                <action android:name="jp.kanagawa.amanojaku.WidgetILSTimerILSBKill" />
            </intent-filter>
        </receiver>
    </application>

</manifest>


<Number>: [00000A0E]  <Date>: 2016/04/16 00:46:26
<Title>: WidgetILSMTimerISLSBKill 010
<Name>: amanojaku@管理人



Widget で(Service から) 複数の Timer を起動、Broadcast による通知で Timer を停止する。
秒単位での短い間隔の繰り返しには Timer オブジェクトが適してるらしいので、ここでも Timer オブジェクトを使用する。
Widget Class 内のフィールド変数ではデータが保持されないので、Timer オブジェクトを Service Class 内のフィールド変数として定義し、Service から起動する。
Broadcast で Timer に通知して停止させる、良く分からないが Widget だと「getBaseContext( )」が使えないようで、それだと Broadcast できないと言うことになるから、結局 (Broadcast に関しては) Service から実行する必要があるようだ。
このプログラムでは MyTimerTask は Abstract(抽象) Class なので、(このプログラムで記されているとおり)必ず MyTimerTask を継承して Class を記述しなければならない。
このプログラムでは「"Alpha"、"Beta"」の2つの Timer を生成しており、Widget の起動から10秒後に Timer "Alpha"を停止させている。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetILSMTimerISLSBKill」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

AlarmService を利用したサービス実行のスケジューリング
http://android.keicode.com/basics/services-schedule-with-alarmmanager.php

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&
WidgetELSStorage
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009FA.+&
WidgetILSTimerILSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A05.+&


『<Project名>→app→main→java→com.example.widgetilsmtimerislsbkill→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetilsmtimerislsbkill;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

import java.util.Timer;
import java.text.SimpleDateFormat;
import java.util.Date;

// Widget_ILSMTimer_ISLSBKill:(Widget/{(Instant Launch Service)[Multiple Timer]/({Instant,Schedule} Launch Service)[Broadcast Kill]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 010";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    SimpleDateFormat oDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    // (世界中で一意の「Intent Filter 名」とするため)「Intent Filter」に設定する文字列は
// パッケージ名「自分のサイトのドメイン名を逆転させた名前+アプリケーション名」を設定する事が推奨されている。
// ここではパッケージ名 自体が手抜きなので とりあえず
// 「国名+自分の大まかな出生地+ハンドル・ネーム+アプリケーション名」としている
// (出生地にしておけば引っ越しても変更する必要がない)。
// これは あくまで暫定的な一例であり、実際の「Intent Filter 名」は もっとチャントしたモノが良いだろう。
// もし、ここの「Intent Filter」を変更した場合は
// 「AndroidManifest.xml」の「Intent Filter 名」も変更しなければならない。
// 本来、「パッケージ名」自体が もっとチャントしたモノになっていれば、
// 「getPackageName( )」を使えば良いだけなだが…。
// このプログラムでは、この「fsIntentFilter_Action」変数に「null」を設定すれば、
// 「getPackageName( )」の値が使用されるようになっている。
// その場合も 当然「AndroidManifest.xml」の「Intent Filter 名」を変更しなければならない。
// ただイキナリ、世界中で一意の「Intent Filter 名」を考え出すのも難しいかもしれないので、
// とりあえず このような変数を使って試行錯誤すると言う手もある
// (試行錯誤する場合、いちいち「パッケージ名」自体を変更するのは面倒)。
    static final String fsIntentFilter_Action = "jp.kanagawa.amanojaku.WidgetILSMTimerISLSBKill";

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");

// サービスの起動を1回だけに限定したい場合は、サービスの起動を onEnabled メソッド内に記述する。
        Intent si = new Intent(ctx, MyService.class);
        ctx.startService(si);
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName(), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");

// Timer の名前を指定してない場合は、現在 生存している Timer を全て Kill し、
// Service も終了させている。
        Intent si = new Intent(ctx, MyService.class);
        si.putExtra("KillKey", Boolean.valueOf(true)); // false; //
        ctx.startService(si);
        /*
        Intent si = new Intent(ctx, MyService.class);
        ctx.stopService(si);
        */
    }

    public static class MyService extends Service {

        // 下記「timer、delay、interval」は あくまでもテンポラリー。
        Timer timer; // = new Timer( );
        long delay; // ms
        long interval; // ms

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "MyService.onStartCommand( );\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            Boolean oKill = si.getBooleanExtra("KillKey", false);
            if( ! oKill.booleanValue( ) ){
                if(DEBUG)Log.i(tag, "MyTimerTask( );");

                delay = 0; // 0ms
                interval = 1000; // 1000ms
                timer = new Timer( );
                timer.scheduleAtFixedRate(new MyTimerTask(timer, "Alpha") {
                    @Override
                    public void run() {
                        String vsDateTime = oDateFormat.format(
                                new Date(System.currentTimeMillis()));
                        if (DEBUG) Log.i(tag, "TimerTask : " +
                                "vsName=" + vsName + "; " +
                                "vsDateTime=" + vsDateTime + "; " +
                                "");
                    }
                }, delay, interval);

                delay = 0; // 0ms
                interval = 2000; // 2000ms
                timer = new Timer( );
                timer.scheduleAtFixedRate(new MyTimerTask(timer, "Beta") {
                    @Override
                    public void run() {
                        String vsDateTime = oDateFormat.format(
                                new Date(System.currentTimeMillis( )));
                        if (DEBUG) Log.i(tag, "TimerTask : " +
                                "vsName=" + vsName + "; " +
                                "vsDateTime=" + vsDateTime + "; " +
                                "");
                    }
                }, delay, interval);

// 10秒後に自分自身(MyService.onStartCommand())を呼び出して Timer "Alpha"を Kill する。
                delay = 10*1000L; // 10s
                Intent asi = new Intent(this, this.getClass( ));
                asi.putExtra("TimerKey", "Alpha");
                asi.putExtra("KillKey", Boolean.valueOf(true)); // false; //
                PendingIntent aspi = PendingIntent.getService(
                        this, 0, asi, 0);
                AlarmManager am = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
                am.set(AlarmManager.RTC, System.currentTimeMillis( )+delay, aspi);
            }else{
                if(DEBUG)Log.i(tag, "Kill;");
                String vsTimer = si.getStringExtra("TimerKey");
                String ifa = fsIntentFilter_Action;
                if( null==ifa ){
                    ifa = getPackageName( );
                }
                Intent bi = new Intent( );
                bi.setAction(ifa);
                bi.putExtra("TimerKey", vsTimer);
                getBaseContext( ).sendBroadcast(bi);
                if( null==vsTimer || "".equals(vsTimer) ){
                    stopSelf( );
                }
            }

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→java→com.example.widgetilsmtimerislsbkill→MyTimerTask(MyTimerTask.java)』(ファイルを作成)


package com.example.widgetilsmtimerislsbkill;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Date;

public abstract class MyTimerTask extends TimerTask {
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = AppWidget.DEBUG;
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。

    static final String fsIntentFilter_Action = AppWidget.fsIntentFilter_Action;

    // static Timer timer; // = new Timer();
    // int iID = -1;
    String vsName = "";
    // static List<String> di1vsName; // = new ArrayList<String>( );
    // static List<Timer> di1oTimer; // = new ArrayList<Timer>( );
    static HashMap<String, Timer> dm1oTimer; // = new HashMap<String, Timer>( );

    SimpleDateFormat oDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    public MyTimerTask(Timer t, String vsN){
        super();
// Timer の同名の多重登録はチェックしていません。
        vsName = vsN;

        if( null==dm1oTimer ){
            dm1oTimer = new HashMap<String, Timer>( );
        }
        dm1oTimer.put(vsName, t);
        if(DEBUG)Log.i(tag, "MyTimerTask( ) : "+
                "vsName="+vsName+"; "+
                "dm1oTimer.size( )="+dm1oTimer.size( )+"; "+
                "");
    }

    public static class MyReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context ctx, Intent ri) {
            if (DEBUG) Log.i(tag, "Receiver.onReceive( );");

            String ifa = fsIntentFilter_Action;
            if( null==ifa ){
                ifa = ctx.getPackageName();
            }
// プログラム側と「AndroidManifest.xml」の「Intent Filter 名」が一致してないような
// ヒューマン・エラー対策として Action を比較する。
            if( ifa.equals(ri.getAction( )) ) {
                if (DEBUG) Log.i(tag, "Action;");
                if( null!=dm1oTimer ){
                    Timer timer;
                    String vsTimer = ri.getStringExtra("TimerKey");
                    if( null!=vsTimer && ! vsTimer.equals("") ){
                        timer = dm1oTimer.get(vsTimer);
                        if( null!=timer ){
                            timer.cancel();
                            dm1oTimer.remove(vsTimer);
                        }
                    }else {
                        String k;
                        String[] key = dm1oTimer.keySet().toArray(new String[0]);
                        for (int i = 0; i < key.length; i++) {
// ↑どうも Android では「for( String k : dm1oTimer.keySet( ) )」のような「拡張 for 文」や、
// Iterator なども実行時エラーになるようだ。
                            k = key[i];
                            if (DEBUG) Log.i(tag, "Receiver.onReceive( ) : " +
                                    "i=" + i + "; " +
                                    "k=" + k + "; " +
                                    "");
                            timer = dm1oTimer.get(k);
                            if (null != timer) {
                                timer.cancel();
                                dm1oTimer.remove(k);
                            }
                        }
                    }
                }
            }
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


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

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
        <receiver android:name=".MyTimerTask$MyReceiver" >
            <intent-filter>
                <action android:name="jp.kanagawa.amanojaku.WidgetILSMTimerISLSBKill" />
            </intent-filter>
        </receiver>
    </application>

</manifest>


<Number>: [00000A19]  <Date>: 2016/04/12 02:40:49
<Title>: Widget_ILFS_Toast
<Name>: amanojaku@管理人



(Widget から起動している) Service を Foreground に設定してやり、Service を死ににくくする。
Service を Foreground に設定すると、(通知バーには表示されない場合があるが、通知バーを下ろした)通知領域にアイコンが表示される、場合によっては通知バーにもアイコンが表示される(良く分からないが そう言う仕様なのか?)。
画面から Widget が削除されると、 Service も終了されるが、その時に Foreground も解除されるので、通知領域のアイコンも消える。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

Service が実行されると Toast を表示する。
通知領域のアイコンをタップしても Service が実行され Toast が表示される。

とりあえず、[New Project]で(1ページ目)[Application name]:「Widget_ILFS_Toast」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

一応、通知用アイコンを Android のマスコット・キャラの「Droid君」にしている。
通知用アイコンは白色が推奨されているらしいので、一部 白ではないが白を基調としている。
下の方に"drawable-hdpi"フォルダー用の画像ファイル(「36x36」Pixel)をアップしてあります(「drawable-hdpi」フォルダーが存在しない場合は作成してやる)。
"drawable-mdpi"フォルダー用は手抜きして作ってないが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい、よって targetSdkVersion の値は「13」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

AlarmService を利用したサービス実行のスケジューリング
http://android.keicode.com/basics/services-schedule-with-alarmmanager.php

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&
WidgetELSStorage
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009FA.+&
WidgetILSTimerILSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A05.+&
WidgetILSMTimerISLSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A0E.+&


『<Project名>→app→main→java→com.example.widget_ilfs_toast→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widget_ilfs_toast;

import android.app.PendingIntent;
import android.app.Service;
import android.app.Notification;
import android.support.v7.app.NotificationCompat;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.view.Gravity;
import android.widget.RemoteViews;
import android.util.Log;
import android.widget.Toast;

// Widget_ILFS_Toast(Widget/{(Instant Launch Foreground Service)/(Toast)}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 010";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    static final int ONGOING_NOTIFICATION = 1;

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");

        Intent si = new Intent(ctx, MyService.class);
        ctx.startService(si);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");

        Intent si = new Intent(ctx, MyService.class);
        ctx.stopService(si);
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate();
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

// 良く分からないが、次の PendingIntent は
// Activity の取得の場合は「getActivity(〜)」、
// Activity の取得 以外は「getService(〜)」になると思われる。
            SettingForeground(PendingIntent.getService(
                    this, 0, si, PendingIntent.FLAG_CANCEL_CURRENT));

            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                    "MyService.onStartCommand( );";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            Toast.makeText(getApplicationContext( ), vsMessage, Toast.LENGTH_SHORT).show();

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        public void SettingForeground(PendingIntent pi) {
            if(DEBUG)Log.i(tag, "MyService.SettingForeground( );");
            Notification notif = new NotificationCompat.Builder(this)
                    .setContentIntent(pi)
                    .setAutoCancel(true)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("Content Text.")
                    // .setContentInfo("Content Info.")
                    // .setTicker("Ticker.")
                    .setWhen(System.currentTimeMillis())
                    .setSmallIcon(R.drawable.x_slogo_android031) // SmallIcon を設定しないと通知されない(例外も発生しない)
                    .build( );
            startForeground(ONGOING_NOTIFICATION, notif);
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
            stopForeground(true);
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


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

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
    </application>

</manifest>


<Number>: [00000A1E]  <Date>: 2016/04/12 08:25:11
<Title>: Widget_ILFS_NELAClick
<Name>: amanojaku@管理人



通知領域のアイコンをタップしてやると Activity を起動する(「Widget_ILFS_NELAClick、WidgetELActivity」を元に作成)。
(Widget から起動している) Service を Foreground に設定してるので、Service が死ににくくなっている。
Service を Foreground に設定すると、(通知バーには表示されない場合があるが、通知バーを下ろした)通知領域にアイコンが表示される、場合によっては通知バーにもアイコンが表示される(良く分からないが そう言う仕様なのか?)。
画面から Widget が削除されると、 Service も終了されるが、その時に Foreground も解除されるので、通知領域のアイコンも消える。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

通知領域のアイコンをタップしてやると Activity を起動する。
Widget をタップしても Activity を起動する。

とりあえず、[New Project]で(1ページ目)[Application name]:「Widget_ILFS_NELAClick」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

「drawable-hdpi」フォルダー(存在しない場合は作成してやる)内に「Android SDK」のデフォルト・アイコン「ic_menu_moreoverflow_normal_holo_light.png」をコピーする。
一応、「アプリの Logo マーク、通知用アイコン」を Android のマスコット・キャラの「Droid君」にしている。
通知用アイコンは白色が推奨されているらしいので、(一部 白ではないが)白を基調としている。
下の方に"drawable-hdpi"フォルダー用の画像ファイル(アプリの Logo マーク:「48x48」Pixel、通知用アイコン:「36x36」Pixel)をアップしてあります。
"drawable-mdpi"フォルダー用は手抜きして作ってないが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。
その場合、当然 画質は悪くなるので画質を綺麗にしたい場合は手抜きせずにチャント"drawable-mdpi"フォルダー用の画像ファイル(「32x32」Pixel)を作成してやれば良い。
なお、Activity の Toolbar の UI はスタンダードな「ActionBarActivity」のような感じになっている。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい、よって targetSdkVersion の値は「13」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

AlarmService を利用したサービス実行のスケジューリング
http://android.keicode.com/basics/services-schedule-with-alarmmanager.php

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&
WidgetELSStorage
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009FA.+&
WidgetILSTimerILSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A05.+&
WidgetILSMTimerISLSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A0E.+&
Widget_ILFS_Toast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A19.+&


『<Project名>→app→main→java→com.example.widget_ilfs_nelaclick→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widget_ilfs_nelaclick;

import android.app.PendingIntent;
import android.app.Service;
import android.app.Notification;
import android.support.v7.app.NotificationCompat;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

// Widget_ILFS_NELAClick(appWidget/{(Instant Launch Foreground Service)/(status Notifications area):(intent Event Launch Activity):(Click)}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 011";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    static final int ONGOING_NOTIFICATION = 1;

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");

        Intent si = new Intent(ctx, MyService.class);
        ctx.startService(si);

// MyService で AppWidget の rv(R.layout.widget_main) を上書きしてしまうので、
// AppWidget での rv に対する処理は無意味ですが、
// AppWidget と MyService ではビミョウに違がっているので、
// そのビミョウな違いのサンプルとして参考にして下さい。
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName(), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "AppWidget.onUpdate( );";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        // 起動するアクティビティの指定
        PendingIntent pi = PendingIntent.getActivity(
                ctx, 0, new Intent(ctx, MainActivity.class), 0);
        rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

        // awm.updateAppWidget により前述の2つの「rv.set〜」の設定が Update される。
        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");

        Intent si = new Intent(ctx, MyService.class);
        ctx.stopService(si);
    }


    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

// 良く分からないが、次の PendingIntent は
// Activity の取得の場合は「getActivity(〜)」、
// Activity の取得 以外は「getService(〜)」になると思われる。
            SettingForeground(PendingIntent.getActivity(
                    this, 0, new Intent(this, MainActivity.class),
                    PendingIntent.FLAG_CANCEL_CURRENT));

// MyService で AppWidget の rv(R.layout.widget_main) を上書きしてしまうので、
// AppWidget での rv に対する処理は無意味ですが、
// AppWidget と MyService ではビミョウに違がっているので、
// そのビミョウな違いのサンプルとして参考にして下さい。
            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                     "MyService.onStartCommand( );";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            // 起動するアクティビティの指定
            PendingIntent pi = PendingIntent.getActivity(
                    this, 0, new Intent(this, MainActivity.class), 0);
            rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

            awm.updateAppWidget(cn, rv);

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        public void SettingForeground(PendingIntent pi) {
            if(DEBUG)Log.i(tag, "MyService.SettingForeground( );");
            Notification notif = new NotificationCompat.Builder(this)
                    .setContentIntent(pi)
                    .setAutoCancel(true)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("Content Text.")
                    // .setContentInfo("Content Info.")
                    // .setTicker("Ticker.")
                    .setWhen(System.currentTimeMillis())
                    .setSmallIcon(R.drawable.x_slogo_android031) // SmallIcon を設定しないと通知されない(例外も発生しない)
                    .build( );
            startForeground(ONGOING_NOTIFICATION, notif);
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
            stopForeground(true);
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->


『<Project名>→app→main→java→com.example.widget_ilfs_nelaclick→MainActivity(MainActivity.java)』


package com.example.widget_ilfs_nelaclick;

import android.os.Bundle;
import android.util.Log;
import android.app.Activity;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
// import android.widget.Toolbar;
import android.content.Context;

import android.view.View;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import android.widget.TextView;
import android.view.KeyEvent;

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener {
    static final String fsDebugCrest = " 008";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。
    MainActivity oMainApp;
    Context oBaseContext;
    Context oAppContext;
    Context oContext;
    ActionBar oActionBar;
    Menu oMainMenu;

    String vsPackageName;
    String vsVersionName;
    int iVersionCode;
    String vsToolbarTitle;
    static final int[] WIDGETS =
            new int[]{R.id.button1, R.id.checkBox1, R.id.toggleButton1, R.id.radioButton1};
    TextView ctvMessage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(DEBUG)Log.i(tag, "Activity.onCreate( );");
        setContentView(R.layout.activity_main);
        oMainApp = this;
// System 共通の Context:別の Application とやりとりするとき。
        oBaseContext = getBaseContext( );
// Application 固有の Context(Application ごとに Context が変化する):Application 共通のモノが対象。
        oAppContext = getApplicationContext( );
// Activity 固有の Context(Activity ごとに Context が変化する):Activity に依存モノが対象。
        oContext = this;

        vsPackageName = getPackageName( );
        try {
            PackageInfo packageInfo = this.getPackageManager( ).getPackageInfo(
                    this.getPackageName( ), PackageManager.GET_META_DATA );
            // 「<Project名>→app→build.gradle」ファイル内の
            // 「versionName、versionCode」の設定値を取得。
            vsVersionName = packageInfo.versionName;
            iVersionCode = packageInfo.versionCode;
        } catch (NameNotFoundException e) {
            // 例外処理
            e.printStackTrace( );
        }

        ctvMessage = (TextView)findViewById(R.id.message);
        ctvMessage.append(
                "PackageName=" + vsPackageName + ";\n" +
                        "VersionName=" + vsVersionName + ";\n" +
                        "BuildNo=" + String.valueOf(iVersionCode) + ";\n" +
                        "");

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        oActionBar = getSupportActionBar();

        // oActionBar.setIcon(R.drawable.ic_menu_info_details);
        oActionBar.setLogo(R.drawable.x_logo_android022);
        // ↑ 実際は Logo マークはアプリ固有の Icon にすべきモノです。
        // アプリ固有の Logo マークと言われてもイメージが湧かない方は
        // ブラウザの Icon(Logo マーク)をイメージしてみて下さい。
        // 下記サイトに主要ブラウザの Icon(Logo マーク)が掲載されています。
        // http://freesoft-100.com/pasokon/browser.html
        // ちなみに Toolbar に表示する Icon サイズのスタンダードは
        // "drawable-mdpi"フォルダー用は「32x32」、
        // "drawable-hdpi"フォルダー用は「48x48」だと思われます。

        vsToolbarTitle = "  "+getString(R.string.app_name)+
                fsDebugCrest+
                " V"+vsVersionName+ // Version.
                " R"+String.valueOf(iVersionCode); // Revision.
// ↑ Toolbar に Logo マークを表示させるとタイトルと Logo マークとの間に隙間がないので、
// タイトルの先頭に半角スペースを2つ入れている。
        oActionBar.setTitle(vsToolbarTitle);

        for (int iWgId : WIDGETS) {
            View cvw = findViewById(iWgId);
            cvw.setOnClickListener(this);
        }
    }

    @Override
    protected void onRestart( ) {
        super.onRestart();
        if(DEBUG)Log.i(tag, "Activity.onRestart( );");
    }

    @Override
    protected void onStart( ) {
        super.onStart();
        if(DEBUG)Log.i(tag, "Activity.onStart( );");
    }

    @Override
    protected void onResume( ) {
        super.onResume();
        if(DEBUG)Log.i(tag, "Activity.onResume( );");
    }

    @Override
    protected void onPause( ) {
        super.onPause();
        if(DEBUG)Log.i(tag, "Activity.onPause( );");
    }

    @Override
    protected void onStop( ) {
        super.onStop();
        if(DEBUG)Log.i(tag, "Activity.onStop( );");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(DEBUG)Log.i(tag, "Activity.onDestroy( );");
    }

    @Override
    public void onClick(View v) {
        // super.onClick(v);
        if(DEBUG) Log.i(tag,"Activity.onClick( );");
        switch(v.getId()) {
            case R.id.button1 :
                if (DEBUG) Log.i(tag, "button1");

                break;
            case R.id.checkBox1 :
                if (DEBUG) Log.i(tag, "checkBox1");

                break;
            case R.id.toggleButton1 :
                if (DEBUG) Log.i(tag, "toggleButton1");

                break;
            case R.id.radioButton1 :
                if (DEBUG) Log.i(tag, "radioButton1");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
                return;
        }
        return;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        // アクションバー内で使用する為のメニューアイテムを実体化
        MenuInflater inflater = getMenuInflater();
        // MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        // 「R.menu.main」の「menu.main」は 恐らく「res」からの「フォルダー名+ファイル名」
        oMainMenu = menu;
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if(DEBUG) Log.i(tag,"Activity.onOptionsItemSelected( );");
        switch(item.getItemId()) {
            case R.id.menu_main_settings :
                if (DEBUG) Log.i(tag, "menu_main_settings");

                break;
            case R.id.menu_main_settings_item1 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item1");

                break;
            case R.id.menu_main_settings_item2 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item2");

                break;
            case R.id.menu_main_settings_item3 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item3");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
                return false; // true; //
        }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        super.dispatchKeyEvent(event);
        if(DEBUG) Log.i(tag,"Activity.dispatchKeyEvent( );");
        final int action = event.getAction();
        final int keyCode = event.getKeyCode();
        switch(keyCode) {
            case KeyEvent.KEYCODE_MENU :
                if(DEBUG) Log.i(tag,"KeyEvent.KEYCODE_MENU;");
                if ( action==KeyEvent.ACTION_UP ) {
                    if(DEBUG) Log.i(tag,"KeyEvent.ACTION_UP;");
                    if ( oMainMenu!=null ) {
                        if(DEBUG) Log.i(tag,"oMainMenu.performIdentifierAction( );");
                        oMainMenu.performIdentifierAction(R.id.menu_main_settings, 0);
                    }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
                    return true; // false; //
                }
                break;

            default :
                if (DEBUG) Log.i(tag, "KeyEvent:Default");

                break;
        }
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
        return false; // true; //
    }

}



『<Project名>→app→main→res→drawable→border.xml』(ファイルを作成)
※「TextView」の枠として使っている。


<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff" />
    <padding android:top="3dp" android:bottom="3dp"
        android:left="7dp" android:right="7dp" />
    <stroke android:width="1px" android:color="#000000" />
    <corners android:radius="5dp" />
</shape>



『<Project名>→app→main→res→layout→activity_main.xml』


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ffffff"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="#9b7fff"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <TextView
        android:id="@+id/message"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_margin = "7dp"
        android:background="@drawable/border"
        android:textSize="@dimen/abc_text_size_medium_material"
        android:layout_weight="1" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 1"
        android:id="@+id/button1"
        android:layout_gravity="center_horizontal" />

    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ToggleButton 1"
        android:id="@+id/toggleButton1"
        android:layout_gravity="center_horizontal" />

    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox 1"
        android:id="@+id/checkBox1"
        android:layout_gravity="center_horizontal" />

    <RadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RadioButton 1"
        android:id="@+id/radioButton1"
        android:layout_gravity="center_horizontal" />

</LinearLayout>



『<Project名>→app→src→main→res→menu→main.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <item android:id="@+id/menu_main_settings"
        android:title="Settings"
        android:icon="@drawable/ic_menu_moreoverflow_normal_holo_light"
        app:showAsAction="always">
        <!-- ↑ showAsAction:Toolbar に対する表示設定。 -->
        <!-- "always":常に表示、"never":常に表示しない、"ifRoom":表示する余裕があれば表示。 -->
        <menu>
            <item android:id="@+id/menu_main_settings_item1"
                android:title="item1">
            </item>

            <item android:id="@+id/menu_main_settings_item2"
                android:title="item2">
            </item>

            <item android:id="@+id/menu_main_settings_item3"
                android:title="item3">
            </item>
        </menu>
    </item>

</menu>



『<Project名>→app→src→main→res→values→styles.xml』


<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>



『<Project名>→app→main→AndroidManifest.xml』


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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <receiver
            android:name=".AppWidget"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
        <activity android:name=".MainActivity" >
        </activity>
    </application>

</manifest>

Block( Address 00000A54 Identity 0000090D )










ページの表示順:{ 新しい順/ 古い順}.
初期・ページの表示・位置:{ 先頭ページ/ 末尾ページ}.
1ページ内のスレッド表示数:

   
   

管理者用 Password:

  




SMT Version 8.022(+A) Release M6.
Author : amanojaku.


- Rental Orbit Space -