Android:图片滚轮

Android客户端中,我们经常要实现图片滚轮的效果。

实现的方式就是自定义相关View,这里主要是包括两个类:ImageScroller和PagerIndicator。

PagerIndicator类:

public class PagerIndicator extends ViewGroup {
    
    public static int mMaxTotalItems = 9;
    private int mTotalItems;
    private int mCurrentItem;
    private int mDotDrawableId;//滑动的图片
    
    public PagerIndicator(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPager();
    }

    public PagerIndicator(Context context) {
        super(context);
        initPager();
    }
    
    private void initPager(){
        setFocusable(false);
        setWillNotDraw(false);
        mDotDrawableId=R.drawable.pager_dots;
    }
    
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(mTotalItems<=0) return;
        createLayout();
    }
    
    private void updateLayout(){
        for(int i=0;i<getChildCount();i++){
            final ImageView img=(ImageView) getChildAt(i);
            TransitionDrawable tmp=(TransitionDrawable)img.getDrawable();
            if(i==mCurrentItem){
                tmp.startTransition(0);
            }else{
                tmp.resetTransition();
            }
        }
    }
    
    private void createLayout(){
        detachAllViewsFromParent();
        
        int dotWidth=getResources().getDrawable(mDotDrawableId).getIntrinsicWidth();
        int separation=9;
        
        int marginLeft=((getWidth())/2)-(((mTotalItems*dotWidth)/2)+(((mTotalItems-1)*separation)/2));
        int marginTop=((getHeight())/2)-(dotWidth/2);
        for(int i=0;i<mTotalItems;i++){
            ImageView dot=new ImageView(getContext());
            
            TransitionDrawable td=(TransitionDrawable)getResources().getDrawable(mDotDrawableId);
            
            td.setCrossFadeEnabled(true);
            dot.setImageDrawable(td);
            ViewGroup.LayoutParams p;
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.FILL_PARENT);
            dot.setLayoutParams(p);
            int childHeightSpec = getChildMeasureSpec(
                    MeasureSpec.makeMeasureSpec(dotWidth, MeasureSpec.UNSPECIFIED), 0, p.height);
            int childWidthSpec = getChildMeasureSpec(
                    MeasureSpec.makeMeasureSpec(dotWidth, MeasureSpec.EXACTLY), 0, p.width);
            dot.measure(childWidthSpec, childHeightSpec);
            
            int left=marginLeft+(i*(dotWidth+separation));
            
            dot.layout(left, marginTop, left+dotWidth,marginTop+dotWidth );
            addViewInLayout(dot, getChildCount(), p, true);
            if(i==mCurrentItem){
                TransitionDrawable tmp=(TransitionDrawable)dot.getDrawable();
                tmp.startTransition(0);
            }
        }
        postInvalidate();
    }
    
    public int getTotalItems() {
        return mTotalItems;
    }

    public void setTotalItems(int totalItems) {
        if(totalItems!=mTotalItems){
            
            if(totalItems <= mMaxTotalItems){
                mTotalItems = totalItems;
            } else {
                mTotalItems = mMaxTotalItems;
            }
            
            createLayout();
        }
    }

    public int getCurrentItem() {
        return mCurrentItem;
    }

    public void setCurrentItem(int currentItem) {
        
        if(currentItem!=mCurrentItem){
            mCurrentItem = currentItem;
            updateLayout();
        }
    }
    
    /**
     * 向左翻页
     * @param pageLeave 在最左边还有多少页没显示
     * @return 左边超出的页数
     */
    public void rollLeft(int currentPage, int totalChild){
        
        if(mCurrentItem == 0){
            setCurrentItem(Math.min(currentPage, mTotalItems / 2) - 1);
        } else {
            setCurrentItem(Math.max(0, mCurrentItem - 1));
        }
    }
    
    /**
     * 向右翻页
     * @param pageLeave 在最右边还有多少页没显示
     * @return 右边超出的页数
     */
    public void rollRight(int currentPage, int totalChild){

        if(mCurrentItem == (mTotalItems - 1)){
             final int pageLeave = totalChild - currentPage - 1;
             if(pageLeave < mTotalItems / 2){
                 setCurrentItem(mTotalItems - pageLeave);
             } else {
                setCurrentItem(mTotalItems / 2);
             }
        } else {
            setCurrentItem(Math.min((mCurrentItem + 1), mTotalItems - 1));
        }
    }
}

