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

  1. /**
  2. * ScrollBanner 支持滑屏效果的FrameLayout子类,可设置屏幕数量,尺寸。<br/>
  3. * 典型的用法:<br/>
  4. *      ScrollBanner scrollBanner = new ScrollBanner(this, mScreenWidth, 100, this);<br/>
  5. *      linearLayout.addView(scrollBanner);<br/>
  6. *注意事项:<br/>
  7. *1.如果重新设置ScrollBanner的LayoutParams,则参数中的宽和高属性将被忽略,仍然采用对象实例化的宽和高<br/>
  8. *2.点击事件的回调如果设为null,则采用默认的事件回调<br/>
  9. *3.通过setOverScrollMode来设置 banner是否能够滑出屏幕的边界<br/>
  10. *4通过xml方式加载banner,需要进行如下调用:<br/>
  11. *      setResolution(width, height);<br/>
  12.         setOnBannerClickListener(bannerClickListener);<br/>
  13.         showBanner()<br/>
  14. * @author singwhatiwanna
  15. * @version 2013.3.4
  16. *
  17. */ 
  18. public class ScrollBanner extends FrameLayout implements  
  19. ComponentCallBack.OnBannerClickListener,  
  20. ResponseHandler.BannerInfoHandler 
  21.  
  22.     private static final String TAG = "ScrollBanner"
  23.  
  24.     private HorizontalScrollViewEx mHorizontalScrollViewEx; 
  25.      
  26.     //ScrollBanner的子view 
  27.     private LinearLayout linearLayoutScrolLayout; 
  28.      
  29.     //linearLayoutScrolLayout的子view,用于放置若干个小圆点 
  30.     private LinearLayout linearLayoutForDot; 
  31.      
  32.     private Scroller mScroller;  
  33.     private Context mContext; 
  34.     private OnBannerClickListener mBannerClickListener; 
  35.  
  36.     //屏幕及其bitmap 
  37.     private List<View> mLinearLayoutScreens = new ArrayList<View>(); 
  38.     private List<Bitmap> mBannerBitmaps = new ArrayList<Bitmap>(); 
  39.  
  40.     //banner信息 
  41.     private List<BannerItem> mBannerItemsList = new ArrayList<BannerItem>(); 
  42.     //小圆点 
  43.     private List<ImageView> mImageViewList = new ArrayList<ImageView>(); 
  44.     private Drawable mPageIndicator; 
  45.     private Drawable mPageIndicatorFocused; 
  46.      
  47.     //banner默认图片 
  48.     private Bitmap mDefaultBitmap; 
  49.  
  50.     private int mScreenWidth; 
  51.     private int mScreenHeight; 
  52.     private int mScrollX; 
  53.  
  54.     //current screen index 
  55.     private int mWhich = 0
  56.  
  57.     public static final int MESSAGE_AUTO_SCROLL = 1
  58.  
  59.     public static final int MESSAGE_FETCH_BANNER_SUCCESS = 2
  60.  
  61.     public static final int MARGIN_BOTTOM = 2
  62.      
  63.     //480*150 banner的图片尺寸 150.0/480=0.3125f 
  64.     public static final float ratio = 0.3125f; 
  65.  
  66.     //banner的位置 
  67.     private int mLocation = -1
  68.  
  69.     //banner分为几屏 
  70.     private int PAGE_COUNT = 4
  71.  
  72.     //滑动方向 是否向右滑动 
  73.     private boolean mScrollToRight = true
  74.  
  75.     //是否自动滑屏 
  76.     private boolean mTimerResume = true
  77.  
  78.     //标志用户是否手动滑动了屏幕 
  79.     private boolean mByUserAction = false
  80.  
  81.     //标志banner是否可以滑出边界 
  82.     private boolean mOverScrollMode = false
  83.     //标志banner可以滑出边界多少像素 
  84.     private int mOverScrollDistance = 0
  85.  
  86.     //定时器 用于banner的自动播放 
  87.     final Timer timer = new Timer(); 
  88.  
  89.     //定时器的时间间隔 单位:ms 
  90.     public static final int TIMER_DURATION = 5000
  91.  
  92.     private TimerTask mTimerTask = new TimerTask() 
  93.     { 
  94.         @Override 
  95.         public void run()  
  96.         { 
  97.             if (mTimerResume && !mByUserAction)  
  98.             { 
  99.                 mHandler.sendEmptyMessage(MESSAGE_AUTO_SCROLL); 
  100.             } 
  101.             mByUserAction = false
  102.         } 
  103.     }; 
  104.  
  105.     //ScrollBanner私有handler 用于处理内部逻辑 
  106.     private Handler mHandler = new Handler() 
  107.     { 
  108.         public void handleMessage(Message msg) 
  109.         { 
  110.             //表示已经执行了onDetachedFromWindow,banner已经被销毁了 
  111.             if( mBannerBitmaps == null || mLinearLayoutScreens == null ||  
  112.                     mImageViewList == null || mBannerItemsList == null || mContext == null
  113.                 return
  114.              
  115.             switch (msg.what) 
  116.             { 
  117.             case MESSAGE_AUTO_SCROLL: 
  118.                 if (mWhich == PAGE_COUNT - 1
  119.                     mScrollToRight = false
  120.                 else if(mWhich == 0
  121.                 { 
  122.                     mScrollToRight = true
  123.                 } 
  124.  
  125.                 if (mScrollToRight)  
  126.                     mWhich++; 
  127.                 else  
  128.                 { 
  129.                     mWhich--; 
  130.                 } 
  131.  
  132.                 mHorizontalScrollViewEx.switchView(mWhich); 
  133.                 break
  134.             case MESSAGE_FETCH_BANNER_SUCCESS: 
  135.                 int more = 0
  136.                 if(mBannerItemsList != null
  137.                     more = mBannerItemsList.size() - PAGE_COUNT; 
  138.                 if(mBannerItemsList.size() > 0
  139.                 { 
  140.                     //如果有banner 显示它 
  141.                     ScrollBanner.this.show(true); 
  142.                 } 
  143.                 //如果后台返回的banneritem的数量大于预设值4 
  144.                 if(more > 0
  145.                 { 
  146.                     for (int i = 0; i < more; i++) 
  147.                         addBannerItem(); 
  148.                 } 
  149.                 fetchBannerImages(); 
  150.                 break
  151.  
  152.             default
  153.                 break
  154.             } 
  155.         }; 
  156.     }; 
  157.  
  158.     //用于获取bitmap 
  159.     private Handler  mBitmapHandler = new Handler() 
  160.     { 
  161.  
  162.         public void handleMessage(Message msg) 
  163.         { 
  164.             //表示已经执行了onDetachedFromWindow,banner已经被销毁了 
  165.             if( mBannerBitmaps == null || mLinearLayoutScreens == null ||  
  166.                     mImageViewList == null || mBannerItemsList == null || mContext == null
  167.                 return
  168.              
  169.             Bitmap bitmap = (Bitmap)msg.obj; 
  170.             String urlString = msg.getData().getString("url"); 
  171.             Logger.d(TAG, "url=" + urlString); 
  172.             if (urlString == null || bitmap == null || mBannerItemsList == null
  173.             { 
  174.                 Logger.w(TAG, "bitmap=null imgurl=" + urlString); 
  175.                 return
  176.             } 
  177.  
  178.             for( int i = 0; i < mBannerItemsList.size(); i++ ) 
  179.             { 
  180.                 BannerItem item = mBannerItemsList.get(i); 
  181.                 if(item != null && urlString.equals(item.imgUrl) ) 
  182.                 { 
  183.                     Logger.d(TAG, "find " + i + urlString); 
  184.                     if( mBannerBitmaps != null )  
  185.                     { 
  186.                         mBannerBitmaps.set( i, bitmap ); 
  187.                         setBannerImages(i); 
  188.                     } 
  189.                     break
  190.                 } 
  191.             } 
  192.              
  193.         }; 
  194.  
  195.     }; 
  196.  
  197.     public ScrollBanner(Context context) 
  198.     { 
  199.         this(context, null); 
  200.     } 
  201.  
  202.     public ScrollBanner(Context context, AttributeSet attrs) 
  203.     { 
  204.         super(context, attrs); 
  205.         mContext = context; 
  206.     } 
  207.  
  208.     public ScrollBanner(Context context, AttributeSet attrs, int defStyle) 
  209.     { 
  210.         super(context, attrs, defStyle); 
  211.         mContext = context; 
  212.     } 
  213.  
  214.     /**
  215.      *
  216.      * @param context activity实例
  217.      * @param width banner的宽度 单位px
  218.      * @param height banner的高度 单位dip,-1表示根据图片比例自适应高度
  219.      * @param bannerClickListener 单击banner的回调接口
  220.      */ 
  221.     public ScrollBanner(Context context, final int width, final int height, OnBannerClickListener bannerClickListener) 
  222.     { 
  223.         this(context, null); 
  224.  
  225.         int activityId = ( (BaseActivity)context ).activityId(); 
  226.         if(activityId == BaseActivity.ACCOUNT_ID)//位置3 
  227.             mLocation = 3
  228.         else if(activityId == BaseActivity.GAMEZONE_ID)//位置2 
  229.         { 
  230.             mLocation = 2
  231.         } 
  232.          
  233.         //初始化时不显示banner 
  234.         this.show(false); 
  235.         setResolution(width, height); 
  236.         setOnBannerClickListener(bannerClickListener); 
  237.         setDefaultBannerImages(); 
  238.         fetchBannerInfo(); 
  239.     } 
  240.  
  241.     /**
  242.      * 通过xml方式加载banner,必须调用此方法才能显示
  243.      */ 
  244.     public void showBanner() 
  245.     { 
  246.         int activityId = ( (BaseActivity)mContext ).activityId(); 
  247.         if(activityId == BaseActivity.ACCOUNT_ID)//位置3 
  248.             mLocation = 3
  249.         else if(activityId == BaseActivity.GAMEZONE_ID)//位置2 
  250.         { 
  251.             mLocation = 2
  252.         } 
  253.          
  254.         setDefaultBannerImages(); 
  255.         fetchBannerInfo(); 
  256.     } 
  257.      
  258.     /**
  259.      * 暂停滚动
  260.      */ 
  261.     public void pauseScroll() 
  262.     { 
  263.         mTimerResume = false
  264.     } 
  265.      
  266.     /**
  267.      * 恢复滚动
  268.      */ 
  269.     public void resumeScroll() 
  270.     { 
  271.         mTimerResume = true
  272.     } 
  273.      
  274.     /**
  275.      * 设置回调接口
  276.      * @param callBack 单击banner的回调接口
  277.      */ 
  278.     public void setOnBannerClickListener(OnBannerClickListener bannerClickListener) 
  279.     { 
  280.         mBannerClickListener = (bannerClickListener != null ? bannerClickListener : ScrollBanner.this); 
  281.     } 
  282.  
  283.     /**
  284.      * 设置banner的解析度
  285.      * @param width banner的宽度
  286.      * @param height banner的高度
  287.      */ 
  288.     public void setResolution(final int width, final int height) 
  289.     { 
  290.         int heightInPx = height; 
  291.          
  292.         if(height == -1
  293.             heightInPx = (int)(ratio * width) ; 
  294.         else 
  295.         { 
  296.             Resources resources = getResources(); 
  297.             heightInPx = Math.round( TypedValue.applyDimension( 
  298.                     TypedValue.COMPLEX_UNIT_DIP, height, resources.getDisplayMetrics()) ); 
  299.         } 
  300.          
  301.         mScreenWidth = width; 
  302.         mScreenHeight = heightInPx; 
  303.         setLayoutParams(new LayoutParams(width, heightInPx)); 
  304.  
  305.         initScrollView(); 
  306.     } 
  307.      
  308.     /**
  309.      * 获取banner的高度
  310.      * @return banner的高度 单位:px
  311.      */ 
  312.     public int getHeightPixels() 
  313.     { 
  314.         return mScreenHeight; 
  315.     } 
  316.  
  317.     /**
  318.      * 设置banner是否可以弹性滑出边界
  319.      * @param canOverScroll true表示可以滑出边界,false不能
  320.      */ 
  321.     public void setOverScrollMode(boolean canOverScroll) 
  322.     { 
  323.         mOverScrollMode = canOverScroll; 
  324.         if(canOverScroll == false
  325.             mOverScrollDistance = 0
  326.     } 
  327.  
  328.     /**
  329.      * 向后台获取banner的各种信息
  330.      */ 
  331.     private void fetchBannerInfo() 
  332.     { 
  333.         NetworkManager netManager = (NetworkManager) AppEngine.getInstance().getManager( 
  334.                 IManager.NETWORK_ID); 
  335.         netManager.getBannerInfo( String.valueOf(mLocation), ScrollBanner.this ); 
  336.     } 
  337.  
  338.     /**
  339.      * 获取banner的滑屏图像
  340.      */ 
  341.     private void setDefaultBannerImages() 
  342.     { 
  343.         //为banner设置默认bitmap 
  344.         BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();   
  345.         bitmapFactoryOptions.inJustDecodeBounds = false;   
  346.         bitmapFactoryOptions.inSampleSize = 2;   
  347.  
  348.         Resources res=mContext.getResources(); 
  349.         mDefaultBitmap = BitmapFactory.decodeResource(res, R.drawable.banner_image_default, bitmapFactoryOptions); 
  350.          
  351.         for(int i = 0; i < PAGE_COUNT; i++) 
  352.             mBannerBitmaps.add(i, mDefaultBitmap); 
  353.  
  354.         //初始化BannerItem对象 
  355.         for (int i = 0; i < PAGE_COUNT; i++) 
  356.             mBannerItemsList.add(i, null); 
  357.  
  358.         setBannerImages(-1); 
  359.     } 
  360.  
  361.     private void fetchBannerImages() 
  362.     { 
  363.         //表示已经执行了onDetachedFromWindow,banner已经被销毁了 
  364.         if( mBannerItemsList == null
  365.             return
  366.          
  367.         //ImageManager 根据url向其获取bitmap 
  368.         ImageManager imageManager = (ImageManager)AppEngine.getInstance(). 
  369.                 getManager(IManager.IMAGE_ID); 
  370.  
  371.         BannerItem item = null
  372.         for(int i = 0; i < PAGE_COUNT; i++) 
  373.         { 
  374.             try 
  375.             { 
  376.                 item = mBannerItemsList.get(i); 
  377.             }  
  378.             catch (IndexOutOfBoundsException e) 
  379.             { 
  380.                 Logger.e(TAG, "fetchBannerImages error: " + e); 
  381.             } 
  382.             catch (Exception e)  
  383.             { 
  384.                 Logger.e(TAG, "fetchBannerImages error: " + e); 
  385.             } 
  386.             //ImageManager为多线程,采用常见的三级cache策略(内存、文件、网络) 
  387.             if( item != null && item.imgUrl != null
  388.                 imageManager.loadBitmap( item.imgUrl, mBitmapHandler ); 
  389.         } 
  390.     } 
  391.  
  392.     /**
  393.      * 设置banner的滑屏图像
  394.      * @param position 如果position=-1,则表示设置全部bitmap
  395.      */ 
  396.     private void setBannerImages(final int position) 
  397.     { 
  398.         int size = mBannerBitmaps.size(); 
  399.         if (size < PAGE_COUNT || mLinearLayoutScreens == null
  400.         { 
  401.             return
  402.         } 
  403.         if(position >=0 && position < PAGE_COUNT ) 
  404.         { 
  405.             Drawable drawable = mLinearLayoutScreens.get(position).getBackground(); 
  406.             mLinearLayoutScreens.get(position).setBackgroundDrawable 
  407.             (new BitmapDrawable( mBannerBitmaps.get(position) ) ); 
  408.             drawable.setCallback(null); 
  409.             drawable = null
  410.              
  411.             return
  412.         } 
  413.  
  414.         for(int i = 0; i < PAGE_COUNT; i++) 
  415.         { 
  416.             mLinearLayoutScreens.get(i).setBackgroundDrawable(new BitmapDrawable(mBannerBitmaps.get(i))); 
  417.         } 
  418.     } 
  419.  
  420.     /**
  421.      * 是否显示banner
  422.      * @param isShow true显示 false不显示
  423.      */ 
  424.     public void show(boolean isShow) 
  425.     { 
  426.         if(isShow) 
  427.         { 
  428.             this.setVisibility(View.VISIBLE); 
  429.             mTimerResume = true
  430.         } 
  431.         else  
  432.         { 
  433.             this.setVisibility(View.GONE); 
  434.             mTimerResume = false
  435.         } 
  436.     } 
  437.  
  438.     /**
  439.      * 切换到指定屏幕
  440.      * @param which 屏幕索引
  441.      */ 
  442.     public void switchToScreen(final int which) 
  443.     { 
  444.         mHorizontalScrollViewEx.switchView(which); 
  445.     } 
  446.      
  447.     /**
  448.      * 设置屏幕的数量 (此函数暂不开放)
  449.      * @param count 屏幕数量
  450.      */ 
  451.     protected void setScreenCount(final int count) 
  452.     { 
  453.         PAGE_COUNT = count; 
  454.     } 
  455.  
  456.     /**
  457.      * 设置偏移的距离 如果mOverScrollMode为false,则此设置无效 (此函数暂不开放)
  458.      * @param distance
  459.      */ 
  460.     protected void setOverScrollDistance(int distance) 
  461.     { 
  462.         if(distance < 0
  463.             distance = 0
  464.  
  465.         mOverScrollDistance = mOverScrollMode ? distance : 0
  466.     } 
  467.  
  468.     /**
  469.      * 切换小圆点
  470.      * @param position current screen index
  471.      */ 
  472.     private void switchScreenPosition(final int position) 
  473.     { 
  474.         if( mPageIndicator == null || mPageIndicatorFocused == null
  475.             return
  476.          
  477.         int length = 0
  478.         if(mImageViewList != null
  479.             length = mImageViewList.size(); 
  480.         if (position >= length || position < 0 || length <= 0
  481.         { 
  482.             return
  483.         } 
  484.  
  485.         for(int i = 0; i < length; i++) 
  486.         { 
  487.             mImageViewList.get(i).setImageDrawable(mPageIndicator); 
  488.         } 
  489.          
  490.         mImageViewList.get(position).setImageDrawable(mPageIndicatorFocused); 
  491.     } 
  492.  
  493.     /**
  494.      * 初始化整个FrameLayout视图组
  495.      */ 
  496.     private void initScrollView() 
  497.     { 
  498.         setLayoutParams(new LayoutParams(mScreenWidth, mScreenHeight )); 
  499.  
  500.         linearLayoutScrolLayout = new LinearLayout(mContext); 
  501.         linearLayoutScrolLayout.setBackgroundColor(Color.WHITE); 
  502.         linearLayoutScrolLayout.setOrientation(LinearLayout.HORIZONTAL); 
  503.          
  504.         int mVersionCode = 8
  505.         try 
  506.         { 
  507.             mVersionCode = Integer.valueOf(android.os.Build.VERSION.SDK); 
  508.             Logger.d(TAG, "sdk version=" + mVersionCode); 
  509.         }  
  510.         catch (Exception e)  
  511.         { 
  512.             e.printStackTrace(); 
  513.         } 
  514.         //针对android1.6及以下的特殊处理 此为android的低版本bug 
  515.         if(mVersionCode <= 5
  516.         { 
  517.             linearLayoutScrolLayout.setBaselineAligned(false); 
  518.         } 
  519.          
  520.         //初始化四个滑动view 
  521.         for(int i = 0; i < PAGE_COUNT; i++) 
  522.         { 
  523.             LinearLayout linearLayoutScreen = new LinearLayout(mContext); 
  524.             linearLayoutScreen.setOrientation(LinearLayout.VERTICAL); 
  525.             linearLayoutScrolLayout.addView(linearLayoutScreen, new LayoutParams( 
  526.                     mScreenWidth,  
  527.                     LayoutParams.FILL_PARENT)); 
  528.  
  529.             mLinearLayoutScreens.add(i, linearLayoutScreen); 
  530.         } 
  531.  
  532.         //初始化小圆点视图 
  533.         RelativeLayout relativeLayout = new RelativeLayout(mContext); 
  534.         relativeLayout.setLayoutParams(new LayoutParams( 
  535.                 LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); 
  536.  
  537.         //linearLayoutForDot为小圆点视图 
  538.         linearLayoutForDot =new LinearLayout(mContext); 
  539.         android.widget.RelativeLayout.LayoutParams layoutParams =  
  540.                 new android.widget.RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,  
  541.                         LayoutParams.WRAP_CONTENT); 
  542.         //小圆点距底部的距离 单位:px 
  543.         layoutParams.bottomMargin = MARGIN_BOTTOM; 
  544.         layoutParams.rightMargin = MARGIN_BOTTOM; 
  545.         layoutParams.addRule(android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM); 
  546.         layoutParams.addRule(android.widget.RelativeLayout.CENTER_HORIZONTAL); 
  547.         linearLayoutForDot.setLayoutParams(layoutParams); 
  548.         linearLayoutForDot.setOrientation(LinearLayout.HORIZONTAL); 
  549.         linearLayoutForDot.setHorizontalGravity(Gravity.CENTER); 
  550.         linearLayoutForDot.setVerticalGravity(Gravity.CENTER); 
  551.         //下面两句实现圆角半透明效果 不采用 
  552.         //      linearLayoutForDot.setBackgroundResource(R.drawable.round_corner_bg); 
  553.         //      linearLayoutForDot.getBackground().setAlpha(100); 
  554.  
  555.         //初始化4个小圆点 
  556.         mPageIndicator = getResources().getDrawable(R.drawable.page_indicator); 
  557.         mPageIndicatorFocused = getResources().getDrawable(R.drawable.page_indicator_focused); 
  558.         for(int i = 0; i < PAGE_COUNT; i++) 
  559.         { 
  560.             ImageView imageView = new ImageView(mContext); 
  561.             imageView.setImageDrawable(mPageIndicator); 
  562.             mImageViewList.add(i, imageView); 
  563.             LinearLayout.LayoutParams layoutParamsForDot =  
  564.                     new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,  
  565.                             LayoutParams.WRAP_CONTENT); 
  566.             layoutParamsForDot.rightMargin = 5
  567.  
  568.             linearLayoutForDot.addView(imageView, layoutParamsForDot); 
  569.         } 
  570.         mImageViewList.get(0).setImageDrawable(mPageIndicatorFocused); 
  571.         relativeLayout.addView(linearLayoutForDot); 
  572.  
  573.         mHorizontalScrollViewEx = new HorizontalScrollViewEx(mContext, null, mBannerClickListener); 
  574.         mHorizontalScrollViewEx.setLayoutParams(new LayoutParams( 
  575.                 mScreenWidth * PAGE_COUNT,  
  576.                 LayoutParams.FILL_PARENT)); 
  577.         mHorizontalScrollViewEx.addView(linearLayoutScrolLayout, new LayoutParams( 
  578.                 LayoutParams.FILL_PARENT,  
  579.                 LayoutParams.FILL_PARENT)); 
  580.  
  581.         mHorizontalScrollViewEx.setHorizontalScrollBarEnabled(false); 
  582.         mHorizontalScrollViewEx.setHorizontalFadingEdgeEnabled(false); 
  583.  
  584.         addView(mHorizontalScrollViewEx); 
  585.         addView(relativeLayout); 
  586.  
  587.         //自动滑屏 5秒一次 
  588.         timer.schedule(mTimerTask, 5000, TIMER_DURATION); 
  589.     } 
  590.  
  591.     /**
  592.      * 加一个banner页面 TODO此函数写的不好
  593.      */ 
  594.     private void addBannerItem() 
  595.     { 
  596.         //表示已经执行了onDetachedFromWindow,banner已经被销毁了 
  597.         if( mBannerBitmaps == null || mLinearLayoutScreens == null ||  
  598.                 mImageViewList == null || mContext == null
  599.             return
  600.          
  601.         //调整屏幕数量和总宽度 
  602.         PAGE_COUNT += 1
  603.         mHorizontalScrollViewEx.getLayoutParams().width = mScreenWidth * PAGE_COUNT; 
  604.                  
  605.         //加载默认图片资源 
  606.         if(mDefaultBitmap == null
  607.         { 
  608.             BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();   
  609.             bitmapFactoryOptions.inJustDecodeBounds = false;   
  610.             bitmapFactoryOptions.inSampleSize = 2;   
  611.             Resources res=mContext.getResources(); 
  612.             mDefaultBitmap = BitmapFactory.decodeResource(res, R.drawable.banner_image_default, bitmapFactoryOptions); 
  613.         } 
  614.         mBannerBitmaps.add(mDefaultBitmap); 
  615.         mBannerItemsList.add(null);  
  616.         //加一个屏幕 
  617.         LinearLayout linearLayoutScreen = new LinearLayout(mContext); 
  618.         linearLayoutScreen.setOrientation(LinearLayout.VERTICAL); 
  619.         linearLayoutScreen.setBackgroundDrawable(new BitmapDrawable( mBannerBitmaps.get(PAGE_COUNT - 1) )); 
  620.         linearLayoutScrolLayout.addView(linearLayoutScreen, new LayoutParams( 
  621.                 mScreenWidth,  
  622.                 LayoutParams.FILL_PARENT)); 
  623.         mLinearLayoutScreens.add(linearLayoutScreen); 
  624.          
  625.         //加一个小圆点 
  626.         ImageView imageView = new ImageView(mContext); 
  627.         imageView.setImageDrawable(mPageIndicator); 
  628.         mImageViewList.add(imageView); 
  629.         LinearLayout.LayoutParams layoutParamsForDot =  
  630.                 new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,  
  631.                         LayoutParams.WRAP_CONTENT); 
  632.         layoutParamsForDot.rightMargin = 5
  633.         linearLayoutForDot.addView(imageView, layoutParamsForDot);       
  634.     } 
  635.      
  636.     private class HorizontalScrollViewEx extends ViewGroup implements  
  637.     OnGestureListener 
  638.     { 
  639.  
  640.         private GestureDetector mGestureDetector; 
  641.         private int mWhichScreen; 
  642.  
  643.         public HorizontalScrollViewEx(Context context, AttributeSet attrs, OnBannerClickListener bannerClickListener) 
  644.         { 
  645.             super(context, attrs); 
  646.              
  647.             mGestureDetector = new GestureDetector(this); 
  648.             //解决长按屏幕后无法拖动的现象 
  649.             mGestureDetector.setIsLongpressEnabled(false); 
  650.  
  651.             //构造弹性滑动对象 
  652.             mScroller = new Scroller(context); 
  653.         } 
  654.  
  655.         /**
  656.          * 切换到指定屏幕
  657.          * @param whichScreen 屏幕index
  658.          */ 
  659.         public void switchView(int whichScreen) 
  660.         { 
  661.             if(mLinearLayoutScreens == null
  662.                 return
  663.              
  664.             // 防止非法参数 
  665.             if (whichScreen < 0
  666.                 whichScreen = 0
  667.             else if(whichScreen >= PAGE_COUNT) 
  668.                 whichScreen = PAGE_COUNT - 1
  669.  
  670.             Logger.i(TAG, "switch view to " + whichScreen); 
  671.  
  672.             int delta = whichScreen * mScreenWidth - HorizontalScrollViewEx.this.getScrollX(); 
  673.  
  674.             //缓慢滚动到指定位置 
  675.             mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 3); 
  676.  
  677.             // refresh 
  678.             invalidate(); 
  679.  
  680.             //delta>0 stands for user scroll view to right 
  681.             if (delta > 0)  
  682.                 mScrollToRight = true
  683.             else  
  684.             { 
  685.                 mScrollToRight = false
  686.             } 
  687.  
  688.             mWhichScreen = whichScreen; 
  689.             mWhich = whichScreen; 
  690.             //切换小圆点 
  691.             switchScreenPosition(mWhichScreen); 
  692.         } 
  693.  
  694.         /**
  695.          * 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发
  696.          */ 
  697.         @Override 
  698.         public boolean onDown(MotionEvent e) 
  699.         { 
  700.             Logger.i("MyGesture", "onDown"); 
  701.              
  702.             mScrollX = HorizontalScrollViewEx.this.getScrollX(); 
  703.  
  704.             return true
  705.         } 
  706.  
  707.         /**
  708.          * 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发
  709.          * 注意和onDown()的区别,强调的是没有松开或者拖动的状态
  710.          */ 
  711.         public void onShowPress(MotionEvent e) 
  712.         { 
  713.             Logger.i("MyGesture", "onShowPress"); 
  714.         } 
  715.  
  716.         /**
  717.          * 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发
  718.          */ 
  719.         public boolean onSingleTapUp(MotionEvent e) 
  720.         { 
  721.             Logger.i("MyGesture", "onSingleTapUp"); 
  722.             if(mBannerItemsList == null || mBannerItemsList.size() <= mWhichScreen) 
  723.                 return false
  724.              
  725.             BannerItem bannerItem = mBannerItemsList.get(mWhichScreen); 
  726.  
  727.             if(bannerItem != null
  728.             { 
  729.                 BannerMotionEvent bannerMotionEvent =  
  730.                         new BannerMotionEvent(mWhichScreen, bannerItem.action, bannerItem.url,  
  731.                                 bannerItem.gameId, bannerItem.gameType, bannerItem.title); 
  732.                 mBannerClickListener.onBannerClick(bannerMotionEvent); 
  733.             } 
  734.  
  735.             return false
  736.         } 
  737.  
  738.         /** 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE,
  739.          * 1个ACTION_UP触发
  740.          */ 
  741.         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
  742.                 float velocityY) 
  743.         { 
  744.             Logger.i("MyGesture", "onFling velocityX=" + velocityX); 
  745.  
  746.             mWhichScreen = velocityX > 0
  747.                     mWhichScreen - 1 
  748.                     : mWhichScreen + 1
  749.             switchView(mWhichScreen); 
  750.  
  751.             return true
  752.         } 
  753.  
  754.         /**
  755.          * 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发
  756.          */ 
  757.         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, 
  758.                 float distanceY) 
  759.         { 
  760.             Logger.i("MyGesture", "onScroll"); 
  761.  
  762.             //禁止弹性滚动 
  763.             if (mOverScrollMode == false)  
  764.             { 
  765.                 float x1 = e1.getX(); 
  766.                 float x2 = e2.getX(); 
  767.                 if(mWhichScreen == 0 && x1 < x2) 
  768.                     return false
  769.                 else if(mWhichScreen == PAGE_COUNT - 1 && x1 > x2) 
  770.                     return false
  771.             } 
  772.              
  773. //          int distance = Math.abs(getScrollX() - mWhichScreen * mScreenWidth);     
  774. //          if ((mWhichScreen ==0 || mWhichScreen == PAGE_COUNT -1) &&  distance > mOverScrollDistance) 
  775. //              return false; 
  776.              
  777.             this.scrollBy((int)distanceX, 0); 
  778.  
  779.             return true
  780.         } 
  781.  
  782.         /**
  783.          *  用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发
  784.          */ 
  785.         public void onLongPress(MotionEvent e) 
  786.         { 
  787.             Logger.i("MyGesture", "onLongPress"); 
  788.         } 
  789.  
  790.         @Override 
  791.         public boolean onTouchEvent(MotionEvent event) 
  792.         { 
  793.             if(event.getAction() == MotionEvent.ACTION_DOWN) 
  794.             { 
  795.                 mTimerResume = false
  796.                 if ( !mScroller.isFinished() ) 
  797.                     mScroller.abortAnimation(); 
  798.             } 
  799.             else if(event.getAction() == MotionEvent.ACTION_UP) 
  800.             { 
  801.                 //开始自动滑屏 
  802.                 mTimerResume = true
  803.                 mByUserAction = true
  804.             } 
  805.  
  806.             boolean consume = mGestureDetector.onTouchEvent(event); 
  807.  
  808.             if (consume == false && event.getAction() == MotionEvent.ACTION_UP)  
  809.             { 
  810.                 int curScrollX = HorizontalScrollViewEx.this.getScrollX(); 
  811.                 int mWhichScreen = (curScrollX + mScreenWidth / 2) /mScreenWidth; 
  812.  
  813.                 switchView(mWhichScreen); 
  814.             } 
  815.  
  816.             return consume; 
  817.         } 
  818.  
  819.         @Override 
  820.         public void computeScroll() 
  821.         { 
  822.             if (mScroller.computeScrollOffset()) 
  823.             { 
  824.                 scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 
  825.                 postInvalidate(); 
  826.             } 
  827.         } 
  828.  
  829.         @Override 
  830.         protected void onLayout(boolean changed, int l, int t, int r, int b) 
  831.         { 
  832.             if (changed)  
  833.             { 
  834.                 int childLeft = 0
  835.                 final int childCount = getChildCount(); 
  836.  
  837.                 for (int i=0; i<childCount; i++) 
  838.                 { 
  839.                     final View childView = getChildAt(i); 
  840.                     if (childView.getVisibility() != View.GONE) 
  841.                     { 
  842.                         final int childWidth = childView.getMeasuredWidth(); 
  843.                         childView.layout(childLeft, 0,  
  844.                                 childLeft+childWidth, childView.getMeasuredHeight()); 
  845.                         childLeft += childWidth; 
  846.                     } 
  847.                 } 
  848.             } 
  849.         } 
  850.  
  851.         @Override   
  852.         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
  853.         { 
  854.             super.onMeasure(widthMeasureSpec, heightMeasureSpec);    
  855.  
  856.             final int width = MeasureSpec.getSize(widthMeasureSpec);    
  857.             final int count = getChildCount();    
  858.              
  859.             for (int i = 0; i < count; i++) 
  860.             {    
  861.                 getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);    
  862.             } 
  863.             scrollTo(mWhich * mScreenWidth, 0); 
  864.         }   
  865.  
  866.     } 
  867.  
  868.     /**
  869.      * override此函数,防止其修改构造方法所定义的宽和高<br/>
  870.      *  注意:在这里,设置宽和高将不起作用
  871.      */ 
  872.     @Override 
  873.     public void setLayoutParams(android.view.ViewGroup.LayoutParams params) 
  874.     { 
  875.         params.width = mScreenWidth; 
  876.         params.height = mScreenHeight; 
  877.  
  878.         super.setLayoutParams(params); 
  879.     } 
  880.  
  881.     //标志view AttachedToWindow 
  882.     @Override 
  883.     protected void onAttachedToWindow() 
  884.     { 
  885.         super.onAttachedToWindow(); 
  886.         mTimerResume = true
  887.     } 
  888.  
  889.     //标志view已经脱离window,典型的情形是view被销毁了,此时取消timer 
  890.     @Override 
  891.     protected void onDetachedFromWindow() 
  892.     { 
  893.         super.onDetachedFromWindow(); 
  894.         Logger.d(TAG, "onDetachedFromWindow"); 
  895.          
  896.         mTimerResume = false
  897.         int activityId = ( (BaseActivity)mContext ).activityId(); 
  898.         //如果是账号管理页面 则释放内存 
  899.         if(activityId == BaseActivity.ACCOUNT_ID) 
  900.         { 
  901.             destroy(); 
  902.         } 
  903.     } 
  904.      
  905.     /**
  906.      * 销毁banner
  907.      */ 
  908.     public void destroy() 
  909.     { 
  910.         mTimerTask.cancel(); 
  911.         timer.cancel(); 
  912.         //去除各种bitmap对activity的引用关系 
  913.         destoryBitmaps(); 
  914.         System.gc(); 
  915.     } 
  916.      
  917.     /**
  918.      * 去除各种bitmap对activity的引用关系
  919.      */ 
  920.     private void destoryBitmaps() 
  921.     {        
  922.         for (View view : mLinearLayoutScreens)  
  923.         { 
  924.             Drawable drawable = view.getBackground(); 
  925.             BitmapDrawable bitmapDrawable = null
  926.             if(drawable instanceof BitmapDrawable) 
  927.                 bitmapDrawable = (BitmapDrawable)drawable; 
  928.              
  929.             if(bitmapDrawable != null
  930.             { 
  931.                 //解除drawable对view的引用 
  932.                 bitmapDrawable.setCallback(null); 
  933.                 bitmapDrawable = null
  934.             } 
  935.         } 
  936.          
  937.         for (ImageView imageView : mImageViewList)  
  938.         { 
  939.             Drawable drawable = imageView.getDrawable(); 
  940.             if(drawable != null
  941.             { 
  942.                 drawable.setCallback(null); 
  943.                 drawable = null
  944.             } 
  945.         } 
  946.          
  947.         mPageIndicator.setCallback(null); 
  948.         mPageIndicator = null
  949.         mPageIndicatorFocused.setCallback(null); 
  950.         mPageIndicatorFocused = null
  951.          
  952.         mLinearLayoutScreens.clear(); 
  953.         mLinearLayoutScreens = null
  954.          
  955.         mBannerBitmaps.clear(); 
  956.         mBannerBitmaps = null
  957.          
  958.         mImageViewList.clear(); 
  959.         mImageViewList = null
  960.          
  961.         mBannerItemsList.clear(); 
  962.         mBannerItemsList = null;     
  963.     } 
  964.      
  965.     //单击事件 
  966.     @Override 
  967.     public void onBannerClick( BannerMotionEvent bannerMotionEvent ) 
  968.     { 
  969.         final int position = bannerMotionEvent.index; 
  970.         if(mContext == null
  971.             return
  972.          
  973.         NotificationInfo notificationInfo = new NotificationInfo(); 
  974.         notificationInfo.msgType = bannerMotionEvent.getAction(); 
  975.         int action = bannerMotionEvent.getAction(); 
  976.         if(action == NotificationInfo.NOTIFICATION_SINGLEGAME_MSG)    //单个游戏消息,直接启动该游戏 
  977.         { 
  978.             try 
  979.             { 
  980.                 notificationInfo.gameId = Integer.parseInt( bannerMotionEvent.getGameId() ); 
  981.                 notificationInfo.gameType = Integer.parseInt( bannerMotionEvent.getGameType() ); 
  982.             }  
  983.             catch (NumberFormatException e)  
  984.             { 
  985.                 Logger.e(TAG, e.toString()); 
  986.                 return
  987.             } 
  988.         } 
  989.         else if(action == NotificationInfo.NOTIFICATION_GAMEPAGE_MSG)    //游戏主页消息,通过客户端展示游戏主页 
  990.         { 
  991.             try 
  992.             { 
  993.                 notificationInfo.gameId = Integer.parseInt( bannerMotionEvent.getGameId() ); 
  994.             }  
  995.             catch (NumberFormatException e)  
  996.             { 
  997.                 Logger.e(TAG, e.toString()); 
  998.                 return
  999.             } 
  1000.             notificationInfo.issueTitle = bannerMotionEvent.getTitle(); 
  1001.         } 
  1002.         else if(action == NotificationInfo.NOTIFICATION_SHOW_WEBVIEW_MSG)    //交叉推广消息,通过一个webview展示 
  1003.         { 
  1004.             notificationInfo.issueTitle = bannerMotionEvent.getTitle(); 
  1005.             notificationInfo.openUrl = bannerMotionEvent.getResponseUrl(); 
  1006.         } 
  1007.         else //reserved 
  1008.         { 
  1009.             return
  1010.         } 
  1011.          
  1012.         Intent intent = notificationInfo.generateIntent(mContext); 
  1013.         if(intent != null
  1014.             mContext.startActivity(intent); 
  1015.     } 
  1016.      
  1017.     /**
  1018.      * ScrollBanner所关联的banner项 可以为多个 一个为一屏
  1019.      */ 
  1020.     public static class BannerItem extends Object 
  1021.     { 
  1022.         public static final String ACTION = "action"
  1023.         public static final String URL = "url"
  1024.         public static final String IMGURL = "imgurl"
  1025.         public static final String GAMEID = "gameid"
  1026.         public static final String GAMETYPE = "gametype"
  1027.         public static final String TITLE = "title"
  1028.  
  1029.         public int index = -1
  1030.         public int action = -1
  1031.         public String url = ""
  1032.         public String imgUrl = ""
  1033.         public String gameId = ""
  1034.         public String gameType = ""
  1035.         public String title = ""
  1036.  
  1037.         public BannerItem(){} 
  1038.     } 
  1039.  
  1040.     /**
  1041.      * BannerMotionEvent:单击banner所产生的事件对象<br/>
  1042.      *getAction()来获取动作类别<br/>
  1043.      *getResponseUrl()来获取响应url<br/>
  1044.      *...
  1045.      */ 
  1046.     public static class BannerMotionEvent extends Object 
  1047.     { 
  1048.         /**
  1049.          * ACTION_PLAY_FLASH: 播放游戏
  1050.          */ 
  1051.         public static final int ACTION_PLAY = 2
  1052.         /**
  1053.          * ACTION_HOMEPAGE:打开官网
  1054.          */ 
  1055.         public static final int ACTION_HOMEPAGE = 3
  1056.         /**
  1057.          * ACTION_OPEN_URL:打开指定url
  1058.          */ 
  1059.         public static final int ACTION_OPEN_URL = 4
  1060.  
  1061.         //banner中屏幕的index 
  1062.         private int index = -1
  1063.         //响应url 
  1064.         private String responseUrl = ""
  1065.         //动作种类 
  1066.         private int action = -1
  1067.         //gameid 
  1068.         private String gameId = ""
  1069.         //gametype flash游戏(0) or h5游戏(1) 
  1070.         private String gameType = ""
  1071.         //webview的标题 
  1072.         private String title = ""
  1073.          
  1074.         public BannerMotionEvent(int index, int action, String responseUrl,  
  1075.                 String gameId, String gameType, String title) 
  1076.         { 
  1077.             BannerMotionEvent.this.index = index; 
  1078.             BannerMotionEvent.this.action = action; 
  1079.             BannerMotionEvent.this.responseUrl = responseUrl; 
  1080.             BannerMotionEvent.this.gameId = gameId; 
  1081.             BannerMotionEvent.this.gameType = gameType; 
  1082.             BannerMotionEvent.this.title = title; 
  1083.         } 
  1084.  
  1085.         /**
  1086.          * 获取当前BannerMotionEvent事件对象的动作种类
  1087.          * @return 动作种类:ACTION_PLAY等
  1088.          */ 
  1089.         public int getAction() 
  1090.         { 
  1091.             return action; 
  1092.         } 
  1093.  
  1094.         /**
  1095.          * 获取当前BannerMotionEvent事件对象的title
  1096.          * @return title webview的标题
  1097.          */ 
  1098.         public String getTitle() 
  1099.         { 
  1100.             return title; 
  1101.         } 
  1102.          
  1103.         /**
  1104.          * 获取当前BannerMotionEvent事件对象的gameId
  1105.          * @return gameId
  1106.          */ 
  1107.         public String getGameId() 
  1108.         { 
  1109.             return gameId; 
  1110.         } 
  1111.          
  1112.         /**
  1113.          * 获取当前BannerMotionEvent事件对象的gameType
  1114.          * @return gameType 0 or 1
  1115.          */ 
  1116.         public String getGameType() 
  1117.         { 
  1118.             return gameType; 
  1119.         } 
  1120.          
  1121.         /**
  1122.          * 获取当前BannerMotionEvent事件对象的响应url
  1123.          * @return 响应url
  1124.          */ 
  1125.         public String getResponseUrl() 
  1126.         { 
  1127.             return responseUrl; 
  1128.         } 
  1129.  
  1130.         @SuppressLint("DefaultLocale"
  1131.         @Override 
  1132.         public String toString() 
  1133.         { 
  1134.             return String.format("BannerMotionEvent { index=%d, action=%d, responseUrl=%s, gameId=%s, gameType=%s, title=%s }",  
  1135.                     index, action, responseUrl, gameId, gameType, title); 
  1136.         } 
  1137.     } 
  1138.  
  1139.     @Override 
  1140.     public void onBannerInfoSuccess(List<BannerItem> items) 
  1141.     { 
  1142.         Logger.d(TAG, "onBannerInfoSuccess"); 
  1143.         mBannerItemsList = items; 
  1144.         mHandler.sendEmptyMessage(MESSAGE_FETCH_BANNER_SUCCESS); 
  1145.     } 
  1146.  
  1147.     @Override 
  1148.     public void onBannerInfoFailed() 
  1149.     { 
  1150.         Logger.e(TAG, "onBannerInfoFailed"); 
  1151.     } 
  1152.  
/**
 * 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

  1. public interface ComponentCallBack  
  2.  
  3.     public static interface OnBannerClickListener 
  4.     { 
  5.         /**
  6.          * banner单击事件
  7.          * @param bannerMotionEvent 单击事件对象,包含所需的响应信息
  8.          * 参见 {@link BannerMotionEvent}
  9.          */ 
  10.         public abstract void onBannerClick( BannerMotionEvent bannerMotionEvent ); 
  11.     } 
  12.      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值