Skip to content

Commit d480e07

Browse files
author
Paul Soucy
committed
initial import
0 parents  commit d480e07

File tree

5 files changed

+492
-0
lines changed

5 files changed

+492
-0
lines changed

HorizontialListView.java

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
/*
2+
* HorizontalListView.java v1.5
3+
*
4+
*
5+
* The MIT License
6+
* Copyright (c) 2011 Paul Soucy ([email protected])
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*
26+
*/
27+
28+
package com.devsmart.android.ui;
29+
30+
import java.util.LinkedList;
31+
import java.util.Queue;
32+
33+
import android.content.Context;
34+
import android.database.DataSetObserver;
35+
import android.graphics.Rect;
36+
import android.util.AttributeSet;
37+
import android.view.GestureDetector;
38+
import android.view.GestureDetector.OnGestureListener;
39+
import android.view.MotionEvent;
40+
import android.view.View;
41+
import android.widget.AdapterView;
42+
import android.widget.ListAdapter;
43+
import android.widget.Scroller;
44+
45+
public class HorizontialListView extends AdapterView<ListAdapter> {
46+
47+
public boolean mAlwaysOverrideTouch = true;
48+
protected ListAdapter mAdapter;
49+
private int mLeftViewIndex = -1;
50+
private int mRightViewIndex = 0;
51+
protected int mCurrentX;
52+
protected int mNextX;
53+
private int mMaxX = Integer.MAX_VALUE;
54+
private int mDisplayOffset = 0;
55+
protected Scroller mScroller;
56+
private GestureDetector mGesture;
57+
private Queue<View> mRemovedViewQueue = new LinkedList<View>();
58+
private OnItemSelectedListener mOnItemSelected;
59+
private OnItemClickListener mOnItemClicked;
60+
private boolean mDataChanged = false;
61+
62+
63+
public HorizontialListView(Context context, AttributeSet attrs) {
64+
super(context, attrs);
65+
initView();
66+
}
67+
68+
private synchronized void initView() {
69+
mLeftViewIndex = -1;
70+
mRightViewIndex = 0;
71+
mDisplayOffset = 0;
72+
mCurrentX = 0;
73+
mNextX = 0;
74+
mMaxX = Integer.MAX_VALUE;
75+
mScroller = new Scroller(getContext());
76+
mGesture = new GestureDetector(getContext(), mOnGesture);
77+
}
78+
79+
@Override
80+
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
81+
mOnItemSelected = listener;
82+
}
83+
84+
@Override
85+
public void setOnItemClickListener(AdapterView.OnItemClickListener listener){
86+
mOnItemClicked = listener;
87+
}
88+
89+
private DataSetObserver mDataObserver = new DataSetObserver() {
90+
91+
@Override
92+
public void onChanged() {
93+
synchronized(HorizontialListView.this){
94+
mDataChanged = true;
95+
}
96+
invalidate();
97+
requestLayout();
98+
}
99+
100+
@Override
101+
public void onInvalidated() {
102+
reset();
103+
invalidate();
104+
requestLayout();
105+
}
106+
107+
};
108+
109+
@Override
110+
public ListAdapter getAdapter() {
111+
return mAdapter;
112+
}
113+
114+
@Override
115+
public View getSelectedView() {
116+
//TODO: implement
117+
return null;
118+
}
119+
120+
@Override
121+
public void setAdapter(ListAdapter adapter) {
122+
if(mAdapter != null) {
123+
mAdapter.unregisterDataSetObserver(mDataObserver);
124+
}
125+
mAdapter = adapter;
126+
mAdapter.registerDataSetObserver(mDataObserver);
127+
reset();
128+
}
129+
130+
private synchronized void reset(){
131+
initView();
132+
removeAllViewsInLayout();
133+
requestLayout();
134+
}
135+
136+
@Override
137+
public void setSelection(int position) {
138+
//TODO: implement
139+
}
140+
141+
private void addAndMeasureChild(final View child, int viewPos) {
142+
LayoutParams params = child.getLayoutParams();
143+
if(params == null) {
144+
params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
145+
}
146+
147+
addViewInLayout(child, viewPos, params, true);
148+
child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
149+
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
150+
}
151+
152+
@Override
153+
protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
154+
super.onLayout(changed, left, top, right, bottom);
155+
156+
if(mAdapter == null){
157+
return;
158+
}
159+
160+
if(mDataChanged){
161+
int oldCurrentX = mCurrentX;
162+
initView();
163+
removeAllViewsInLayout();
164+
mNextX = oldCurrentX;
165+
mDataChanged = false;
166+
}
167+
168+
if(mScroller.computeScrollOffset()){
169+
int scrollx = mScroller.getCurrX();
170+
mNextX = scrollx;
171+
}
172+
173+
if(mNextX < 0){
174+
mNextX = 0;
175+
mScroller.forceFinished(true);
176+
}
177+
if(mNextX > mMaxX) {
178+
mNextX = mMaxX;
179+
mScroller.forceFinished(true);
180+
}
181+
182+
int dx = mCurrentX - mNextX;
183+
184+
removeNonVisibleItems(dx);
185+
fillList(dx);
186+
positionItems(dx);
187+
188+
mCurrentX = mNextX;
189+
190+
if(!mScroller.isFinished()){
191+
post(new Runnable(){
192+
@Override
193+
public void run() {
194+
requestLayout();
195+
}
196+
});
197+
198+
}
199+
}
200+
201+
private void fillList(final int dx) {
202+
int edge = 0;
203+
View child = getChildAt(getChildCount()-1);
204+
if(child != null) {
205+
edge = child.getRight();
206+
}
207+
fillListRight(edge, dx);
208+
209+
edge = 0;
210+
child = getChildAt(0);
211+
if(child != null) {
212+
edge = child.getLeft();
213+
}
214+
fillListLeft(edge, dx);
215+
216+
217+
}
218+
219+
private void fillListRight(int rightEdge, final int dx) {
220+
while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {
221+
222+
View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);
223+
addAndMeasureChild(child, -1);
224+
rightEdge += child.getMeasuredWidth();
225+
226+
if(mRightViewIndex == mAdapter.getCount()-1){
227+
mMaxX = mCurrentX + rightEdge - getWidth();
228+
}
229+
mRightViewIndex++;
230+
}
231+
232+
}
233+
234+
private void fillListLeft(int leftEdge, final int dx) {
235+
while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {
236+
View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);
237+
addAndMeasureChild(child, 0);
238+
leftEdge -= child.getMeasuredWidth();
239+
mLeftViewIndex--;
240+
mDisplayOffset -= child.getMeasuredWidth();
241+
}
242+
}
243+
244+
private void removeNonVisibleItems(final int dx) {
245+
View child = getChildAt(0);
246+
while(child != null && child.getRight() + dx <= 0) {
247+
mDisplayOffset += child.getMeasuredWidth();
248+
mRemovedViewQueue.offer(child);
249+
removeViewInLayout(child);
250+
mLeftViewIndex++;
251+
child = getChildAt(0);
252+
253+
}
254+
255+
child = getChildAt(getChildCount()-1);
256+
while(child != null && child.getLeft() + dx >= getWidth()) {
257+
mRemovedViewQueue.offer(child);
258+
removeViewInLayout(child);
259+
mRightViewIndex--;
260+
child = getChildAt(getChildCount()-1);
261+
}
262+
}
263+
264+
private void positionItems(final int dx) {
265+
if(getChildCount() > 0){
266+
mDisplayOffset += dx;
267+
int left = mDisplayOffset;
268+
for(int i=0;i<getChildCount();i++){
269+
View child = getChildAt(i);
270+
int childWidth = child.getMeasuredWidth();
271+
child.layout(left, 0, left + childWidth, child.getMeasuredHeight());
272+
left += childWidth;
273+
}
274+
}
275+
}
276+
277+
public synchronized void scrollTo(int x) {
278+
mScroller.startScroll(mNextX, 0, x - mNextX, 0);
279+
requestLayout();
280+
}
281+
282+
@Override
283+
public boolean dispatchTouchEvent(MotionEvent ev) {
284+
boolean handled = mGesture.onTouchEvent(ev);
285+
return handled;
286+
}
287+
288+
protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
289+
float velocityY) {
290+
synchronized(HorizontialListView.this){
291+
mScroller.fling(mNextX, 0, (int)-velocityX, 0, 0, mMaxX, 0, 0);
292+
}
293+
requestLayout();
294+
295+
return true;
296+
}
297+
298+
protected boolean onDown(MotionEvent e) {
299+
mScroller.forceFinished(true);
300+
return true;
301+
}
302+
303+
private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {
304+
305+
@Override
306+
public boolean onDown(MotionEvent e) {
307+
return HorizontialListView.this.onDown(e);
308+
}
309+
310+
@Override
311+
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
312+
float velocityY) {
313+
return HorizontialListView.this.onFling(e1, e2, velocityX, velocityY);
314+
}
315+
316+
@Override
317+
public boolean onScroll(MotionEvent e1, MotionEvent e2,
318+
float distanceX, float distanceY) {
319+
320+
synchronized(HorizontialListView.this){
321+
mNextX += (int)distanceX;
322+
}
323+
requestLayout();
324+
325+
return true;
326+
}
327+
328+
@Override
329+
public boolean onSingleTapConfirmed(MotionEvent e) {
330+
Rect viewRect = new Rect();
331+
for(int i=0;i<getChildCount();i++){
332+
View child = getChildAt(i);
333+
int left = child.getLeft();
334+
int right = child.getRight();
335+
int top = child.getTop();
336+
int bottom = child.getBottom();
337+
viewRect.set(left, top, right, bottom);
338+
if(viewRect.contains((int)e.getX(), (int)e.getY())){
339+
if(mOnItemClicked != null){
340+
mOnItemClicked.onItemClick(HorizontialListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
341+
}
342+
if(mOnItemSelected != null){
343+
mOnItemSelected.onItemSelected(HorizontialListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
344+
}
345+
break;
346+
}
347+
348+
}
349+
return true;
350+
}
351+
352+
353+
354+
};
355+
356+
357+
358+
}

0 commit comments

Comments
 (0)