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