zoukankan      html  css  js  c++  java
  • 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:https://files.cnblogs.com/zhouchanwen/bannerDemo.7z

  • 相关阅读:
    .dll 无法查找或者打开PDB文件
    VC++中解决“在查找预编译头使用时跳过”的方法
    如何重置设置开发环境
    opencv与VS的配置
    supermap开发webgis的经验
    Json 与GeoJson
    地理配准
    DBMS
    C#三层构架
    重装系统简要步骤
  • 原文地址:https://www.cnblogs.com/zhouchanwen/p/2757704.html
Copyright © 2011-2022 走看看