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