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