横向滚动的listview
今天在实现业务需求的时候,抛出了一个展示已选人员的头像的需求。本想使用RecyclerView,但奈何项目使用的support包版本太低,又是用eclipse开发,写好后无法使用,故抛弃改用listview。
查找资料后,准备使用HorizontalListView,国外已大神写好的,准备引用到项目,后发现项目里面已经存在了...故直接使用。
使用方法和使用listview一样,设置adapter即可。
1 public class HorizontalListView extends AdapterView<ListAdapter> { 2 3 public boolean mAlwaysOverrideTouch = true; 4 protected ListAdapter mAdapter; 5 private int mLeftViewIndex = -1; 6 private int mRightViewIndex = 0; 7 protected int mCurrentX; 8 protected int mNextX; 9 private int mMaxX = Integer.MAX_VALUE; 10 private int mDisplayOffset = 0; 11 protected Scroller mScroller; 12 private GestureDetector mGesture; 13 private Queue<View> mRemovedViewQueue = new LinkedList<View>(); 14 private OnItemSelectedListener mOnItemSelected; 15 private OnItemClickListener mOnItemClicked; 16 private OnItemLongClickListener mOnItemLongClicked; 17 private boolean mDataChanged = false; 18 19 public HorizontalListView(Context context, AttributeSet attrs) { 20 super(context, attrs); 21 initView(); 22 } 23 24 private synchronized void initView() { 25 mLeftViewIndex = -1; 26 mRightViewIndex = 0; 27 mDisplayOffset = 0; 28 mCurrentX = 0; 29 mNextX = 0; 30 mMaxX = Integer.MAX_VALUE; 31 mScroller = new Scroller(getContext()); 32 mGesture = new GestureDetector(getContext(), mOnGesture); 33 } 34 35 @Override 36 public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) { 37 mOnItemSelected = listener; 38 } 39 40 @Override 41 public void setOnItemClickListener(AdapterView.OnItemClickListener listener) { 42 mOnItemClicked = listener; 43 } 44 45 @Override 46 public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) { 47 mOnItemLongClicked = listener; 48 } 49 50 private DataSetObserver mDataObserver = new DataSetObserver() { 51 52 @Override 53 public void onChanged() { 54 synchronized (HorizontalListView.this) { 55 mDataChanged = true; 56 } 57 invalidate(); 58 requestLayout(); 59 } 60 61 @Override 62 public void onInvalidated() { 63 reset(); 64 invalidate(); 65 requestLayout(); 66 } 67 68 }; 69 70 @Override 71 public ListAdapter getAdapter() { 72 return mAdapter; 73 } 74 75 @Override 76 public View getSelectedView() { 77 // TODO: implement 78 return null; 79 } 80 81 @Override 82 public void setAdapter(ListAdapter adapter) { 83 if (mAdapter != null) { 84 mAdapter.unregisterDataSetObserver(mDataObserver); 85 } 86 mAdapter = adapter; 87 mAdapter.registerDataSetObserver(mDataObserver); 88 reset(); 89 } 90 91 private synchronized void reset() { 92 initView(); 93 removeAllViewsInLayout(); 94 requestLayout(); 95 } 96 97 @Override 98 public void setSelection(int position) { 99 // TODO: implement 100 } 101 102 private void addAndMeasureChild(final View child, int viewPos) { 103 LayoutParams params = child.getLayoutParams(); 104 if (params == null) { 105 params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); 106 } 107 108 addViewInLayout(child, viewPos, params, true); 109 child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), 110 MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); 111 } 112 113 @Override 114 protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) { 115 super.onLayout(changed, left, top, right, bottom); 116 117 if (mAdapter == null) { 118 return; 119 } 120 121 if (mDataChanged) { 122 int oldCurrentX = mCurrentX; 123 initView(); 124 removeAllViewsInLayout(); 125 mNextX = oldCurrentX; 126 mDataChanged = false; 127 } 128 129 if (mScroller.computeScrollOffset()) { 130 int scrollx = mScroller.getCurrX(); 131 mNextX = scrollx; 132 } 133 134 if (mNextX <= 0) { 135 mNextX = 0; 136 mScroller.forceFinished(true); 137 } 138 if (mNextX >= mMaxX) { 139 mNextX = mMaxX; 140 mScroller.forceFinished(true); 141 } 142 143 int dx = mCurrentX - mNextX; 144 145 removeNonVisibleItems(dx); 146 fillList(dx); 147 positionItems(dx); 148 149 mCurrentX = mNextX; 150 151 if (!mScroller.isFinished()) { 152 post(new Runnable() { 153 @Override 154 public void run() { 155 requestLayout(); 156 } 157 }); 158 159 } 160 } 161 162 private void fillList(final int dx) { 163 int edge = 0; 164 View child = getChildAt(getChildCount() - 1); 165 if (child != null) { 166 edge = child.getRight(); 167 } 168 fillListRight(edge, dx); 169 170 edge = 0; 171 child = getChildAt(0); 172 if (child != null) { 173 edge = child.getLeft(); 174 } 175 fillListLeft(edge, dx); 176 177 } 178 179 private void fillListRight(int rightEdge, final int dx) { 180 while (rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) { 181 182 View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this); 183 addAndMeasureChild(child, -1); 184 rightEdge += child.getMeasuredWidth(); 185 186 if (mRightViewIndex == mAdapter.getCount() - 1) { 187 mMaxX = mCurrentX + rightEdge - getWidth(); 188 } 189 190 if (mMaxX < 0) { 191 mMaxX = 0; 192 } 193 mRightViewIndex++; 194 } 195 196 } 197 198 private void fillListLeft(int leftEdge, final int dx) { 199 while (leftEdge + dx > 0 && mLeftViewIndex >= 0) { 200 View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this); 201 addAndMeasureChild(child, 0); 202 leftEdge -= child.getMeasuredWidth(); 203 mLeftViewIndex--; 204 mDisplayOffset -= child.getMeasuredWidth(); 205 } 206 } 207 208 private void removeNonVisibleItems(final int dx) { 209 View child = getChildAt(0); 210 while (child != null && child.getRight() + dx <= 0) { 211 mDisplayOffset += child.getMeasuredWidth(); 212 mRemovedViewQueue.offer(child); 213 removeViewInLayout(child); 214 mLeftViewIndex++; 215 child = getChildAt(0); 216 217 } 218 219 child = getChildAt(getChildCount() - 1); 220 while (child != null && child.getLeft() + dx >= getWidth()) { 221 mRemovedViewQueue.offer(child); 222 removeViewInLayout(child); 223 mRightViewIndex--; 224 child = getChildAt(getChildCount() - 1); 225 } 226 } 227 228 private void positionItems(final int dx) { 229 if (getChildCount() > 0) { 230 mDisplayOffset += dx; 231 int left = mDisplayOffset; 232 for (int i = 0; i < getChildCount(); i++) { 233 View child = getChildAt(i); 234 int childWidth = child.getMeasuredWidth(); 235 child.layout(left, 0, left + childWidth, child.getMeasuredHeight()); 236 left += childWidth + child.getPaddingRight(); 237 } 238 } 239 } 240 241 public synchronized void scrollTo(int x) { 242 mScroller.startScroll(mNextX, 0, x - mNextX, 0); 243 requestLayout(); 244 } 245 246 @Override 247 public boolean dispatchTouchEvent(MotionEvent ev) { 248 boolean handled = super.dispatchTouchEvent(ev); 249 handled |= mGesture.onTouchEvent(ev); 250 return handled; 251 } 252 253 protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 254 synchronized (HorizontalListView.this) { 255 mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0); 256 } 257 requestLayout(); 258 259 return true; 260 } 261 262 protected boolean onDown(MotionEvent e) { 263 mScroller.forceFinished(true); 264 return true; 265 } 266 267 private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { 268 269 @Override 270 public boolean onDown(MotionEvent e) { 271 return HorizontalListView.this.onDown(e); 272 } 273 274 @Override 275 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 276 return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY); 277 } 278 279 @Override 280 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 281 282 synchronized (HorizontalListView.this) { 283 mNextX += (int) distanceX; 284 } 285 requestLayout(); 286 287 return true; 288 } 289 290 @Override 291 public boolean onSingleTapConfirmed(MotionEvent e) { 292 for (int i = 0; i < getChildCount(); i++) { 293 View child = getChildAt(i); 294 if (isEventWithinView(e, child)) { 295 if (mOnItemClicked != null) { 296 mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, 297 mAdapter.getItemId(mLeftViewIndex + 1 + i)); 298 } 299 if (mOnItemSelected != null) { 300 mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, 301 mAdapter.getItemId(mLeftViewIndex + 1 + i)); 302 } 303 break; 304 } 305 } 306 return true; 307 } 308 309 @Override 310 public void onLongPress(MotionEvent e) { 311 int childCount = getChildCount(); 312 for (int i = 0; i < childCount; i++) { 313 View child = getChildAt(i); 314 if (isEventWithinView(e, child)) { 315 if (mOnItemLongClicked != null) { 316 mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, 317 mAdapter.getItemId(mLeftViewIndex + 1 + i)); 318 } 319 break; 320 } 321 } 322 } 323 324 private boolean isEventWithinView(MotionEvent e, View child) { 325 Rect viewRect = new Rect(); 326 int[] childPosition = new int[2]; 327 child.getLocationOnScreen(childPosition); 328 int left = childPosition[0]; 329 int right = left + child.getWidth(); 330 int top = childPosition[1]; 331 int bottom = top + child.getHeight(); 332 viewRect.set(left, top, right, bottom); 333 return viewRect.contains((int) e.getRawX(), (int) e.getRawY()); 334 } 335 }; 336 337 }