本文实现AndroidQQ5.0侧滑效果,现在QQ8.8已经不用这种效果了,但是现在最新的酷狗使用的是这种效果。
请看原图:
实现原理:
其实是使用一个水平布局的ScrollView来放一个线性布局,通过监听屏幕的滑动事件来决定是否显示线性布局里面的侧边栏。
代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:drakeet="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<me.drakeet.qqsliddingmenu.QQSliddingMenu
android:id="@+id/qqsm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/img_frame_background"
drakeet:rigthtPadding="160dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<include layout="@layout/left_menu" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/qq">
<TextView
android:id="@+id/my_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="主页"
android:textColor="#f00"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="20dp"
android:onClick="toggleMenu"
android:text="切换菜单"
android:textColor="#fff" />
RelativeLayout>
LinearLayout>
me.drakeet.qqsliddingmenu.QQSliddingMenu>
RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/left_rl_item1"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_1"
android:layout_width="@dimen/image_dimen"
android:layout_height="@dimen/image_dimen"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@drawable/img_1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_toRightOf="@id/iv_1"
android:text="第一个菜单"
android:textColor="#ffffff"
android:textSize="@dimen/text_size" />
RelativeLayout>
<RelativeLayout
android:id="@+id/left_rl_item2"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_2"
android:layout_width="@dimen/image_dimen"
android:layout_height="@dimen/image_dimen"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@drawable/img_2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_toRightOf="@id/iv_2"
android:text="第二个菜单"
android:textColor="#ffffff"
android:textSize="@dimen/text_size" />
RelativeLayout>
<RelativeLayout
android:id="@+id/left_rl_item3"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_3"
android:layout_width="@dimen/image_dimen"
android:layout_height="@dimen/image_dimen"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@drawable/img_3" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_toRightOf="@id/iv_3"
android:text="第三个菜单"
android:textColor="#ffffff"
android:textSize="@dimen/text_size" />
RelativeLayout>
<RelativeLayout
android:id="@+id/left_rl_item4"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_4"
android:layout_width="@dimen/image_dimen"
android:layout_height="@dimen/image_dimen"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@drawable/img_4" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_toRightOf="@id/iv_4"
android:text="第四个菜单"
android:textColor="#ffffff"
android:textSize="@dimen/text_size" />
RelativeLayout>
<RelativeLayout
android:id="@+id/left_rl_item5"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_5"
android:layout_width="@dimen/image_dimen"
android:layout_height="@dimen/image_dimen"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@drawable/img_5" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_toRightOf="@id/iv_5"
android:text="第五个菜单"
android:textColor="#ffffff"
android:textSize="@dimen/text_size" />
RelativeLayout>
LinearLayout>
RelativeLayout>
其实上面两个布局文件是可以写在一起的,第一个布局文件include包含第二个布局文件,但是你也可以把第二个布局文件设计成一个碎片,这样所有的点击事件都是在碎片的类中完成的。
package me.drakeet.qqsliddingmenu;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import com.nineoldandroids.view.ViewHelper;
/**
* 水平的滚动视图,实现侧滑功能
*/
public class QQSliddingMenu extends HorizontalScrollView {
private LinearLayout mWapper;
private ViewGroup mMenuViewGroup;
private ViewGroup mContentViewGroup;
private int mScreenWidth;
private int mMenuRightPadding = 50;
private int mMenuWidth;//侧滑菜单栏的宽度
private boolean mIsOnce = true;
private boolean mIsOpen;
/**
* 构造方法
*/
public QQSliddingMenu(Context context) {
this(context, null);
}
public QQSliddingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public QQSliddingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
mScreenWidth = displayMetrics.widthPixels;
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.QQSliddingMenu, defStyleAttr, 0);
int n = typedArray.length();
for (int i = 0; i < n; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.QQSliddingMenu_rigthtPadding:
mMenuRightPadding = typedArray.getDimensionPixelSize(
attr,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics())
);
break;
default:
break;
}
}
typedArray.recycle();
}
/**
* 测量布局
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mIsOnce) {
mWapper = (LinearLayout) getChildAt(0);
mMenuViewGroup = (ViewGroup) mWapper.getChildAt(0);
mContentViewGroup = (ViewGroup) mWapper.getChildAt(1);
mMenuWidth = mMenuViewGroup.getLayoutParams().width = mScreenWidth - mMenuRightPadding;
mContentViewGroup.getLayoutParams().width = mScreenWidth;
mIsOnce = false;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
this.scrollTo(mMenuWidth, 0);
}
}
/**
* 事件响应
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
if (scrollX >= mMenuWidth / 2) {
smoothScrollTo(mMenuWidth, 0);
mIsOpen = false;
} else {
smoothScrollTo(0, 0);
mIsOpen = true;
}
return true;
}
return super.onTouchEvent(ev);
}
/**
* 打开菜单栏
*/
public void openMenu() {
if (mIsOpen)
return;
smoothScrollTo(0, 0);
mIsOpen = true;
}
/**
* 关闭菜单栏
*/
public void closeMenu() {
if (!mIsOpen)
return;
smoothScrollTo(mMenuWidth, 0);
mIsOpen = false;
}
/**
* 切换菜单
*/
public void toggleMenu() {
if (mIsOpen)
closeMenu();
else
openMenu();
}
/**
* 页面滑动的距离的改变
*/
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
float scale = l * 1.0f / mMenuWidth; // l: 1.0 ~ 0
ViewHelper.setTranslationX(mMenuViewGroup, mMenuWidth * scale * 0.7f);
float rightScale = 0.7f + 0.3f * scale;
ViewHelper.setPivotX(mContentViewGroup, 0);
ViewHelper.setPivotY(mContentViewGroup, mContentViewGroup.getHeight() / 2);
ViewHelper.setScaleX(mContentViewGroup, rightScale);
ViewHelper.setScaleY(mContentViewGroup, rightScale);
float leftScale = 1.0f - 0.3f * scale;
ViewHelper.setScaleX(mMenuViewGroup, leftScale);
ViewHelper.setScaleY(mMenuViewGroup, leftScale);
float leftAlpha = 0.1f + 0.9f * (1 - scale);
ViewHelper.setAlpha(mMenuViewGroup, leftAlpha);
}
}
package me.drakeet.qqsliddingmenu;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* 侧边栏的示例
*/
public class MyActivity extends AppCompatActivity implements View.OnClickListener {
//侧滑的自定义View对象
private QQSliddingMenu mQQSliddingMenu;
TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
mQQSliddingMenu = (QQSliddingMenu) findViewById(R.id.qqsm);
text = (TextView) findViewById(R.id.my_text);
initLeftItem();
}
/**
* 给左边的侧滑条目设置监听事件
*/
private void initLeftItem() {
RelativeLayout rl1 = (RelativeLayout) findViewById(R.id.left_rl_item1);
RelativeLayout rl2 = (RelativeLayout) findViewById(R.id.left_rl_item2);
RelativeLayout rl3 = (RelativeLayout) findViewById(R.id.left_rl_item3);
RelativeLayout rl4 = (RelativeLayout) findViewById(R.id.left_rl_item4);
RelativeLayout rl5 = (RelativeLayout) findViewById(R.id.left_rl_item5);
rl1.setOnClickListener(this);
rl2.setOnClickListener(this);
rl3.setOnClickListener(this);
rl4.setOnClickListener(this);
rl5.setOnClickListener(this);
}
/**
* 点击切换菜单按钮的监听事件
*/
public void toggleMenu(View view) {
mQQSliddingMenu.toggleMenu();
}
/**
* 点击左边的菜单栏的条目的监听事件
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.left_rl_item1:
text.setText("选择了第一个按钮");
break;
case R.id.left_rl_item2:
text.setText("选择了第二个按钮");
break;
case R.id.left_rl_item3:
text.setText("选择了第三个按钮");
break;
case R.id.left_rl_item4:
text.setText("选择了第四个按钮");
break;
case R.id.left_rl_item5:
text.setText("选择了第五个按钮");
break;
}
//切换菜单
mQQSliddingMenu.toggleMenu();
}
}
到这里一个QQ5.0侧拉效果的功能就实现了,其实最主要的就是那个自定义的View,这个工具类是可以直接拿来使用的。
左边的菜单条目也是有点击效果的:
点击第三个菜单栏的条目显得的效果:
这里只是简单显示右边的效果,实际中可以把右边换成一个新的页面。
这里如果变成现在最小版本的QQ效果,只要侧滑时,Y轴方向的大小不改变就可以了。
这里注销两句话,就实现了!
//ViewHelper.setPivotY(mContentViewGroup, mContentViewGroup.getHeight() / 2);
// ViewHelper.setScaleY(mContentViewGroup, rightScale);
如图:
改变后的效果,侧滑时:
侧滑后:
最后还得说一下,在实际开发中,如果开发的是一个比较大的程序,比如QQ这种,肯定是要把左边的菜单栏设计成一个碎片fragment,右边的布局是一个帧布局FrameLayout,用来动态创建碎片。这样所有的逻辑操作都是在对应的碎片的类Fragment中实现,避免逻辑混乱。
下面提供一下程序设计的源码下载地址:
http://download.csdn.net/detail/wenzhi20102321/9723485