Android ストップウォッチ機能とお絵かきを同時に行うアプリ

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
The_sato

Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#1

投稿記事 by The_sato » 12年前

初めてこの掲示板を利用させていただきます。
現在Androidアプリを製作しているのですが、具体的な内容としては
canvasを利用したお絵かき機能と
(筆記時間)→(手を離している時間)→(筆記時間)→(手を離している時間)→(筆記時間)→・・・
を一個一個計測し、記録できるストップウォッチを組み合わせたものとなっています。

進行状況としましては
筆記時間、手を離している時間を連続で測定できるストップウォッチと、canvasで自由にお絵かきできるプログラムはそれぞれ別個に完成しています。
しかし、これら2つの.javaを同じパッケージ内に入れて動作させたところ
・お絵かきするために設定した箇所をタッチすると、お絵かきはできるがストップウォッチが動作しない
・お絵かき用に設定した箇所以外をタッチすると、ストップウォッチが動作する
といった問題がおきています。

解決のためにcanvasのonTouchの方にストップウォッチのコードを書き移したり、layoutの順序を変える等も試してみました。
しかし、どれも予期せぬエラーのためアプリが強制終了してしまうといった状態でした。

自分のプログラムの知識は、大学でC言語のif文、for文、配列を勉強した程度でして、javaというものは触れてまだ2週間程度です。
明らかな勉強不足ですが、期限が近いこともあり自分だけでは解決できないと判断したので質問させていただきました。
どのようにすれば理想とした動作をさせることができるのでしょうか?回答やアドバイス、お願い致します。

コード:

 public class MainActivity extends Activity {
    ArrayAdapter<String> adapter;
    //CanvasView View;
    
   /** Called when the activity is first created. */
   @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        }
   
   ArrayList<String> laptime = new ArrayList<String>();

 //指の動きに応じてストップウォッチ起動&ラップ保存
   @Override
    public boolean onTouchEvent(MotionEvent event) {
	    //View =new CanvasView(this);
	    //setContentView(View);
	    
       adapter = new ArrayAdapter<String>(
          	    this, android.R.layout.simple_list_item_1,laptime);
          	    ListView list = (ListView)findViewById(R.id.list);
          	    list.setAdapter(adapter);

       countText = (TextView)findViewById(R.id.Counter);

     switch (event.getAction()) {
       case MotionEvent.ACTION_DOWN :
    	   //ifでn==0なら"開始"を表示し、そうでなら手を離していた時間(×)を表示
    	   if(n==0){
    		   laptime.add("開始");
    		   loopEngine.start();
    	        startDate =System.currentTimeMillis();
    		   n++;
    		   }
    	   else{
    		   laptime.add("×"+" : "+String.valueOf(System.currentTimeMillis()-startDate));
    		   loopEngine.start();
    		   startDate =System.currentTimeMillis();
    		   n++;
    		   }
         break;

       case MotionEvent.ACTION_UP:
           //n個目の○を筆記してる時間
       	laptime.add(n+"個目"+" : "+String.valueOf(System.currentTimeMillis()-startDate));
        loopEngine.start();
        startDate =System.currentTimeMillis();
         break;
       }
       return true;
    }
   
    // カウントを表示するテキストボックス
     TextView countText; 
    
    public long startDate;
    public long n=0;
    public void update(){
    	//(現在時刻)-(スタート時刻)を表示
        countText.setText(String.valueOf(System.currentTimeMillis()-startDate));
    }
    
    //一定時間後にupdateを呼ぶためのオブジェクト
    class LoopEngine extends Handler {
        private boolean isUpdate;
        public void start(){
                this.isUpdate = true;
                handleMessage(new Message());
        }
        public void stop(){
                this.isUpdate = false;
        }
        @Override
        public void handleMessage(Message msg) {
                this.removeMessages(0);//既存のメッセージは削除
                if(this.isUpdate){
                MainActivity.this.update();//自信が発したメッセージを取得してupdateを実行
                        sendMessageDelayed(obtainMessage(0), 10);//10ミリ秒後にメッセージを出力
                }
        } 
    };
    protected LoopEngine loopEngine = new LoopEngine();

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
}

コード:

//参考:初めてのAndroidアプリ開発Android4対応版 秀和システム 山田 祥寛(著)
public class CanvasView extends View {
   Path path;
   Paint p;
  
