android中左右滑屏的實現(廣告位banner組件)

原理
參見下圖。整個組件是一個FrameLayout,裡面有兩個view,第一個是LinearLayout,承載瞭4個(或多個)可以滑動的view,見圖中綠色背景的部分;第二個是一個RelativeLayout,在其底部放置瞭一個LinearLayout,在LinearLayout的內部放置瞭若幹個小圓點,用來指示當前屏幕的索引。手勢檢測用瞭GestureDetector,並實現瞭OnGestureListener接口。為瞭能控制滑動速度,采用瞭Scroller彈性滑動對象。

為什麼組件繼承FrameLayout,是因為用於指示的小圓點是出現在view上面的,一個view疊在另一個view上面,這就是FrameLayout的特性

什麼是banner組件?在許多android應用上,比如愛奇藝客戶端、百度美拍、應用寶等上面,都有一個可以手動滑動的小廣告條,這就是banner,實際應用中的banner,其信息(圖片和點擊行為)是後臺可配置的,是需要通過網絡從後臺拉取的。網上有許多手動滑屏的例子,但是一般隻是個demo,無法在實際中使用,因為其一般沒有考慮如下幾類問題:圖片緩存、OOM問題、是否可靈活配置、是否預留外部接口以及是否封裝良好。沒有良好的封裝,手動滑屏加在代碼中,會使得代碼變得很爛很脆弱。

 

 

        2.     功能、效果

banner屬性可動態設置,默認數量為4,可以調整默認的數量
banner信息從後臺獲取,banner的條數就是屏幕的數量
可自動滑動也能手動滑動
圖片下載為多線程,並采用常見的三級cache策略(內存、文件、網絡),節省流量,並處理瞭OOM異常
內部處理點擊事件,同時預留出瞭接口函數
banner封裝成一個ViewGroup類,使用起來簡單,最少隻需要兩行代碼

 

        3.    代碼

 

代碼註釋寫的比較詳細,應該很好理解。分為2個文件,一個是banner的類,另一個是接口聲明。

ScrollBanner.java

[java]
/**
 * ScrollBanner 支持滑屏效果的FrameLayout子類,可設置屏幕數量,尺寸。<br/>
 * 典型的用法:<br/>
 *      ScrollBanner scrollBanner = new ScrollBanner(this, mScreenWidth, 100, this);<br/>
 *      linearLayout.addView(scrollBanner);<br/>
 *註意事項:<br/>
 *1.如果重新設置ScrollBanner的LayoutParams,則參數中的寬和高屬性將被忽略,仍然采用對象實例化的寬和高<br/>
 *2.點擊事件的回調如果設為null,則采用默認的事件回調<br/>
 *3.通過setOverScrollMode來設置 banner是否能夠滑出屏幕的邊界<br/>
 *4通過xml方式加載banner,需要進行如下調用:<br/>
 *      setResolution(width, height);<br/>
        setOnBannerClickListener(bannerClickListener);<br/>
        showBanner()<br/>
 * @author singwhatiwanna
 * @version 2013.3.4
 *
 */ 
