學習完極客學院的自定義左右菜單,在此記錄。
效果如下:
1.自定義一個類MainUI繼承自RelativeLayout
2.定義三個FrameLayout 分別代表左菜單、當前顯示區域、右菜單。
private FrameLayout leftMenu;
private FrameLayout middleMenu;
private FrameLayout rightMenu;
3.寫一個初始化方法將各個控件初始化並調用addView方法加載到MainUI上,記得爲每個控件設置id,當然要在各個構造方法中調用初始化方法
public static final int LEFT_ID = 0xaabbcc;
public static final int MIDEELE_ID = 0xaaccbb;
public static final int RIGHT_ID = 0xccbbaa;
private void initView(Context context) {
// TODO Auto-generated method stub
this.context = context;
mScroller = new Scroller(context, new DecelerateInterpolator());
leftMenu = new FrameLayout(context);
rightMenu = new FrameLayout(context);
middleMenu = new FrameLayout(context);
middleMask = new FrameLayout(context);
leftMenu.setBackgroundColor(0x8851a9ea);
rightMenu.setBackgroundColor(0x8851a9ea);
middleMenu.setBackgroundColor(0x88b7ab96);
middleMask.setBackgroundColor(0x88000000);
leftMenu.setId(LEFT_ID);
rightMenu.setId(RIGHT_ID);
middleMenu.setId(MIDEELE_ID);
addView(leftMenu);
addView(middleMenu);
addView(rightMenu);
addView(middleMask);
middleMask.setAlpha(0);
}
4.重寫onMeasure方法爲leftMenu,middleMenu,rightMenu設置大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
middleMenu.measure(widthMeasureSpec, heightMeasureSpec);
middleMask.measure(widthMeasureSpec, heightMeasureSpec);
int realWidth = MeasureSpec.getSize(widthMeasureSpec);
int tempWidthMeasure = MeasureSpec.makeMeasureSpec((int)(realWidth * 0.8f), MeasureSpec.EXACTLY);
leftMenu.measure(tempWidthMeasure, heightMeasureSpec);
rightMenu.measure(tempWidthMeasure, heightMeasureSpec);
}
5.重寫onLayout方法爲leftMenu,middleMenu,rightMenu設置位置,都是左--上--右--下(其中上下都是相同的,注意leftMenu,rightMenu左右怎麼得來的)
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
super.onLayout(changed, l, t, r, b);
middleMenu.layout(l, t, r, b);
middleMask.layout(l, t, r, b);
leftMenu.layout(l-leftMenu.getMeasuredWidth(), t, r, b);
rightMenu.layout(l+middleMenu.getMeasuredWidth(), t, l + middleMenu.getMeasuredWidth()
+ rightMenu.getMeasuredWidth(), b);
}
6.因爲涉及到滑動事件,定義一個Scroller對象並在初始化方法中初始化
private Scroller mScroller;
7.重寫dispatchTouchEvent監聽滑動事件。首先判斷是點擊還是滑動,距離超過TEST_DIS則判斷爲滑動,否則則爲點擊;然後判斷是否爲左右滑動,顯然上下滑動左右菜單並不會出現。
private boolean isTestCompete;
//測試點擊還是滑動
private boolean isleftrightEvent;
//測試是否爲左右滑動
private Point point = new Point();
private static final int TEST_DIS = 20;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!isTestCompete){
getEventType(ev);
return true;
}
if(isleftrightEvent){
switch(ev.getActionMasked()){
case MotionEvent.ACTION_MOVE:
int curScrollX = getScrollX();//滑動距離
int dis_x = (int)(ev.getX() - point.x);//手指滑動距離+TEST_DIS=滑動距離
int expectX = -dis_x + curScrollX;
int finalX = 0;
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();//重繪
//標識置爲false
isleftrightEvent = false;
isTestCompete = false;
break;
}
}else{//否則爲上下滑動,只需將標誌位置爲false
switch(ev.getActionMasked()){
case MotionEvent.ACTION_UP:
isleftrightEvent = false;
isTestCompete = false;
break;
default:
break;
}
}
return super.dispatchTouchEvent(ev);
}
其中的
private void getEventType(MotionEvent ev) {//確定事件的類型
// TODO Auto-generated method stub
switch (ev.getActionMasked()){
case MotionEvent.ACTION_DOWN:
point.x = (int)ev.getX();
point.y = (int)ev.getY();
super.dispatchTouchEvent(ev);
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){//左右滑動
isleftrightEvent = true;
isTestCompete = true;
point.x = (int)ev.getX();
point.y = (int)ev.getY();
}else if(dY >= TEST_DIS && dY >dX){//上下滑動
isleftrightEvent = false;
isTestCompete = true;
point.x = (int)ev.getX();
point.y = (int)ev.getY();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
super.dispatchTouchEvent(ev);
isleftrightEvent = false;
isTestCompete = false;
break;
}
}
重寫scrollTo方法是爲了讓middleMask的alpha值由透明逐漸變深,因爲middleMask與middleMenu重合(最開始透明),這樣相當於增加滑動時顯示的特效
@Override
public void scrollTo(int x, int y) {
// TODO Auto-generated method stub
super.scrollTo(x, y);
onMiddleMask();
int curX = Math.abs(getScrollX());
float scale = curX / (float)leftMenu.getMeasuredWidth();
middleMask.setAlpha(scale);
}
8.最後在MainActivity中使用,getSupportFragmentManager().beginTransaction().add(MainUI.LEFT_ID, left).commit();爲左右菜單添加子控件
public class MainActivity extends FragmentActivity {
private MainUI mainUI;
private leftMenu left;
private rightMenu right;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mainUI = new MainUI(this);
setContentView(mainUI);
left = new leftMenu();
right = new rightMenu();
getSupportFragmentManager().beginTransaction().add(MainUI.LEFT_ID, left).commit();
getSupportFragmentManager().beginTransaction().add(MainUI.RIGHT_ID, right).commit();
}
}
其中leftMenu
public class leftMenu extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View v = inflater.inflate(R.layout.left, container, false);
v.findViewById(R.id.button1).setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Log.i("width", "left");
}
});
return v;
}
}