SoFunction
Updated on 2025-04-10

Detailed explanation of the use of android horizontal loop scrolling control

This article shares the specific code of the Android horizontal loop scroll control for your reference. The specific content is as follows


package ; 
 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
 
@SuppressWarnings("deprecation") 
public class CycleScrollView<T> extends ViewGroup implements OnGestureListener { 
 
 static final String TAG = "CycleScrollView"; 
 Context mContext; 
 
 /** 
  * Scroll velocity. 
  */ 
 public static final long SCROLL_VELOCITY = 50; 
 
 /** 
  * Scroll offset. 
  */ 
 public static final int SCROLL_OFFSET = -1; 
 
 /** 
  * Touch delay. 
  */ 
 public static final long TOUCH_DELAYMILLIS = 2000; 
 
 /** 
  * Fling duration. 
  */ 
 public static final int FLING_DURATION = 2000; 
 
 /** 
  * Filing max velocity x. 
  */ 
 public static final int MAX_VELOCITY_X = 1000; 
 
 private GestureDetector detector; 
 private Handler mHandler; 
 private Scroller mScroller; 
 
 /** 
  * Callback interface adapter and OnItemClick. 
  */ 
 private CycleScrollAdapter<T> mAdapter; 
 private OnItemClickListener mOnItemClickListener; 
 
 /** 
  * Scroll index 
  */ 
 private int mPreIndex; 
 private int mCurrentIndex; 
 private int mNextIndex; 
 private View mCurrentView; 
 private View mPreView; 
 private View mNextView; 
 
 private float mLastMotionX; 
 
 // The reLayout is false can not invoke onLayout. 
 private boolean reLayout = false; 
 
 // If the item count more than screen that can scroll. 
 private boolean canScroll = false; 
 
 // A flag for switch current view. 
 private boolean mCurrentViewAtLeft = true; 
 
 // Fling distance. 
 private int mFlingX = 0; 
 
 private boolean isMoveAction = false; 
 
 private int defaultItemY = 10; 
   
 private int maxItemCount = 7; 
 
 private int initItemX = 20; 
 
 /** 
  * The screen width. 
  */ 
 private int screenWidth; 
 
 /** 
  * Item view height. 
  */ 
 private int itemHeight; 
 
 /** 
  * Item view width. 
  */ 
 private int itemWidth; 
 
 /** 
  * Item view layout x. 
  */ 
 private int itemX = getInitItemX(); 
 
 /** 
  * Item view layout y. 
  */ 
 private int itemY = defaultItemY; 
 
 // Auto scroll view task. 
 private final Runnable mScrollTask = new Runnable() { 
 
  @Override 
  public void run() { 
   if (canScroll) { 
    scrollView(SCROLL_OFFSET); 
    (this, SCROLL_VELOCITY);// Loop self. 
   } 
  } 
 }; 
 
 public CycleScrollView(Context context) { 
  super(context); 
  onCreate(context); 
 } 
 
 public CycleScrollView(Context context, AttributeSet attrs) { 
  super(context, attrs); 
  onCreate(context); 
 } 
 
 public CycleScrollView(Context context, AttributeSet attrs, int defStyle) { 
  super(context, attrs, defStyle); 
  onCreate(context); 
 } 
 
 private void onCreate(Context context) { 
  mContext = context; 
  detector = new GestureDetector(this); 
  mHandler = new Handler(); 
  mScroller = new Scroller(context); 
 } 
 
 /** 
  * Create scroll index. 
  */ 
 public void createIndex() { 
  if (canScroll) { 
   mPreIndex = maxItemCount - 1; 
   mCurrentIndex = 0; 
   mNextIndex = 1; 
   mPreView = getChildAt(mPreIndex); 
   mCurrentView = getChildAt(mCurrentIndex); 
   mNextView = getChildAt(mNextIndex); 
  } 
 } 
 
 /** 
  * Set item click callback. 
  * 
  * @param onItemClickListener 
  *   The callback 
  */ 
 public void setOnItemClickListener(OnItemClickListener onItemClickListener) { 
  mOnItemClickListener = onItemClickListener; 
 } 
 
 /** 
  * Set itemAdapter for addItem and bindItem. 
  * 
  * @param itemAdapter 
  */ 
 public void setAdapter(CycleScrollAdapter<T> adapter) { 
  mAdapter = adapter; 
 } 
 
 /** 
  * Start auto scroll. 
  */ 
 public void startScroll() { 
  if (canScroll) { 
   (mScrollTask); 
  } 
 } 
 
