Android realization analysis Tenth of SlidingMenu sliding menu
SlidingMenu sliding menu is a relatively new configuration settings interface or the interface effect, slide left or right slide interface effect is provided in the main interface, you can easily perform various operations. A lot of good applications using this interface programs like facebook, all networks, everynote, Google +, Netease news, know almost daily, the proper way cloud notes, etc.
Sliding menu implementation principles:
the layout of an Activity in need of two parts, one is the menu (menu) layout is a layout of content (content) of. Two transversely aligned layout, the layout of the menu on the left, the right content layout. Initialization of the offset to the left menu layout that can be completely hidden, so the content layout will be completely displayed in an Activity. Then by listening to the finger sliding events, to change the menu layout left offset distance, thereby controlling the display and hide the menu layout.
Two: the implementation process: opening the button to expand the sliding menu, click again to close the sliding menu.
1, the introduction of custom components SlideMenu
SlideMenu.java
package com.example.walkerlogin1.view;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
public class SlideMenu extends ViewGroup {
public static final int SCREEN_MENU = 0;
public static final int SCREEN_MAIN = 1;
private static final int SCREEN_INVALID = -1;
private int mCurrentScreen;
private int mNextScreen = SCREEN_INVALID;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private int mTouchSlop;
private float mLastMotionX;
private float mLastMotionY;
private final static int TOUCH_STATE_REST = 0;
private final static int TOUCH_STATE_SCROLLING = 1;
private static final int SNAP_VELOCITY = 1000;
public int mTouchState = TOUCH_STATE_REST;
private boolean mLocked;
private boolean mAllowLongPress;
public SlideMenu(Context context) {
this(context, null, 0);
}
public SlideMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScroller = new Scroller(getContext());
mCurrentScreen = SCREEN_MAIN;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureViews(widthMeasureSpec, heightMeasureSpec);
}
public void measureViews(int widthMeasureSpec, int heightMeasureSpec) {
View menuView = getChildAt(0);
menuView.measure(menuView.getLayoutParams().width + menuView.getLeft()
+ menuView.getRight(), heightMeasureSpec);
View contentView = getChildAt(1);
contentView.measure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
if (childCount != 2) {
throw new IllegalStateException(
"The childCount of SlidingMenu must be 2");
}
View menuView = getChildAt(0);
final int width = menuView.getMeasuredWidth();
menuView.layout(-width, 0, 0, menuView.getMeasuredHeight());
View contentView = getChildAt(1);
contentView.layout(0, 0, contentView.getMeasuredWidth(),
contentView.getMeasuredHeight());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
View child;
for (int i = 0; i < getChildCount(); i++) {
child = getChildAt(i);
child.setFocusable(true);
child.setClickable(true);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mLocked) {
return true;
}
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE)
&& (mTouchState != TOUCH_STATE_REST)) {
return true;
}
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int yDiff = (int) Math.abs(y - mLastMotionY);
final int touchSlop = mTouchSlop;
boolean xMoved = xDiff > touchSlop;
boolean yMoved = yDiff > touchSlop;
if (xMoved || yMoved) {
if (xMoved) {
// Scroll if the user moved far enough along the X axis
mTouchState = TOUCH_STATE_SCROLLING;
enableChildrenCache();
}
// Either way, cancel any pending longpress
if (mAllowLongPress) {
mAllowLongPress = false;
// Try canceling the long press. It could also have been
// scheduled
// by a distant descendant, so use the mAllowLongPress flag
// to block
// everything
final View currentScreen = getChildAt(mCurrentScreen);
currentScreen.cancelLongPress();
}
}
break;
case MotionEvent.ACTION_DOWN:
// Remember location of down touch
mLastMotionX = x;
mLastMotionY = y;
mAllowLongPress = true;
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
: TOUCH_STATE_SCROLLING;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// Release the drag
clearChildrenCache();
mTouchState = TOUCH_STATE_REST;
mAllowLongPress = false;
break;
}
/*
* The only time we want to intercept motion events is if we are in the
* drag mode.
*/
return mTouchState != TOUCH_STATE_REST;
}
void enableChildrenCache() {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View layout = (View) getChildAt(i);
layout.setDrawingCacheEnabled(true);
}
}
void clearChildrenCache() {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View layout = (View) getChildAt(i);
layout.setDrawingCacheEnabled(false);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLocked) {
return true;
}
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
final float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
// Remember where the motion event started
mLastMotionX = x;
break;
case MotionEvent.ACTION_MOVE:
if (mTouchState == TOUCH_STATE_SCROLLING) {
// Scroll to follow the motion event
final int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;
if (deltaX < 0) {
if (deltaX + getScrollX() >= -getChildAt(0).getWidth()) {
scrollBy(deltaX, 0);
}
} else if (deltaX > 0) {
final int availableToScroll = getChildAt(
getChildCount() - 1).getRight()
- getScrollX() - getWidth();
if (availableToScroll > 0) {
scrollBy(Math.min(availableToScroll, deltaX), 0);
}
}
}
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_SCROLLING) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
int velocityX = (int) velocityTracker.getXVelocity();
if (velocityX > SNAP_VELOCITY && mCurrentScreen == SCREEN_MAIN) {
// Fling hard enough to move left
snapToScreen(SCREEN_MENU);
} else if (velocityX < -SNAP_VELOCITY
&& mCurrentScreen == SCREEN_MENU) {
// Fling hard enough to move right
snapToScreen(SCREEN_MAIN);
} else {
snapToDestination();
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
}
return true;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
} else if (mNextScreen != SCREEN_INVALID) {
mCurrentScreen = Math.max(0,
Math.min(mNextScreen, getChildCount() - 1));
mNextScreen = SCREEN_INVALID;
clearChildrenCache();
}
}
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
postInvalidate();
}
@Override
protected void dispatchDraw(Canvas canvas) {
final int scrollX = getScrollX();
super.dispatchDraw(canvas);
canvas.translate(scrollX, 0);
}
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
if (direction == View.FOCUS_LEFT) {
if (getCurrentScreen() > 0) {
snapToScreen(getCurrentScreen() - 1);
return true;
}
} else if (direction == View.FOCUS_RIGHT) {
if (getCurrentScreen() < getChildCount() - 1) {
snapToScreen(getCurrentScreen() + 1);
return true;
}
}
return super.dispatchUnhandledMove(focused, direction);
}
protected void snapToScreen(int whichScreen) {
enableChildrenCache();
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
boolean changingScreens = whichScreen != mCurrentScreen;
mNextScreen = whichScreen;
View focusedChild = getFocusedChild();
if (focusedChild != null && changingScreens
&& focusedChild == getChildAt(mCurrentScreen)) {
focusedChild.clearFocus();
}
final int newX = (whichScreen - 1) * getChildAt(0).getWidth();
final int delta = newX - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
invalidate();
}
protected void snapToDestination() {
if (getScrollX() == 0) {
return;
}
final int screenWidth = getChildAt(0).getWidth();
final int whichScreen = (screenWidth + getScrollX() + (screenWidth / 2))
/ screenWidth;
snapToScreen(whichScreen);
}
public int getCurrentScreen() {
return mCurrentScreen;
}
public boolean isMainScreenShowing() {
return mCurrentScreen == SCREEN_MAIN;
}
public void openMenu() {
mCurrentScreen = SCREEN_MENU;
snapToScreen(mCurrentScreen);
}
public void closeMenu() {
mCurrentScreen = SCREEN_MAIN;
snapToScreen(mCurrentScreen);
}
public void unlock() {
mLocked = false;
}
public void lock() {
mLocked = true;
}
}
In the following vlues strings.xml, add the following codes
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item>用户登录</item>
<item>运动测试</item>
<item>个人信息</item>
<item>行程记录</item>
<item>天气查询</item>
<item>健康栏目</item>
<item>软件设置</item>
</string-array>
</resources>
Item.java
package com.example.walkerlogin1.view;
public class Item {
private String name;
private int imageId;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
public Item(String name, int imageId) {
super();
this.name = name;
this.imageId = imageId;
}
}
Set up a data source
ItemAdapter.java
package com.example.walkerlogin1.view;
import java.util.List;
import com.example.walkerlogin1.R;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class ItemAdapter extends ArrayAdapter<Item> {
public ItemAdapter(Context context, int textViewResourceId,
List<Item> objects) {
super(context, textViewResourceId, objects);
// TODO Auto-generated constructor stub
resourceId=textViewResourceId;
}
private int resourceId;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Item item = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
viewHolder = new ViewHolder();
viewHolder.ivIcon = (ImageView) view.findViewById(R.id.ivIcon);
viewHolder.tvIntroduction = (TextView) view.findViewById(R.id.tvIntroduction);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.ivIcon.setImageResource(item.getImageId());
viewHolder.tvIntroduction.setText(item.getName());
return view;
}
class ViewHolder {
ImageView ivIcon;
TextView tvIntroduction;
}
}
The main interface MainActivity2.java
package com.example.walkerlogin1;
import java.util.ArrayList;
import java.util.List;
import com.example.walkerlogin1.view.Item;
import com.example.walkerlogin1.view.ItemAdapter;
import com.example.walkerlogin1.view.SlideMenu;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity2 extends Activity {
private SlideMenu slideMenu;
private ImageView ivSwitchSlideMenu;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main2);
initMenuList();
slideMenu=(SlideMenu) findViewById(R.id.slideMenu);
ivSwitchSlideMenu=(ImageView) findViewById(R.id.switch_slidemenu);
ivSwitchSlideMenu.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
if(slideMenu.isMainScreenShowing()){
slideMenu.openMenu();
}else{
slideMenu.closeMenu();
}
}
});
}
private void initMenuList() {
int[] icons = { R.drawable.icons_menu_login,
R.drawable.icons_menu_sport, R.drawable.icons_menu_inform,
R.drawable.icons_menu_history, R.drawable.icons_menu_weather,
R.drawable.icons_menu_health, R.drawable.icons_menu_setting };
final String[] introductons = getResources().getStringArray(R.array.menulist);
List<Item> items=new ArrayList<Item>();
for(int i=0;i<icons.length;i++){
items.add(new Item(introductons[i],icons[i]));
}
ListView lvMenuList=(ListView) findViewById(R.id.lvMenuList);
ItemAdapter itemAdapter=new ItemAdapter(this, R.layout.menulist_item, items);
lvMenuList.setAdapter(itemAdapter);
lvMenuList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> adapterView, View view, int position,
long id) {
//Toast.makeText(MainActivity2.this," 你点击了"+introductons[position],
//Toast.LENGTH_LONG).show();
Intent intent=new Intent();
switch (position) {
case 0: //这里可以进行listview的点击跳转
Toast.makeText(MainActivity2.this," 你点击了"+introductons[position],
Toast.LENGTH_LONG).show();
intent.setClass(getApplicationContext(), MainActivity.class);
startActivity(intent);
break;
default:
break;
}
}
});
}
}
UI layout activity_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"
tools:context=".MainActivity" >
<com.example.walkerlogin1.view.SlideMenu
android:id="@+id/slideMenu"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- 侧滑菜单 -->
<include layout="@layout/leftmenu" />
<!-- 主界面 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include layout="@layout/main_layout_titlebar" />
<include layout="@layout/main_layout_content" />
</LinearLayout>
</com.example.walkerlogin1.view.SlideMenu>
</RelativeLayout>
leftmenu.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.walkerlogin1"
android:layout_width="200dp"
android:layout_height="match_parent"
android:background="@drawable/leftmenu_bg" >
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/rivUserPhoto"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:src="@drawable/test_photo"
app:riv_oval="true" />
<TextView
android:id="@+id/tvMotto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/rivUserPhoto"
android:layout_marginBottom="24dp"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@+id/rivUserPhoto"
android:text="奔跑无止境"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/white" />
<ListView
android:id="@+id/lvMenuList"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_below="@id/tvMotto"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:divider="@color/whitesmoke"
android:dividerHeight="1dp"
android:listSelector="#00000000" >
</ListView>
</RelativeLayout>
main_layout_titlebar.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="45.0dip"
android:background="@drawable/titlebar_bg"
android:gravity="center_vertical" >
<ImageView
android:id="@+id/switch_slidemenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="3.0dip"
android:gravity="center"
android:src="@drawable/switch_silidemenu" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="Walker"
android:textColor="@color/white"
android:textSize="22sp" />
<ImageView
android:id="@+id/switch_map"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="15.0dip"
android:src="@drawable/switch_map" />
</RelativeLayout>
main_layout_content.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/welcome_bg" >
<TextView
android:id="@+id/tvCity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:text="滨州市"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/tvTemperature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/tvCity"
android:layout_below="@+id/tvCity"
android:layout_marginRight="26dp"
android:layout_marginTop="40dp"
android:text="temperature"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/tvDay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/tvTemperature"
android:layout_below="@+id/tvTemperature"
android:layout_marginTop="20dp"
android:text="星期日"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/tvWeather"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/tvDay"
android:layout_below="@+id/tvDay"
android:layout_marginTop="24dp"
android:text="晴"
android:textAppearance="?android:attr/textAppearanceMedium" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/tvTemperature"
android:layout_marginLeft="24dp"
android:layout_toRightOf="@+id/tvTemperature"
android:background="#00000000"
android:src="@drawable/monkey" />
</RelativeLayout>
The final layout
Click the button to the left or right to slide;