public class ScrollBanner extends FrameLayout implements  
ComponentCallBack.OnBannerClickListener,  
ResponseHandler.BannerInfoHandler 

 
    private static final String TAG = "ScrollBanner"; 
 
    private HorizontalScrollViewEx mHorizontalScrollViewEx; 
     
    //ScrollBanner的子view  
    private LinearLayout linearLayoutScrolLayout; 
     
    //linearLayoutScrolLayout的子view,用於放置若幹個小圓點  
    private LinearLayout linearLayoutForDot; 
     
    private Scroller mScroller;  
    private Context mContext; 
    private OnBannerClickListener mBannerClickListener; 
 
    //屏幕及其bitmap  
    private List<View> mLinearLayoutScreens = new ArrayList<View>(); 
    private List<Bitmap> mBannerBitmaps = new ArrayList<Bitmap>(); 
 
    //banner信息  
    private List<BannerItem> mBannerItemsList = new ArrayList<BannerItem>(); 
    //小圓點  
    private List<ImageView> mImageViewList = new ArrayList<ImageView>(); 
    private Drawable mPageIndicator; 
    private Drawable mPageIndicatorFocused; 
     
    //banner默認圖片  
    private Bitmap mDefaultBitmap; 
 
    private int mScreenWidth; 
    private int mScreenHeight; 
    private int mScrollX; 
 
    //current screen index  
    private int mWhich = 0; 
 
    public static final int MESSAGE_AUTO_SCROLL = 1; 
 
    public static final int MESSAGE_FETCH_BANNER_SUCCESS = 2; 
 
    public static final int MARGIN_BOTTOM = 2; 
     
    //480*150 banner的圖片尺寸 150.0/480=0.3125f  
    public static final float ratio = 0.3125f; 
 
    //banner的位置  
    private int mLocation = -1; 
 
    //banner分為幾屏  
    private int PAGE_COUNT = 4; 
 
    //滑動方向 是否向右滑動  
    private boolean mScrollToRight = true; 
 
    //是否自動滑屏  
    private boolean mTimerResume = true; 
 
    //標志用戶是否手動滑動瞭屏幕  
    private boolean mByUserAction = false; 
 
    //標志banner是否可以滑出邊界  
    private boolean mOverScrollMode = false; 
    //標志banner可以滑出邊界多少像素  
    private int mOverScrollDistance = 0; 
 
    //定時器 用於banner的自動播放  
    final Timer timer = new Timer(); 
 
    //定時器的時間間隔 單位:ms  
    public static final int TIMER_DURATION = 5000; 
 
    private TimerTask mTimerTask = new TimerTask() 
    { 
        @Override 
        public void run()  
        { 
            if (mTimerResume && !mByUserAction)  
            { 
                mHandler.sendEmptyMessage(MESSAGE_AUTO_SCROLL); 
            } 
            mByUserAction = false; 
        } 
    }; 
 
    //ScrollBanner私有handler 用於處理內部邏輯  
    private Handler mHandler = new Handler() 
    { 
        public void handleMessage(Message msg) 
        { 
            //表示已經執行瞭onDetachedFromWindow,banner已經被銷毀瞭  
            if( mBannerBitmaps == null || mLinearLayoutScreens == null ||  
                    mImageViewList == null || mBannerItemsList == null || mContext == null ) 
                return; 
             
            switch (msg.what) 
            { 
            case MESSAGE_AUTO_SCROLL: 
                if (mWhich == PAGE_COUNT – 1) 
                    mScrollToRight = false; 
                else if(mWhich == 0) 
                { 
                    mScrollToRight = true; 
                } 
 
                if (mScrollToRight)  
                    mWhich++; 
                else  
                { 
                    mWhich–; 
                } 
 
                mHorizontalScrollViewEx.switchView(mWhich); 
                break; 
            case MESSAGE_FETCH_BANNER_SUCCESS: 
                int more = 0; 
                if(mBannerItemsList != null) 
                    more = mBannerItemsList.size() – PAGE_COUNT; 
                if(mBannerItemsList.size() > 0) 
                { 
                    //如果有banner 顯示它  
                    ScrollBanner.this.show(true); 
                } 
                //如果後臺返回的banneritem的數量大於預設值4  
                if(more > 0) 
                { 
                    for (int i = 0; i < more; i++) 
                        addBannerItem(); 
                } 
                fetchBannerImages(); 
                break; 
 
            default: 
                break; 
            } 
        }; 
    }; 
 
    //用於獲取bitmap  
    private Handler  mBitmapHandler = new Handler() 
    { 
 
        public void handleMessage(Message msg) 
        { 
            //表示已經執行瞭onDetachedFromWindow,banner已經被銷毀瞭  
            if( mBannerBitmaps == null || mLinearLayoutScreens == null ||  
                    mImageViewList == null || mBannerItemsList == null || mContext == null ) 
                return; 
             
            Bitmap bitmap = (Bitmap)msg.obj; 
            String urlString = msg.getData().getString("url"); 
            Logger.d(TAG, "url=" + urlString); 
            if (urlString == null || bitmap == null || mBannerItemsList == null) 
            { 
                Logger.w(TAG, "bitmap=null imgurl=" + urlString); 
                return; 
            } 
 
            for( int i = 0; i < mBannerItemsList.size(); i++ ) 
            { 
                BannerItem item = mBannerItemsList.get(i); 
                if(item != null && urlString.equals(item.imgUrl) ) 
                { 
                    Logger.d(TAG, "find " + i + urlString); 
                    if( mBannerBitmaps != null )  
                    { 
                        mBannerBitmaps.set( i, bitmap ); 
                        setBannerImages(i); 
                    } 
                    break; 
                } 
            } 
             
        }; 
 
    }; 
 
    public ScrollBanner(Context context) 
    { 
        this(context, null); 
    } 
 
    public ScrollBanner(Context context, AttributeSet attrs) 
    { 
        super(context, attrs); 
        mContext = context; 
    } 
 
    public ScrollBanner(Context context, AttributeSet attrs, int defStyle) 
    { 
        super(context, attrs, defStyle); 
        mContext = context; 
    } 
 
    /**
     * 
     * @param context activity實例
     * @param width banner的寬度 單位px
     * @param height banner的高度 單位dip,-1表示根據圖片比例自適應高度
     * @param bannerClickListener 單擊banner的回調接口
     */ 
    public ScrollBanner(Context context, final int width, final int height, OnBannerClickListener bannerClickListener) 
    { 
        this(context, null); 
 
        int activityId = ( (BaseActivity)context ).activityId(); 
        if(activityId == BaseActivity.ACCOUNT_ID)//位置3  
            mLocation = 3; 
        else if(activityId == BaseActivity.GAMEZONE_ID)//位置2  
        { 
            mLocation = 2; 
        } 
         
        //初始化時不顯示banner  
        this.show(false); 
        setResolution(width, height); 
        setOnBannerClickListener(bannerClickListener); 
        setDefaultBannerImages(); 
        fetchBannerInfo(); 
    } 
 
    /**
     * 通過xml方式加載banner,必須調用此方法才能顯示
     */ 
    public void showBanner() 
    { 
        int activityId = ( (BaseActivity)mContext ).activityId(); 
        if(activityId == BaseActivity.ACCOUNT_ID)//位置3  
            mLocation = 3; 
        else if(activityId == BaseActivity.GAMEZONE_ID)//位置2  
        { 
            mLocation = 2; 
        } 
         
        setDefaultBannerImages(); 
        fetchBannerInfo(); 
    } 
     
    /**
     * 暫停滾動
     */ 
    public void pauseScroll() 
    { 
        mTimerResume = false; 
    } 
     
    /**
     * 恢復滾動
     */ 
    public void resumeScroll() 
    { 
        mTimerResume = true; 
    } 
     
    /**
     * 設置回調接口
     * @param callBack 單擊banner的回調接口
     */ 
    public void setOnBannerClickListener(OnBannerClickListener bannerClickListener) 
    { 
        mBannerClickListener = (bannerClickListener != null ? bannerClickListener : ScrollBanner.this); 
    } 
 
    /**
     * 設置banner的解析度
     * @param width banner的寬度
     * @param height banner的高度
     */ 
    public void setResolution(final int width, final int height) 
    { 
        int heightInPx = height; 
         
        if(height == -1) 
            heightInPx = (int)(ratio * width) ; 
        else 
        { 
            Resources resources = getResources(); 
            heightInPx = Math.round( TypedValue.applyDimension( 
                    TypedValue.COMPLEX_UNIT_DIP, height, resources.getDisplayMetrics()) ); 
        } 
         
        mScreenWidth = width; 
        mScreenHeight = heightInPx; 
        setLayoutParams(new LayoutParams(width, heightInPx)); 
 
        initScrollView(); 
    } 
     
    /**
     * 獲取banner的高度
     * @return banner的高度 單位:px
     */ 
    public int getHeightPixels() 
    { 
        return mScreenHeight; 
    } 
 
    /**
     * 設置banner是否可以彈性滑出邊界
     * @param canOverScroll true表示可以滑出邊界,false不能
     */ 
    public void setOverScrollMode(boolean canOverScroll) 
    { 
        mOverScrollMode = canOverScroll; 
        if(canOverScroll == false) 
            mOverScrollDistance = 0; 
    } 
 
    /**
     * 向後臺獲取banner的各種信息
     */ 
    private void fetchBannerInfo() 
    { 
        NetworkManager netManager = (NetworkManager) AppEngine.getInstance().getManager( 
                IManager.NETWORK_ID); 
        netManager.getBannerInfo( String.valueOf(mLocation), ScrollBanner.this ); 
    } 
 
    /**
     * 獲取banner的滑屏圖像
     */ 
    private void setDefaultBannerImages() 
    { 
        //為banner設置默認bitmap  
        BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();   
        bitmapFactoryOptions.inJustDecodeBounds = false;   
        bitmapFactoryOptions.inSampleSize = 2;   
 
        Resources res=mContext.getResources(); 
        mDefaultBitmap = BitmapFactory.decodeResource(res, R.drawable.banner_image_default, bitmapFactoryOptions); 
         
        for(int i = 0; i < PAGE_COUNT; i++) 
            mBannerBitmaps.add(i, mDefaultBitmap); 
 
        //初始化BannerItem對象  
        for (int i = 0; i < PAGE_COUNT; i++) 
            mBannerItemsList.add(i, null); 
 
        setBannerImages(-1); 
    } 
 
    private void fetchBannerImages() 
    { 
        //表示已經執行瞭onDetachedFromWindow,banner已經被銷毀瞭  
        if( mBannerItemsList == null ) 
            return; 
         
        //ImageManager 根據url向其獲取bitmap  
        ImageManager imageManager = (ImageManager)AppEngine.getInstance(). 
                getManager(IManager.IMAGE_ID); 
 
        BannerItem item = null; 
        for(int i = 0; i < PAGE_COUNT; i++) 
        { 
            try 
            { 
                item = mBannerItemsList.get(i); 
            }  
            catch (IndexOutOfBoundsException e) 
            { 
                Logger.e(TAG, "fetchBannerImages error: " + e); 
            } 
            catch (Exception e)  
            { 
                Logger.e(TAG, "fetchBannerImages error: " + e); 
            } 
            //ImageManager為多線程,采用常見的三級cache策略(內存、文件、網絡)  
            if( item != null && item.imgUrl != null ) 
                imageManager.loadBitmap( item.imgUrl, mBitmapHandler ); 
        } 
    } 
 
    /**
     * 設置banner的滑屏圖像
     * @param position 如果position=-1,則表示設置全部bitmap
     */ 
    private void setBannerImages(final int position) 
    { 
        int size = mBannerBitmaps.size(); 
        if (size < PAGE_COUNT || mLinearLayoutScreens == null) 
        { 
            return; 
        } 
        if(position >=0 && position < PAGE_COUNT ) 
        { 
            Drawable drawable = mLinearLayoutScreens.get(position).getBackground(); 
            mLinearLayoutScreens.get(position).setBackgroundDrawable 
            (new BitmapDrawable( mBannerBitmaps.get(position) ) ); 
            drawable.setCallback(null); 
            drawable = null; 
             
            return; 
        } 
 
        for(int i = 0; i < PAGE_COUNT; i++) 
        { 
            mLinearLayoutScreens.get(i).setBackgroundDrawable(new BitmapDrawable(mBannerBitmaps.get(i))); 
        } 
    } 
 
    /**
     * 是否顯示banner
     * @param isShow true顯示 false不顯示
     */ 
    public void show(boolean isShow) 
    { 
        if(isShow) 
        { 
            this.setVisibility(View.VISIBLE); 
            mTimerResume = true; 
        } 
        else  
        { 
            this.setVisibility(View.GONE); 
            mTimerResume = false; 
        } 
    } 
 
    /**
     * 切換到指定屏幕
     * @param which 屏幕索引
     */ 
    public void switchToScreen(final int which) 
    { 
        mHorizontalScrollViewEx.switchView(which); 
    } 
     
    /**
     * 設置屏幕的數量 (此函數暫不開放)
     * @param count 屏幕數量
     */ 
    protected void setScreenCount(final int count) 
    { 
        PAGE_COUNT = count; 
    } 
 
    /**
     * 設置偏移的距離 如果mOverScrollMode為false,則此設置無效 (此函數暫不開放)
     * @param distance
     */ 
    protected void setOverScrollDistance(int distance) 
    { 
        if(distance < 0) 
            distance = 0; 
 
        mOverScrollDistance = mOverScrollMode ? distance : 0; 
    } 
 
    /**
     * 切換小圓點
     * @param position current screen index
     */ 
    private void switchScreenPosition(final int position) 
    { 
        if( mPageIndicator == null || mPageIndicatorFocused == null ) 
            return; 
         
        int length = 0; 
        if(mImageViewList != null) 
            length = mImageViewList.size(); 
        if (position >= length || position < 0 || length <= 0) 
        { 
            return; 
        } 
 
        for(int i = 0; i < length; i++) 
        { 
            mImageViewList.get(i).setImageDrawable(mPageIndicator); 
        } 
         
        mImageViewList.get(position).setImageDrawable(mPageIndicatorFocused); 
    } 
 
    /**
     * 初始化整個FrameLayout視圖組
     */ 
    private void initScrollView() 
    { 
        setLayoutParams(new LayoutParams(mScreenWidth, mScreenHeight )); 
 
        linearLayoutScrolLayout = new LinearLayout(mContext); 
        linearLayoutScrolLayout.setBackgroundColor(Color.WHITE); 
        linearLayoutScrolLayout.setOrientation(LinearLayout.HORIZONTAL); 
         
        int mVersionCode = 8; 
        try 
        { 
            mVersionCode = Integer.valueOf(android.os.Build.VERSION.SDK); 
            Logger.d(TAG, "sdk version=" + mVersionCode); 
        }  
        catch (Exception e)  
        { 
            e.printStackTrace(); 
        } 
        //針對android1.6及以下的特殊處理 此為android的低版本bug  
        if(mVersionCode <= 5) 
        { 
            linearLayoutScrolLayout.setBaselineAligned(false); 
        } 
         
        //初始化四個滑動view  
        for(int i = 0; i < PAGE_COUNT; i++) 
        { 
            LinearLayout linearLayoutScreen = new LinearLayout(mContext); 
            linearLayoutScreen.setOrientation(LinearLayout.VERTICAL); 
            linearLayoutScrolLayout.addView(linearLayoutScreen, new LayoutParams( 
                    mScreenWidth,  
                    LayoutParams.FILL_PARENT)); 
 
            mLinearLayoutScreens.add(i, linearLayoutScreen); 
        } 
 
        //初始化小圓點視圖  
        RelativeLayout relativeLayout = new RelativeLayout(mContext); 
        relativeLayout.setLayoutParams(new LayoutParams( 
                LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); 
 
        //linearLayoutForDot為小圓點視圖  
        linearLayoutForDot =new LinearLayout(mContext); 
        android.widget.RelativeLayout.LayoutParams layoutParams =  
                new android.widget.RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,  
                        LayoutParams.WRAP_CONTENT); 
        //小圓點距底部的距離 單位:px  
        layoutParams.bottomMargin = MARGIN_BOTTOM; 
        layoutParams.rightMargin = MARGIN_BOTTOM; 
        layoutParams.addRule(android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM); 
        layoutParams.addRule(android.widget.RelativeLayout.CENTER_HORIZONTAL); 
        linearLayoutForDot.setLayoutParams(layoutParams); 
        linearLayoutForDot.setOrientation(LinearLayout.HORIZONTAL); 
        linearLayoutForDot.setHorizontalGravity(Gravity.CENTER); 
        linearLayoutForDot.setVerticalGravity(Gravity.CENTER); 
        //下面兩句實現圓角半透明效果 不采用  
        //      linearLayoutForDot.setBackgroundResource(R.drawable.round_corner_bg);  
        //      linearLayoutForDot.getBackground().setAlpha(100);  
 
        //初始化4個小圓點  
        mPageIndicator = getResources().getDrawable(R.drawable.page_indicator); 
        mPageIndicatorFocused = getResources().getDrawable(R.drawable.page_indicator_focused); 
        for(int i = 0; i < PAGE_COUNT; i++) 
        { 
            ImageView imageView = new ImageView(mContext); 
            imageView.setImageDrawable(mPageIndicator); 
            mImageViewList.add(i, imageView); 
            LinearLayout.LayoutParams layoutParamsForDot =  
                    new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,  
                            LayoutParams.WRAP_CONTENT); 
            layoutParamsForDot.rightMargin = 5; 
 
            linearLayoutForDot.addView(imageView, layoutParamsForDot); 
        } 
        mImageViewList.get(0).setImageDrawable(mPageIndicatorFocused); 
        relativeLayout.addView(linearLayoutForDot); 
 
        mHorizontalScrollViewEx = new HorizontalScrollViewEx(mContext, null, mBannerClickListener); 
        mHorizontalScrollViewEx.setLayoutParams(new LayoutParams( 
                mScreenWidth * PAGE_COUNT,  
                LayoutParams.FILL_PARENT)); 
        mHorizontalScrollViewEx.addView(linearLayoutScrolLayout, new LayoutParams( 
                LayoutParams.FILL_PARENT,  
                LayoutParams.FILL_PARENT)); 
 
        mHorizontalScrollViewEx.setHorizontalScrollBarEnabled(false); 
        mHorizontalScrollViewEx.setHorizontalFadingEdgeEnabled(false); 
 
        addView(mHorizontalScrollViewEx); 
        addView(relativeLayout); 
 
        //自動滑屏 5秒一次  
        timer.schedule(mTimerTask, 5000, TIMER_DURATION); 
    } 
 
    /**
     * 加一個banner頁面 TODO此函數寫的不好
     */ 
    private void addBannerItem() 
    { 
        //表示已經執行瞭onDetachedFromWindow,banner已經被銷毀瞭  
        if( mBannerBitmaps == null || mLinearLayoutScreens == null ||  
                mImageViewList == null || mContext == null ) 
            return; 
         
        //調整屏幕數量和總寬度  
        PAGE_COUNT += 1; 
        mHorizontalScrollViewEx.getLayoutParams().width = mScreenWidth * PAGE_COUNT; 
                 
        //加載默認圖片資源  
        if(mDefaultBitmap == null) 
        { 
            BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();   
            bitmapFactoryOptions.inJustDecodeBounds = false;   
            bitmapFactoryOptions.inSampleSize = 2;   
            Resources res=mContext.getResources(); 
            mDefaultBitmap = BitmapFactory.decodeResource(res, R.drawable.banner_image_default, bitmapFactoryOptions); 
        } 
        mBannerBitmaps.add(mDefaultBitmap); 
        mBannerItemsList.add(null);  
        //加一個屏幕  
        LinearLayout linearLayoutScreen = new LinearLayout(mContext); 
        linearLayoutScreen.setOrientation(LinearLayout.VERTICAL); 
        linearLayoutScreen.setBackgroundDrawable(new BitmapDrawable( mBannerBitmaps.get(PAGE_COUNT – 1) )); 
        linearLayoutScrolLayout.addView(linearLayoutScreen, new LayoutParams( 
                mScreenWidth,  
                LayoutParams.FILL_PARENT)); 
        mLinearLayoutScreens.add(linearLayoutScreen); 
         
        //加一個小圓點  
        ImageView imageView = new ImageView(mContext); 
        imageView.setImageDrawable(mPageIndicator); 
        mImageViewList.add(imageView); 
        LinearLayout.LayoutParams layoutParamsForDot =  
                new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,  
                        LayoutParams.WRAP_CONTENT); 
        layoutParamsForDot.rightMargin = 5; 
        linearLayoutForDot.addView(imageView, layoutParamsForDot);       
    } 
     
    private class HorizontalScrollViewEx extends ViewGroup implements  
    OnGestureListener 
    { 
 
        private GestureDetector mGestureDetector; 
        private int mWhichScreen; 
 
        public HorizontalScrollViewEx(Context context, AttributeSet attrs, OnBannerClickListener bannerClickListener) 
        { 
            super(context, attrs); 
             
            mGestureDetector = new GestureDetector(this); 
            //解決長按屏幕後無法拖動的現象  
            mGestureDetector.setIsLongpressEnabled(false); 
 
            //構造彈性滑動對象  
            mScroller = new Scroller(context); 
        } 
 
        /**
         * 切換到指定屏幕
         * @param whichScreen 屏幕index
         */ 
        public void switchView(int whichScreen) 
        { 
            if(mLinearLayoutScreens == null) 
                return; 
             
            // 防止非法參數  
            if (whichScreen < 0) 
                whichScreen = 0; 
            else if(whichScreen >= PAGE_COUNT) 
                whichScreen = PAGE_COUNT – 1; 
 
            Logger.i(TAG, "switch view to " + whichScreen); 
 
            int delta = whichScreen * mScreenWidth – HorizontalScrollViewEx.this.getScrollX(); 
 
            //緩慢滾動到指定位置  
            mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 3); 
 
            // refresh  
            invalidate(); 
 
            //delta>0 stands for user scroll view to right  
            if (delta > 0)  
                mScrollToRight = true; 
            else  
            { 
                mScrollToRight = false; 
            } 
 
            mWhichScreen = whichScreen; 
            mWhich = whichScreen; 
            //切換小圓點  
            switchScreenPosition(mWhichScreen); 
        } 
 
        /**
         * 用戶輕觸觸摸屏,由1個MotionEvent ACTION_DOWN觸發
         */ 
        @Override 
        public boolean onDown(MotionEvent e) 
        { 
            Logger.i("MyGesture", "onDown"); 
             
            mScrollX = HorizontalScrollViewEx.this.getScrollX(); 
 
            return true; 
        } 
 
        /**
         * 用戶輕觸觸摸屏,尚未松開或拖動,由一個1個MotionEvent ACTION_DOWN觸發
         * 註意和onDown()的區別,強調的是沒有松開或者拖動的狀態
         */ 
        public void onShowPress(MotionEvent e) 
        { 
            Logger.i("MyGesture", "onShowPress"); 
        } 
 
        /**
         * 用戶(輕觸觸摸屏後)松開,由一個1個MotionEvent ACTION_UP觸發
         */ 
        public boolean onSingleTapUp(MotionEvent e) 
        { 
            Logger.i("MyGesture", "onSingleTapUp"); 
            if(mBannerItemsList == null || mBannerItemsList.size() <= mWhichScreen) 
                return false; 
             
            BannerItem bannerItem = mBannerItemsList.get(mWhichScreen); 
 
            if(bannerItem != null) 
            { 
                BannerMotionEvent bannerMotionEvent =  
                        new BannerMotionEvent(mWhichScreen, bannerItem.action, bannerItem.url,  
                                bannerItem.gameId, bannerItem.gameType, bannerItem.title); 
                mBannerClickListener.onBannerClick(bannerMotionEvent); 
            } 
 
            return false; 
        } 
 
        /** 用戶按下觸摸屏、快速移動後松開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE,
         * 1個ACTION_UP觸發
         */ 
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
                float velocityY) 
        { 
            Logger.i("MyGesture", "onFling velocityX=" + velocityX); 
 
            mWhichScreen = velocityX > 0 ? 
                    mWhichScreen – 1 
                    : mWhichScreen + 1; 
            switchView(mWhichScreen); 
 
            return true; 
        } 
 
        /** 
         * 用戶按下觸摸屏,並拖動,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE觸發
         */ 
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, 
                float distanceY) 
        { 
            Logger.i("MyGesture", "onScroll"); 
 
            //禁止彈性滾動  
            if (mOverScrollMode == false)  
            { 
                float x1 = e1.getX(); 
                float x2 = e2.getX(); 
                if(mWhichScreen == 0 && x1 < x2) 
                    return false; 
                else if(mWhichScreen == PAGE_COUNT – 1 && x1 > x2) 
                    return false; 
            } 
             
//          int distance = Math.abs(getScrollX() – mWhichScreen * mScreenWidth);      
//          if ((mWhichScreen ==0 || mWhichScreen == PAGE_COUNT -1) &&  distance > mOverScrollDistance)  
//              return false;  
             
            this.scrollBy((int)distanceX, 0); 
 
            return true; 
        } 
 
        /**
         *  用戶長按觸摸屏,由多個MotionEvent ACTION_DOWN觸發
         */ 
        public void onLongPress(MotionEvent e) 
        { 
            Logger.i("MyGesture", "onLongPress"); 
        } 
 
        @Override 
        public boolean onTouchEvent(MotionEvent event) 
        { 
            if(event.getAction() == MotionEvent.ACTION_DOWN) 
            { 
                mTimerResume = false; 
                if ( !mScroller.isFinished() ) 
                    mScroller.abortAnimation(); 
            } 
            else if(event.getAction() == MotionEvent.ACTION_UP) 
            { 
                //開始自動滑屏  
                mTimerResume = true; 
                mByUserAction = true; 
            } 
 
            boolean consume = mGestureDetector.onTouchEvent(event); 
 
            if (consume == false && event.getAction() == MotionEvent.ACTION_UP)  
            { 
                int curScrollX = HorizontalScrollViewEx.this.getScrollX(); 
                int mWhichScreen = (curScrollX + mScreenWidth / 2) /mScreenWidth; 
 
                switchView(mWhichScreen); 
            } 
 
            return consume; 
        } 
 
        @Override 
        public void computeScroll() 
        { 
            if (mScroller.computeScrollOffset()) 
            { 
                scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 
                postInvalidate(); 
            } 
        } 
 
        @Override 
        protected void onLayout(boolean changed, int l, int t, int r, int b) 
        { 
            if (changed)  
            { 
                int childLeft = 0; 
                final int childCount = getChildCount(); 
 
                for (int i=0; i<childCount; i++) 
                { 
                    final View childView = getChildAt(i); 
                    if (childView.getVisibility() != View.GONE) 
                    { 
                        final int childWidth = childView.getMeasuredWidth(); 
                        childView.layout(childLeft, 0,  
                                childLeft+childWidth, childView.getMeasuredHeight()); 
                        childLeft += childWidth; 
                    } 
                } 
            } 
        } 
 
        @Override   
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        { 
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);    
 
            final int width = MeasureSpec.getSize(widthMeasureSpec);    
            final int count = getChildCount();    
             
            for (int i = 0; i < count; i++) 
            {    
                getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);    
            } 
            scrollTo(mWhich * mScreenWidth, 0); 
        }   
 
    } 
 
    /**
     * override此函數,防止其修改構造方法所定義的寬和高<br/>
     *  註意:在這裡,設置寬和高將不起作用
     */ 
    @Override 
    public void setLayoutParams(android.view.ViewGroup.LayoutParams params) 
    { 
        params.width = mScreenWidth; 
        params.height = mScreenHeight; 
 
        super.setLayoutParams(params); 
    } 
 
    //標志view AttachedToWindow  
    @Override 
    protected void onAttachedToWindow() 
    { 
        super.onAttachedToWindow(); 
        mTimerResume = true; 
    } 
 
    //標志view已經脫離window,典型的情形是view被銷毀瞭,此時取消timer  
    @Override 
    protected void onDetachedFromWindow() 
    { 
        super.onDetachedFromWindow(); 
        Logger.d(TAG, "onDetachedFromWindow"); 
         
        mTimerResume = false; 
        int activityId = ( (BaseActivity)mContext ).activityId(); 
        //如果是賬號管理頁面 則釋放內存  
        if(activityId == BaseActivity.ACCOUNT_ID) 
        { 
            destroy(); 
        } 
    } 
     
    /**
     * 銷毀banner
     */ 
    public void destroy() 
    { 
        mTimerTask.cancel(); 
        timer.cancel(); 
        //去除各種bitmap對activity的引用關系  
        destoryBitmaps(); 
        System.gc(); 
    } 
     
    /**
     * 去除各種bitmap對activity的引用關系
     */ 
    private void destoryBitmaps() 
    {        
        for (View view : mLinearLayoutScreens)  
        { 
            Drawable drawable = view.getBackground(); 
            BitmapDrawable bitmapDrawable = null; 
            if(drawable instanceof BitmapDrawable) 
                bitmapDrawable = (BitmapDrawable)drawable; 
             
            if(bitmapDrawable != null) 
            { 
                //解除drawable對view的引用  
                bitmapDrawable.setCallback(null); 
                bitmapDrawable = null; 
            } 
        } 
         
        for (ImageView imageView : mImageViewList)  
        { 
            Drawable drawable = imageView.getDrawable(); 
            if(drawable != null) 
            { 
                drawable.setCallback(null); 
                drawable = null; 
            } 
        } 
         
        mPageIndicator.setCallback(null); 
        mPageIndicator = null; 
        mPageIndicatorFocused.setCallback(null); 
        mPageIndicatorFocused = null; 
         
        mLinearLayoutScreens.clear(); 
        mLinearLayoutScreens = null; 
         
        mBannerBitmaps.clear(); 
        mBannerBitmaps = null; 
         
        mImageViewList.clear(); 
        mImageViewList = null; 
         
        mBannerItemsList.clear(); 
        mBannerItemsList = null;     
    } 
     
    //單擊事件  
    @Override 
    public void onBannerClick( BannerMotionEvent bannerMotionEvent ) 
    { 
        final int position = bannerMotionEvent.index; 
        if(mContext == null) 
            return; 
         
        NotificationInfo notificationInfo = new NotificationInfo(); 
        notificationInfo.msgType = bannerMotionEvent.getAction(); 
        int action = bannerMotionEvent.getAction(); 
        if(action == NotificationInfo.NOTIFICATION_SINGLEGAME_MSG)    //單個遊戲消息,直接啟動該遊戲  
        { 
            try 
            { 
                notificationInfo.gameId = Integer.parseInt( bannerMotionEvent.getGameId() ); 
                notificationInfo.gameType = Integer.parseInt( bannerMotionEvent.getGameType() ); 
            }  
            catch (NumberFormatException e)  
            { 
                Logger.e(TAG, e.toString()); 
                return; 
            } 
        } 
        else if(action == NotificationInfo.NOTIFICATION_GAMEPAGE_MSG)    //遊戲主頁消息,通過客戶端展示遊戲主頁  
        { 
            try 
            { 
                notificationInfo.gameId = Integer.parseInt( bannerMotionEvent.getGameId() ); 
            }  
            catch (NumberFormatException e)  
            { 
                Logger.e(TAG, e.toString()); 
                return; 
            } 
            notificationInfo.issueTitle = bannerMotionEvent.getTitle(); 
        } 
        else if(action == NotificationInfo.NOTIFICATION_SHOW_WEBVIEW_MSG)    //交叉推廣消息,通過一個webview展示  
        { 
            notificationInfo.issueTitle = bannerMotionEvent.getTitle(); 
            notificationInfo.openUrl = bannerMotionEvent.getResponseUrl(); 
        } 
        else //reserved  
        { 
            return; 
        } 
         
        Intent intent = notificationInfo.generateIntent(mContext); 
        if(intent != null) 
            mContext.startActivity(intent); 
    } 
     
    /**
     * ScrollBanner所關聯的banner項 可以為多個 一個為一屏
     */ 
    public static class BannerItem extends Object 
    { 
        public static final String ACTION = "action"; 
        public static final String URL = "url"; 
        public static final String IMGURL = "imgurl"; 
        public static final String GAMEID = "gameid"; 
        public static final String GAMETYPE = "gametype"; 
        public static final String TITLE = "title"; 
 
        public int index = -1; 
        public int action = -1; 
        public String url = ""; 
        public String imgUrl = ""; 
        public String gameId = ""; 
        public String gameType = ""; 
        public String title = ""; 
 
        public BannerItem(){} 
    } 
 
    /**
     * BannerMotionEvent:單擊banner所產生的事件對象<br/>
     *getAction()來獲取動作類別<br/>
     *getResponseUrl()來獲取響應url<br/>
     *…
     */ 
    public static class BannerMotionEvent extends Object 
    { 
        /**
         * ACTION_PLAY_FLASH: 播放遊戲
         */ 
        public static final int ACTION_PLAY = 2; 
        /**
         * ACTION_HOMEPAGE:打開官網
         */ 
        public static final int ACTION_HOMEPAGE = 3; 
        /**
         * ACTION_OPEN_URL:打開指定url
         */ 
        public static final int ACTION_OPEN_URL = 4; 
 
        //banner中屏幕的index  
        private int index = -1; 
        //響應url  
        private String responseUrl = ""; 
        //動作種類  
        private int action = -1; 
        //gameid  
        private String gameId = ""; 
        //gametype flash遊戲(0) or h5遊戲(1)  
        private String gameType = ""; 
        //webview的標題  
        private String title = ""; 
         
        public BannerMotionEvent(int index, int action, String responseUrl,  
                String gameId, String gameType, String title) 
        { 
            BannerMotionEvent.this.index = index; 
            BannerMotionEvent.this.action = action; 
            BannerMotionEvent.this.responseUrl = responseUrl; 
            BannerMotionEvent.this.gameId = gameId; 
            BannerMotionEvent.this.gameType = gameType; 
            BannerMotionEvent.this.title = title; 
        } 
 
        /**
         * 獲取當前BannerMotionEvent事件對象的動作種類
         * @return 動作種類:ACTION_PLAY等
         */ 
        public int getAction() 
        { 
            return action; 
        } 
 
        /**
         * 獲取當前BannerMotionEvent事件對象的title
         * @return title webview的標題
         */ 
        public String getTitle() 
        { 
            return title; 
        } 
         
        /**
         * 獲取當前BannerMotionEvent事件對象的gameId
         * @return gameId
         */ 
        public String getGameId() 
        { 
            return gameId; 
        } 
         
        /**
         * 獲取當前BannerMotionEvent事件對象的gameType
         * @return gameType 0 or 1
         */ 
        public String getGameType() 
        { 
            return gameType; 
        } 
         
        /**
         * 獲取當前BannerMotionEvent事件對象的響應url
         * @return 響應url
         */ 
        public String getResponseUrl() 
        { 
            return responseUrl; 
        } 
 
        @SuppressLint("DefaultLocale") 
        @Override 
        public String toString() 
        { 
            return String.format("BannerMotionEvent { index=%d, action=%d, responseUrl=%s, gameId=%s, gameType=%s, title=%s }",  
                    index, action, responseUrl, gameId, gameType, title); 
        } 
    } 
 
    @Override 
    public void onBannerInfoSuccess(List<BannerItem> items) 
    { 
        Logger.d(TAG, "onBannerInfoSuccess"); 
        mBannerItemsList = items; 
        mHandler.sendEmptyMessage(MESSAGE_FETCH_BANNER_SUCCESS); 
    } 
 
    @Override 
    public void onBannerInfoFailed() 
    { 
        Logger.e(TAG, "onBannerInfoFailed"); 
    } 
 

