前提・実現したいこと
ビューの動きを自分で制御する必要があるため、LinearLayoutManagerを継承したクラスを作成し、scrollVerticallyBy()
内でビューの作成・削除を制御しています。
画面を上から下へスクロールし、上にあるビューを再表示しようとすると、上部しか表示されない・タップイベントも表示領域分しか取れないといった現象が起きています。
下から上へスクロールし、下からビューを表示する場合には起きません。
何故描画が切れるのか?が聞きたいことになります。
発生している問題・エラーメッセージ
上からスクロールするとき、再生成されるビューの表示が途切れる
該当のソースコード
Java
1package com.example.myapplication; 2 3import android.content.Context; 4import android.support.v7.widget.LinearLayoutManager; 5import android.support.v7.widget.RecyclerView; 6import android.view.View; 7 8/** 9 * カスタムレイアウトマネージャ 10 */ 11public class CustomLinearLayoutManager extends LinearLayoutManager { 12 /** 13 * コンストラクタ 14 */ 15 public CustomLinearLayoutManager(Context context) { 16 super(context); 17 } 18 19 /** 20 * コンストラクタ 21 */ 22 public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 23 super(context, orientation, reverseLayout); 24 } 25 26 /** 27 * {@inheritDoc} 28 */ 29 @Override 30 public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { 31 final int count = getChildCount(); 32 if (count == 0) { 33 return 0; 34 } 35 36 // 表示されているビューのうち、最初のビューと最後のビュー 37 View firstView = getChildAt(0); 38 View lastView = getChildAt(getChildCount() - 1); 39 40 if (firstView == null || lastView == null) { 41 return 0; 42 } 43 44 int firstPosition = getPosition(firstView); 45 int lastPosition = getPosition(lastView); 46 47 // 最初の項目が下へ行かないようにする 48 if (firstPosition == 0) { 49 final int viewTop = firstView.getTop(); 50 if (viewTop - dy > getPaddingTop()) { 51 dy = viewTop - getPaddingTop(); 52 } 53 } 54 // 最後の項目が上へ行かないようにする 55 if (lastPosition == getItemCount() - 1) { 56 final int viewBottom = lastView.getTop() + lastView.getMeasuredHeight(); 57 if (viewBottom - dy < getHeight()) { 58 dy = viewBottom - getHeight(); 59 } 60 } 61 62 // ビューの座標を確定する 63 for (int i = 0; i < count; ++i) { 64 View view = getChildAt(i); 65 if (view == null) { 66 continue; 67 } 68 69 view.setTop(view.getTop() - dy); 70 } 71 72 if (dy > 0) { 73 // 上へスクロール(通常) 74 removeFirstView(recycler); 75 76 addLastView(recycler); 77 } else { 78 // 下へスクロール(逆) 79 removeLastView(recycler); 80 81 addFirstView(recycler); 82 } 83 84 return dy; 85 } 86 87 /** 88 * 最初のビューを削除する 89 * 90 * @param recycler Recycler 91 */ 92 private void removeFirstView(RecyclerView.Recycler recycler) { 93 final int count = getChildCount(); 94 if (count == 0) { 95 return; 96 } 97 98 final View firstView = getChildAt(0); 99 if (firstView == null) { 100 return; 101 } 102 103 if (firstView.getTop() + firstView.getMeasuredHeight() < 0) { 104 removeAndRecycleView(firstView, recycler); 105 } 106 } 107 108 /** 109 * 最後のビューを削除する 110 * 111 * @param recycler Recycler 112 */ 113 private void removeLastView(RecyclerView.Recycler recycler) { 114 final int count = getChildCount(); 115 if (count == 0) { 116 return; 117 } 118 119 final View lastView = getChildAt(count - 1); 120 if (lastView == null) { 121 return; 122 } 123 124 if (lastView.getTop() > getHeight()) { 125 removeAndRecycleView(lastView, recycler); 126 } 127 } 128 129 /** 130 * 最初にビューを追加する 131 * 132 * @param recycler Recycler 133 */ 134 private void addFirstView(RecyclerView.Recycler recycler) { 135 final int count = getChildCount(); 136 if (count == 0) { 137 return; 138 } 139 140 final View firstView = getChildAt(0); 141 if (firstView == null) { 142 return; 143 } 144 145 final int firstIndex = findFirstVisibleItemPosition(); 146 if (firstIndex > 0 && firstView.getTop() > 0) { 147 View view = recycler.getViewForPosition(firstIndex - 1); 148 addView(view, 0); 149 150 measureChildWithMargins(view, 0, 0); 151 152 final int left = getPaddingLeft(); 153 final int right = getWidth() - getPaddingRight(); 154 final int bottom = firstView.getTop(); 155 final int top = bottom - view.getMeasuredHeight(); 156 layoutDecorated(view, left, top, right, bottom); 157 158 view.setLayoutParams(view.getLayoutParams()); 159 } 160 } 161 162 /** 163 * 最後にビューを追加する 164 * 165 * @param recycler Recycler 166 */ 167 private void addLastView(RecyclerView.Recycler recycler) { 168 final int count = getChildCount(); 169 if (count == 0) { 170 return; 171 } 172 173 final View lastView = getChildAt(count - 1); 174 if (lastView == null) { 175 return; 176 } 177 178 final int lastIndex = findLastVisibleItemPosition(); 179 if (lastIndex < getItemCount() - 1 && lastView.getTop() + lastView.getMeasuredHeight() < getHeight()) { 180 View view = recycler.getViewForPosition(lastIndex + 1); 181 addView(view); 182 183 measureChildWithMargins(view, 0, 0); 184 185 final int left = getPaddingLeft(); 186 final int right = getWidth() - getPaddingRight(); 187 final int top = (lastView.getTop() + lastView.getMeasuredHeight()); 188 final int bottom = top + view.getMeasuredHeight(); 189 layoutDecorated(view, left, top, right, bottom); 190 } 191 } 192}
試したこと
別途setLayoutParamなどしてみたが意味なし
補足情報(FW/ツールのバージョンなど)
Android Studio 3.1.4
Android 8.0 (Xperia XZ, HUAWEI P10 Lite)
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2018/09/23 00:38