 /** 
  * Stop auto scroll and filing scroll task. 
  */ 
 public void stopScroll() { 
  (mScrollTask); 
 } 
 
 /** 
  * Delay start auto scroll task. 
  */ 
 public void delayStartScroll() { 
  if (canScroll) { 
   (mScrollTask, TOUCH_DELAYMILLIS); 
  } 
 } 
 
 @Override 
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
  (widthMeasureSpec, heightMeasureSpec); 
  int count = (); 
  for (int i = 0; i < count; i++) { 
   View child = (i); 
   (widthMeasureSpec, heightMeasureSpec); 
  } 
 } 
 
 @Override 
 protected void onLayout(boolean changed, int l, int t, int r, int b) { 
  /** 
   * On layout set the child view layout by x y width and height. 
   */ 
  if (reLayout) {// Run one times. 
   for (int i = 0; i < getChildCount(); i++) { 
    View child = (i); 
    (); 
    (itemX, getItemY(), itemX + getItemWidth(), 
      getItemY() + getItemHeight()); 
    itemX += getItemMargin(); 
   } 
   reLayout = !reLayout; 
  } 
 } 
 
 /** 
  * When fling view run the fling task scroll view. 
  */ 
 @Override 
 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
   float velocityY) { 
 
  if (e1 == null || e2 == null) { 
   return false; 
  } 
 
  // When deltaX and velocityX not good return false. 
  if ((velocityX) < MAX_VELOCITY_X) { 
   return false; 
  } 
 
  // Get the delta x. 
  float deltaX = (() - ()); 
 
  /** 
   * If can fling stop other scroll task at first , delay the task after 
   * fling. 
   */ 
  (mScrollTask); 
  if (canScroll) { 
   (mScrollTask, TOUCH_DELAYMILLIS 
     + FLING_DURATION - 1000); 
  } 
 
  /** 
   * The flingX is fling distance. 
   */ 
  mFlingX = (int) deltaX; 
 
  // Start scroll with fling x. 
  (0, 0, mFlingX, 0, FLING_DURATION); 
  return false; 
 } 
 
 @Override 
 public void computeScroll() { 
  if (canScroll && ()) { 
   /** 
    * The () approach mFlingX , the deltaX more and 
    * more small. 
    */ 
   int deltaX = mFlingX - (); 
   scrollView(-deltaX / 10); 
   postInvalidate(); 
  } 
 } 
 
 /** 
  * When touch event is move scroll child view. 
  */ 
 @Override 
 public boolean onTouchEvent(MotionEvent ev) { 
 
  // Get event x,y at parent view. 
  final float x = (); 
 
  /** 
   * Get event x,y at screen. 
   */ 
  final int rawX = (int) (); 
  final int rawY = (int) (); 
 
  switch (()) { 
  case MotionEvent.ACTION_DOWN: 
   // Reset isMoveAction. 
   isMoveAction = false; 
   // Get motionX. 
   mLastMotionX = x; 
   break; 
  case MotionEvent.ACTION_MOVE: 
   // When action move set isMoveAction true. 
   isMoveAction = true; 
   // Only support one pointer. 
   if (() == 1) { 
    // Compute delta X. 
    int deltaX = 0; 
    deltaX = (int) (x - mLastMotionX); 
    mLastMotionX = x; 
    // When canScroll is true, scrollView width deltaX. 
    if (canScroll) { 
     scrollView(deltaX); 
    } 
   } 
   break; 
  case MotionEvent.ACTION_UP: 
   /** 
    * If not move find click item and invoke click event. 
    */ 
   if (!isMoveAction) { 
    View view = getClickItem(rawX, rawY); 
    if (view != null) { 
     ((view 
       .getTag().toString())); 
    } 
   } 
   break; 
  } 
  return (ev); 
 } 
 
 /** 
  * Get click item view by rawX and rawY. 
  * @param rawX the x at screen. 
  * @param rawY the y at screen. 
  * @return the click item view. 
  */ 
 private View getClickItem(final int rawX, final int rawY) { 
  for (int i = 0; i < getChildCount(); i++) { 
   View child = getChildAt(i); 
   // Get item view rect. 
   Rect rect = new Rect(); 
   (rect); 
   // If click point on the item view, invoke the click event. 
   if ((rawX, rawY)) { 
    return child; 
   } 
  } 
  return null; 
 } 
 
 /** 
  * Scroll view by delta x. 
  * 
  * @param deltaX 
  *   The scroll distance. 
  */ 
 private void scrollView(int deltaX) { 
  // Move child view by deltaX. 
  moveChildView(deltaX); 
  // After move change index. 
  if (deltaX < 0) {// move left 
   // If current at right switch current view to left. 
   switchCurrentViewToLeft(); 
   // change previous current next index. 
   moveToNext(); 
  } else {// move right 
   // If current at left switch current view to right. 
   switchCurrentViewToRight(); 
   // change previous current next index. 
   moveToPre(); 
  } 
  invalidate(); 
 } 
 
 /** 
  * Move view by delta x. 
  * 
  * @param deltaX 
  *   The move distance. 
  */ 
 private void moveChildView(int deltaX) { 
  for (int i = 0; i < getChildCount(); i++) { 
   View child = getChildAt(i); 
   (() + deltaX, (), 
     () + deltaX, ()); 
  } 
 } 
 
 /** 
  * Current event is move to left, if current view at right switch current 
  * view to left. 
  */ 
 private void switchCurrentViewToLeft() { 
  if (!mCurrentViewAtLeft) { 
   mPreIndex = mCurrentIndex; 
   mCurrentIndex = mNextIndex; 
   mNextIndex++; 
   if (mNextIndex > maxItemCount - 1) { 
    mNextIndex = 0; 
   } 
   mCurrentView = getChildAt(mCurrentIndex); 
   mPreView = getChildAt(mPreIndex); 
   mNextView = getChildAt(mNextIndex); 
   mCurrentViewAtLeft = !mCurrentViewAtLeft; 
  } 
 } 
 
 /** 
  * Current event is move to right, if current view at left switch current 
  * view to right. 
  */ 
 private void switchCurrentViewToRight() { 
  if (mCurrentViewAtLeft) { 
   mNextIndex = mCurrentIndex; 
   mCurrentIndex = mPreIndex; 
   mPreIndex--; 
   if (mPreIndex < 0) { 
    mPreIndex = maxItemCount - 1; 
   } 
   mCurrentView = getChildAt(mCurrentIndex); 
   mPreView = getChildAt(mPreIndex); 
   mNextView = getChildAt(mNextIndex); 
   mCurrentViewAtLeft = !mCurrentViewAtLeft; 
  } 
 } 
 
 /** 
  * Current event is move to left,if current view move out of screen move the 
  * current view to right and reBind the item change index. 
  */ 
 private void moveToNext() { 
  if (() < 0) { 
   (() + getItemMargin(), 
     getItemY(), () + getItemMargin() 
       + getItemWidth(), getItemY() + getItemHeight()); 
 
   if (() != null) { 
    int listIndex = (Integer) (); 
    int index = (listIndex + maxItemCount) % (); 
    (mCurrentView, (index)); 
    (index); 
   } 
 
   mPreIndex = mCurrentIndex; 
   mCurrentIndex = mNextIndex; 
   mNextIndex++; 
   if (mNextIndex > maxItemCount - 1) { 
    mNextIndex = 0; 
   } 
   mCurrentView = getChildAt(mCurrentIndex); 
   mPreView = getChildAt(mPreIndex); 
   mNextView = getChildAt(mNextIndex); 
   moveToNext(); 
  } 
 } 
 
 /** 
  * Current event is move to right,if current view move out of screen move 
  * the current view to left and reBind the item change index. 
  */ 
 private void moveToPre() { 
  if (() > getScreenWidth()) { 
   (() - getItemMargin(), 
     getItemY(), () - getItemMargin() 
       + getItemWidth(), getItemY() + getItemHeight()); 
 
   if (() != null) { 
    int listIndex = (Integer) (); 
    int index = (listIndex - maxItemCount + ()) 
      % (); 
    (mCurrentView, (index)); 
    (index); 
   } 
 
   mNextIndex = mCurrentIndex; 
   mCurrentIndex = mPreIndex; 
   mPreIndex--; 
   if (mPreIndex < 0) { 
    mPreIndex = maxItemCount - 1; 
   } 
   mCurrentView = getChildAt(mCurrentIndex); 
   mPreView = getChildAt(mPreIndex); 
   mNextView = getChildAt(mNextIndex); 
   moveToPre(); 
  } 
 } 
 
 @Override 
 public boolean onDown(MotionEvent e) { 
  return true; 
 } 
 
 @Override 
 public void onShowPress(MotionEvent e) { 
 } 
 
 @Override 
 public boolean onSingleTapUp(MotionEvent e) { 
  return false; 
 } 
 
 @Override 
 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, 
   float distanceY) { 
  return false; 
 } 
 
 @Override 
 public void onLongPress(MotionEvent e) { 
 } 
 
 public int getMaxItemCount() { 
  return maxItemCount; 
 } 
 
 public void setMaxItemCount(int maxItemCount) { 
   = maxItemCount; 
 } 
 
 public void setReLayout(boolean reLayout) { 
   = reLayout; 
 } 
 
 public void setCanScroll(boolean canScroll) { 
   = canScroll; 
 } 
 
 public int getItemX() { 
  return itemX; 
 } 
 
 public void setItemX(int itemX) { 
   = itemX; 
 } 
 
 public int getItemY() { 
  return itemY; 
 } 
 
 public void setItemY(int itemY) { 
   = itemY; 
 } 
 
 public int getItemWidth() { 
  return itemWidth; 
 } 
 
 public void setItemWidth(int itemWidth) { 
   = itemWidth; 
 } 
 
 public int getItemHeight() { 
  return itemHeight; 
 } 
 
 public void setItemHeight(int itemHeight) { 
   = itemHeight; 
 } 
 
 public int getItemMargin() { 
  return (screenWidth - itemWidth * (maxItemCount - 1) - initItemX * 2)/(maxItemCount - 2) + itemWidth; 
 } 
 
 public int getScreenWidth() { 
  return screenWidth; 
 } 
 
 public void setScreenWidth(int screenWidth) { 
   = screenWidth; 
 } 
 
 public int getInitItemX() { 
  return initItemX; 
 } 
 
 public void setInitItemX(int initItemX) { 
   = initItemX; 
 } 
 
 /** 
  * The interface for item click callback. 
  */ 
 interface OnItemClickListener { 
  public boolean onItemClick(int position); 
 } 
 
} 


