android 實現自動滾動的 Banner 橫幅


很多音樂播放器如qq音樂,kugou音樂等都有一個專輯推薦的那個橫幅,它擴展了軟件的空間,也為用戶帶來了更好的交互感受。

在此,我也模仿着實現了此效果,不足之處請大家見諒,歡迎提出問題,和大家一起學習。

我給他取名叫【BannerLayout】,主要是覺得它也如其他layout特性差不多吧。

public class BannerLayout extends ViewGroup {

public BannerLayout(Context context) {
        super(context);

        // TODO Auto-generated constructor stub
    }

    public BannerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
    }

    public BannerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }
}
BannerLayout 繼承與 ViewGroup. 
這個BannerLayout可以自動滾動,所以我們需要一個滾動器Scroller .
this.scroller = new Scroller(context, new DecelerateInterpolator(2));

我給scroller設置了一個插值器DecelerateInterpolator,就可以再滾動的時候實現滾動速度是中間快,開始和結束的時候慢的效果,個人覺得這個效果顯得比較優雅。

要實現自動滾動,因此,我們可以使用handler還幫我們起到計時的作用。此外也可以使用timer等其他方式。

private Handler handler=new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
       //autoScroll是一標志boolean亮,用來標志是否需要滾動,因此就能手動控制其滾動了,
if(autoScroll && currentWhat==msg.what) { currentScreenIndex=(currentScreenIndex+1)%getChildCount(); scrollToScreen(currentScreenIndex); Log.i("TAG","handleMessage scrollToScreen:"+currentScreenIndex); if(autoScroll)//給自身發送延時消息, handler.sendEmptyMessageDelayed(currentWhat, scrollTime); } } };

由於BannerLayout里面的子元素如圖片都是水平布局的,所以我們需要手動控制它們在此布局的位置了,

重寫onMeasure函數:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int maxHeight=-1;
        
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
            
            maxHeight=Math.max(maxHeight, getChildAt(i).getMeasuredHeight());
            
        }
// maxHeight
=Math.min(maxHeight, MeasureSpec.getSize(heightMeasureSpec)); Log.e("TAG","onMeasure Height:"+maxHeight); setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),maxHeight); }
@Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {

        final int count = getChildCount();

        int cLeft = 0;
        
        
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == View.GONE)
                continue;
            
//            child.setVisibility(View.VISIBLE);
            final int childWidth = child.getMeasuredWidth();
            child.layout(cLeft, 0, cLeft +childWidth, child.getMeasuredHeight());

            cLeft += childWidth;
        }
    }

此外,我們需要處理用戶觸摸事件,實現用戶按下的時候停止滾動,用戶拖動的時候能夠隨之移動,

@Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (getChildCount() == 0)
            return false;
        final int action = ev.getAction();
        final float x = ev.getX();
        switch (action) {
        case MotionEvent.ACTION_DOWN:

            autoScroll=false;
            
            currentWhat++;
            
            mLastMotionX = x;
            if (!scroller.isFinished()) {
                scroller.abortAnimation();
            }
            
//            Log.i("TAG","ACTION_DOWN");
            
            return true;

        case MotionEvent.ACTION_MOVE:
            final int deltaX = (int) (mLastMotionX - x);
//            boolean xMoved = Math.abs(deltaX) > mTouchSlop;
            mLastMotionX = x;
            
            if((0==currentScreenIndex && deltaX<0) || (getChildCount()-1==currentScreenIndex && deltaX>0))
                scrollBy(deltaX/4, 0);//此處實現了越界時候的阻尼效果
            else
//            Log.i("TAG","ACTION_MOVE");
//            if (xMoved)
                scrollBy(deltaX, 0);
            
            
            final int screenWidth = getWidth();
            currentScreenIndex=(getScrollX() + (screenWidth / 2))/ screenWidth;
            
            return true;
        case MotionEvent.ACTION_UP:
            snapToDestination();
            
            if(!autoScroll)
            {
                autoScroll=true;
                handler.sendEmptyMessageDelayed(currentWhat, scrollTime);
            }
            break;
        case MotionEvent.ACTION_CANCEL:
            snapToDestination();
            if(!autoScroll)
            {
                autoScroll=true;
                handler.sendEmptyMessageDelayed(currentWhat, scrollTime);
            }
            
        }
        return false;
    }

接下來,我們需要實現自定滾動到某一屏的效果:

private void scrollToScreen(int whichScreen)
    {
//        if (!scroller.isFinished())
//            return;
//        Log.e("TAG","scrollToScreen:"+whichScreen);
        int delta = 0;
        
        delta = whichScreen * getWidth() - getScrollX();
        
//        scroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
        scroller.startScroll(getScrollX(), 0, delta, 0, 1500);
        invalidate();
        
        currentScreenIndex=whichScreen;
    }
    private void snapToDestination()
    {
        final int x=getScrollX();
        final int screenWidth = getWidth();
        
        scrollToScreen((x + (screenWidth / 2))/ screenWidth);
    }

