質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.36%

  • Java

    10446questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Android Studio

    2708questions

    Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

変数が【NullPointerException】。その対処方法に関して

解決済

回答 3

投稿 2017/12/06 09:54

  • 評価
  • クリップ 0
  • VIEW 167

blueHornet

score 6

現在、とある機器からデータをMain Activityの配列で取得して
それをNextActivityにIntentで流して、
それとはさらにChartActivityにてCanvasとPaintを使って
グラフを描こうとしています。

ChartActivityは横向き(landscape)表示でx,y,z値の3本を表示させます。

ここで、TextViewを使って都度可変するグラフの目盛の表示を変えて
併せて表示したいのですが、普通にCanvasとPaintにTextViewを乗せると
エラーで終了してしまいます。(CanvasとPaintでのグラフのみの描画は成功しています)

そこでこちらの情報をもとに
TestViewクラスを作成しレイアウトにタグを追加してみたのですが
MainActivityやIntentでNextActivityに流したデータが
【NullPointerException】で取得が出来ずNextActivityに切り替えると強制終了してしまいます。

やってみたこと:

MainActivityをNewして変数を取りに行ってみましたが
宣言している段階で代入されていれば、それはNullPointerExceptionになりません(当然ですが)
グローバル変数にして、やってみましたが同じ状態です。

どのように対応すれば、CanvasとPaintにTextViewを乗せることができるでしょうか。
よろしくお願いします。

MainActivity

public class MainActivity extends AppCompatActivity {

    int data1[] ;   //これだとNullPointerException
    int data2[] ={55,60,100};   //エラーにならない
    common Common;  //グローバル変数
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void onNext(View view){
        Intent intent = new Intent(this,NextActivity.class);

        data1 = new int [3];
        data1[0]=55;
        data1[1]=60;
        data1[2]=100;
        intent.putExtra("data",data1);
        startActivity(intent);
    }
}
NextActivity

public class NextActivity extends AppCompatActivity {

    Intent intent =getIntent();
    private TextView label;
    private TestView testView;
    public int data[] = intent.getIntArrayExtra("data");

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

        label = (TextView) this.findViewById(R.id.label);
        label.setText("abcd123456789cdefg\n" + "1234567893456978\n" +
                "hijklmnopq012698745rst4565848uvwxlmnopq\n" +
                "\n\n\n012698745rst4565848\nuvwx" );

        testView = (TestView) this.findViewById(R.id.test_view);

    }
}
TestView.java

public class TestView extends View {
    Paint paint;
    MainActivity ma = new MainActivity();

    public TestView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
    }

    @Override
    protected void onDraw(Canvas canvas) {

        // 背景、半透明
        canvas.drawColor(Color.argb(125, 0, 0, 255));

        // 円
        paint.setColor(Color.argb(255, 68, 125, 255));
        paint.setStrokeWidth(30);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        // (x1,y1,r,paint) 中心x1座標, 中心y1座標, r半径
        canvas.drawCircle(450, 450, 100, paint);

        // 矩形
        paint.setColor(Color.argb(255, 255, 0, 255));
        paint.setStrokeWidth(10);
        paint.setStyle(Paint.Style.STROKE);
        // (x1,y1,x2,y2,paint) 左上の座標(x1,y1), 右下の座標(x2,y2)
        canvas.drawRect(480, 480, 850, 880, paint);

        // 線
        paint.setStrokeWidth(15);
        paint.setColor(Color.argb(255, 0, 255, 120));
        // (x1,y1,x2,y2,paint) 始点の座標(x1,y1), 終点の座標(x2,y2)
        canvas.drawLine(350, 850, 750, 630, paint);

        // 線
        paint.setStrokeWidth(15);
        paint.setColor(Color.argb(255, 0, 255, 120));
        // (x1,y1,x2,y2,paint) 始点の座標(x1,y1), 終点の座標(x2,y2)
        canvas.drawLine(ma.data1[0], ma.data1[1], ma.data1[2], 630, paint); //ここがエラー

    }
}

