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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Android

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

Android Studio

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

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

2回答

4548閲覧

CalenderViewの日付をタップし、RecyclerViewにその日付と同じデータがある場合その位置までスクロールしたい

kbayashi

総合スコア18

Android

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

Android Studio

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

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2020/04/02 10:44

編集2020/04/03 00:39

実現したいこと

今現在todoアプリを作っています。CalenderViewの日付をタップし、RecyclerViewが保持しているデータを取得し、選択した日付とデータが保持している日付と照らし合わせて、一致しているならそのセルまでスクロールしたいのですがうまくいきません。

###問題
CalenderViewでRecyclerViewに表示されていないデータがある日付をタップするとMainActivityのcalendarView.setOnDateChangeListener内の
var item = task_list.findViewHolderForAdapterPosition(counter) as TaskAdapter.ViewHolderでNullPointが発生します。これが、現在作成中のtodoアプリです。今現在見えていませんが、4月1日にもデータがあります。この状態でカレンダーの4月1日をタップするとnullpointExceptionが発生し強制終了します。2日の場合は表示されているのでスクロールされました。
イメージ説明

###ソースコード

MainActivity

1 2class MainActivity : AppCompatActivity() { 3 private lateinit var realm: Realm 4 private var date: String? = null 5 private var adapter: TaskAdapter? = null 6 7 override fun onCreate(savedInstanceState: Bundle?) { 8 super.onCreate(savedInstanceState) 9 setContentView(R.layout.activity_main) 10 setSupportActionBar(toolbar) 11 //インスタンスを取得 12 realm = Realm.getDefaultInstance() 13 task_list.layoutManager = LinearLayoutManager(this) 14 var task = realm.where<Task>().findAll() 15 task = task.sort("date") 16 adapter = TaskAdapter(task,realm) 17 task_list.adapter = adapter 18 19 //タスク 追加ボタンタップ 20 addTask.setOnClickListener { view -> 21 //日付を編集画面に渡す 22 val nowDate = Date() 23 var format = SimpleDateFormat("HH:mm") 24 val nowTime = format.format(nowDate) 25 //選択されず初期の状態で追加ボタンを押されたら現在日時を渡す 26 if(date == null){ 27 format = SimpleDateFormat("yyyy/MM/dd") 28 date = format.format(nowDate) 29 } 30 val intent = Intent(this, newTaskAddActivity::class.java) 31 date += " ${nowTime}" 32 intent.putExtra("DATE",date) 33 startActivity(intent) 34 } 35 //カレンダーの日付が選択された時 36 calendarView.setOnDateChangeListener { 37 calendarView, year, month, day -> 38 39 date = "${year}/${month+1}/${day}" 40 var test = "${year}" 41 if(month < 10){ 42 test += "/0${month+1}" 43 }else{ 44 test += "/${month+1}" 45 } 46 if(day < 10){ 47 test += "/0${day}" 48 }else{ 49 test += "/${day}" 50 } 51 Log.e("counter", "${task.count()}") 52 var counter = 0 53 while (task.count()-1 > counter) { 54 55 var item = task_list.findViewHolderForAdapterPosition(counter) as TaskAdapter.ViewHolder 56 Log.e("aaa", item.date.text.toString()) 57 if(item.date.text.toString() == test){ 58 (task_list.getLayoutManager() as LinearLayoutManager). 59 scrollToPositionWithOffset(counter, 0) 60 return@setOnDateChangeListener 61 } 62 counter++ 63 64 } 65 66 } 67 68 //adapterのlistenerに関数をセット 69 adapter?.setOnclickListener {id:Long? -> 70 val intent = Intent(this, TaskAddActivity::class.java) 71 .putExtra("task_id",id) 72 startActivity(intent) 73 } 74 75 //switchが切り替わった時の処理をリスナーに渡す 76 adapter?.setSwitchListener{id:Long?,boolean:Boolean-> 77 realm.executeTransaction {db: Realm-> 78 var get_task : Task? 79 get_task = db.where<Task>().equalTo("id", id).findFirst() 80 get_task?.flg = boolean 81 } 82 } 83 84 //リストの削除ボタン 85 adapter?.setDelListener {id -> 86 realm.executeTransaction { db: Realm -> 87 db.where<Task>().equalTo("id", id) 88 .findFirst() 89 ?.deleteFromRealm() 90 db.where<subTask>().equalTo("mainId", id) 91 .findAll() 92 ?.deleteAllFromRealm() 93 } 94 adapter?.notifyDataSetChanged() 95 } 96 //Recyclerがスクロールされたら 97 /*task_list.addOnScrollListener( 98 99 )*/ 100 } 101 102 override fun onCreateOptionsMenu(menu: Menu): Boolean { 103 // Inflate the menu; this adds items to the action bar if it is present. 104 menuInflater.inflate(R.menu.menu_main, menu) 105 return true 106 } 107 108 override fun onOptionsItemSelected(item: MenuItem): Boolean { 109 // Handle action bar item clicks here. The action bar will 110 // automatically handle clicks on the Home/Up button, so long 111 // as you specify a parent activity in AndroidManifest.xml. 112 return when (item.itemId) { 113 R.id.action_settings -> true 114 else -> super.onOptionsItemSelected(item) 115 } 116 } 117 118 override fun onDestroy() { 119 super.onDestroy() 120 realm.close() 121 } 122 123 //画面が一番上になればすべてのセルに更新をかける 124 override fun onRestart() { 125 super.onRestart() 126 //データ更新 127 adapter?.notifyDataSetChanged() 128 } 129 130 131} 132

adapter

1class TaskAdapter(data: OrderedRealmCollection<Task>, val realm: Realm): 2RealmRecyclerViewAdapter<Task, TaskAdapter.ViewHolder>(data, true){ 3 4 private var listener:((Long?)-> Unit)? = null 5 private var delListener:((Long?)-> Unit)? = null 6 private var switchListener:((Long?,Boolean)-> Unit)? = null 7 8 fun setOnclickListener(listener: (Long?)-> Unit){ 9 this.listener = listener 10 } 11 12 fun setSwitchListener(listener:(Long?,Boolean)->Unit){ 13 this.switchListener = listener 14 } 15 16 fun setDelListener(listener: (Long?)-> Unit){ 17 this.delListener = listener 18 } 19 20 init { 21 setHasStableIds(true) 22 } 23 24 //セルに使用するビューを決める 25 class ViewHolder(cell: View) : RecyclerView.ViewHolder(cell){ 26 val date: TextView = cell.findViewById(R.id.monthText) 27 val title: TextView = cell.findViewById(R.id.textTask) 28 val time: TextView = cell.findViewById(R.id.timeText) 29 val switch: Switch = cell.findViewById(R.id.switch1) 30 val deleteImage: ImageView = cell.findViewById(R.id.taskDeleteImage) 31 } 32 33 //セルが必要になるたびに呼び出される 34 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskAdapter.ViewHolder { 35 val inflater = LayoutInflater.from(parent.context) 36 //セルにビューを適用 37 val view = inflater.inflate(R.layout.task_list, parent, false) 38 39 return ViewHolder(view) 40 41 } 42 43 //指定された位置にデータを表示する必要がある時、呼び出される 44 override fun onBindViewHolder(holder: TaskAdapter.ViewHolder, position: Int) { 45 46 //データを取得 47 val task: Task? = getItem(position) 48 //Viewに値をセット 49 holder.date.text = DateFormat.format("yyyy/MM/dd", task?.date) 50 //holder.date.setVisibility(View.VISIBLE) 51 holder.time.text = DateFormat.format("HH:mm", task?.date) 52 holder.switch.setOnCheckedChangeListener(null) 53 holder.title.text = task?.title 54 55 //タスクに色を設定 56 if(task?.backColor != null) 57 holder.title.setBackgroundColor(backPaint(task?.backColor)) 58 59 if(task?.flg != null) { 60 holder.switch.isChecked = task?.flg 61 holder.title.text = fontDraw(task?.flg, holder.title) 62 if(task?.flg == true){ 63 holder.deleteImage.visibility = View.VISIBLE 64 }else{ 65 holder.deleteImage.visibility = View.GONE 66 } 67 } 68 69 //タスクがタップされた時 70 holder.title.setOnClickListener { 71 //メインでセットした関数を実行 72 listener?.invoke(task?.id) 73 } 74 //削除 75 holder.deleteImage.setOnClickListener { 76 delListener?.invoke(task?.id) 77 } 78 79 80 //前の項目があるなら 81 val prevTask: Task? = if (position > 0) getItem(position - 1) else null 82 val df = SimpleDateFormat("yyyy/MM/dd") 83 //日付が一致するなら日付を消してくっつける 84 if(prevTask != null && df.format(prevTask.date) == df.format(task?.date)){ 85 holder.date.setVisibility(View.GONE); 86 }else { 87 holder.date.setVisibility(View.VISIBLE); 88 } 89 90 //swichビューが切り替わった時 91 holder.switch.setOnCheckedChangeListener { compoundButton, b -> 92 switchListener?.invoke(task?.id, holder.switch.isChecked) 93 94 } 95 } 96 97 override fun getItemId(position: Int): Long { 98 return getItem(position)?.id ?: 0 99 } 100 101 //タスクの背景に色を付与 102 private fun backPaint(color:Int): Int{ 103 //タスクの初期化のと時か 104 if(color == 0){ 105 return Color.parseColor("#00BFFF") 106 }else{ 107 return color 108 } 109 } 110 111 //タスクが完了済みなら線を引く、そうでなければ線を消す 112 private fun fontDraw(flg:Boolean, font: TextView): String{ 113 114 val paint = font.paint 115 if(flg) { 116 // なぜか再代入しなければ、取り消し線が引かれない 117 font.text = font.text 118 // 取り消し線を引く 119 paint.flags = font.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG 120 // アンチエイリアスをオンにする 121 paint.isAntiAlias = true 122 }else{ 123 font.text = font.text 124 paint.flags = font.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() 125 } 126 127 return font.text.toString() 128 } 129 130}

Task

1open class Task : RealmObject(){ 2 @PrimaryKey 3 var id: Long = 0 4 var date: Date = Date() 5 var title: String = "" 6 var detail: String = "" 7 var flg: Boolean = false 8 var backColor: Int = 0 9}

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2020/04/02 11:36

具体的に, どのようなコードを書き, どのように実現しようとしてどんなことが起きたのでしょう. 調査・テストした結果等もありましたらご提示ください.
jimbe

2020/04/02 12:44

追加編集ありがとうございます. 「nullpointExceptionが発生」したときのスタックトレースではどのファイルのどの行が発生元とされているでしょうか.
guest

回答2

0

ベストアンサー

なんとなく findViewHolderForAdapterPosition が間違っている気がします.
ドキュメントで要領を得なかったのですが, findViewHolder という名前からして, 表示中の行でなければ null が返ってくるのではないでしょうか.

処理内容から考えますと, TaskAdapter にメソッドを追加して指定日のタスクの先頭位置を検索させ, その位置が表示されるようにスクロール指定すれば良いのではないでしょうか.

投稿2020/04/02 13:05

jimbe

総合スコア12545

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kbayashi

2020/04/03 00:02

回答ありがとうございます。 デバックして調べましたが、おっしゃるとおりで表示されていない場合はnullが帰ってきていました。 アドバイスどおりにadapterにメソッドを作成してそこからスクロールを指定できるようにしようと思います。
guest

0

アクティビティとアダプターに下記のコードを追加しました。現状正常に動作しています。

MainActivity

1 2 //カレンダーの日付が選択された時 3 calendarView.setOnDateChangeListener { 4 calendarView, year, month, day -> 5 6 date = "${year}/${month+1}/${day}" 7 8 //スクロールさせる 9 var dateString = toDateString(year, (month+1), day) 10 val target = adapter?.searchDateItem(dateString) 11 Log.e("test", "$target") 12 if(target != -1 && target != null) { 13 (task_list.getLayoutManager() as LinearLayoutManager).scrollToPositionWithOffset( 14 target, 0) 15 } 16 17 } 18 19 //カレンダーから受け取った数値を文字型の日付に変換 20 private fun toDateString(year:Int, month:Int, day: Int):String{ 21 var toString = "$year/" 22 23 if(month < 10){ 24 toString += "0$month/" 25 }else{ 26 toString += "$month/" 27 } 28 if(day < 10){ 29 toString +="0$day" 30 }else{ 31 toString += "$day" 32 } 33 34 return toString 35 }

adapter

1 //カレンダーに選択された日付と同じ日付があればそのデータが表示されるまでにスクロールさせる 2 fun searchDateItem(searchDate:String): Int{ 3 var counter = 0 4 Log.e("count", "${itemCount}") 5 while (itemCount-1 >= counter){ 6 var targetTask = getItem(counter) 7 if(DateFormat.format("yyyy/MM/dd", targetTask?.date) == searchDate){ 8 return counter 9 } 10 counter++ 11 } 12 return -1 13 }

投稿2020/04/03 01:36

kbayashi

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問