android水平循环滚动控件使用详解
作者:sjiang 发布时间:2023-07-24 15:25:34
标签:android,水平循环,滚动
本文实例为大家分享了android水平循环滚动控件的具体代码,供大家参考,具体内容如下
CycleScrollView.java
package com.example.test;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
@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);
mHandler.postDelayed(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) {
mHandler.post(mScrollTask);
}
}
/**
* Stop auto scroll and filing scroll task.
*/
public void stopScroll() {
mHandler.removeCallbacks(mScrollTask);
}
/**
* Delay start auto scroll task.
*/
public void delayStartScroll() {
if (canScroll) {
mHandler.postDelayed(mScrollTask, TOUCH_DELAYMILLIS);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
child.measure(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 = this.getChildAt(i);
child.setVisibility(View.VISIBLE);
child.layout(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 (Math.abs(velocityX) < MAX_VELOCITY_X) {
return false;
}
// Get the delta x.
float deltaX = (e1.getX() - e2.getX());
/**
* If can fling stop other scroll task at first , delay the task after
* fling.
*/
mHandler.removeCallbacks(mScrollTask);
if (canScroll) {
mHandler.postDelayed(mScrollTask, TOUCH_DELAYMILLIS
+ FLING_DURATION - 1000);
}
/**
* The flingX is fling distance.
*/
mFlingX = (int) deltaX;
// Start scroll with fling x.
mScroller.startScroll(0, 0, mFlingX, 0, FLING_DURATION);
return false;
}
@Override
public void computeScroll() {
if (canScroll && mScroller.computeScrollOffset()) {
/**
* The Scroller.getCurrX() approach mFlingX , the deltaX more and
* more small.
*/
int deltaX = mFlingX - mScroller.getCurrX();
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 = ev.getX();
/**
* Get event x,y at screen.
*/
final int rawX = (int) ev.getRawX();
final int rawY = (int) ev.getRawY();
switch (ev.getAction()) {
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 (ev.getPointerCount() == 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) {
mOnItemClickListener.onItemClick(Integer.valueOf(view
.getTag().toString()));
}
}
break;
}
return this.detector.onTouchEvent(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();
child.getGlobalVisibleRect(rect);
// If click point on the item view, invoke the click event.
if (rect.contains(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);
child.layout(child.getLeft() + deltaX, child.getTop(),
child.getRight() + deltaX, child.getBottom());
}
}
/**
* 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 (mCurrentView.getRight() < 0) {
mCurrentView.layout(mPreView.getLeft() + getItemMargin(),
getItemY(), mPreView.getLeft() + getItemMargin()
+ getItemWidth(), getItemY() + getItemHeight());
if (mCurrentView.getTag() != null) {
int listIndex = (Integer) mCurrentView.getTag();
int index = (listIndex + maxItemCount) % mAdapter.getCount();
mAdapter.bindView(mCurrentView, mAdapter.get(index));
mCurrentView.setTag(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 (mCurrentView.getLeft() > getScreenWidth()) {
mCurrentView.layout(mNextView.getLeft() - getItemMargin(),
getItemY(), mNextView.getLeft() - getItemMargin()
+ getItemWidth(), getItemY() + getItemHeight());
if (mCurrentView.getTag() != null) {
int listIndex = (Integer) mCurrentView.getTag();
int index = (listIndex - maxItemCount + mAdapter.getCount())
% mAdapter.getCount();
mAdapter.bindView(mCurrentView, mAdapter.get(index));
mCurrentView.setTag(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) {
this.maxItemCount = maxItemCount;
}
public void setReLayout(boolean reLayout) {
this.reLayout = reLayout;
}
public void setCanScroll(boolean canScroll) {
this.canScroll = canScroll;
}
public int getItemX() {
return itemX;
}
public void setItemX(int itemX) {
this.itemX = itemX;
}
public int getItemY() {
return itemY;
}
public void setItemY(int itemY) {
this.itemY = itemY;
}
public int getItemWidth() {
return itemWidth;
}
public void setItemWidth(int itemWidth) {
this.itemWidth = itemWidth;
}
public int getItemHeight() {
return itemHeight;
}
public void setItemHeight(int itemHeight) {
this.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) {
this.screenWidth = screenWidth;
}
public int getInitItemX() {
return initItemX;
}
public void setInitItemX(int initItemX) {
this.initItemX = initItemX;
}
/**
* The interface for item click callback.
*/
interface OnItemClickListener {
public boolean onItemClick(int position);
}
}
CycleScrollAdapter.java
package com.example.test;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.View;
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) {
this.list = list;
mContext = context;
mCycleScrollView = cycleScrollView;
mCycleScrollView.setAdapter(this);
GetScreenWidthPixels();
initView(list);
}
/**
* Get screen width pixels.
*/
private void GetScreenWidthPixels() {
DisplayMetrics dm = new DisplayMetrics();
Activity a = (Activity) mContext;
a.getWindowManager().getDefaultDisplay().getMetrics(dm);
mCycleScrollView.setScreenWidth(dm.widthPixels);
}
/**
* Bind list to view.
*
* @param list
* The list data.
*/
protected void initView(List<T> list) {
if (list == null || list.size() == 0) {
return;
}
// Clear all view from ViewGroup at first.
mCycleScrollView.removeAllViewsInLayout();
// Loop list.
for (int i = 0; i < list.size(); i++) {
/**
* If list size more than MaxItemCount break the loop, only create
* view count is MaxItemCount.
*/
if (i == mCycleScrollView.getMaxItemCount()) {
break;
}
/**
* If list size less than MaxItemCount at the last loop reLayout
* otherwise at the MaxItemCount index reLayout.
*/
if (i == list.size() - 1
|| i == mCycleScrollView.getMaxItemCount() - 1) {
mCycleScrollView.setItemX(mCycleScrollView.getInitItemX());
mCycleScrollView.setReLayout(true);
}
add(list.get(i), i);
}
/**
* If list count more than MaxItemCount the view can scroll otherwise
* can not scroll.
*/
if (list.size() >= mCycleScrollView.getMaxItemCount()) {
mCycleScrollView.setCanScroll(true);
} else {
mCycleScrollView.setCanScroll(false);
}
/**
* If list count more than MaxItemCount reBuild index.
*/
mCycleScrollView.createIndex();
}
/**
* Get list size.
*
* @return The list size.
*/
public int getCount() {
return list.size();
}
/**
* 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 list.get(index);
}
/**
* Adds the specified object at the end of this and refresh view.
*
* @param t
* the object to add.
*/
public void addItem(T t) {
list.add(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) {
list.remove(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);
mCycleScrollView.addView(view);
view.setTag(index);
}
/**
* If item size is null compute item size.
*
* @param view
* the item view.
*/
private void ComputeItemSize(View view) {
if (mCycleScrollView.getItemWidth() == 0
|| mCycleScrollView.getItemHeight() == 0) {
int w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
view.measure(w, h);
int height = view.getMeasuredHeight();
int width = view.getMeasuredWidth();
mCycleScrollView.setItemHeight(height);
mCycleScrollView.setItemWidth(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);
}
以上两个是核心类,下面是测试代码。
实现CycleScrollAdapter
AppCycleScrollAdapter.java绑定视图和应用数据
package com.example.test;
import java.util.List;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
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) {
super.initView(list);
}
@Override
public void bindView(View child, PackageInfo pi) {
ImageView image = (ImageView) child.findViewById(R.id.item_image);
TextView text = (TextView) child.findViewById(R.id.item_text);
image.setImageDrawable(pi.applicationInfo.loadIcon(mContext
.getPackageManager()));
text.setText(pi.applicationInfo.loadLabel(mContext.getPackageManager()));
}
@Override
public View getView(PackageInfo pi) {
View view = View.inflate(mContext, R.layout.view_item, null);
// inflate APP icon view
ImageView image = (ImageView) view.findViewById(R.id.item_image);
// inflate APP name view
TextView text = (TextView) view.findViewById(R.id.item_text);
image.setImageDrawable(pi.applicationInfo.loadIcon(mContext
.getPackageManager()));
text.setText(pi.applicationInfo.loadLabel(mContext.getPackageManager()));
return view;
}
}
入口Activity
package com.example.test;
import java.util.List;
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.os.Bundle;
import android.view.Menu;
public class MainActivity extends Activity{
private CycleScrollView<PackageInfo> mCycleScrollView;
private AppCycleScrollAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCycleScrollView = ((CycleScrollView<PackageInfo>) this.findViewById(R.id.cycle_scroll_view));
/**
* Get APP list and sort by update time.
*/
List<PackageInfo> list = this.getPackageManager()
.getInstalledPackages(0);
mAdapter = new AppCycleScrollAdapter(list, mCycleScrollView, this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/item_image"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_y="5dip"
android:layout_x="10dip"
/>
<TextView
android:id="@+id/item_text"
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:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/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" />
<com.example.test.CycleScrollView
android:id="@+id/cycle_scroll_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#B9000000"
/>
</RelativeLayout>
来源:http://blog.csdn.net/sjiang2142/article/details/11263227
0
投稿
猜你喜欢
- 这里记录下C#中using关键字的使用方法。Using的使用大致分别以下三种:1 :using 指令(命名空间)using System;u
- 一直以为这个方法是java8的,今天才知道是是1.7的时候,然后翻了一下源码。这片文章中会总结一下与a.equals(b)的区别,然后对源码
- C#将对象序列化成JSON字符串public string GetJsonString() { List<Product> p
- idea 鼠标悬浮显示详情2020 版 idea 之前file -> settings设置如下 2020 版 idea 之后
- 在业务开发过程中我们会遇到形形色色的注解,但是框架自有的注解并不是总能满足复杂的业务需求,我们可以自定义注解来满足我们的需求。根据注解使用的
- 公钥与私钥公钥与私钥是成对的,一般的,我们认为的是公钥加密、私钥解密、私钥签名、公钥验证,有人说成私钥加密,公钥解密时不对的。公钥与私钥的生
- TV 3D卡片无限循环效果,供大家参考,具体内容如下##前言1、需求:实现3个卡片实现无限循环效果:1-2-3-1-2-3-1…,而且要实现
- Activiti 介绍Activiti是一个开源的工作流引擎,它实现了BPMN 2.0规范,可以发布设计好的流程定义,并通过api进行流程调
- Eclipse 最佳字体 推荐:步骤:Eclipse->Windows[窗口]->Preferences[首选项]->Ge
- 给新建的winform程序添加资源文件夹Resources小菜鸟开始学习WinForm程序别人的项目都有资源文件夹放图片之类的,我的就是没有
- trim中prefix与suffix等标签用法1.prefix 前缀增加的内容2.suffix 后缀增加的内容3.prefixOverrid
- 泛型概述我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进
- 这些天忙着刷题,又怕遗忘了spring boot, 所以抽出一点时间折腾折腾,加深点印象。 spring boot 的文件上传与 sprin
- 一、背景Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和
- 也许很多朋友在学习NIO的时候都会感觉有点吃力,对里面的很多概念都感觉不是那么明朗。在进入Java NIO编程之前,我们今天先来讨论一些比较
- WPF的ImageBrush是一个比较常见也比较复杂的笔刷,它继承自图块笔刷(TileBrush)。使用图块画笔绘制区域涉及以下三个组成部分
- DeferredResult的超时处理,采用委托机制,也就是在实例DeferredResult时给予一个超时时长(毫秒),同时在onTime
- 本文实例讲述了Java针对ArrayList自定义排序的2种实现方法。分享给大家供大家参考,具体如下:Java中实现对list的自定义排序主
- 作为一个初级GIS程序员,关于封装那些宏观的概念暂且不提,编程经常面对的就是“字段,属性,方法”,这也是面向对象的基本概念之一。1.字段通常
- 概述: 当希望能直接在数据库语言中只检索符合条件的记录,不需要再通过程序对其做处理时,SQL语句分页