페이스북 앱을 보면 좌측 메뉴 버튼을 눌렀을 때 좌측에서 슬라이드로 메뉴가 나오게 됩니다.
원리는 메인화면을 구성하는 xml에 전체를 FrameLayout으로 감싸고 내부에 메뉴 레이아웃을 깔고
그 위에 메인 화면의 레이아웃을 포개어 놓습니다. 그러고 메인화면의 메뉴 버튼을 눌렀을 때 메인화면 레이아웃을
애니메이션 처리하여 우측으로 밀어버려 아래에있던 메뉴 레이아웃이 노출되는 식입니다.
<activity_main.xml>
구성요소는 다음과 같습니다.
1. MainActivity.java
2. OpenAnimation.java
3. CloseAnimation.java
4. activity_main.xml
5. leftslidemenu.xml
먼저 MainActivity 입니다.
public class MainActivity extends Activity implements OnClickListener {
// slide menu
private DisplayMetrics metrics;
private LinearLayout ll_mainLayout;
private LinearLayout ll_menuLayout;
private FrameLayout.LayoutParams leftMenuLayoutPrams;
private int leftMenuWidth;
private static boolean isLeftExpanded;
private Button bt_left, btn1, btn2, btn3, btn4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initSildeMenu();
}
private void initSildeMenu() {
// init left menu width
metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
leftMenuWidth = (int) ((metrics.widthPixels) * 0.75);
// init main view
ll_mainLayout = (LinearLayout) findViewById(R.id.ll_mainlayout);
// init left menu
ll_menuLayout = (LinearLayout) findViewById(R.id.ll_menuLayout);
leftMenuLayoutPrams = (FrameLayout.LayoutParams) ll_menuLayout
.getLayoutParams();
leftMenuLayoutPrams.width = leftMenuWidth;
ll_menuLayout.setLayoutParams(leftMenuLayoutPrams);
// init ui
bt_left = (Button) findViewById(R.id.bt_left);
bt_left.setOnClickListener(this);
btn1 = (Button) findViewById(R.id.btn1);
btn2 = (Button) findViewById(R.id.btn2);
btn3 = (Button) findViewById(R.id.btn3);
btn4 = (Button) findViewById(R.id.btn4);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
btn4.setOnClickListener(this);
}
/**
* left menu toggle
*/
private void menuLeftSlideAnimationToggle() {
if (!isLeftExpanded) {
isLeftExpanded = true;
// Expand
new OpenAnimation(ll_mainLayout, leftMenuWidth,
Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, 0.75f, 0, 0.0f, 0, 0.0f);
// enable all of menu view
FrameLayout viewGroup = (FrameLayout) findViewById(R.id.ll_menuLayout)
.getParent();
enableDisableViewGroup(viewGroup, true);
// enable empty view
((LinearLayout) findViewById(R.id.ll_empty))
.setVisibility(View.VISIBLE);
findViewById(R.id.ll_empty).setEnabled(true);
findViewById(R.id.ll_empty).setOnTouchListener(
new OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
menuLeftSlideAnimationToggle();
return true;
}
});
} else {
isLeftExpanded = false;
// Collapse
new CloseAnimation(ll_mainLayout, leftMenuWidth,
TranslateAnimation.RELATIVE_TO_SELF, 0.75f,
TranslateAnimation.RELATIVE_TO_SELF, 0.0f, 0, 0.0f, 0, 0.0f);
// enable all of menu view
FrameLayout viewGroup = (FrameLayout) findViewById(R.id.ll_menuLayout)
.getParent();
enableDisableViewGroup(viewGroup, false);
// disable empty view
((LinearLayout) findViewById(R.id.ll_empty))
.setVisibility(View.GONE);
findViewById(R.id.ll_empty).setEnabled(false);
}
}
public static void enableDisableViewGroup(ViewGroup viewGroup,
boolean enabled) {
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = viewGroup.getChildAt(i);
view.setEnabled(enabled);
if (view instanceof ViewGroup) {
enableDisableViewGroup((ViewGroup) view, enabled);
}
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_left:
menuLeftSlideAnimationToggle();
break;
case R.id.btn1:
Toast.makeText(getApplicationContext(), "1", Toast.LENGTH_SHORT)
.show();
break;
case R.id.btn2:
Toast.makeText(getApplicationContext(), "2", Toast.LENGTH_SHORT)
.show();
break;
case R.id.btn3:
Toast.makeText(getApplicationContext(), "3", Toast.LENGTH_SHORT)
.show();
break;
case R.id.btn4:
Toast.makeText(getApplicationContext(), "4", Toast.LENGTH_SHORT)
.show();
break;
}
}
}
최초에 initSildeMenu()매소드에서 사용 할 메인 레이아웃과 메뉴 레이아웃을 초기화 합니다.
메뉴 레이아웃은 열렸을 때 화면 전체너비로 열리는 것이 아니라 우측에 메인 레이아웃이 살짝 걸치므로
메뉴 레아이웃 너비가 될 leftMenuWidth 변수에 화면전체 너비에 0.75 정도 비율을 넣어 줍니다.
이 후에 메인 레이아웃을 findViewById 하고 메뉴 레이아웃도 findViewById 한 후
아까 만든 0.75의 너비를 width로 넣어줍니다. 나머지 버튼들도 리스너를 달아줍니다.
이제 핵심이 되는 menuLeftSlideAnimationToggle() 매소드 입니다
토글이라는 말과 같이 메뉴가 열고 닫힐 때 모두 이 매소드를 사용하며
isLeftExpanded라는 boolean 값으로 열고 닫힐 때를 판단하게 됩니다.
우선 열릴 때 입니다.
메뉴버튼을 눌러 메뉴가 열리게 되면 우선 isLeftExpanded = true; 를 줘서 메뉴가 열렸다를 저장하고
OpenAnimation 이란 클래스를 통하여 메뉴가 열리게 됩니다.
처음 구조 설명 할 때 설명을 안했지만 메인 레이아웃 위에 empty 레이아웃을 하나 더 포개었는데
이 뷰를 VISIBLE 시키게 됩니다. 이 ll_empty 뷰는 투명한 뷰이며
메뉴가 열려있을 때 우측에 살짝 보이는 메인 레이아웃을 덮고 있습니다.
이 뷰를 터치하면 menuLeftSlideAnimationToggle(); 매소드를 호출하여 메뉴가 닫히게 됩니다.
이는 꼭 메뉴버튼을 눌러야 메뉴가 닫히는것이 아닌 그냥 메인 뷰만 터치하면 닫히도록하는 편리함을 위함입니다.
public class OpenAnimation extends TranslateAnimation implements
Animation.AnimationListener {
private LinearLayout mainLayout
int panelWidth;
public OpenAnimation(LinearLayout layout, int width, int fromXType,
float fromXValue, int toXType, float toXValue, int fromYType,
float fromYValue, int toYType, float toYValue) {
super(fromXType, fromXValue, toXType, toXValue, fromYType, fromYValue,
toYType, toYValue);
// init
mainLayout = layout;
panelWidth = width;
setDuration(250);
setFillAfter(false);
setInterpolator(new AccelerateDecelerateInterpolator());
setAnimationListener(this);
mainLayout.startAnimation(this);
}
public void onAnimationEnd(Animation arg0) {
LayoutParams params = (LayoutParams) mainLayout.getLayoutParams();
params.leftMargin = panelWidth;
params.gravity = Gravity.LEFT;
mainLayout.clearAnimation();
mainLayout.setLayoutParams(params);
mainLayout.requestLayout();
}
public void onAnimationRepeat(Animation arg0) {
}
public void onAnimationStart(Animation arg0) {
}
}
OpenAnimation 클래스입니다. 받아온 속성들로 초기화를 하며 setDuration(250);이 부분이 열릴 때의 속도입니다.
메뉴가 닫힐 때는 isLeftExpanded로 닫힐 때를 가려내고 isLeftExpanded = flase;를 줘 닫힘을 저장합니다.
OpenAnimarion 클래스와 같이 CloseAnimation을 호출하게되고 활성화 되어있던 empty뷰를 꺼서
메인 레이아웃이 다시 노출되게 합니다.
public class CloseAnimation extends TranslateAnimation implements
TranslateAnimation.AnimationListener {
private LinearLayout mainLayout;
int panelWidth;
public CloseAnimation(LinearLayout layout, int width, int fromXType,
float fromXValue, int toXType, float toXValue, int fromYType,
float fromYValue, int toYType, float toYValue) {
super(fromXType, fromXValue, toXType, toXValue, fromYType, fromYValue,
toYType, toYValue);
// Initialize
mainLayout = layout;
panelWidth = width;
setDuration(250);
setFillAfter(false);
setInterpolator(new AccelerateDecelerateInterpolator());
setAnimationListener(this);
// Clear left and right margins
LayoutParams params = (LayoutParams) mainLayout.getLayoutParams();
params.rightMargin = 0;
params.leftMargin = 0;
mainLayout.setLayoutParams(params);
mainLayout.requestLayout();
mainLayout.startAnimation(this);
}
public void onAnimationEnd(Animation animation) {
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
}
}
CloseAnimation입니다.
아래엔 사용한 xml 파일입니다.
<-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/ll_menuLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff393c46"
android:gravity="left"
android:orientation="vertical"
android:textColor="#ff000000" >
<!-- include -->
<include
android:id="@+id/ic_leftslidemenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="@layout/leftslidemenu" />
</LinearLayout>
<!-- slide layout -->
<LinearLayout
android:id="@+id/ll_mainlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffffff"
android:gravity="left"
android:orientation="vertical" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/bt_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Left Slide" />
<LinearLayout
android:id="@+id/ll_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</LinearLayout>
<LinearLayout
android:id="@+id/ll_empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/transparent" />
</FrameLayout>
</LinearLayout>
</FrameLayout>
</LinearLayout>
<-- leftslidemenu.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:background="#994444cc"
android:orientation="vertical"
android:padding="10dp" >
<Button
android:id="@+id/btn1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Button 1 " />
<Button
android:id="@+id/btn2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Button 2 " />
<Button
android:id="@+id/btn3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Button 3 " />
<Button
android:id="@+id/btn4"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Button 4 " />
</LinearLayout>
위 처럼 메인 레이아웃에 메뉴 레이아웃이 include 되어있습니다.
-- 2013 10 04 내용추가
현재 버전에는 메인 화면에서는 메인 화면이 덮고있는 중에도 터치를 하면 뒤 LeftMenu의 버튼이 눌리게 됩니다.
초보개발자 님 말씀대로 visible방법도 가능하고
저는 뷰를 닫을 때 LeftMenu에 있는 모든 뷰를 다 setEnable로 꺼버리고
열 때는 켜는 식을 사용 했습니다.
열고 닫을 때
FrameLayout viewGroup = (FrameLayout) findViewById(R.id.ll_menuLayout)
.getParent();
이런식으로 menu 레이아웃을 잡고
enableDisableViewGroup(viewGroup, false);
setEnable을 true 할 건지 false 할 건지 정해 주시면 됩니다.
그럼
public
static
void
enableDisableViewGroup(ViewGroup viewGroup,
boolean
enabled) {
int
childCount = viewGroup.getChildCount();
for
(
int
i =
0
; i < childCount; i++) {
View view = viewGroup.getChildAt(i);
view.setEnabled(enabled);
if
(view
instanceof
ViewGroup) {
enableDisableViewGroup((ViewGroup) view, enabled);
}
}
}
FrameLayout viewGroup = (FrameLayout) findViewById(R.id.ll_fragment)
.getParent();
이렇게 좌측 슬라이드 메뉴 레이아웃을 잡고
enableDisableViewGroup 매소드 for문 안쪽에
View view = viewGroup.getChildAt(i);
if (view.getId() != R.id.ib_left) {
view.setEnabled(enabled);
if (view instanceof ViewGroup) {
enableDisableViewGroup((ViewGroup) view, enabled);
}
}
이런식으로 LeftSlide 버튼만 살려서 disable을 막는 방법이 있습니다.
-- 2013 10 22 내용추가
좌 우 측 슬라이드가 모두 되는 소스 첨부 합니다.
우측 슬라이드도 좌측 슬라이드와 같은 방식으로 만들었습니다.