エラーメッセージ(activity_next.xml):
java.lang.NullPointerException
at com.example.zd2565.testchart20171204_2.TestView.onDraw_Original(TestView.java:53)
at com.example.zd2565.testchart20171204_2.TestView.onDraw(TestView.java)
at android.view.View.draw(View.java:17071)
at android.view.View.draw(View.java:16979)
at android.view.ViewGroup.drawChild_Original(ViewGroup.java:3764)
at android.view.ViewGroup_Delegate.drawChild(ViewGroup_Delegate.java:61)
at android.view.ViewGroup.drawChild(ViewGroup.java:3764)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3550)
at android.view.View.draw(View.java:16977)
at android.view.ViewGroup.drawChild_Original(ViewGroup.java:3764)
at android.view.ViewGroup_Delegate.drawChild(ViewGroup_Delegate.java:61)
at android.view.ViewGroup.drawChild(ViewGroup.java:3764)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3550)
at android.view.View.draw(View.java:16977)
at android.view.ViewGroup.drawChild_Original(ViewGroup.java:3764)
at android.view.ViewGroup_Delegate.drawChild(ViewGroup_Delegate.java:61)
at android.view.ViewGroup.drawChild(ViewGroup.java:3764)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3550)
at android.view.View.draw(View.java:17074)
at com.android.layoutlib.bridge.impl.RenderSessionImpl.renderAndBuildResult(RenderSessionImpl.java:401)
at com.android.layoutlib.bridge.impl.RenderSessionImpl.renderAndBuildResult(RenderSessionImpl.java:539)
at com.android.layoutlib.bridge.impl.RenderSessionImpl.render(RenderSessionImpl.java:422)
at com.android.layoutlib.bridge.BridgeRenderSession.render(BridgeRenderSession.java:104)
at com.android.ide.common.rendering.api.RenderSession.render(RenderSession.java:154)
at com.android.ide.common.rendering.api.RenderSession.render(RenderSession.java:136)
at com.android.tools.idea.rendering.RenderTask.lambda$renderInner$3(RenderTask.java:752)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

いろいろ不明点は多いですが、現段階で言えることを書きます。

Activityは自分でnewしてはいけない

ActivityはAndroidのライフサイクルの中で初期化などの処理を行うため、自分でnewしてインスタンス生成するとおかしな挙動になります。

Activity#getIntentはメソッド内で

上記の理由で、遷移先のActivityでIntentが有効になるのは、Activityの初期化処理が終わった後です。
メンバ変数の宣言の段階ではまだ初期化が完了していないため、getIntentしてもnullが返ってきます。

投稿 2017/12/06 10:34

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/06 11:28

    分かりにくい質問を確認して、ご回答いただきありがとうございます。
    2項目のご回答大変参考になりました。

    キャンセル

+1

 まずは結論

設計を変えた方が良いと思います。
TestViewの中にupdateViewのようなメソッドを定義し、その引数にdataを入れて渡してあげます。
ActivityやIntentの扱いに関しては他のお二方のおっしゃる通りですので、そちらを参考になさってください。

 問題点

問題はTestViewの中の以下の部分です。

canvas.drawLine(ma.data1[0], ma.data1[1], ma.data1[2], 630, paint); //ここがエラー


基本的に、ViewはActivityがなんであるかを知りません。これはインターフェースを考える上で重要ですね。例えばですが、他のActivityからTestViewをもしも使いたくなった時に、TestViewがMainActivityに依存していると実装をやり直さなければなりません。
そこで、そもそもとしてViewの中に特定のActivityを持たせないのが基本方針になります。
参考url: 疎結合って何?

ソフトウェアの拡張性とは「いかに変更に容易に適応できるかどうか」である。
依存関係が多いと、あるプログラムの変更が他のプログラムに影響を及ぼし、変更を行うごとに大量のテストが必要になる。

 具体的な解決策

ではどうすれば良いのでしょうか???
それは、View側にActivityからデータを渡してあげるのです。
ここが重要で、質問者様は「ViewからActivityのデータを取りに行く」コードを書いていますが、「ActivityからViewにデータを渡す」のです。

具体的には、TestViewの中にpublicで以下のようなメソッドを定義します。
TestView

  public void updateView(int[] data) {
      canvas.drawLine(data[0], data[1], data[2], 630, paint);
      invalidate();                 // これを呼ばないと描画内容が更新されない
  }


そして、これをNextActivityの側から呼び出します。(Intentの受け取りは他の回答の通りに修正し、受け取れているとします。)
参考url: AndroidのViewに絵を描く
NextActivity

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

        Intent intent = getIntent();
        data = intent.getIntArrayExtra("data");

        // TestViewのインスタンス取得
        testView = (TestView) this.findViewById(R.id.test_view);
        // ここでデータをTestViewに付与する。
        testView.updateView(data);
    }

おそらく以上の説明だけだと不明な点も多々あると思うので、その都度質問頂ければと思います。

投稿 2017/12/07 01:28

編集 2017/12/07 01:35

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/07 15:16

    hiramekunさん
    ご回答いただきましてありがとうございます。
    ご回答いただいた内容につきまして、私にて解釈してみたのですが
    【// TestViewのインスタンス取得
    testView = (TestView) this.findViewById(R.id.test_view);】
    この部分で都度操作したいTestView_Classのインスタンスを取得して、
    そのTestView_Classのメソッドに引数で変数を渡して、
    Class内の変数に渡す。という解釈であっていますでしょうか。
    すみません、つたない質問で大変恐れ入ります。

    キャンセル

  • 2017/12/07 22:33

    大丈夫です、その解釈で合っていますよ!

    キャンセル

  • 2017/12/11 09:11

    hiramekunさん
    ご回答いただきましてありがとうございます。
    ご教示いただきました内容にて無事に変数の受け渡しが成功しました。
    引数に直接渡していくので、直感的に扱えて大変便利ですね。
    貴重な情報ご提供いただきましてありがとうございました。

    キャンセル

checkベストアンサー

0