差不多就這些了,其實挺簡單的,哈哈

下面是全部代碼:

public class BannerLayout extends ViewGroup {

    private Scroller scroller;
    private float mLastMotionX;
//    private int mTouchSlop;
    private int currentScreenIndex=0;
    
    private boolean autoScroll=true;
    
    private int scrollTime=3*1000;
    
    private int currentWhat=0;
    
    private Handler handler=new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            if(autoScroll && currentWhat==msg.what)
            {
                currentScreenIndex=(currentScreenIndex+1)%getChildCount();
                scrollToScreen(currentScreenIndex);
                
                Log.i("TAG","handleMessage scrollToScreen:"+currentScreenIndex);
                
                if(autoScroll)
                    handler.sendEmptyMessageDelayed(currentWhat, scrollTime);
            }
        }
    };
    
    public BannerLayout(Context context) {
        super(context);

        initView(context);
        // TODO Auto-generated constructor stub
    }

    public BannerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public BannerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
        // TODO Auto-generated constructor stub
    }

    private void initView(final Context context) {
        this.scroller = new Scroller(context, new DecelerateInterpolator(2));//OvershootInterpolator(1.1f)

        
        handler.sendEmptyMessageDelayed(currentWhat, scrollTime);
        
//        final ViewConfiguration configuration = ViewConfiguration
//                .get(getContext());
//        mTouchSlop = configuration.getScaledTouchSlop();
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int maxHeight=-1;
        
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
            
            maxHeight=Math.max(maxHeight, getChildAt(i).getMeasuredHeight());
            
        }
        maxHeight=Math.min(maxHeight, MeasureSpec.getSize(heightMeasureSpec));
        
        Log.e("TAG","onMeasure Height:"+maxHeight);
        
        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),maxHeight);
    }
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {

        final int count = getChildCount();

        int cLeft = 0;
        
        
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == View.GONE)
                continue;
            
//            child.setVisibility(View.VISIBLE);
            final int childWidth = child.getMeasuredWidth();
            child.layout(cLeft, 0, cLeft +childWidth, child.getMeasuredHeight());

            cLeft += childWidth;
        }
    }

    @Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            scrollTo(scroller.getCurrX(), 0);
            postInvalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (getChildCount() == 0)
            return false;
        final int action = ev.getAction();
        final float x = ev.getX();
        switch (action) {
        case MotionEvent.ACTION_DOWN:

            autoScroll=false;
            
            currentWhat++;
            
            mLastMotionX = x;
            if (!scroller.isFinished()) {
                scroller.abortAnimation();
            }
            
//            Log.i("TAG","ACTION_DOWN");
            
            return true;

        case MotionEvent.ACTION_MOVE:
            final int deltaX = (int) (mLastMotionX - x);
//            boolean xMoved = Math.abs(deltaX) > mTouchSlop;
            mLastMotionX = x;
            
            if((0==currentScreenIndex && deltaX<0) || (getChildCount()-1==currentScreenIndex && deltaX>0))
                scrollBy(deltaX/4, 0);
            else
//            Log.i("TAG","ACTION_MOVE");
//            if (xMoved)
                scrollBy(deltaX, 0);
            
            
            final int screenWidth = getWidth();
            currentScreenIndex=(getScrollX() + (screenWidth / 2))/ screenWidth;
            
            return true;
        case MotionEvent.ACTION_UP:
            snapToDestination();
            
            if(!autoScroll)
            {
                autoScroll=true;
                handler.sendEmptyMessageDelayed(currentWhat, scrollTime);
            }
            break;
        case MotionEvent.ACTION_CANCEL:
            snapToDestination();
            if(!autoScroll)
            {
                autoScroll=true;
                handler.sendEmptyMessageDelayed(currentWhat, scrollTime);
            }
            
        }
        return false;
    }
    private void scrollToScreen(int whichScreen)
    {
//        if (!scroller.isFinished())
//            return;
//        Log.e("TAG","scrollToScreen:"+whichScreen);
        int delta = 0;
        
        delta = whichScreen * getWidth() - getScrollX();
        
//        scroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
        scroller.startScroll(getScrollX(), 0, delta, 0, 1500);
        invalidate();
        
        currentScreenIndex=whichScreen;
    }
    private void snapToDestination()
    {
        final int x=getScrollX();
        final int screenWidth = getWidth();
        
        scrollToScreen((x + (screenWidth / 2))/ screenWidth);
    }

    @Override
    protected void finalize() throws Throwable {

        Log.e("TAG","finalize===");

        super.finalize();
    }
    
}

 ok,附圖一張

 

 

附上源碼demo:http://files.cnblogs.com/zhouchanwen/bannerDemo.7z


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM