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

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

ただいまの
回答率

90.83%

  • Android

    6113questions

    Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

  • Android Studio

    3382questions

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

RecyclerViewの非表示箇所をクリック時にNullPointエラー

解決済

回答 1

投稿

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

FSNY

score 8

RecyclerViewを使って1行で横方向にスクロールする画像のリストを作成しています。
そしてその各リストアイテムごとにOnClickのリスナーを付けて、クリックされた画像に選択されたことを示す枠を画像の背景として付けています。

リストのアイテム数が画面に表示できる数に収まっている場合は正常にクリックイベントを取得できて、
・既存の選択画像から枠を外す
・新しい選択画像に枠を付ける
という処理ができるのですが、
RecyclerViewには表示できる数しかアイテムが登録されておらず、表示できる数を超えていてリストをスクロール後にクリックすると存在しないViewを参照しようとしてNullPointエラーになってしまいます。
何か最初に一気にRecyclerViewにアイテムを登録する方法などあるのでしょうか?

下記
・RecyclerViewのAdapter
・RecyclerViewを利用するActivity
・RecyclerViewに登録するアイテム
それぞれのソースになります。

■RecyclerViewのAdapter

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

    private LayoutInflater inflater;
    private ArrayList<String> photoPathList;
    private RecyclerViewClickListener recyclerViewClickListener;

    public interface RecyclerViewClickListener{
        void onRecyclerViewClicked(View v, int position);
    }

    public RecyclerViewAdapter(Context context, ArrayList<String> pathList, RecyclerViewClickListener listener) {
        inflater = LayoutInflater.from(context);
        photoPathList = pathList;
        recyclerViewClickListener = listener;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewHolder(inflater.inflate(R.layout.thumbnail_list_item, parent, false));
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
        viewHolder.thumbnailImage.setImageResource(R.drawable.make_movie_no_choice);
        viewHolder.thumbnailNo.setText(Integer.toString(position + 1));

        if(position == 0){
            viewHolder.thumbnailImage.setBackgroundResource(R.drawable.thumbnail_border);
        }

        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                recyclerViewClickListener.onRecyclerViewClicked(view, position);
            }
        });
    }

    @Override
    public int getItemCount() {
        if (photoPathList == null) {
            return 0;
        }
        else {
            return photoPathList.size();
        }
    }

    // ViewHolder
    public class ViewHolder extends RecyclerView.ViewHolder {
        FrameLayout thumbnailLayout;
        ImageView thumbnailImage;
        TextView thumbnailNo;

        public ViewHolder(View itemView) {
            super(itemView);
            thumbnailLayout = (FrameLayout)itemView.findViewById(R.id.list_item_layout);
            thumbnailImage = (ImageView)itemView.findViewById(R.id.list_item_thumbnail);
            thumbnailNo = (TextView)itemView.findViewById(R.id.list_item_number);
        }
    }
}


■RecyclerViewを利用するActivity

private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private LinearLayoutManager layoutManager;
private ArrayList<String> filePathList;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.scene_confirm_photo_activity);

    filePathList = intent.getStringArrayListExtra("addPhotoFilePathList");

    recyclerView = findViewById(R.id.scene_confirm_photo_thumbnail_list);
    recyclerView.setHasFixedSize(true);
    layoutManager = new LinearLayoutManager(this);
    layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
    recyclerView.setLayoutManager(layoutManager);
    adapter = new RecyclerViewAdapter(this, filePathList, this);
    recyclerView.setAdapter(adapter);
}

@Override
public void onRecyclerViewClicked(View v, int position) {
    FrameLayout thumbnailLayout;
    ImageView thumbnailImage;

    for(int i = 0; i < filePathList.size(); i++){
        thumbnailLayout = (FrameLayout)recyclerView.getChildAt(i);
        thumbnailImage = (ImageView)thumbnailLayout.getChildAt(0);

        if(i == position){
            thumbnailImage.setBackgroundResource(R.drawable.thumbnail_border);
        }
        else{
            thumbnailImage.setBackgroundResource(0);
            thumbnailImage.setPadding(0, 0, 0, 0);
        }
    }
}


■RecyclerViewに登録するアイテム

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/list_item_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="horizontal"
    android:layout_margin="5dp">

    <ImageView
        android:id="@+id/list_item_thumbnail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"/>

    <TextView
        android:id="@+id/list_item_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:text="1"
        android:paddingTop="3dp"
        android:paddingBottom="3dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:background="@color/colorBlack"
        android:textColor="@color/colorWhite" />

</FrameLayout>


■エラーメッセージ
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.widget.FrameLayout.getChildAt(int)' on a null object reference

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

RecyclerViewではメモリ節約やパフォーマンス向上のために、範囲外に出た子Viewは画面から取り除かれ、次に新しい子Viewが必要になった時にリサイクルされ使いまわされます。(そもそもそれがRecyclerViewという名称の由来です。)

なので、getChildAtで子Viewを取得して何らかの操作をするというのはRecyclerViewでは使えません。アイテムクリック時に子Viewを自分でいじるのではなく、子Viewの状態を表すデータをAdapterに渡して、それをonBindViewHolder内で子Viewに反映するように考え方を切り替えてみてください。

ソースコードを拝見した感じでは、一つのアイテムを選択できるようにするのが目的のようなので、以下のような実装でいかがでしょうか。

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

    private LayoutInflater inflater;
    private ArrayList<String> photoPathList;
    private RecyclerViewClickListener recyclerViewClickListener;

    // 選択されたアイテムの位置
    private int selectedPosition = 0;

    // 指定した位置のアイテムを選択状態にする
    public void selectItemAt(int position) {
        this.selectedPosition = position;
        // 子Viewの状態が変わったことを通知し更新を促す
        notifyDataSetChanged();
    }

    ...

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
        viewHolder.thumbnailImage.setImageResource(R.drawable.make_movie_no_choice);
        viewHolder.thumbnailNo.setText(Integer.toString(position + 1));

        // 選択されたアイテムなら背景を表示
        if (position == selectedPosition){
            viewHolder.thumbnailImage.setBackgroundResource(R.drawable.thumbnail_border);
        } else{
            viewHolder.thumbnailImage.setBackgroundResource(0);
            viewHolder.thumbnailImage.setPadding(0, 0, 0, 0);
        }

        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                recyclerViewClickListener.onRecyclerViewClicked(view, position);
            }
        });
    }
}

// in Activity
@Override
public void onRecyclerViewClicked(View v, int position) {
    adapter.selectItemAt(position);
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/05/14 10:20

    ご教示いただいた修正案で思う形の動作を行うことができました!
    大変助かりました、ありがとうございます。

    キャンセル

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

  • ただいまの回答率 90.83%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

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

  • Android

    6113questions

    Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

  • Android Studio

    3382questions

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