package ; 
 
import ; 
 
import ; 
import ; 
import ; 
import ; 
 
public abstract class CycleScrollAdapter<T> { 
 
 private List<T> list; 
 private CycleScrollView<T> mCycleScrollView; 
 Context mContext; 
 
 /** 
  * Initial CycleScrollAdapter bind list to view. 
  * 
  * @param list 
  *   The list data. 
  * @param cycleScrollView 
  *   The CycleScrollView. 
  * @param context 
  *   The Context. 
  */ 
 public CycleScrollAdapter(List<T> list, CycleScrollView<T> cycleScrollView, 
   Context context) { 
   = list; 
  mContext = context; 
  mCycleScrollView = cycleScrollView; 
  (this); 
  GetScreenWidthPixels(); 
  initView(list); 
 } 
 
 /** 
  * Get screen width pixels. 
  */ 
 private void GetScreenWidthPixels() { 
  DisplayMetrics dm = new DisplayMetrics(); 
  Activity a = (Activity) mContext; 
  ().getDefaultDisplay().getMetrics(dm); 
  (); 
 } 
 
 /** 
  * Bind list to view. 
  * 
  * @param list 
  *   The list data. 
  */ 
 protected void initView(List<T> list) { 
  if (list == null || () == 0) { 
   return; 
  } 
   
  // Clear all view from ViewGroup at first. 
  (); 
 
  // Loop list. 
  for (int i = 0; i < (); i++) { 
   /** 
    * If list size more than MaxItemCount break the loop, only create 
    * view count is MaxItemCount. 
    */ 
   if (i == ()) { 
    break; 
   } 
 
   /** 
    * If list size less than MaxItemCount at the last loop reLayout 
    * otherwise at the MaxItemCount index reLayout. 
    */ 
   if (i == () - 1 
     || i == () - 1) { 
    (()); 
    (true); 
   } 
   add((i), i); 
  } 
 
  /** 
   * If list count more than MaxItemCount the view can scroll otherwise 
   * can not scroll. 
   */ 
  if (() >= ()) { 
   (true); 
  } else { 
   (false); 
  } 
 
  /** 
   * If list count more than MaxItemCount reBuild index. 
   */ 
  (); 
 } 
 
