当前位置: 代码迷 >> Android >> android水准循环滚动控件
  详细解决方案

android水准循环滚动控件

热度:76   发布时间:2016-04-28 08:12:45.0
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>
<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>



  相关解决方案