近來學習了極客學院有關於界面左右滑動的視頻,就在這里寫個博客,鞏固一下知識點,以免忘了。
這里主要介紹界面是如何左右滑動的:
1.首先我們應該設置好將要滑動的三個界面,即leftMenu、middleMenu、rightMenu三個布局,並且放置好它們的位置,這段大家自己在源碼中看
2.當位置放好后,就可以開始關於滑動方面的代碼。
頁面的滑動是通過點的坐標變化距離來進行來實現的。首先我們定義了20dp來確保最小下限滑動的距離,來確定是否進行了滑動;接着就可以進行判斷頁面的滑動方向,ACTION_DOWN、ACTION_MOVE、ACTION-UP分別對應了手指點擊時的按下,移動,抬起時的事件。當按下時,我們獲取此時點擊點的坐標,隨后我們實時獲取活動過程中點的滑動坐標,后者減去前者就得到了滑動的距離。當滑動的距離大於TEST_DIS時,如果此時在左右滑動的距離大於在上下滑動距離,就置isLeftRightFragment為true,用於下面的判斷。
private Point point = new Point(); private boolean isLeftRightFragment; //設置比較值20;當移動小於20dp時,默認沒有移動 private static final int TEST_DIS = 20; private void getTypeEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: point.x = (int) ev.getX(); point.y = (int) ev.getY(); break; case MotionEvent.ACTION_MOVE: int dX = Math.abs((int)ev.getX() - point.x); int dY = Math.abs((int)ev.getY() - point.y); if (dX>TEST_DIS&&dX>dY) { //左右滑動 isLeftRightFragment = true; isTestCompete = true; point.x = (int) ev.getX(); point.y = (int) ev.getY(); }else if (dY>TEST_DIS&&dY>dX) { //上下滑動 isLeftRightFragment = false; isTestCompete = true; point.x = (int) ev.getX(); point.y = (int) ev.getY(); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; default: break; } }
3.這部分一些地方我也不太明白,大家看我寫的注釋吧,沒有的地方我也不太懂(比如回調的方法是干嘛的,Action_move下面的兩行代碼,知道的給我留言啊)。
public boolean isTestCompete; public int finalX=0; @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!isTestCompete) { getTypeEvent(ev); return true; } if (isLeftRightFragment) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_MOVE: int curScrollX = getScrollX(); //獲取滑動的距離,向左為負,向右為正 int dis_x = (int) (ev.getX() - point.x); int expectX = -dis_x + curScrollX; if (expectX<0) { //向左滑動
finalX = Math.max(expectX, -leftMenu.getMeasuredWidth()); }else { //向右滑動
finalX = Math.min(expectX, rightMenu.getMeasuredWidth()); } //跟隨點的滑動,頁面滑動
scrollTo(finalX, 0); point.x= (int) ev.getX(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: curScrollX = getScrollX(); if (Math.abs(curScrollX) > leftMenu.getMeasuredWidth() >> 1) { //當滑動的距離大於外布局寬度的一半時 if (curScrollX < 0) { //向左滑動,前兩個參數是開始時坐標,中間是將要移動的距離,200是動畫的時間單位毫秒
mScroller.startScroll(curScrollX, 0, -leftMenu.getMeasuredWidth()-curScrollX, 0 ,200); }else { //向右滑動 mScroller.startScroll(curScrollX, 0, leftMenu.getMeasuredWidth()-curScrollX, 0,200); } }else { //小於寬度的一半,自動返回原位 mScroller.startScroll(curScrollX, 0, -curScrollX, 0,200); } invalidate(); //view的重繪 isLeftRightFragment = false; isTestCompete = false; break; } }else { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: isLeftRightFragment = false; isTestCompete = false; break; default: break; } } return super.dispatchTouchEvent(ev); } //回調的方法 @Override public void computeScroll() { super.computeScroll(); if (!mScroller.computeScrollOffset()) { return; } int tempX = mScroller.getCurrX(); scrollTo(tempX, 0); }
4.三完成后,就可以實現頁面滑動了,第四步是在頁面滑動時,中間的那個布局會變暗。思路是在定義一個布局middleMask,將它的位置和顏色設置完畢。初始化中設置透明度middleMask.setAlpha(0);
再在重寫的ScrollTo方法中設置middleMask的透明度跟隨移動漸變。
@Override public void scrollTo(int x, int y) { super.scrollTo(x, y); // 設置middlemask的透明度隨移動距離漸變 int curX = Math.abs(getScrollX()); //scale的范圍為0-1 float scale = curX/ (float)leftMenu.getMeasuredWidth(); middleMask.setAlpha(scale); }
源碼:
MainActivity:
public class MainActivity extends ActionBarActivity { private MenuUI menuUI; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); menuUI=new MenuUI(this); setContentView(menuUI); } }
MenuUI:
public class MenuUI extends RelativeLayout{ private Context context; private FrameLayout leftMenu; private FrameLayout middleMenu; private FrameLayout rightMenu; private FrameLayout middleMask; private Scroller mScroller; public MenuUI(Context context) { super(context); initView(context); } public MenuUI(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } //初始化 private void initView(Context context){ this.context=context; mScroller = new Scroller(context, new DecelerateInterpolator()); //第二個參數為渲染器 leftMenu = new FrameLayout(context); middleMenu = new FrameLayout(context); rightMenu = new FrameLayout(context); middleMask = new FrameLayout(context); leftMenu.setBackgroundColor(Color.YELLOW); middleMenu.setBackgroundColor(Color.GREEN); rightMenu.setBackgroundColor(Color.YELLOW); middleMask.setBackgroundColor(0x88000000); addView(leftMenu); addView(middleMenu); addView(rightMenu); addView(middleMask); //設置透明度 middleMask.setAlpha(0); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); middleMenu.measure(widthMeasureSpec, heightMeasureSpec); middleMask.measure(widthMeasureSpec, heightMeasureSpec); //得到菜單的寬的大小 int realWidth = MeasureSpec.getSize(widthMeasureSpec); //令tempRealtempwidth等於菜單0.8倍的寬,用於設置左右菜單的寬度 int tempRealWidth = MeasureSpec.makeMeasureSpec((int)(realWidth*0.8f), MeasureSpec.EXACTLY); //設置左右菜單的大小 leftMenu.measure(tempRealWidth, heightMeasureSpec); rightMenu.measure(tempRealWidth, heightMeasureSpec); } public float onMiddleMask(){ System.out.println("透明度:"+middleMask.getAlpha()); return middleMask.getAlpha(); } @Override public void scrollTo(int x, int y) {
super.scrollTo(x, y);
// 設置middlemask的透明度隨移動距離漸變
int curX = Math.abs(getScrollX()); //scale的范圍為0-1 float scale = curX/ (float)leftMenu.getMeasuredWidth(); middleMask.setAlpha(scale); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //設置各個布局菜單的位置 super.onLayout(changed, l, t, r, b); //將中間界面的位置設置為l,t,r,b middleMenu.layout(l, t, r, b); middleMask.layout(l, t, r, b); //根據middleMenu的位置確定其他菜單的位置 leftMenu.layout(l-leftMenu.getMeasuredWidth(), t, r-middleMenu.getMeasuredWidth(), b); rightMenu.layout(l+middleMenu.getMeasuredWidth(), t, l+rightMenu.getMeasuredWidth()+middleMenu.getMeasuredWidth(), b); } public boolean isTestCompete; public int finalX=0; @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!isTestCompete) { getTypeEvent(ev); return true; } if (isLeftRightFragment) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_MOVE: int curScrollX = getScrollX(); //獲取滑動的距離,向左為負,向右為正 int dis_x = (int) (ev.getX() - point.x); int expectX = -dis_x + curScrollX; if (expectX<0) { finalX = Math.max(expectX, -leftMenu.getMeasuredWidth()); }else { finalX = Math.min(expectX, rightMenu.getMeasuredWidth()); } scrollTo(finalX, 0); point.x= (int) ev.getX(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: curScrollX = getScrollX(); if (Math.abs(curScrollX) > leftMenu.getMeasuredWidth() >> 1) { //當滑動的距離大於外布局寬度的一半時 if (curScrollX < 0) { //向左滑動 mScroller.startScroll(curScrollX, 0, -leftMenu.getMeasuredWidth()-curScrollX, 0 ,200); }else { //向右滑動 mScroller.startScroll(curScrollX, 0, leftMenu.getMeasuredWidth()-curScrollX, 0,200); } }else { //小於寬度的一半,自動返回原位 mScroller.startScroll(curScrollX, 0, -curScrollX, 0,200); } invalidate(); //view的重繪 isLeftRightFragment = false; isTestCompete = false; break; } }else { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: isLeftRightFragment = false; isTestCompete = false; break; default: break; } } return super.dispatchTouchEvent(ev); } //回調的方法 @Override public void computeScroll() { super.computeScroll(); if (!mScroller.computeScrollOffset()) { return; } int tempX = mScroller.getCurrX(); scrollTo(tempX, 0); }
private Point point = new Point(); private boolean isLeftRightFragment; private static final int TEST_DIS = 20;
private void getTypeEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: point.x = (int) ev.getX(); point.y = (int) ev.getY(); break; case MotionEvent.ACTION_MOVE: int dX = Math.abs((int)ev.getX() - point.x); int dY = Math.abs((int)ev.getY() - point.y); if (dX>TEST_DIS&&dX>dY) { //左右滑動 isLeftRightFragment = true; isTestCompete = true; point.x = (int) ev.getX(); point.y = (int) ev.getY(); }else if (dY>TEST_DIS&&dY>dX) { //上下滑動 isLeftRightFragment = false; isTestCompete = true; point.x = (int) ev.getX(); point.y = (int) ev.getY(); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; default: break; } } }