 /** 
  * Get list size. 
  * 
  * @return The list size. 
  */ 
 public int getCount() { 
  return (); 
 } 
 
 /** 
  * Returns the element at the specified location in this 
  * 
  * @param index 
  *   the index of the element to return. 
  * @return the element at the specified location. 
  */ 
 public T get(int index) { 
  return (index); 
 } 
 
 /** 
  * Adds the specified object at the end of this and refresh view. 
  * 
  * @param t 
  *   the object to add. 
  */ 
 public void addItem(T t) { 
  (t); 
  initView(list); 
 } 
 
 /** 
  * Removes the first occurrence of the specified object from this and 
  * refresh view. 
  * 
  * @param t 
  *   the object to remove. 
  */ 
 public void removeItem(T t) { 
  (t); 
  initView(list); 
 } 
 
 /** 
  * Add the specified view to the index. 
  * 
  * @param t 
  *   The data to add. 
  * @param index 
  *   the index. 
  */ 
 private void add(T t, int index) { 
  View view = getView(t); 
  ComputeItemSize(view); 
  (view); 
  (index); 
 } 
 
 /** 
  * If item size is null compute item size. 
  * 
  * @param view 
  *   the item view. 
  */ 
 private void ComputeItemSize(View view) { 
  if (() == 0 
    || () == 0) { 
   int w = (0, 
     ); 
   int h = (0, 
     ); 
   (w, h); 
   int height = (); 
   int width = (); 
   (height); 
   (width); 
  } 
 } 
 
