duyojiぶろぐ

技術系ときどき日常系

AndroidでFacebookのようなサイドメニューの実装

実装した物(動画)

ソースコード

githubソースコードを置いた。
https://github.com/duyoji/SideMenuSample

MainActivity.java

import java.util.ArrayList;
import side.menu.scroll.MenuAdapter;
import side.menu.scroll.ScrollerLinearLayout;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
	
	private LinearLayout rootLayout;	
	private ScrollerLinearLayout sideSlideLayout;
	
	private ListView listView;		
	private final String[] menuTitles = {"メニュー1", 
										 "メニュー2", 
										 "メニュー3", 
										 "メニュー4", 
										 "メニュー5",
										 "メニュー6"};	

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        init();
        setMenuButton();
        setListView();
        setContent(0);
    }       
    
    private void init(){    	
    	this.sideSlideLayout = (ScrollerLinearLayout) findViewById(R.id.menu_content_side_slide_layout);
    	this.rootLayout = (LinearLayout) findViewById(R.id.menu_content_root);    	    	
    }
    
    
    private void setMenuButton(){
    	Button menuButton = (Button) findViewById(R.id.main_tmp_button);
    	menuButton.setOnClickListener(new OnClickListener() {			
			@Override
			public void onClick(View v) {
				sideSlideLayout.scroll();
			}
		});
    }
    
    
    private void setListView() {
    	
    	ArrayList<String> items = new ArrayList<String>();
    	for (int i = 0; i < menuTitles.length; i++) {
    		items.add(menuTitles[i]);
		}
    	
    	listView = (ListView) findViewById(R.id.menu_content_menulist);
    	listView.setFadingEdgeLength(0);
    	
    	MenuAdapter menuAdapter = new MenuAdapter(this, items, this);
		listView.setAdapter(menuAdapter);
		listView.setOnItemClickListener(new OnItemClickListener() {			
			@Override
			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
					long arg3) {
				// TODO Auto-generated method stub
				setContent(arg2);
				sideSlideLayout.scroll();
			}
		});
	}
    
    private void setContent(int position){
    	rootLayout.removeAllViews();
    	TextView tmpText = new TextView(this);
    	tmpText.setText(menuTitles[position]);
    	rootLayout.addView(tmpText);
    }
}

MenuAdapter.java

import java.util.ArrayList;
import side.menu.sample.R;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class MenuAdapter extends BaseAdapter {	
	private Context context;
	private ArrayList<String> items;
	private LayoutInflater inflater;	
	

	public MenuAdapter(Context context, ArrayList<String> items, Activity act) {
		this.context = context;
		this.items = items;
		this.inflater = LayoutInflater.from(context);		
	}
			

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		convertView = inflater.inflate(R.layout.item_menu, null);
		TextView title = (TextView) convertView.findViewById(R.id.menu_title);
		title.setText(getItem(position));			
		return convertView;
	}

	@Override
	public int getCount() {
		return items.size();
	}

	@Override
	public String getItem(int position) {
		return items.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

}

ScrollerLinearLayout.java

import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.Scroller;

public class ScrollerLinearLayout extends LinearLayout {
	
	private Context context;
	private Scroller scroller;	
	private float density;
	private int scrollSizeWidth;
	
	
	public ScrollerLinearLayout(Context context) {
		super(context);
		this.context = context;
		init();
	}

	
	public ScrollerLinearLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		this.context = context;
		init();
	}
	
	
	private void init(){
		scroller = new Scroller(context);
		
		WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display disp = wm.getDefaultDisplay();
		int screenWidth = disp.getWidth();
        
        DisplayMetrics metrics = new DisplayMetrics();  
		((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);		
		this.density = metrics.density;
		
		scrollSizeWidth = (int)(screenWidth-(48*density));	
	}	
	
	public void scroll(){
		animationStart();
	}	
		
	@Override
    public void computeScroll() {		
        if(scroller.computeScrollOffset()){
            // Scrollerから移動位置を決定する
            scrollTo(scroller.getCurrX(),scroller.getCurrY());
            postInvalidate();
        }
    }
 
    private void animationStart(){        	
    	if (scroller.getCurrX() < 0)	
    		scroller.startScroll(scroller.getCurrX(), scroller.getCurrY(), -1*scroller.getCurrX(), 0, 500);
    	else
			scroller.startScroll(scroller.getCurrX(), scroller.getCurrY(), -scrollSizeWidth, 0, 500);    	
    	
        invalidate();
    }
}