  public CanvasView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
  }

  public CanvasView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }

  public CanvasView(Context context) {
    super(context);
    init();
  }

   private void init() {
    p = new Paint();
    p.setColor(Color.BLUE);
    p.setStrokeWidth(3);
    p.setStyle(Paint.Style.STROKE);
    p.setStrokeJoin(Paint.Join.ROUND);
    path = new Path();
  }

  @Override
  public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(path, p);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN :
        path.moveTo(event.getX(), event.getY());
        break;
      case MotionEvent.ACTION_MOVE:
        path.lineTo(event.getX(), event.getY());
        break;
      case MotionEvent.ACTION_UP:
        path.lineTo(event.getX(), event.getY());
        break;
      }
      invalidate();
      return true;
  }
}

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#2

投稿記事 by ISLe » 12年前

CanvasViewのonTouchEventの戻り値をfalseにすると、下位のViewにもonTouchEventが発生するようになります。

最終的にonTouchEventでtrueを返す必要があるのでViewがレイアウト上オーバーラップしていなければいけません。

The_sato

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#3

投稿記事 by The_sato » 12年前

返信ありがとうございます!
試しにCanvasViewのonTouchEventの戻り値をfalseに変えてみたところ、ストップウォッチのみが動作するようになりました。
ISLe さんが書きました:下位のViewにもonTouchEventが発生するようになります。

最終的にonTouchEventでtrueを返す必要があるのでViewがレイアウト上オーバーラップしていなければいけません。
とありましたが、具体的にはlayout内のcanvasView1を一番上に持ってくると良いという意味でしょうか?
現在、アウトラインは

RelativeLayout
├Counter(TextView)-"0"
├canvasView1
∟list(ListView)

という形で実行しており、canvasView1が画面いっぱいを覆っている状態です。これ以外の順番で実行したところ、予期せぬエラーのためアプリが終了してしまいました。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#4

投稿記事 by ISLe » 12年前

The_sato さんが書きました:とありましたが、具体的にはlayout内のcanvasView1を一番上に持ってくると良いという意味でしょうか?
勝手にCanvasViewが上位にあると思ってしまいました。

上位のViewのonTouchEventでfalseを返して、下位のViewのonTouchEventでtrueを返すようにしてください。

途中でonTouchEventを処理して最終的にtrueを返さなかった場合、タッチイベントに対してonTouchEventの呼び出しが正しく行われなくなるので注意してください。

The_sato

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#5

投稿記事 by The_sato » 12年前

返信ありがとうございます!
何度も申し訳ありません。
Viewの上位、下位のonTouchEventをそれぞれfalse,trueで返すというのは今回書いたプログラムの場合

layoutでは
RelativeLayout
├Counter(TextView)-"0"
├canvasView1

のようにして、Counter部分の上にcanvasViewを重ねて上位とし、

上位 → public class CanvasView extends View にあるお絵かき機能を動作をさせるonTouchEventはfalse
下位 → public class MainActivity extends Activity にあるストップウォッチを操作するonTouchEventはtrue

というように書き換えることとは違うのでしょうか?
この方法は前回の書き込みの時に試してみましたが、ストップウォッチしか動作しませんでした。

それとも、
ISLe さんが書きました:上位のViewのonTouchEventでfalseを返して、下位のViewのonTouchEventでtrueを返すようにしてください。
Viewというのはpublic class CanvasView extends View内のonTouchEventのことを指していて
この中のお絵かきのonTouchEventを上位とし、これとは別の下位となる新たなonTouchEventを作成してこちらでtrueを返す工夫をしなさい
ということでしょうか?

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#6

投稿記事 by ISLe » 12年前

ごめんなさい。
ViewのonTouchEventでfalseを返すと継続するイベントが通知されなくなります。
なので継続してイベントを処理するときは必ずtrueを返す必要があります。
これまでの回答は忘れてください。


ActivityでOnTouchListenerを実装して、onTouchメソッドでタッチイベントを監視できます。
先にonTouchメソッドに通知され、falseを返すとViewのonTouchEventにもタッチイベントが通知されます。

このActivityで取得するイベントを使ってストップウォッチを操作できます。
MainActivity#onTouchEventをそのままonTouchに変えてしまうだけ(但し戻り値はfalseに変更)で良いのではないでしょうか。

The_sato

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#7

投稿記事 by The_sato » 12年前

ありがとうございます!
onTouchについてもう少し勉強してみます。

ISLe さんが書きました:MainActivity#onTouchEventをそのままonTouchに変えてしまうだけ(但し戻り値はfalseに変更)で良いのではないでしょうか。
この方法を試してみましたが、絵を描く機能しか動作しませんでした。念のため戻り値をあえてtrueにもしてみましたが、こちらも同様に絵を描く機能しか動作しませんでした。
他にも逆パターンとしてCanvasViewの方のonTouchEventをonTouchに変えて、戻り値はtrue,false両方でも試してみましたが、この場合はどちらもストップウォッチしか動作しませんでした。

onTouchEventと書いた方をfalse、onTouchと書いた方をtrueとした場合は絵の機能もストップウォッチの機能も動作しなくなったのですが、
以上より何が原因となっているのでしょうか?

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#8

投稿記事 by ISLe » 12年前

onTouchEventの内容をonTouchに流用できるという話は、『ActivityでOnTouchListenerを実装して』という前提があってのことですが。
とうぜんCanvasViewにリスナーをセットしていますよね。

CanvasViewを変更する必要はありません。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#9

投稿記事 by ISLe » 12年前

最初の投稿のMainActivityのコードに対して…

以下の変更。

コード:

// 1行目
public class MainActivity extends Activity {
// ↓
public class MainActivity extends Activity implements OnTouchListener {

// 16行目
    public boolean onTouchEvent(MotionEvent event) {
// ↓
    public boolean onTouch(View v, MotionEvent event) {

// 51行目
       return true;
// ↓
       return false;
9行目と10行目のあいだに以下を挿入。

コード:

        findViewById(R.id.canvasView1).setOnTouchListener(this);
こちらで確認した限りでは、これでお絵かきとストップウォッチの両方が機能しているように思えますが。

The_sato

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#10

投稿記事 by The_sato » 12年前

親切に具体的な記述例まで教えていただきありがとうございます!

現在、言われた通りの部分を変更して試していますが、絵を描く機能しか動いてくれません・・・。
以下が現在のMainActivityのコードです。

コード:

public class MainActivity extends Activity implements OnTouchListener{//変更点①
    ArrayAdapter<String> adapter;
    CanvasView View;
    
   /** Called when the activity is first created. */
   @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        }
   
   ArrayList<String> laptime = new ArrayList<String>();
 
 //指の動きに応じてストップウォッチ起動&ラップ保存
   @Override
    public boolean onTouch(View v, MotionEvent event) {//変更点②
       View =new CanvasView(this);
       setContentView(View);
       
       findViewById(R.id.canvasView1).setOnTouchListener(this);//変更点③
       
       adapter = new ArrayAdapter<String>(
                this, android.R.layout.simple_list_item_1,laptime);
                ListView list = (ListView)findViewById(R.id.list);
                list.setAdapter(adapter);
 
       countText = (TextView)findViewById(R.id.Counter);
 
     switch (event.getAction()) {
       case MotionEvent.ACTION_DOWN :
           //ifでn==0なら"開始"を表示し、そうでなら手を離していた時間(×)を表示
           if(n==0){
               laptime.add("開始");
               loopEngine.start();
                startDate =System.currentTimeMillis();
               n++;
               }
           else{
               laptime.add("×"+" : "+String.valueOf(System.currentTimeMillis()-startDate));
               loopEngine.start();
               startDate =System.currentTimeMillis();
               n++;
               }
         break;
 
       case MotionEvent.ACTION_UP:
           //n個目の○を筆記してる時間
        laptime.add(n+"個目"+" : "+String.valueOf(System.currentTimeMillis()-startDate));
        loopEngine.start();
        startDate =System.currentTimeMillis();
         break;
       }
       return false;//変更点④
    }
   
    // カウントを表示するテキストボックス
     TextView countText; 
    
    public long startDate;
    public long n=0;
    public void update(){
        //(現在時刻)-(スタート時刻)を表示
        countText.setText(String.valueOf(System.currentTimeMillis()-startDate));
    }
    
    //一定時間後にupdateを呼ぶためのオブジェクト
    class LoopEngine extends Handler {
        private boolean isUpdate;
        public void start(){
                this.isUpdate = true;
                handleMessage(new Message());
        }
        public void stop(){
                this.isUpdate = false;
        }
        @Override
        public void handleMessage(Message msg) {
                this.removeMessages(0);//既存のメッセージは削除
                if(this.isUpdate){
                MainActivity.this.update();//自信が発したメッセージを取得してupdateを実行
                        sendMessageDelayed(obtainMessage(0), 10);//10ミリ秒後にメッセージを出力
                }
        } 
    };
    protected LoopEngine loopEngine = new LoopEngine();
 
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
}

しかしそちら側では正常に動作しているということは、自分がどこかで単純なミスを犯している可能性があるので、もうしばらく試行錯誤してみます。


無知な自分に丁寧に教えてくださり、本当にありがとうございます!

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#11

投稿記事 by ISLe » 12年前

変更点③の位置が違うと思います。

変更点③のコードが実行されることによってonTouchメソッドが呼ばれるようになるので、onTouchメソッドの中にあっても永遠に呼ばれることはありません。

17,18行のコードはなぜ復活しているのでしょう。
というか、どうしてこんなコードがあるのでしょう。
これだと直後のfindViewById(R.id.list)やfindViewById(R.id.Counter)が必ず失敗するのでは。

The_sato

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#12

投稿記事 by The_sato » 12年前

ありがとうございます!
修正してみました。こういうことでしょうか?

コード:

public class MainActivity extends Activity implements OnTouchListener{
    ArrayAdapter<String> adapter;
    
   /** Called when the activity is first created. */
   @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.canvasView1).setOnTouchListener(this);
        }