ImageScroller类:

public class ImageScroller extends ViewGroup {
    
    private static String TAG = ImageScroller.class.getSimpleName();
    
    private static final int INVALID_SCREEN = -1;
    private static final int SNAP_VELOCITY = 800;
    private final static int TOUCH_STATE_REST = 0;
    private final static int TOUCH_STATE_SCROLLING = 1;
    
    private boolean mTapChange;
    
    private PagerIndicator mIndicator;
    
    private int currentScreen;
    private int nextScreen = INVALID_SCREEN;
    private int lastMotionX;
    private int lastMotionY;
    
    private int touchState = TOUCH_STATE_REST;
    private int touchSlop;

    private Map<String, SoftReference<Bitmap>> mCacheBitmap;
    private Set<ImageView> mMissingView;
    private ExecutorService mExecutorService;

    private VelocityTracker mVelocityTracker;
    private Scroller scroller;
    private Resources mResources;
    private static final int FETCH_IMAGE_BITMAP = 1;
    
    private boolean mSyncLoad = false;        //是否需异步加载

    public ImageScroller(Context context) {
        this(context, null);
    }

    public ImageScroller(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ImageScroller(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        
        touchSlop = ViewConfiguration.getTouchSlop();//用户拖动时应该的距离
        scroller = new Scroller(getContext());
        mResources = getResources();
        mCacheBitmap = new HashMap<String, SoftReference<Bitmap>>();
        mMissingView = new HashSet<ImageView>();
        mExecutorService = Executors.newFixedThreadPool(3);//线程池
    }

    public void setPageIndicator(PagerIndicator indicator){
        mIndicator = indicator;
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
        int childLeft = 0;
        final int count = getChildCount();
        
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                final int childWidth = child.getMeasuredWidth();
                final int childHeight = child.getMeasuredHeight();
                child.layout(childLeft, 0, childLeft + childWidth,
                        child.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (touchState != TOUCH_STATE_REST)) {
            return true;
        }

        final float x = ev.getX();
        final float y = ev.getY();

        switch (action) {
        case MotionEvent.ACTION_MOVE:
            final int xDiff = (int) Math.abs(x - lastMotionX);
            final int yDiff = (int) Math.abs(y - lastMotionY);
            boolean xMoved = xDiff > touchSlop;
            boolean yMoved = yDiff > touchSlop;
            
            if(xMoved || yMoved){
                if (xMoved) {
                    touchState = TOUCH_STATE_SCROLLING;
                }
            }
            
            break;

        case MotionEvent.ACTION_DOWN:
            lastMotionX = (int) x;
            touchState = scroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
            break;

        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            touchState = TOUCH_STATE_REST;
            break;
        }
        return touchState != TOUCH_STATE_REST;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);

        final int action = ev.getAction();
        final float x = ev.getX();

        switch (action) {
        case MotionEvent.ACTION_DOWN:

            if (!scroller.isFinished()) {
                scroller.abortAnimation();
            }
            lastMotionX = (int) x;
            break;
        case MotionEvent.ACTION_MOVE:
            
            if (touchState == TOUCH_STATE_SCROLLING) {
                final int deltaX = (int) (lastMotionX - x);
                lastMotionX = (int) x;
                
                if (deltaX < 0 && getScrollX() > 0) {
                    
                    scrollBy(Math.max(-getScrollX(), deltaX), 0);
                    
                } else if (deltaX > 0) {
                    final int availableToScroll = getChildAt(getChildCount() - 1).getRight() - getScrollX() - getWidth();
                    if (availableToScroll > 0) {
                        scrollBy(Math.min(availableToScroll, deltaX), 0);
                    }
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            
            if (touchState == TOUCH_STATE_SCROLLING) {
                final VelocityTracker velocityTracker = mVelocityTracker;
                velocityTracker.computeCurrentVelocity(1000);
                int velocityX = (int) velocityTracker.getXVelocity();

                if (velocityX > SNAP_VELOCITY && currentScreen > 0) {
                    scrollToScreen(currentScreen - 1);
                } else if (velocityX < -SNAP_VELOCITY && currentScreen < getChildCount() - 1) {
                    scrollToScreen(currentScreen + 1);
                } else {
                    snapToDestination();
                }

                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
            }
            touchState = TOUCH_STATE_REST;
            break;
        case MotionEvent.ACTION_CANCEL:
            touchState = TOUCH_STATE_REST;
            break;
        }

        return true;
    }
    
    private void snapToDestination() {
        final int screenWidth = getWidth();
        final int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth;
        scrollToScreen(whichScreen);
    }
    
    public void scrollToScreen(int whichScreen) {
        boolean changingScreens = whichScreen != currentScreen;
        
        nextScreen = whichScreen;
        
        View focusedChild = getFocusedChild();
        if (focusedChild != null && changingScreens && focusedChild == getChildAt(currentScreen)) {
          focusedChild.clearFocus();
        }
        
        
        final int newX = whichScreen * getWidth();
        final int delta = newX - getScrollX();
        scroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
        invalidate();    
    }
    
    public void scrollToScreen(int whichScreen, int duration) {
        boolean changingScreens = whichScreen != currentScreen;
        
        nextScreen = whichScreen;
        
        View focusedChild = getFocusedChild();
        if (focusedChild != null && changingScreens && focusedChild == getChildAt(currentScreen)) {
          focusedChild.clearFocus();
        }
        
        
        final int newX = whichScreen * getWidth();
        final int delta = newX - getScrollX();
        scroller.startScroll(getScrollX(), 0, delta, 0, duration);
        invalidate();    
    }
    
    @Override
    public void computeScroll() {

        if (scroller.computeScrollOffset()) {
            scrollTo(scroller.getCurrX(), scroller.getCurrY());
            postInvalidate();
        } else if (nextScreen != INVALID_SCREEN) {
            
            if(mTapChange){
                mTapChange = false;
            } else {
                if(mIndicator != null && currentScreen < nextScreen){
                    
                    mIndicator.rollRight(currentScreen, getChildCount());
                } else if (mIndicator != null && currentScreen > nextScreen){

                    mIndicator.rollLeft(currentScreen, getChildCount());
                }
            }
            
            currentScreen = nextScreen;
            nextScreen = INVALID_SCREEN;
            
            if(mSyncLoad){
                ImageView child = (ImageView) getChildAt(currentScreen);
                if(child == null){
                    return;
                }
                
                Movie movie = (Movie) child.getTag();
                if(movie != null){
                    
                    SoftReference<Bitmap> ref = mCacheBitmap.get(movie.getImage().getBigImage());
                    if(ref != null){
                        Bitmap bitmap = ref.get();
                        if(bitmap != null) {
                            child.setImageBitmap(bitmap);
                        }
                    } else {
                        fetchBitmapFromCache(child, movie);
                    }
                }
            }
        }
    }
    
    private void fetchBitmapFromCache(final ImageView child, final Movie coupon) {
        mMissingView.add(child);
        
        mExecutorService.execute(new Runnable() {
            
            @Override
            public void run() {
                
                String imagePath = coupon.getImage().getBigImage();
                Bitmap bitmap = loadBitmapFromUri(imagePath);
                if(bitmap != null){
                    bitmap = ImageUtil.getRoundedCornerBitmap(bitmap, 10);
                    mCacheBitmap.put(imagePath, new SoftReference<Bitmap>(bitmap));
                    
                    Message message = new Message();
                    message.what = FETCH_IMAGE_BITMAP;
                    message.obj = child;
                    handler.sendMessage(message);
                }
            }
        });
    }
    
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what){
            case FETCH_IMAGE_BITMAP:
                
                ImageView view = (ImageView) msg.obj;
                Movie movie = (Movie) view.getTag();
                if(view == null || movie == null){
                    break;
                }
                
                SoftReference<Bitmap> ref = mCacheBitmap.get(movie.getImage().getBigImage());
                if(ref != null){
                    Bitmap bitmap = ref.get();
                    if(bitmap != null){
                        view.setImageBitmap(bitmap);
                        mMissingView.remove(view);
                    }
                } else {
                    mCacheBitmap.remove(movie.getId());
                }
                break;
            }
        }
    };
    
    private Bitmap loadBitmapFromUri(String path){
        Bitmap bitmap = null;
        URL url;
        InputStream i = null;
        try {
            url = new URL(path);
            i = (InputStream) url.getContent();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        bitmap = BitmapFactory.decodeStream(i);
        return bitmap;
    }
    
    public void setTapChange(boolean value){
        mTapChange = value;
    }
    
    public void setSyncLoad(boolean syncLoad) {
        mSyncLoad = syncLoad;
    }
}

 

原文地址:https://www.cnblogs.com/gongcb/p/2494502.html