很多音樂播放器如qq音樂,kugou音樂等都有一個專輯推薦的那個橫幅,它擴展了軟件的空間,也為用戶帶來了更好的交互感受。
在此,我也模仿着實現了此效果,不足之處請大家見諒,歡迎提出問題,和大家一起學習。
我給他取名叫【BannerLayout】,主要是覺得它也如其他layout特性差不多吧。
public class BannerLayout extends ViewGroup { public BannerLayout(Context context) { super(context); // TODO Auto-generated constructor stub } public BannerLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } public BannerLayout(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } }
BannerLayout 繼承與 ViewGroup.
這個BannerLayout可以自動滾動,所以我們需要一個滾動器Scroller .
this.scroller = new Scroller(context, new DecelerateInterpolator(2));
我給scroller設置了一個插值器DecelerateInterpolator,就可以再滾動的時候實現滾動速度是中間快,開始和結束的時候慢的效果,個人覺得這個效果顯得比較優雅。
要實現自動滾動,因此,我們可以使用handler還幫我們起到計時的作用。此外也可以使用timer等其他方式。
private Handler handler=new Handler() { @Override public void handleMessage(Message msg) {
//autoScroll是一標志boolean亮,用來標志是否需要滾動,因此就能手動控制其滾動了,
if(autoScroll && currentWhat==msg.what) { currentScreenIndex=(currentScreenIndex+1)%getChildCount(); scrollToScreen(currentScreenIndex); Log.i("TAG","handleMessage scrollToScreen:"+currentScreenIndex); if(autoScroll)//給自身發送延時消息, handler.sendEmptyMessageDelayed(currentWhat, scrollTime); } } };
由於BannerLayout里面的子元素如圖片都是水平布局的,所以我們需要手動控制它們在此布局的位置了,
重寫onMeasure函數:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // super.onMeasure(widthMeasureSpec, heightMeasureSpec); int maxHeight=-1; final int count = getChildCount(); for (int i = 0; i < count; i++) { getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); maxHeight=Math.max(maxHeight, getChildAt(i).getMeasuredHeight()); }
// maxHeight=Math.min(maxHeight, MeasureSpec.getSize(heightMeasureSpec)); Log.e("TAG","onMeasure Height:"+maxHeight); setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),maxHeight); }
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { final int count = getChildCount(); int cLeft = 0; for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() == View.GONE) continue; // child.setVisibility(View.VISIBLE); final int childWidth = child.getMeasuredWidth(); child.layout(cLeft, 0, cLeft +childWidth, child.getMeasuredHeight()); cLeft += childWidth; } }
此外,我們需要處理用戶觸摸事件,實現用戶按下的時候停止滾動,用戶拖動的時候能夠隨之移動,
@Override public boolean onTouchEvent(MotionEvent ev) { if (getChildCount() == 0) return false; final int action = ev.getAction(); final float x = ev.getX(); switch (action) { case MotionEvent.ACTION_DOWN: autoScroll=false; currentWhat++; mLastMotionX = x; if (!scroller.isFinished()) { scroller.abortAnimation(); } // Log.i("TAG","ACTION_DOWN"); return true; case MotionEvent.ACTION_MOVE: final int deltaX = (int) (mLastMotionX - x); // boolean xMoved = Math.abs(deltaX) > mTouchSlop; mLastMotionX = x; if((0==currentScreenIndex && deltaX<0) || (getChildCount()-1==currentScreenIndex && deltaX>0)) scrollBy(deltaX/4, 0);//此處實現了越界時候的阻尼效果 else // Log.i("TAG","ACTION_MOVE"); // if (xMoved) scrollBy(deltaX, 0); final int screenWidth = getWidth(); currentScreenIndex=(getScrollX() + (screenWidth / 2))/ screenWidth; return true; case MotionEvent.ACTION_UP: snapToDestination(); if(!autoScroll) { autoScroll=true; handler.sendEmptyMessageDelayed(currentWhat, scrollTime); } break; case MotionEvent.ACTION_CANCEL: snapToDestination(); if(!autoScroll) { autoScroll=true; handler.sendEmptyMessageDelayed(currentWhat, scrollTime); } } return false; }
接下來,我們需要實現自定滾動到某一屏的效果:
private void scrollToScreen(int whichScreen) { // if (!scroller.isFinished()) // return; // Log.e("TAG","scrollToScreen:"+whichScreen); int delta = 0; delta = whichScreen * getWidth() - getScrollX(); // scroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2); scroller.startScroll(getScrollX(), 0, delta, 0, 1500); invalidate(); currentScreenIndex=whichScreen; } private void snapToDestination() { final int x=getScrollX(); final int screenWidth = getWidth(); scrollToScreen((x + (screenWidth / 2))/ screenWidth); }
差不多就這些了,其實挺簡單的,哈哈
下面是全部代碼:
public class BannerLayout extends ViewGroup { private Scroller scroller; private float mLastMotionX; // private int mTouchSlop; private int currentScreenIndex=0; private boolean autoScroll=true; private int scrollTime=3*1000; private int currentWhat=0; private Handler handler=new Handler() { @Override public void handleMessage(Message msg) { if(autoScroll && currentWhat==msg.what) { currentScreenIndex=(currentScreenIndex+1)%getChildCount(); scrollToScreen(currentScreenIndex); Log.i("TAG","handleMessage scrollToScreen:"+currentScreenIndex); if(autoScroll) handler.sendEmptyMessageDelayed(currentWhat, scrollTime); } } }; public BannerLayout(Context context) { super(context); initView(context); // TODO Auto-generated constructor stub } public BannerLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub initView(context); } public BannerLayout(Context context, AttributeSet attrs) { super(context, attrs); initView(context); // TODO Auto-generated constructor stub } private void initView(final Context context) { this.scroller = new Scroller(context, new DecelerateInterpolator(2));//OvershootInterpolator(1.1f) handler.sendEmptyMessageDelayed(currentWhat, scrollTime); // final ViewConfiguration configuration = ViewConfiguration // .get(getContext()); // mTouchSlop = configuration.getScaledTouchSlop(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // super.onMeasure(widthMeasureSpec, heightMeasureSpec); int maxHeight=-1; final int count = getChildCount(); for (int i = 0; i < count; i++) { getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); maxHeight=Math.max(maxHeight, getChildAt(i).getMeasuredHeight()); } maxHeight=Math.min(maxHeight, MeasureSpec.getSize(heightMeasureSpec)); Log.e("TAG","onMeasure Height:"+maxHeight); setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),maxHeight); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { final int count = getChildCount(); int cLeft = 0; for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() == View.GONE) continue; // child.setVisibility(View.VISIBLE); final int childWidth = child.getMeasuredWidth(); child.layout(cLeft, 0, cLeft +childWidth, child.getMeasuredHeight()); cLeft += childWidth; } } @Override public void computeScroll() { if (scroller.computeScrollOffset()) { scrollTo(scroller.getCurrX(), 0); postInvalidate(); } } @Override public boolean onTouchEvent(MotionEvent ev) { if (getChildCount() == 0) return false; final int action = ev.getAction(); final float x = ev.getX(); switch (action) { case MotionEvent.ACTION_DOWN: autoScroll=false; currentWhat++; mLastMotionX = x; if (!scroller.isFinished()) { scroller.abortAnimation(); } // Log.i("TAG","ACTION_DOWN"); return true; case MotionEvent.ACTION_MOVE: final int deltaX = (int) (mLastMotionX - x); // boolean xMoved = Math.abs(deltaX) > mTouchSlop; mLastMotionX = x; if((0==currentScreenIndex && deltaX<0) || (getChildCount()-1==currentScreenIndex && deltaX>0)) scrollBy(deltaX/4, 0); else // Log.i("TAG","ACTION_MOVE"); // if (xMoved) scrollBy(deltaX, 0); final int screenWidth = getWidth(); currentScreenIndex=(getScrollX() + (screenWidth / 2))/ screenWidth; return true; case MotionEvent.ACTION_UP: snapToDestination(); if(!autoScroll) { autoScroll=true; handler.sendEmptyMessageDelayed(currentWhat, scrollTime); } break; case MotionEvent.ACTION_CANCEL: snapToDestination(); if(!autoScroll) { autoScroll=true; handler.sendEmptyMessageDelayed(currentWhat, scrollTime); } } return false; } private void scrollToScreen(int whichScreen) { // if (!scroller.isFinished()) // return; // Log.e("TAG","scrollToScreen:"+whichScreen); int delta = 0; delta = whichScreen * getWidth() - getScrollX(); // scroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2); scroller.startScroll(getScrollX(), 0, delta, 0, 1500); invalidate(); currentScreenIndex=whichScreen; } private void snapToDestination() { final int x=getScrollX(); final int screenWidth = getWidth(); scrollToScreen((x + (screenWidth / 2))/ screenWidth); } @Override protected void finalize() throws Throwable { Log.e("TAG","finalize==="); super.finalize(); } }
ok,附圖一張