   ArrayList<String> laptime = new ArrayList<String>();

 //指の動きに応じてストップウォッチ起動&ラップ保存
   @Override
    public boolean onTouch(View v, MotionEvent event) {
	   
       
       adapter = new ArrayAdapter<String>(
                this, android.R.layout.simple_list_item_1,laptime);
                ListView list = (ListView)findViewById(R.id.list);
                list.setAdapter(adapter);
 
                countText = (TextView)findViewById(R.id.Counter);
 
     switch (event.getAction()) {
       case MotionEvent.ACTION_DOWN :
           //ifでn==0なら"開始"を表示し、そうでなら手を離していた時間(×)を表示
           if(n==0){
               laptime.add("開始");
               loopEngine.start();
                startDate =System.currentTimeMillis();
               n++;
               }
           else{
               laptime.add("×"+" : "+String.valueOf(System.currentTimeMillis()-startDate));
               loopEngine.start();
               startDate =System.currentTimeMillis();
               n++;
               }
         break;
 
       case MotionEvent.ACTION_UP:
           //n個目の○を筆記してる時間
        laptime.add(n+"個目"+" : "+String.valueOf(System.currentTimeMillis()-startDate));
        loopEngine.start();
        startDate =System.currentTimeMillis();
         break;
       }
       return false;
    }
   
    // カウントを表示するテキストボックス
     TextView countText; 

    public long startDate;
    public long n=0;
    public void update(){
        //(現在時刻)-(スタート時刻)を表示
        countText.setText(String.valueOf(System.currentTimeMillis()-startDate));
    }
    
    //一定時間後にupdateを呼ぶためのオブジェクト
    class LoopEngine extends Handler {
        private boolean isUpdate;
        public void start(){
                this.isUpdate = true;
                handleMessage(new Message());
        }
        public void stop(){
                this.isUpdate = false;
        }
        @Override
        public void handleMessage(Message msg) {
                this.removeMessages(0);//既存のメッセージは削除
                if(this.isUpdate){
                MainActivity.this.update();//自信が発したメッセージを取得してupdateを実行
                        sendMessageDelayed(obtainMessage(0), 10);//10ミリ秒後にメッセージを出力
                }
        } 
    };
    protected LoopEngine loopEngine = new LoopEngine();
 
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
}

載せていただいた変更点一覧の9行目と10行目に挿入って意味だと思っていました・・・。
しかしこれでもまだ絵を描く機能しか動作しません・・・
ISLe さんが書きました:とうぜんCanvasViewにリスナーをセットしていますよね。

CanvasViewを変更する必要はありません。
これはMainActivity同様に
 implements OnTouchListener や
 findViewById(R.id.canvasView1).setOnTouchListener(this) を書き加えて、それ以外は変更しなくても良いということでしょうか?
現在のCanvasViewは最初に載せたコードからまったく手を加えていません。

implements などのはじめて見たコードの意味も理解せずにあてずっぽでコードを書いているもので
レベルの低い質問になってしまい申し訳ありません・・・。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#13

投稿記事 by ISLe » 12年前

こちらで、最初の投稿のCanvasViewのコードと、最新のMainActivityのコードをコピペして動かしてみました。

青い線を引いていくと、時間がリスト表示されていきます。

これ以上は分かりません。

ちなみに以下が実験に使用したレイアウトです。

コード:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/Counter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />

    <com.example.hoge.CanvasView
        android:id="@+id/canvasView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <ListView
        android:id="@+id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

The_sato

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#14

投稿記事 by The_sato » 12年前

そうですか・・・
ということは一応このコードなら動くはずなんですね。
別のところになにか問題があるのかな・・・。

別の箇所でおかしな部分を探してみることにします。
丁寧に教えていただき本当にありがとうございました。

The_sato

Re: Android ストップウォッチ機能とお絵かきを同時に行うアプリ

#15

投稿記事 by The_sato » 12年前

ありがとうございます!!!!!
新規にプロジェクトを作成してコードをコピペして実行してみたところ、うまく動作しました!!!

お世話になりました!
本当にありがとうございました!!!

閉鎖

“C言語何でも質問掲示板” へ戻る