 /** 
  * Get item view. 
  * 
  * @param t 
  *   the data need bind to view. 
  * @return the view. 
  */ 
 public abstract View getView(T t); 
 
 /** 
  * Bind the item to view. 
  * 
  * @param child 
  *   the item view need bind. 
  * @param t 
  *   the item. 
  */ 
 public abstract void bindView(View child, T t); 
} 

The above two are core classes, and the following are the test code.

Implement CycleScrollAdapter

Bind view and application data

package ; 
 
import ; 
 
import ; 
import ; 
import ; 
import ; 
import ; 
 
public class AppCycleScrollAdapter extends CycleScrollAdapter<PackageInfo> { 
  
 public AppCycleScrollAdapter(List<PackageInfo> list, 
   CycleScrollView<PackageInfo> cycleScrollView, Context context) { 
  super(list, cycleScrollView, context); 
 } 
  
 @Override 
 protected void initView(List<PackageInfo> list) { 
  (list); 
 } 
 
 @Override 
 public void bindView(View child, PackageInfo pi) { 
  ImageView image = (ImageView) (.item_image); 
  TextView text = (TextView) (.item_text); 
  ((mContext 
    .getPackageManager())); 
  ((())); 
 } 
 
 @Override 
 public View getView(PackageInfo pi) { 
  View view = (mContext, .view_item, null); 
  // inflate APP icon view 
  ImageView image = (ImageView) (.item_image); 
  // inflate APP name view 
  TextView text = (TextView) (.item_text); 
  ((mContext 
    .getPackageManager())); 
  ((())); 
  return view; 
 } 
} 

Entrance Activity

package ; 
 
 
import ; 
 
 
import ; 
import ; 
import ; 
import ; 
 
public class MainActivity extends Activity{ 
  
 private CycleScrollView<PackageInfo> mCycleScrollView; 
 private AppCycleScrollAdapter mAdapter; 
  
 @Override 
 public void onCreate(Bundle savedInstanceState) { 
  (savedInstanceState); 
  setContentView(.activity_main); 
   
  mCycleScrollView = ((CycleScrollView<PackageInfo>) (.cycle_scroll_view)); 
 
  /** 
   * Get APP list and sort by update time. 
   */ 
  List<PackageInfo> list = () 
    .getInstalledPackages(0); 
 
  mAdapter = new AppCycleScrollAdapter(list, mCycleScrollView, this); 
   
 } 
  
 
 
 @Override 
 public boolean onCreateOptionsMenu(Menu menu) { 
  getMenuInflater().inflate(.activity_main, menu); 
  return true; 
 } 
 
} 

Layout file

<?xml version="1.0" encoding="utf-8"?> 
<AbsoluteLayout xmlns:andro 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 > 
 
 <ImageView 
  android: 
  android:layout_width="60dip" 
  android:layout_height="60dip" 
  android:layout_y="5dip" 
  android:layout_x="10dip" 
   /> 
 
 <TextView 
  android: 
  android:layout_width="80dip" 
  android:layout_height="20dip" 
  android:layout_y="65dip" 
  android:layout_x="0dip" 
  android:gravity="center_horizontal" /> 
 
</AbsoluteLayout> 
[java] view plain copy
<RelativeLayout xmlns:andro 
 xmlns:tools="/tools" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" > 
 
 <TextView 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_centerHorizontal="true" 
  android:layout_centerVertical="true" 
  android:text="@string/hello_world" 
  tools:context=".MainActivity" /> 
  
  < 
  android: 
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" 
  android:background="#B9000000" 
  /> 
 
</RelativeLayout> 

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.