main.xml

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent">
	<ListView 
	    android:id="@+id/menu_content_menulist"
	    android:layout_width="match_parent"
	    android:layout_height="match_parent"/>		
	<RelativeLayout 
	    android:layout_width="match_parent"
	    android:layout_height="match_parent">
	    <side.menu.scroll.ScrollerLinearLayout	    
		    android:id="@+id/menu_content_side_slide_layout"
		    android:layout_width="match_parent"
		    android:layout_height="match_parent"	    
		    android:orientation="vertical">
		    <LinearLayout 
		        android:id="@+id/menu_content_frame2"
		        android:layout_width="match_parent"
		        android:layout_height="match_parent"
		        android:background="#fff"
		        android:orientation="vertical">
		        <Button
			        android:id="@+id/main_tmp_button"
			        android:layout_width="wrap_content"
			        android:layout_height="wrap_content"
			        android:text="menu"/>  
				<LinearLayout
				    android:id="@+id/menu_content_root"
				    android:layout_width="match_parent"
				    android:layout_height="match_parent"
				    android:background="#f00"/>
		    </LinearLayout>	    		    
		</side.menu.scroll.ScrollerLinearLayout>			
	</RelativeLayout>		
	
</RelativeLayout>

item_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:paddingLeft="10dip"
	android:paddingRight="10dip">
	<RelativeLayout 
	    android:layout_width="match_parent"
	    android:layout_height="wrap_content">
		<TextView 
		    android:id="@+id/menu_title"
		    android:layout_width="270dip"
		    android:layout_height="wrap_content"
		    android:layout_alignParentLeft="true"
		    android:textSize="18sp"
		    android:textColor="#000"
		    android:maxLines="1"
			android:scrollHorizontally="true"
			android:ellipsize="end"/>    
	</RelativeLayout>
	
</LinearLayout>

説明

ScrollerLinaerLayout

横スライドしているものはLinearLayoutを継承して
作ったScrollerLinearLayout。
ScrollerLinearLayoutのインスタンスを作り、インスタンスメソッドである
scroll()メソッドを呼ぶと横スライドアニメーションが始まる。
Scrollerを使って動かすのだが、ScrollerLinaerLayout#animationStartの中で
scroller.getCurrX()を見てViewの左のポジションが0より大きい場合は左に
xの値だけスライド、そうでない場合は右に自分が設定した値だけスライドする。

main.xml

main.xml内で上で説明したScrollerLinearLayoutをセットして、
その子ビューに動画でいうと左上にボタン下に赤い画面のViewをセットしている。
この部分が横スライドをする部分となるので、横スライドさせたいものは、
ScrollerLinearLayoutの子ビューにセットすれば良い。

子ビューの一つとしてセットしているLinearLayout(id="@+id/menu_content_root")に
動的に変更したものがあればプログラム(javaファイル)側でViewを追加したりする。

MainActivity

init()メソッドでxmlでセットしたScrollerLinarLayoutとLinearLayout(id="@+id/menu_content_root")の
インスタンスを作っている。
これらは横スライドするためのメソッドを呼ぶときと赤い部分のLayoutを変更するための
初期設定をしている。

ListViewはmain.xmlを見るとわかると思うが、ScrollerLinearLayoutの後ろに
隠れるようにセットして上の画面がスライドしたらしたのメニューが見れるようになっている。

あとは、画面左上のボタンと、メニューを押したときにそれぞれイベントをゲットして
ScrollerLinearLayout#scroll()メソッドを呼び出して完成。

実際の使い方

画面を切り替えるときはstartActivityなどを呼び出して別の
Actvityを表示するようにするのが普通だと思うがそれだと
全画面が切り替わるため横スライドメニューができない。
やりたいことはイメージとしてTabActivityのように画面の一部の
Viewだけが切り替わるようにしたい。

そこでActivity内に作ったViewを生成するコードを
独自のLayoutファイルを作って、そこでViewの構成を
作るコードを書いて、それをLinearLayout(id="@+id/menu_content_root")に
addViewするような感じで使う。