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

http://blog.csdn.net/singwhatiwanna/article/details/8875241

  1. 原理

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

 

ComponentCallBack.java

[java] view plaincopy
  1. public interface ComponentCallBack   
  2. {  
  3.   
  4.     public static interface OnBannerClickListener  
  5.     {  
  6.         /** 
  7.          * banner單擊事件 
  8.          * @param bannerMotionEvent 單擊事件對象,包含所需的響應信息 
  9.          * 參見 {@link BannerMotionEvent} 
  10.          */  
  11.         public abstract void onBannerClick( BannerMotionEvent bannerMotionEvent );  
  12.     }  
  13.       

發佈了4 篇原創文章 · 獲贊 4 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章