/**
 * ScrollBanner 支持滑屏效果的FrameLayout子類,可設置屏幕數量,尺寸。<br/>
 * 典型的用法:<br/>
 *   ScrollBanner scrollBanner = new ScrollBanner(this, mScreenWidth, 100, this);<br/>
 *  linearLayout.addView(scrollBanner);<br/>
 *註意事項:<br/>
 *1.如果重新設置ScrollBanner的LayoutParams,則參數中的寬和高屬性將被忽略,仍然采用對象實例化的寬和高<br/>
 *2.點擊事件的回調如果設為null,則采用默認的事件回調<br/>
 *3.通過setOverScrollMode來設置 banner是否能夠滑出屏幕的邊界<br/>
 *4通過xml方式加載banner,需要進行如下調用:<br/>
 *  setResolution(width, height);<br/>
  setOnBannerClickListener(bannerClickListener);<br/>
  showBanner()<br/>
 * @author singwhatiwanna
 * @version 2013.3.4
 *
 */
public class ScrollBanner extends FrameLayout implements
ComponentCallBack.OnBannerClickListener,
ResponseHandler.BannerInfoHandler
{

 private static final String TAG = "ScrollBanner";

 private HorizontalScrollViewEx mHorizontalScrollViewEx;
 
 //ScrollBanner的子view
 private LinearLayout linearLayoutScrolLayout;
 
 //linearLayoutScrolLayout的子view,用於放置若幹個小圓點
 private LinearLayout linearLayoutForDot;
 
 private Scroller mScroller; 
 private Context mContext;
 private OnBannerClickListener mBannerClickListener;

 //屏幕及其bitmap
 private List<View> mLinearLayoutScreens = new ArrayList<View>();
 private List<Bitmap> mBannerBitmaps = new ArrayList<Bitmap>();

 //banner信息
 private List<BannerItem> mBannerItemsList = new ArrayList<BannerItem>();
 //小圓點
 private List<ImageView> mImageViewList = new ArrayList<ImageView>();
 private Drawable mPageIndicator;
 private Drawable mPageIndicatorFocused;
 
 //banner默認圖片
 private Bitmap mDefaultBitmap;

 private int mScreenWidth;
 private int mScreenHeight;
 private int mScrollX;

 //current screen index
 private int mWhich = 0;

 public static final int MESSAGE_AUTO_SCROLL = 1;

 public static final int MESSAGE_FETCH_BANNER_SUCCESS = 2;

 public static final int MARGIN_BOTTOM = 2;
 
 //480*150 banner的圖片尺寸 150.0/480=0.3125f
 public static final float ratio = 0.3125f;

 //banner的位置
 private int mLocation = -1;

 //banner分為幾屏
 private int PAGE_COUNT = 4;

 //滑動方向 是否向右滑動
 private boolean mScrollToRight = true;

 //是否自動滑屏
 private boolean mTimerResume = true;

 //標志用戶是否手動滑動瞭屏幕
 private boolean mByUserAction = false;

 //標志banner是否可以滑出邊界
 private boolean mOverScrollMode = false;
 //標志banner可以滑出邊界多少像素
 private int mOverScrollDistance = 0;

 //定時器 用於banner的自動播放
 final Timer timer = new Timer();

 //定時器的時間間隔 單位:ms
 public static final int TIMER_DURATION = 5000;

 private TimerTask mTimerTask = new TimerTask()
 {
  @Override
  public void run()
  {
   if (mTimerResume && !mByUserAction)
   {
    mHandler.sendEmptyMessage(MESSAGE_AUTO_SCROLL);
   }
   mByUserAction = false;
  }
 };

 //ScrollBanner私有handler 用於處理內部邏輯
 private Handler mHandler = new Handler()
 {
  public void handleMessage(Message msg)
  {
   //表示已經執行瞭onDetachedFromWindow,banner已經被銷毀瞭
   if( mBannerBitmaps == null || mLinearLayoutScreens == null ||
     mImageViewList == null || mBannerItemsList == null || mContext == null )
    return;
   
   switch (msg.what)
   {
   case MESSAGE_AUTO_SCROLL:
    if (mWhich == PAGE_COUNT – 1)
     mScrollToRight = false;
    else if(mWhich == 0)
    {
     mScrollToRight = true;
    }

    if (mScrollToRight)
     mWhich++;
    else
    {
     mWhich–;
    }

    mHorizontalScrollViewEx.switchView(mWhich);
    break;
   case MESSAGE_FETCH_BANNER_SUCCESS:
    int more = 0;
    if(mBannerItemsList != null)
     more = mBannerItemsList.size() – PAGE_COUNT;
    if(mBannerItemsList.size() > 0)
    {
     //如果有banner 顯示它
     ScrollBanner.this.show(true);
    }
    //如果後臺返回的banneritem的數量大於預設值4
    if(more > 0)
    {
     for (int i = 0; i < more; i++)
      addBannerItem();
    }
    fetchBannerImages();
    break;

   default:
    break;
   }
  };
 };

 //用於獲取bitmap
 private Handler  mBitmapHandler = new Handler()
 {

  public void handleMessage(Message msg)
  {
   //表示已經執行瞭onDetachedFromWindow,banner已經被銷毀瞭
   if( mBannerBitmaps == null || mLinearLayoutScreens == null ||
     mImageViewList == null || mBannerItemsList == null || mContext == null )
    return;
   
   Bitmap bitmap = (Bitmap)msg.obj;
   String urlString = msg.getData().getString("url");
   Logger.d(TAG, "url=" + urlString);
   if (urlString == null || bitmap == null || mBannerItemsList == null)
   {
    Logger.w(TAG, "bitmap=null imgurl=" + urlString);
    return;
   }

   for( int i = 0; i < mBannerItemsList.size(); i++ )
   {
    BannerItem item = mBannerItemsList.get(i);
    if(item != null && urlString.equals(item.imgUrl) )
    {
     Logger.d(TAG, "find " + i + urlString);
     if( mBannerBitmaps != null )
     {
      mBannerBitmaps.set( i, bitmap );
      setBannerImages(i);
     }
     break;
    }
   }
   
  };

 };

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

 public ScrollBanner(Context context, AttributeSet attrs)
 {
  super(context, attrs);
  mContext = context;
 }

 public ScrollBanner(Context context, AttributeSet attrs, int defStyle)
 {
  super(context, attrs, defStyle);
  mContext = context;
 }

 /**
  *
  * @param context activity實例
  * @param width banner的寬度 單位px
  * @param height banner的高度 單位dip,-1表示根據圖片比例自適應高度
  * @param bannerClickListener 單擊banner的回調接口
  */
 public ScrollBanner(Context context, final int width, final int height, OnBannerClickListener bannerClickListener)
 {
  this(context, null);

  int activityId = ( (BaseActivity)context ).activityId();
  if(activityId == BaseActivity.ACCOUNT_ID)//位置3
   mLocation = 3;
  else if(activityId == BaseActivity.GAMEZONE_ID)//位置2
  {
   mLocation = 2;
  }
  
  //初始化時不顯示banner
  this.show(false);
  setResolution(width, height);
  setOnBannerClickListener(bannerClickListener);
  setDefaultBannerImages();
  fetchBannerInfo();
 }

 /**
  * 通過xml方式加載banner,必須調用此方法才能顯示
  */
 public void showBanner()
 {
  int activityId = ( (BaseActivity)mContext ).activityId();
  if(activityId == BaseActivity.ACCOUNT_ID)//位置3
   mLocation = 3;
  else if(activityId == BaseActivity.GAMEZONE_ID)//位置2
  {
   mLocation = 2;
  }
  
  setDefaultBannerImages();
  fetchBannerInfo();
 }
 
 /**
  * 暫停滾動
  */
 public void pauseScroll()
 {
  mTimerResume = false;
 }
 
 /**
  * 恢復滾動
  */
 public void resumeScroll()
 {
  mTimerResume = true;
 }
 
 /**
  * 設置回調接口
  * @param callBack 單擊banner的回調接口
  */
 public void setOnBannerClickListener(OnBannerClickListener bannerClickListener)
 {
  mBannerClickListener = (bannerClickListener != null ? bannerClickListener : ScrollBanner.this);
 }

 /**
  * 設置banner的解析度
  * @param width banner的寬度
  * @param height banner的高度
  */
 public void setResolution(final int width, final int height)
 {
  int heightInPx = height;
  
  if(height == -1)
   heightInPx = (int)(ratio * width) ;
  else
  {
   Resources resources = getResources();
   heightInPx = Math.round( TypedValue.applyDimension(
     TypedValue.COMPLEX_UNIT_DIP, height, resources.getDisplayMetrics()) );
  }
  
  mScreenWidth = width;
  mScreenHeight = heightInPx;
  setLayoutParams(new LayoutParams(width, heightInPx));

  initScrollView();
 }
 
 /**
  * 獲取banner的高度
  * @return banner的高度 單位:px
  */
 public int getHeightPixels()
 {
  return mScreenHeight;
 }

 /**
  * 設置banner是否可以彈性滑出邊界
  * @param canOverScroll true表示可以滑出邊界,false不能
  */
 public void setOverScrollMode(boolean canOverScroll)
 {
  mOverScrollMode = canOverScroll;
  if(canOverScroll == false)
   mOverScrollDistance = 0;
 }

 /**
  * 向後臺獲取banner的各種信息
  */
 private void fetchBannerInfo()
 {
  NetworkManager netManager = (NetworkManager) AppEngine.getInstance().getManager(
    IManager.NETWORK_ID);
  netManager.getBannerInfo( String.valueOf(mLocation), ScrollBanner.this );
 }

 /**
  * 獲取banner的滑屏圖像
  */
 private void setDefaultBannerImages()
 {
  //為banner設置默認bitmap
        BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options(); 
        bitmapFactoryOptions.inJustDecodeBounds = false; 
        bitmapFactoryOptions.inSampleSize = 2; 

  Resources res=mContext.getResources();
  mDefaultBitmap = BitmapFactory.decodeResource(res, R.drawable.banner_image_default, bitmapFactoryOptions);
  
  for(int i = 0; i < PAGE_COUNT; i++)
   mBannerBitmaps.add(i, mDefaultBitmap);

  //初始化BannerItem對象
  for (int i = 0; i < PAGE_COUNT; i++)
   mBannerItemsList.add(i, null);

  setBannerImages(-1);
 }

 private void fetchBannerImages()
 {
  //表示已經執行瞭onDetachedFromWindow,banner已經被銷毀瞭
  if( mBannerItemsList == null )
   return;
  
  //ImageManager 根據url向其獲取bitmap
  ImageManager imageManager = (ImageManager)AppEngine.getInstance().
    getManager(IManager.IMAGE_ID);

  BannerItem item = null;
  for(int i = 0; i < PAGE_COUNT; i++)
  {
   try
   {
    item = mBannerItemsList.get(i);
   }
   catch (IndexOutOfBoundsException e)
   {
    Logger.e(TAG, "fetchBannerImages error: " + e);
   }
   catch (Exception e)
   {
    Logger.e(TAG, "fetchBannerImages error: " + e);
   }
   //ImageManager為多線程,采用常見的三級cache策略(內存、文件、網絡)
   if( item != null && item.imgUrl != null )
    imageManager.loadBitmap( item.imgUrl, mBitmapHandler );
  }
 }

 /**
  * 設置banner的滑屏圖像
  * @param position 如果position=-1,則表示設置全部bitmap
  */
 private void setBannerImages(final int position)
 {
  int size = mBannerBitmaps.size();
  if (size < PAGE_COUNT || mLinearLayoutScreens == null)
  {
   return;
  }
  if(position >=0 && position < PAGE_COUNT )
  {
   Drawable drawable = mLinearLayoutScreens.get(position).getBackground();
   mLinearLayoutScreens.get(position).setBackgroundDrawable
   (new BitmapDrawable( mBannerBitmaps.get(position) ) );
   drawable.setCallback(null);
   drawable = null;
   
   return;
  }

  for(int i = 0; i < PAGE_COUNT; i++)
  {
   mLinearLayoutScreens.get(i).setBackgroundDrawable(new BitmapDrawable(mBannerBitmaps.get(i)));
  }
 }

 /**
  * 是否顯示banner
  * @param isShow true顯示 false不顯示
  */
 public void show(boolean isShow)
 {
  if(isShow)
  {
   this.setVisibility(View.VISIBLE);
   mTimerResume = true;
  }
  else
  {
   this.setVisibility(View.GONE);
   mTimerResume = false;
  }
 }

 /**
  * 切換到指定屏幕
  * @param which 屏幕索引
  */
 public void switchToScreen(final int which)
 {
  mHorizontalScrollViewEx.switchView(which);
 }
 
 /**
  * 設置屏幕的數量 (此函數暫不開放)
  * @param count 屏幕數量
  */
 protected void setScreenCount(final int count)
 {
  PAGE_COUNT = count;
 }

 /**
  * 設置偏移的距離 如果mOverScrollMode為false,則此設置無效 (此函數暫不開放)
  * @param distance
  */
 protected void setOverScrollDistance(int distance)
 {
  if(distance < 0)
   distance = 0;

  mOverScrollDistance = mOverScrollMode ? distance : 0;
 }

 /**
  * 切換小圓點
  * @param position current screen index
  */
 private void switchScreenPosition(final int position)
 {
  if( mPageIndicator == null || mPageIndicatorFocused == null )
   return;
  
  int length = 0;
  if(mImageViewList != null)
   length = mImageViewList.size();
  if (position >= length || position < 0 || length <= 0)
  {
   return;
  }

  for(int i = 0; i < length; i++)
  {
   mImageViewList.get(i).setImageDrawable(mPageIndicator);
  }
  
  mImageViewList.get(position).setImageDrawable(mPageIndicatorFocused);
 }

 /**
  * 初始化整個FrameLayout視圖組
  */
 private void initScrollView()
 {
  setLayoutParams(new LayoutParams(mScreenWidth, mScreenHeight ));

  linearLayoutScrolLayout = new LinearLayout(mContext);
  linearLayoutScrolLayout.setBackgroundColor(Color.WHITE);
  linearLayoutScrolLayout.setOrientation(LinearLayout.HORIZONTAL);
  
  int mVersionCode = 8;
  try
  {
   mVersionCode = Integer.valueOf(android.os.Build.VERSION.SDK);
   Logger.d(TAG, "sdk version=" + mVersionCode);
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
  //針對android1.6及以下的特殊處理 此為android的低版本bug
  if(mVersionCode <= 5)
  {
   linearLayoutScrolLayout.setBaselineAligned(false);
  }
  
  //初始化四個滑動view
  for(int i = 0; i < PAGE_COUNT; i++)
  {
   LinearLayout linearLayoutScreen = new LinearLayout(mContext);
   linearLayoutScreen.setOrientation(LinearLayout.VERTICAL);
   linearLayoutScrolLayout.addView(linearLayoutScreen, new LayoutParams(
     mScreenWidth,
     LayoutParams.FILL_PARENT));

   mLinearLayoutScreens.add(i, linearLayoutScreen);
  }

  //初始化小圓點視圖
  RelativeLayout relativeLayout = new RelativeLayout(mContext);
  relativeLayout.setLayoutParams(new LayoutParams(
    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

  //linearLayoutForDot為小圓點視圖
  linearLayoutForDot =new LinearLayout(mContext);
  android.widget.RelativeLayout.LayoutParams layoutParams =
    new android.widget.RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
      LayoutParams.WRAP_CONTENT);
  //小圓點距底部的距離 單位:px
  layoutParams.bottomMargin = MARGIN_BOTTOM;
  layoutParams.rightMargin = MARGIN_BOTTOM;
  layoutParams.addRule(android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM);
  layoutParams.addRule(android.widget.RelativeLayout.CENTER_HORIZONTAL);
  linearLayoutForDot.setLayoutParams(layoutParams);
  linearLayoutForDot.setOrientation(LinearLayout.HORIZONTAL);
  linearLayoutForDot.setHorizontalGravity(Gravity.CENTER);
  linearLayoutForDot.setVerticalGravity(Gravity.CENTER);
  //下面兩句實現圓角半透明效果 不采用
  //  linearLayoutForDot.setBackgroundResource(R.drawable.round_corner_bg);
  //  linearLayoutForDot.getBackground().setAlpha(100);

  //初始化4個小圓點
  mPageIndicator = getResources().getDrawable(R.drawable.page_indicator);
  mPageIndicatorFocused = getResources().getDrawable(R.drawable.page_indicator_focused);
  for(int i = 0; i < PAGE_COUNT; i++)
  {
   ImageView imageView = new ImageView(mContext);
   imageView.setImageDrawable(mPageIndicator);
   mImageViewList.add(i, imageView);
   LinearLayout.LayoutParams layoutParamsForDot =
     new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
       LayoutParams.WRAP_CONTENT);
   layoutParamsForDot.rightMargin = 5;

   linearLayoutForDot.addView(imageView, layoutParamsForDot);
  }
  mImageViewList.get(0).setImageDrawable(mPageIndicatorFocused);
  relativeLayout.addView(linearLayoutForDot);

  mHorizontalScrollViewEx = new HorizontalScrollViewEx(mContext, null, mBannerClickListener);
  mHorizontalScrollViewEx.setLayoutParams(new LayoutParams(
    mScreenWidth * PAGE_COUNT,
    LayoutParams.FILL_PARENT));
  mHorizontalScrollViewEx.addView(linearLayoutScrolLayout, new LayoutParams(
    LayoutParams.FILL_PARENT,
    LayoutParams.FILL_PARENT));

  mHorizontalScrollViewEx.setHorizontalScrollBarEnabled(false);
  mHorizontalScrollViewEx.setHorizontalFadingEdgeEnabled(false);

  addView(mHorizontalScrollViewEx);
  addView(relativeLayout);

  //自動滑屏 5秒一次
  timer.schedule(mTimerTask, 5000, TIMER_DURATION);
 }

 /**
  * 加一個banner頁面 TODO此函數寫的不好
  */
 private void addBannerItem()
 {
  //表示已經執行瞭onDetachedFromWindow,banner已經被銷毀瞭
  if( mBannerBitmaps == null || mLinearLayoutScreens == null ||
    mImageViewList == null || mContext == null )
   return;
  
  //調整屏幕數量和總寬度
  PAGE_COUNT += 1;
  mHorizontalScrollViewEx.getLayoutParams().width = mScreenWidth * PAGE_COUNT;
         
        //加載默認圖片資源
  if(mDefaultBitmap == null)
  {
         BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options(); 
         bitmapFactoryOptions.inJustDecodeBounds = false; 
         bitmapFactoryOptions.inSampleSize = 2; 
   Resources res=mContext.getResources();
   mDefaultBitmap = BitmapFactory.decodeResource(res, R.drawable.banner_image_default, bitmapFactoryOptions);
  }
  mBannerBitmaps.add(mDefaultBitmap);
  mBannerItemsList.add(null); 
  //加一個屏幕
  LinearLayout linearLayoutScreen = new LinearLayout(mContext);
  linearLayoutScreen.setOrientation(LinearLayout.VERTICAL);
  linearLayoutScreen.setBackgroundDrawable(new BitmapDrawable( mBannerBitmaps.get(PAGE_COUNT – 1) ));
  linearLayoutScrolLayout.addView(linearLayoutScreen, new LayoutParams(
    mScreenWidth,
    LayoutParams.FILL_PARENT));
  mLinearLayoutScreens.add(linearLayoutScreen);
  
  //加一個小圓點
  ImageView imageView = new ImageView(mContext);
  imageView.setImageDrawable(mPageIndicator);
  mImageViewList.add(imageView);
  LinearLayout.LayoutParams layoutParamsForDot =
    new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
      LayoutParams.WRAP_CONTENT);
  layoutParamsForDot.rightMargin = 5;
  linearLayoutForDot.addView(imageView, layoutParamsForDot);  
 }
 
 private class HorizontalScrollViewEx extends ViewGroup implements
 OnGestureListener
 {

  private GestureDetector mGestureDetector;
  private int mWhichScreen;

  public HorizontalScrollViewEx(Context context, AttributeSet attrs, OnBannerClickListener bannerClickListener)
  {
   super(context, attrs);
   
   mGestureDetector = new GestureDetector(this);
   //解決長按屏幕後無法拖動的現象
   mGestureDetector.setIsLongpressEnabled(false);

   //構造彈性滑動對象
   mScroller = new Scroller(context);
  }

  /**
   * 切換到指定屏幕
   * @param whichScreen 屏幕index
   */
  public void switchView(int whichScreen)
  {
   if(mLinearLayoutScreens == null)
    return;
   
   // 防止非法參數
   if (whichScreen < 0)
    whichScreen = 0;
   else if(whichScreen >= PAGE_COUNT)
    whichScreen = PAGE_COUNT – 1;

   Logger.i(TAG, "switch view to " + whichScreen);

   int delta = whichScreen * mScreenWidth – HorizontalScrollViewEx.this.getScrollX();

   //緩慢滾動到指定位置
   mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 3);

   // refresh
   invalidate();

   //delta>0 stands for user scroll view to right
   if (delta > 0)
    mScrollToRight = true;
   else
   {
    mScrollToRight = false;
   }

   mWhichScreen = whichScreen;
   mWhich = whichScreen;
   //切換小圓點
   switchScreenPosition(mWhichScreen);
  }

  /**
   * 用戶輕觸觸摸屏,由1個MotionEvent ACTION_DOWN觸發
   */
  @Override
  public boolean onDown(MotionEvent e)
  {
   Logger.i("MyGesture", "onDown");
   
   mScrollX = HorizontalScrollViewEx.this.getScrollX();

   return true;
  }

  /**
   * 用戶輕觸觸摸屏,尚未松開或拖動,由一個1個MotionEvent ACTION_DOWN觸發
   * 註意和onDown()的區別,強調的是沒有松開或者拖動的狀態
   */
  public void onShowPress(MotionEvent e)
  {
   Logger.i("MyGesture", "onShowPress");
  }

  /**
   * 用戶(輕觸觸摸屏後)松開,由一個1個MotionEvent ACTION_UP觸發
   */
  public boolean onSingleTapUp(MotionEvent e)
  {
   Logger.i("MyGesture", "onSingleTapUp");
   if(mBannerItemsList == null || mBannerItemsList.size() <= mWhichScreen)
    return false;
   
   BannerItem bannerItem = mBannerItemsList.get(mWhichScreen);

   if(bannerItem != null)
   {
    BannerMotionEvent bannerMotionEvent =
      new BannerMotionEvent(mWhichScreen, bannerItem.action, bannerItem.url,
        bannerItem.gameId, bannerItem.gameType, bannerItem.title);
    mBannerClickListener.onBannerClick(bannerMotionEvent);
   }

   return false;
  }

  /** 用戶按下觸摸屏、快速移動後松開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE,
   * 1個ACTION_UP觸發
   */
  public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
    float velocityY)
  {
   Logger.i("MyGesture", "onFling velocityX=" + velocityX);

   mWhichScreen = velocityX > 0 ?
     mWhichScreen – 1
     : mWhichScreen + 1;
   switchView(mWhichScreen);

   return true;
  }

  /**
   * 用戶按下觸摸屏,並拖動,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE觸發
   */
  public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
    float distanceY)
  {
   Logger.i("MyGesture", "onScroll");

   //禁止彈性滾動
   if (mOverScrollMode == false)
   {
    float x1 = e1.getX();
    float x2 = e2.getX();
    if(mWhichScreen == 0 && x1 < x2)
     return false;
    else if(mWhichScreen == PAGE_COUNT – 1 && x1 > x2)
     return false;
   }
   
//   int distance = Math.abs(getScrollX() – mWhichScreen * mScreenWidth); 
//   if ((mWhichScreen ==0 || mWhichScreen == PAGE_COUNT -1) &&  distance > mOverScrollDistance)
//    return false;
   
   this.scrollBy((int)distanceX, 0);

   return true;
  }

  /**
   *  用戶長按觸摸屏,由多個MotionEvent ACTION_DOWN觸發
   */
  public void onLongPress(MotionEvent e)
  {
   Logger.i("MyGesture", "onLongPress");
  }

  @Override
  public boolean onTouchEvent(MotionEvent event)
  {
   if(event.getAction() == MotionEvent.ACTION_DOWN)
   {
    mTimerResume = false;
    if ( !mScroller.isFinished() )
     mScroller.abortAnimation();
   }
   else if(event.getAction() == MotionEvent.ACTION_UP)
   {
    //開始自動滑屏
    mTimerResume = true;
    mByUserAction = true;
   }

   boolean consume = mGestureDetector.onTouchEvent(event);

   if (consume == false && event.getAction() == MotionEvent.ACTION_UP)
   {
    int curScrollX = HorizontalScrollViewEx.this.getScrollX();
    int mWhichScreen = (curScrollX + mScreenWidth / 2) /mScreenWidth;

    switchView(mWhichScreen);
   }

   return consume;
  }

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

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b)
  {
   if (changed)
   {
    int childLeft = 0;
    final int childCount = getChildCount();

    for (int i=0; i<childCount; i++)
    {
     final View childView = getChildAt(i);
     if (childView.getVisibility() != View.GONE)
     {
      final int childWidth = childView.getMeasuredWidth();
      childView.layout(childLeft, 0,
        childLeft+childWidth, childView.getMeasuredHeight());
      childLeft += childWidth;
     }
    }
   }
  }

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

   final int width = MeasureSpec.getSize(widthMeasureSpec);  
   final int count = getChildCount();  
   
   for (int i = 0; i < count; i++)
   {  
    getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);  
   }
   scrollTo(mWhich * mScreenWidth, 0);
  } 

 }

 /**
  * override此函數,防止其修改構造方法所定義的寬和高<br/>
  *  註意:在這裡,設置寬和高將不起作用
  */
 @Override
 public void setLayoutParams(android.view.ViewGroup.LayoutParams params)
 {
  params.width = mScreenWidth;
  params.height = mScreenHeight;

  super.setLayoutParams(params);
 }

 //標志view AttachedToWindow
 @Override
 protected void onAttachedToWindow()
 {
  super.onAttachedToWindow();
  mTimerResume = true;
 }

 //標志view已經脫離window,典型的情形是view被銷毀瞭,此時取消timer
 @Override
 protected void onDetachedFromWindow()
 {
  super.onDetachedFromWindow();
  Logger.d(TAG, "onDetachedFromWindow");
  
  mTimerResume = false;
  int activityId = ( (BaseActivity)mContext ).activityId();
  //如果是賬號管理頁面 則釋放內存
  if(activityId == BaseActivity.ACCOUNT_ID)
  {
   destroy();
  }
 }
 
 /**
  * 銷毀banner
  */
 public void destroy()
 {
  mTimerTask.cancel();
  timer.cancel();
  //去除各種bitmap對activity的引用關系
  destoryBitmaps();
  System.gc();
 }
 
 /**
  * 去除各種bitmap對activity的引用關系
  */
 private void destoryBitmaps()
 {  
  for (View view : mLinearLayoutScreens)
  {
   Drawable drawable = view.getBackground();
   BitmapDrawable bitmapDrawable = null;
   if(drawable instanceof BitmapDrawable)
    bitmapDrawable = (BitmapDrawable)drawable;
   
   if(bitmapDrawable != null)
   {
    //解除drawable對view的引用
    bitmapDrawable.setCallback(null);
    bitmapDrawable = null;
   }
  }
  
  for (ImageView imageView : mImageViewList)
  {
   Drawable drawable = imageView.getDrawable();
   if(drawable != null)
   {
    drawable.setCallback(null);
    drawable = null;
   }
  }
  
  mPageIndicator.setCallback(null);
  mPageIndicator = null;
  mPageIndicatorFocused.setCallback(null);
  mPageIndicatorFocused = null;
  
  mLinearLayoutScreens.clear();
  mLinearLayoutScreens = null;
  
  mBannerBitmaps.clear();
  mBannerBitmaps = null;
  
  mImageViewList.clear();
  mImageViewList = null;
  
  mBannerItemsList.clear();
  mBannerItemsList = null; 
 }
 
 //單擊事件
 @Override
 public void onBannerClick( BannerMotionEvent bannerMotionEvent )
 {
  final int position = bannerMotionEvent.index;
  if(mContext == null)
   return;
  
  NotificationInfo notificationInfo = new NotificationInfo();
  notificationInfo.msgType = bannerMotionEvent.getAction();
  int action = bannerMotionEvent.getAction();
  if(action == NotificationInfo.NOTIFICATION_SINGLEGAME_MSG)    //單個遊戲消息,直接啟動該遊戲
  {
   try
   {
    notificationInfo.gameId = Integer.parseInt( bannerMotionEvent.getGameId() );
    notificationInfo.gameType = Integer.parseInt( bannerMotionEvent.getGameType() );
   }
   catch (NumberFormatException e)
   {
    Logger.e(TAG, e.toString());
    return;
   }
  }
  else if(action == NotificationInfo.NOTIFICATION_GAMEPAGE_MSG)    //遊戲主頁消息,通過客戶端展示遊戲主頁
  {
   try
   {
    notificationInfo.gameId = Integer.parseInt( bannerMotionEvent.getGameId() );
   }
   catch (NumberFormatException e)
   {
    Logger.e(TAG, e.toString());
    return;
   }
   notificationInfo.issueTitle = bannerMotionEvent.getTitle();
  }
  else if(action == NotificationInfo.NOTIFICATION_SHOW_WEBVIEW_MSG)    //交叉推廣消息,通過一個webview展示
  {
   notificationInfo.issueTitle = bannerMotionEvent.getTitle();
   notificationInfo.openUrl = bannerMotionEvent.getResponseUrl();
  }
  else //reserved
  {
   return;
  }
  
  Intent intent = notificationInfo.generateIntent(mContext);
  if(intent != null)
   mContext.startActivity(intent);
 }
 
 /**
  * ScrollBanner所關聯的banner項 可以為多個 一個為一屏
  */
 public static class BannerItem extends Object
 {
  public static final String ACTION = "action";
  public static final String URL = "url";
  public static final String IMGURL = "imgurl";
  public static final String GAMEID = "gameid";
  public static final String GAMETYPE = "gametype";
  public static final String TITLE = "title";

  public int index = -1;
  public int action = -1;
  public String url = "";
  public String imgUrl = "";
  public String gameId = "";
  public String gameType = "";
  public String title = "";

  public BannerItem(){}
 }

 /**
  * BannerMotionEvent:單擊banner所產生的事件對象<br/>
  *getAction()來獲取動作類別<br/>
  *getResponseUrl()來獲取響應url<br/>
  *…
  */
 public static class BannerMotionEvent extends Object
 {
  /**
   * ACTION_PLAY_FLASH: 播放遊戲
   */
  public static final int ACTION_PLAY = 2;
  /**
   * ACTION_HOMEPAGE:打開官網
   */
  public static final int ACTION_HOMEPAGE = 3;
  /**
   * ACTION_OPEN_URL:打開指定url
   */
  public static final int ACTION_OPEN_URL = 4;

  //banner中屏幕的index
  private int index = -1;
  //響應url
  private String responseUrl = "";
  //動作種類
  private int action = -1;
  //gameid
  private String gameId = "";
  //gametype flash遊戲(0) or h5遊戲(1)
  private String gameType = "";
  //webview的標題
  private String title = "";
  
  public BannerMotionEvent(int index, int action, String responseUrl,
    String gameId, String gameType, String title)
  {
   BannerMotionEvent.this.index = index;
   BannerMotionEvent.this.action = action;
   BannerMotionEvent.this.responseUrl = responseUrl;
   BannerMotionEvent.this.gameId = gameId;
   BannerMotionEvent.this.gameType = gameType;
   BannerMotionEvent.this.title = title;
  }

  /**
   * 獲取當前BannerMotionEvent事件對象的動作種類
   * @return 動作種類:ACTION_PLAY等
   */
  public int getAction()
  {
   return action;
  }

  /**
   * 獲取當前BannerMotionEvent事件對象的title
   * @return title webview的標題
   */
  public String getTitle()
  {
   return title;
  }
  
  /**
   * 獲取當前BannerMotionEvent事件對象的gameId
   * @return gameId
   */
  public String getGameId()
  {
   return gameId;
  }
  
  /**
   * 獲取當前BannerMotionEvent事件對象的gameType
   * @return gameType 0 or 1
   */
  public String getGameType()
  {
   return gameType;
  }
  
  /**
   * 獲取當前BannerMotionEvent事件對象的響應url
   * @return 響應url
   */
  public String getResponseUrl()
  {
   return responseUrl;
  }

  @SuppressLint("DefaultLocale")
  @Override
  public String toString()
  {
   return String.format("BannerMotionEvent { index=%d, action=%d, responseUrl=%s, gameId=%s, gameType=%s, title=%s }",
     index, action, responseUrl, gameId, gameType, title);
  }
 }

 @Override
 public void onBannerInfoSuccess(List<BannerItem> items)
 {
  Logger.d(TAG, "onBannerInfoSuccess");
  mBannerItemsList = items;
  mHandler.sendEmptyMessage(MESSAGE_FETCH_BANNER_SUCCESS);
 }

 @Override
 public void onBannerInfoFailed()
 {
  Logger.e(TAG, "onBannerInfoFailed");
 }

}

ComponentCallBack.java

[java]
public interface ComponentCallBack  

 
    public static interface OnBannerClickListener 
    { 
        /**
         * banner單擊事件
         * @param bannerMotionEvent 單擊事件對象,包含所需的響應信息
         * 參見 {@link BannerMotionEvent}
         */ 
        public abstract void onBannerClick( BannerMotionEvent bannerMotionEvent ); 
    } 
     

You May Also Like