NextActivity.javaで、dataというフィールドをpublicで宣言していますが、こうしたやり方はあまり良くないとされます。下記のように変数はprivateで宣言し、それを取得するためのメソッドを用意するのがいいでしょう。

NextActivity.java

public class NextActivity extends AppCompatActivity {

    private TextView label;
    private TestView testView;
    private int[] data;

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

        // Intentによるデータ取得はここで行う
        Intent intent = getIntent();
        data = intent.getIntArrayExtra("data");

        // 中略
    }

    // dataを取得するためのgetter
    int[] getData() {
        return data;
    }

}

そのメソッドをTestViewから呼び出します。Viewを継承したクラスのコンストラクターの第1引数であるContext型の値は、このViewが描画されたActivity(この場合だとNextActivity)のインスタンスが示されます。これを利用して、NextActivityが持つメソッドを呼び出すことができます。

TestView.java

public class TestView extends View {

    Paint paint;
    private Context mContext;

    public TestView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        mContext = context; // contextを保持しておく
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // NextActivityのメソッドを呼び出す(ここが最重要ポイント)
        int[] data = ((NextActivity) mContext).getData();

        // 中略

        // 上記で受け取ったdataを使って描画
        canvas.drawLine(data[0], data[1], data[2], 630, paint);
    }
}

こんな感じで目的を達することができるんじゃないでしょうか?

ただ、質問に記されたTestView.javaでは、MainActivityのインスタンスを作ろうとしていますよね。これは間違った方法ではあるのですが、意図していたのはIntentなどを介して情報をバケツリレーするのではなく、TestViewのコードでMainActivityの情報をダイレクトに参照したかったということなのでしょうか?

投稿 2017/12/07 00:06

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/07 01:42 編集

    Viewに特定のActivityを持たせるのは疎結合の観点から見て拡張性がないと思います。さらにContextをキャストして特定のActivityにしているのはかなり危ない設計だと思いますが、これらの点はいかがでしょうか?

    キャンセル

  • 2017/12/07 15:02

    keicha_hrsさん
    前回に引き続き、ご回答いただきましてありがとうございます。
    大変参考になりました。
    Contextというのは、いろいろ調べている上で目にしていたのですが
    ぼんやりとして、理解しきれていませんでした。
    しかし、今回ご提供いただいたサンプルの動向がとても分かりやすく、
    スッと落ちてきました。
    大変感謝いたします!

    キャンセル

  • 2017/12/07 15:05

    hiramekunさん
    keicha_hrsさんにコメント頂いた点で、もしご不都合なければ
    無知な私に教えてください。
    【Contextをキャストして特定のActivityにしているのはかなり危ない設計】というのは
    例えば、具体的にどういったリスクが起こりうるのでしょうか。
    よろしくお願いいたします。

    キャンセル

  • 2017/12/07 22:52 編集

    Contextというものは、どのActivityからも取得できるものですね。

    そして、TestViewを使いたい!と思ってActivityから呼び出したとします。しかし、もちろん他のActivityからは使えません。最悪でも引数などに特定のActivityを受け取るのなら、使うときに「あれ、このActivityからしか使えないのかな」と気付けるかもしれませんが、いつでもViewが参照しているContextを内部でキャストしてることには気づきにくく、バグの原因になり得ます。(引数で渡すことも疎結合の観点からやめたほうがいいです)

    おそらく今まで実装してきて特定のActivityでしか使えないViewというものは見たことがないでしょうし、私も見たことがありません。

    キャンセル

  • 2017/12/08 00:50 編集

    これが一般的に行われる手法かと言われると正直そこまでの自信はありません。hiramekunさんのおっしゃる通り、私のやり方には問題がある可能性は高そうです。もう少し調べてはみますが、こうした作り方による資料をすぐに提示することはできそうもありません(見た記憶自体はあるのですが、それ自体が問題作であり、誤ったやり方を学んでしまったということなのかも)。blueHornetさんにおきましては、そのつもりでご覧いただけないかと思います。

    (最初もう少し文量あったのですが、あまりに遜り過ぎて逆に不快を招きかねないと思ったので、少し簡素にしました。もし先に読んでいたら申し訳ありません。)

    キャンセル

  • 2017/12/08 09:14

    hiramekunさん
    ご回答頂きありがとうございます。
    疎結合の観点(オブジェクト指向の考え方)にならってということがよくわかりました。
    コーディングをする上では、とても重要なところです。
    実際、私はまだそこまでクオリティの高いコーディングはできてませんが
    大変貴重なお話ありがとうございました。

    キャンセル

  • 2017/12/08 09:17

    keicha_hrsさん
    コメント頂きありがとうございます。
    今回keicha_hrsさんに教えて頂いた内容につきましても
    私にとっては大変貴重な情報だったと感謝しております。
    実際に試して、情報がきっちり受け渡されてきたときは
    とてもうれしかったです。
    1つの方法として、身につけておきたいと思います。
    ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

91.36%

関連した質問

同じタグがついた質問を見る

  • Java

    10446questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Android Studio

    2708questions

    Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。