Android仿QQ6.0主頁(yè)面?zhèn)然Ч?/h1>
更新時(shí)間:2016年11月02日 10:11:51 作者:z240336124
這篇文章主要為大家詳細(xì)介紹了Android仿QQ6.0主頁(yè)面?zhèn)然Ч?具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
1.概述
最近一直都在帶實(shí)習(xí)生做項(xiàng)目,發(fā)現(xiàn)自己好久沒(méi)有寫(xiě)博客了,這幾天更新會(huì)比較頻繁,今天玩QQ的時(shí)候發(fā)現(xiàn)QQ主頁(yè)菜單滑動(dòng)效果早就變了,實(shí)在忍不住晚上就來(lái)實(shí)現(xiàn)一下了!

2.實(shí)現(xiàn)
2.1. 實(shí)現(xiàn)的方式多種多樣
2.1.1 自定義ViewGroup ,處理其onTouch事件
2.1.2 FrameLayout + 手勢(shì)處理類(lèi)GestureDetector
2.2.3 使用Google自帶的DrawerLayout 對(duì)其進(jìn)行修改
2.2.4 繼承自水平滾動(dòng)HorizontalScrollView
大家可以看一下這個(gè)http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0909/6612.html,這種方式繼承自ViewGroup,個(gè)人覺(jué)得還行但是還是比較繁瑣要處理的東西也比較多,那么我就用最后一種2.2.4的方式實(shí)現(xiàn),有人說(shuō)直接去網(wǎng)上下載一個(gè)源代碼就可以了,這我就只能GG了。
2.3. 自定義SlidingMenu extends HorizontalScrollView 然后寫(xiě)好布局文件這個(gè)和ScrollView的用法一樣,只不過(guò)是橫向滾動(dòng)的
/**
* description:
* 仿QQ5.0主頁(yè)面?zhèn)然淖远╒iew
* Created by 曾輝 on 2016/11/1.
* QQ:240336124
* Email: 240336124@qq.com
* Version:1.0
*/
public class SlidingMenu extends HorizontalScrollView {
public SlidingMenu(Context context) {
super(context);
}
public SlidingMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
2.4. 運(yùn)行起來(lái)之后發(fā)現(xiàn)布局不對(duì),完全亂了明明都是match_parent可是就是不行那么我們就需要用代碼指定菜單和內(nèi)容的寬度:
菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
內(nèi)容的寬度 = 屏幕的寬度
/**
* description:
* 仿QQ5.0主頁(yè)面?zhèn)然淖远╒iew
* Created by 曾輝 on 2016/11/1.
* QQ:240336124
* Email: 240336124@qq.com
* Version:1.0
*/
public class SlidingMenu extends HorizontalScrollView {
private View mMenuView;
private View mContentView;
private int mMenuWidth;
public SlidingMenu(Context context) {
this(context, null);
}
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 獲取自定義的右邊留出的寬度
TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.SlidingMenu);
float rightPadding = array.getDimension(
R.styleable.SlidingMenu_rightPadding,dip2px(50));
// 計(jì)算菜單的寬度 = 屏幕的寬度 - 自定義右邊留出的寬度
mMenuWidth = (int) (getScreenWidth() - rightPadding);
array.recycle();
}
/**
* 把dip 轉(zhuǎn)成像素
*/
private float dip2px(int dip) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 1.獲取根View也就是外層的LinearLayout
ViewGroup container = (ViewGroup) this.getChildAt(0);
int containerChildCount = container.getChildCount();
if(containerChildCount>2){
// 里面只允許放置兩個(gè)布局 一個(gè)是Menu(菜單布局) 一個(gè)是Content(主頁(yè)內(nèi)容布局)
throw new IllegalStateException("SlidingMenu 根布局LinearLayout下面只允許兩個(gè)布局,菜單布局和主頁(yè)內(nèi)容布局");
}
// 2.獲取菜單和內(nèi)容布局
mMenuView = container.getChildAt(0);
mContentView = container.getChildAt(1);
// 3.指定內(nèi)容和菜單布局的寬度
// 3.1 菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
mMenuView.getLayoutParams().width = mMenuWidth;
// 3.2 內(nèi)容的寬度 = 屏幕的寬度
mContentView.getLayoutParams().width = getScreenWidth();
}
/**
* 獲取屏幕的寬度
*/
public int getScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
return dm.widthPixels;
}
}
目前的效果就是可以滑動(dòng),并且菜單和主頁(yè)面內(nèi)容的布局寬度正常

2.5 接下來(lái)一開(kāi)始就讓菜單滑動(dòng)到關(guān)閉狀態(tài),手指滑動(dòng)抬起判斷菜單打開(kāi)和關(guān)閉并做相應(yīng)的處理 onLayout() onTouch() smoothScrollTo(),當(dāng)手指快速的時(shí)候切換菜單的狀態(tài)利用GestureDetector 手勢(shì)處理類(lèi):
/**
* description:
* 仿QQ5.0主頁(yè)面?zhèn)然淖远╒iew
* Created by 曾輝 on 2016/11/1.
* QQ:240336124
* Email: 240336124@qq.com
* Version:1.0
*/
public class SlidingMenu extends HorizontalScrollView {
private View mMenuView;
private View mContentView;
private int mMenuWidth;
// 手勢(shì)處理類(lèi) 主要用來(lái)處理手勢(shì)快速滑動(dòng)
private GestureDetector mGestureDetector;
// 菜單是否打開(kāi)
private boolean mMenuIsOpen = false;
public SlidingMenu(Context context) {
this(context, null);
}
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 獲取自定義的右邊留出的寬度
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenu);
float rightPadding = array.getDimension(
R.styleable.SlidingMenu_rightPadding, dip2px(50));
// 計(jì)算菜單的寬度 = 屏幕的寬度 - 自定義右邊留出的寬度
mMenuWidth = (int) (getScreenWidth() - rightPadding);
array.recycle();
// 實(shí)例化手勢(shì)處理類(lèi)
mGestureDetector = new GestureDetector(context,new GestureListener());
}
/**
* 把dip 轉(zhuǎn)成像素
*/
private float dip2px(int dip) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 1.獲取根View也就是外層的LinearLayout
ViewGroup container = (ViewGroup) this.getChildAt(0);
int containerChildCount = container.getChildCount();
if (containerChildCount > 2) {
// 里面只允許放置兩個(gè)布局 一個(gè)是Menu(菜單布局) 一個(gè)是Content(主頁(yè)內(nèi)容布局)
throw new IllegalStateException("SlidingMenu 根布局LinearLayout下面只允許兩個(gè)布局,菜單布局和主頁(yè)內(nèi)容布局");
}
// 2.獲取菜單和內(nèi)容布局
mMenuView = container.getChildAt(0);
mContentView = container.getChildAt(1);
// 3.指定內(nèi)容和菜單布局的寬度
// 3.1 菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
mMenuView.getLayoutParams().width = mMenuWidth;
// 3.2 內(nèi)容的寬度 = 屏幕的寬度
mContentView.getLayoutParams().width = getScreenWidth();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// 處理手指快速滑動(dòng)
if(mGestureDetector.onTouchEvent(ev)){
return mGestureDetector.onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
// 手指抬起獲取滾動(dòng)的位置
int currentScrollX = getScrollX();
if (currentScrollX > mMenuWidth / 2) {
// 關(guān)閉菜單
closeMenu();
} else {
// 打開(kāi)菜單
openMenu();
}
return false;
}
return super.onTouchEvent(ev);
}
/**
* 打開(kāi)菜單
*/
private void openMenu() {
smoothScrollTo(0, 0);
mMenuIsOpen = true;
}
/**
* 關(guān)閉菜單
*/
private void closeMenu() {
smoothScrollTo(mMenuWidth, 0);
mMenuIsOpen = false;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// 布局指定后會(huì)從新擺放子布局,當(dāng)其擺放完畢后,讓菜單滾動(dòng)到不可見(jiàn)狀態(tài)
if (changed) {
scrollTo(mMenuWidth, 0);
}
}
/**
* 獲取屏幕的寬度
*/
public int getScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
return dm.widthPixels;
}
private class GestureListener extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// 當(dāng)手指快速滑動(dòng)時(shí)候回調(diào)的方法
Log.e("TAG",velocityX+"");
// 如果菜單打開(kāi) 并且是向左快速滑動(dòng) 切換菜單的狀態(tài)
if(mMenuIsOpen){
if(velocityX<-500){
toggleMenu();
return true;
}
}else{
// 如果菜單關(guān)閉 并且是向右快速滑動(dòng) 切換菜單的狀態(tài)
if(velocityX>500){
toggleMenu();
return true;
}
}
return false;
}
}
/**
* 切換菜單的狀態(tài)
*/
private void toggleMenu() {
if(mMenuIsOpen){
closeMenu();
}else{
openMenu();
}
}
}
到了這一步之后我們就可以切換菜單了,并且處理了手指快速滑動(dòng),迫不及待的看下效果

2.6. 實(shí)現(xiàn)菜單左邊抽屜樣式的動(dòng)畫(huà)效果,監(jiān)聽(tīng)滾動(dòng)回調(diào)的方法onScrollChanged() 這個(gè)就非常簡(jiǎn)單了一句話(huà)就搞定,效果就不看了一起看終極效果吧
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
// l 是 當(dāng)前滾動(dòng)的x距離 在滾動(dòng)的時(shí)候會(huì)不斷反復(fù)的回調(diào)這個(gè)方法
Log.e(TAG,l+"");
mMenuView.setTranslationX(l*0.8f);
}
2.7. 實(shí)現(xiàn)菜單右邊菜單的陰影透明度效果,這個(gè)打算在主頁(yè)面內(nèi)容布局上面加一層陰影,用ImageView即可,那么我們的內(nèi)容View就需要換了
/**
* description:
* 仿QQ5.0主頁(yè)面?zhèn)然淖远╒iew
* Created by 曾輝 on 2016/11/1.
* QQ:240336124
* Email: 240336124@qq.com
* Version:1.0
*/
public class SlidingMenu extends HorizontalScrollView {
private static final String TAG = "HorizontalScrollView";
private Context mContext;
// 4.給菜單和內(nèi)容View指定寬高 - 左邊菜單View
private View mMenuView;
// 4.給菜單和內(nèi)容View指定寬高 - 菜單的寬度
private int mMenuWidth;
// 5.3 手勢(shì)處理類(lèi) 主要用來(lái)處理手勢(shì)快速滑動(dòng)
private GestureDetector mGestureDetector;
// 5.3 菜單是否打開(kāi)
private boolean mMenuIsOpen = false;
// 7(4). 主頁(yè)面內(nèi)容View的布局包括陰影ImageView
private ViewGroup mContentView;
// 7.給內(nèi)容添加陰影效果 - 陰影的ImageView
private ImageView mShadowIv;
public SlidingMenu(Context context) {
this(context, null);
}
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//4.1 計(jì)算左邊菜單的寬度
//4.1.1 獲取自定義的右邊留出的寬度
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenu);
float rightPadding = array.getDimension(
R.styleable.SlidingMenu_rightPadding, dip2px(50));
// 4.1.2 計(jì)算菜單的寬度 = 屏幕的寬度 - 自定義右邊留出的寬度
mMenuWidth = (int) (getScreenWidth() - rightPadding);
array.recycle();
// 5.3 實(shí)例化手勢(shì)處理類(lèi)
mGestureDetector = new GestureDetector(context,new GestureListener());
this.mContext = context;
}
/**
* 把dip 轉(zhuǎn)成像素
*/
private float dip2px(int dip) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 4.2 指定菜單和內(nèi)容View的寬度
// 4.2.1.獲取根View也就是外層的LinearLayout
ViewGroup container = (ViewGroup) this.getChildAt(0);
int containerChildCount = container.getChildCount();
if (containerChildCount > 2) {
// 里面只允許放置兩個(gè)布局 一個(gè)是Menu(菜單布局) 一個(gè)是Content(主頁(yè)內(nèi)容布局)
throw new IllegalStateException("SlidingMenu 根布局LinearLayout下面只允許兩個(gè)布局,菜單布局和主頁(yè)內(nèi)容布局");
}
// 4.2.2.獲取菜單和內(nèi)容布局
mMenuView = container.getChildAt(0);
// 7.給內(nèi)容添加陰影效果
// 7.1 先new一個(gè)主內(nèi)容布局用來(lái)放 陰影和LinearLayout原來(lái)的內(nèi)容布局
mContentView = new FrameLayout(mContext);
ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
// 7.2 獲取原來(lái)的內(nèi)容布局,并把原來(lái)的內(nèi)容布局從LinearLayout中異常
View oldContentView = container.getChildAt(1);
container.removeView(oldContentView);
// 7.3 把原來(lái)的內(nèi)容View 和 陰影加到我們新創(chuàng)建的內(nèi)容布局中
mContentView.addView(oldContentView);
// 7.3.1 創(chuàng)建陰影ImageView
mShadowIv = new ImageView(mContext);
mShadowIv.setBackgroundColor(Color.parseColor("#99000000"));
mContentView.addView(mShadowIv);
// 7.4 把包含陰影的新的內(nèi)容View 添加到 LinearLayout中
container.addView(mContentView);
// 4.2.3.指定內(nèi)容和菜單布局的寬度
// 4.2.3.1 菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
mMenuView.getLayoutParams().width = mMenuWidth;
// 4.2.3.2 內(nèi)容的寬度 = 屏幕的寬度
mContentView.getLayoutParams().width = getScreenWidth();
}
/**
* 5.處理手指抬起和快速滑動(dòng)切換菜單
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
// 5.3 處理手指快速滑動(dòng)
if(mGestureDetector.onTouchEvent(ev)){
return mGestureDetector.onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
// 5.1 手指抬起獲取滾動(dòng)的位置
int currentScrollX = getScrollX();
if (currentScrollX > mMenuWidth / 2) {
// 5.1.1 關(guān)閉菜單
closeMenu();
} else {
// 5.1.2 打開(kāi)菜單
openMenu();
}
return false;
}
return super.onTouchEvent(ev);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
// l 是 當(dāng)前滾動(dòng)的x距離 在滾動(dòng)的時(shí)候會(huì)不斷反復(fù)的回調(diào)這個(gè)方法
Log.e(TAG,l+"");
// 6. 實(shí)現(xiàn)菜單左邊抽屜樣式的動(dòng)畫(huà)效果
mMenuView.setTranslationX(l*0.8f);
// 7.給內(nèi)容添加陰影效果 - 計(jì)算梯度值
float gradientValue = l * 1f / mMenuWidth;// 這是 1 - 0 變化的值
// 7.給內(nèi)容添加陰影效果 - 給陰影的View指定透明度 0 - 1 變化的值
float shadowAlpha = 1 - gradientValue;
mShadowIv.setAlpha(shadowAlpha);
}
/**
* 5.1.2 打開(kāi)菜單
*/
private void openMenu() {
smoothScrollTo(0, 0);
mMenuIsOpen = true;
}
/**
* 5.1.1 關(guān)閉菜單
*/
private void closeMenu() {
smoothScrollTo(mMenuWidth, 0);
mMenuIsOpen = false;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// 布局指定后會(huì)從新擺放子布局,當(dāng)其擺放完畢后,讓菜單滾動(dòng)到不可見(jiàn)狀態(tài)
if (changed) {
scrollTo(mMenuWidth, 0);
}
}
/**
* 獲取屏幕的寬度
*/
public int getScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
return dm.widthPixels;
}
/**
* 5.3 處理手指快速滑動(dòng)
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// 當(dāng)手指快速滑動(dòng)時(shí)候回調(diào)的方法
Log.e(TAG,velocityX+"");
// 5.3.1 如果菜單打開(kāi) 并且是向左快速滑動(dòng) 切換菜單的狀態(tài)
if(mMenuIsOpen){
if(velocityX<0){
toggleMenu();
return true;
}
}else{
// 5.3.2 如果菜單關(guān)閉 并且是向右快速滑動(dòng) 切換菜單的狀態(tài)
if(velocityX>0){
toggleMenu();
return true;
}
}
return false;
}
}
/**
* 切換菜單的狀態(tài)
*/
private void toggleMenu() {
if(mMenuIsOpen){
closeMenu();
}else{
openMenu();
}
}
}
我們來(lái)看一下最后的效果吧,最終代碼量不是很多啦,需要的請(qǐng)下載源碼,如果是實(shí)現(xiàn)QQ5.0或是酷狗的側(cè)滑效果可以自己改改。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
您可能感興趣的文章:- Android高仿QQ6.0側(cè)滑刪除實(shí)例代碼
- Android使用ViewDragHelper實(shí)現(xiàn)仿QQ6.0側(cè)滑界面(一)
- Android使用ViewDragHelper實(shí)現(xiàn)QQ6.X最新版本側(cè)滑界面效果實(shí)例代碼
- Android滑動(dòng)優(yōu)化高仿QQ6.0側(cè)滑菜單(滑動(dòng)優(yōu)化)
- Android使用DrawerLayout實(shí)現(xiàn)仿QQ雙向側(cè)滑菜單
- 基于Android實(shí)現(xiàn)仿QQ5.0側(cè)滑
- Android基于ViewDragHelper仿QQ5.0側(cè)滑界面效果
- Android程序開(kāi)發(fā)之使用Design包實(shí)現(xiàn)QQ動(dòng)畫(huà)側(cè)滑效果和滑動(dòng)菜單導(dǎo)航
- Android自定義view系列之99.99%實(shí)現(xiàn)QQ側(cè)滑刪除效果實(shí)例代碼詳解
- Android自定義布局實(shí)現(xiàn)仿qq側(cè)滑部分代碼
相關(guān)文章
-
Android手勢(shì)密碼的實(shí)現(xiàn)
這篇文章主要介紹了Android手勢(shì)密碼的實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下 2016-04-04
-
Android利用GridView實(shí)現(xiàn)單選功能
這篇文章主要為大家詳細(xì)介紹了Android利用GridView實(shí)現(xiàn)單選功能的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下 2017-02-02
-
sqlite查詢(xún)結(jié)果在listview中展示的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇sqlite查詢(xún)結(jié)果在listview中展示的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧 2017-04-04
-
Android studio實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Android studio實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下 2020-03-03
-
android 修改launcher行數(shù)和列數(shù)的方法
這篇文章主要介紹了android 修改launcher行數(shù)和列數(shù)的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下 2018-07-07
-
Android IPC機(jī)制利用Messenger實(shí)現(xiàn)跨進(jìn)程通信
這篇文章主要介紹了Android IPC機(jī)制中 Messager 實(shí)現(xiàn)跨進(jìn)程通信的知識(shí),對(duì)Android學(xué)習(xí)通信知識(shí)非常重要,需要的同學(xué)可以參考下 2016-07-07
-
基于Android開(kāi)發(fā)支持表情的實(shí)現(xiàn)詳解
本篇文章是對(duì)在Android開(kāi)發(fā)中支持表情的實(shí)現(xiàn)代碼進(jìn)行了介紹。需要的朋友參考下 2013-05-05
最新評(píng)論
1.概述
最近一直都在帶實(shí)習(xí)生做項(xiàng)目,發(fā)現(xiàn)自己好久沒(méi)有寫(xiě)博客了,這幾天更新會(huì)比較頻繁,今天玩QQ的時(shí)候發(fā)現(xiàn)QQ主頁(yè)菜單滑動(dòng)效果早就變了,實(shí)在忍不住晚上就來(lái)實(shí)現(xiàn)一下了!

2.實(shí)現(xiàn)
2.1. 實(shí)現(xiàn)的方式多種多樣
2.1.1 自定義ViewGroup ,處理其onTouch事件
2.1.2 FrameLayout + 手勢(shì)處理類(lèi)GestureDetector
2.2.3 使用Google自帶的DrawerLayout 對(duì)其進(jìn)行修改
2.2.4 繼承自水平滾動(dòng)HorizontalScrollView
大家可以看一下這個(gè)http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0909/6612.html,這種方式繼承自ViewGroup,個(gè)人覺(jué)得還行但是還是比較繁瑣要處理的東西也比較多,那么我就用最后一種2.2.4的方式實(shí)現(xiàn),有人說(shuō)直接去網(wǎng)上下載一個(gè)源代碼就可以了,這我就只能GG了。
2.3. 自定義SlidingMenu extends HorizontalScrollView 然后寫(xiě)好布局文件這個(gè)和ScrollView的用法一樣,只不過(guò)是橫向滾動(dòng)的
/**
* description:
* 仿QQ5.0主頁(yè)面?zhèn)然淖远╒iew
* Created by 曾輝 on 2016/11/1.
* QQ:240336124
* Email: 240336124@qq.com
* Version:1.0
*/
public class SlidingMenu extends HorizontalScrollView {
public SlidingMenu(Context context) {
super(context);
}
public SlidingMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
2.4. 運(yùn)行起來(lái)之后發(fā)現(xiàn)布局不對(duì),完全亂了明明都是match_parent可是就是不行那么我們就需要用代碼指定菜單和內(nèi)容的寬度:
菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
內(nèi)容的寬度 = 屏幕的寬度
/**
* description:
* 仿QQ5.0主頁(yè)面?zhèn)然淖远╒iew
* Created by 曾輝 on 2016/11/1.
* QQ:240336124
* Email: 240336124@qq.com
* Version:1.0
*/
public class SlidingMenu extends HorizontalScrollView {
private View mMenuView;
private View mContentView;
private int mMenuWidth;
public SlidingMenu(Context context) {
this(context, null);
}
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 獲取自定義的右邊留出的寬度
TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.SlidingMenu);
float rightPadding = array.getDimension(
R.styleable.SlidingMenu_rightPadding,dip2px(50));
// 計(jì)算菜單的寬度 = 屏幕的寬度 - 自定義右邊留出的寬度
mMenuWidth = (int) (getScreenWidth() - rightPadding);
array.recycle();
}
/**
* 把dip 轉(zhuǎn)成像素
*/
private float dip2px(int dip) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 1.獲取根View也就是外層的LinearLayout
ViewGroup container = (ViewGroup) this.getChildAt(0);
int containerChildCount = container.getChildCount();
if(containerChildCount>2){
// 里面只允許放置兩個(gè)布局 一個(gè)是Menu(菜單布局) 一個(gè)是Content(主頁(yè)內(nèi)容布局)
throw new IllegalStateException("SlidingMenu 根布局LinearLayout下面只允許兩個(gè)布局,菜單布局和主頁(yè)內(nèi)容布局");
}
// 2.獲取菜單和內(nèi)容布局
mMenuView = container.getChildAt(0);
mContentView = container.getChildAt(1);
// 3.指定內(nèi)容和菜單布局的寬度
// 3.1 菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
mMenuView.getLayoutParams().width = mMenuWidth;
// 3.2 內(nèi)容的寬度 = 屏幕的寬度
mContentView.getLayoutParams().width = getScreenWidth();
}
/**
* 獲取屏幕的寬度
*/
public int getScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
return dm.widthPixels;
}
}
目前的效果就是可以滑動(dòng),并且菜單和主頁(yè)面內(nèi)容的布局寬度正常

2.5 接下來(lái)一開(kāi)始就讓菜單滑動(dòng)到關(guān)閉狀態(tài),手指滑動(dòng)抬起判斷菜單打開(kāi)和關(guān)閉并做相應(yīng)的處理 onLayout() onTouch() smoothScrollTo(),當(dāng)手指快速的時(shí)候切換菜單的狀態(tài)利用GestureDetector 手勢(shì)處理類(lèi):
/**
* description:
* 仿QQ5.0主頁(yè)面?zhèn)然淖远╒iew
* Created by 曾輝 on 2016/11/1.
* QQ:240336124
* Email: 240336124@qq.com
* Version:1.0
*/
public class SlidingMenu extends HorizontalScrollView {
private View mMenuView;
private View mContentView;
private int mMenuWidth;
// 手勢(shì)處理類(lèi) 主要用來(lái)處理手勢(shì)快速滑動(dòng)
private GestureDetector mGestureDetector;
// 菜單是否打開(kāi)
private boolean mMenuIsOpen = false;
public SlidingMenu(Context context) {
this(context, null);
}
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 獲取自定義的右邊留出的寬度
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenu);
float rightPadding = array.getDimension(
R.styleable.SlidingMenu_rightPadding, dip2px(50));
// 計(jì)算菜單的寬度 = 屏幕的寬度 - 自定義右邊留出的寬度
mMenuWidth = (int) (getScreenWidth() - rightPadding);
array.recycle();
// 實(shí)例化手勢(shì)處理類(lèi)
mGestureDetector = new GestureDetector(context,new GestureListener());
}
/**
* 把dip 轉(zhuǎn)成像素
*/
private float dip2px(int dip) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 1.獲取根View也就是外層的LinearLayout
ViewGroup container = (ViewGroup) this.getChildAt(0);
int containerChildCount = container.getChildCount();
if (containerChildCount > 2) {
// 里面只允許放置兩個(gè)布局 一個(gè)是Menu(菜單布局) 一個(gè)是Content(主頁(yè)內(nèi)容布局)
throw new IllegalStateException("SlidingMenu 根布局LinearLayout下面只允許兩個(gè)布局,菜單布局和主頁(yè)內(nèi)容布局");
}
// 2.獲取菜單和內(nèi)容布局
mMenuView = container.getChildAt(0);
mContentView = container.getChildAt(1);
// 3.指定內(nèi)容和菜單布局的寬度
// 3.1 菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
mMenuView.getLayoutParams().width = mMenuWidth;
// 3.2 內(nèi)容的寬度 = 屏幕的寬度
mContentView.getLayoutParams().width = getScreenWidth();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// 處理手指快速滑動(dòng)
if(mGestureDetector.onTouchEvent(ev)){
return mGestureDetector.onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
// 手指抬起獲取滾動(dòng)的位置
int currentScrollX = getScrollX();
if (currentScrollX > mMenuWidth / 2) {
// 關(guān)閉菜單
closeMenu();
} else {
// 打開(kāi)菜單
openMenu();
}
return false;
}
return super.onTouchEvent(ev);
}
/**
* 打開(kāi)菜單
*/
private void openMenu() {
smoothScrollTo(0, 0);
mMenuIsOpen = true;
}
/**
* 關(guān)閉菜單
*/
private void closeMenu() {
smoothScrollTo(mMenuWidth, 0);
mMenuIsOpen = false;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// 布局指定后會(huì)從新擺放子布局,當(dāng)其擺放完畢后,讓菜單滾動(dòng)到不可見(jiàn)狀態(tài)
if (changed) {
scrollTo(mMenuWidth, 0);
}
}
/**
* 獲取屏幕的寬度
*/
public int getScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
return dm.widthPixels;
}
private class GestureListener extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// 當(dāng)手指快速滑動(dòng)時(shí)候回調(diào)的方法
Log.e("TAG",velocityX+"");
// 如果菜單打開(kāi) 并且是向左快速滑動(dòng) 切換菜單的狀態(tài)
if(mMenuIsOpen){
if(velocityX<-500){
toggleMenu();
return true;
}
}else{
// 如果菜單關(guān)閉 并且是向右快速滑動(dòng) 切換菜單的狀態(tài)
if(velocityX>500){
toggleMenu();
return true;
}
}
return false;
}
}
/**
* 切換菜單的狀態(tài)
*/
private void toggleMenu() {
if(mMenuIsOpen){
closeMenu();
}else{
openMenu();
}
}
}
到了這一步之后我們就可以切換菜單了,并且處理了手指快速滑動(dòng),迫不及待的看下效果

2.6. 實(shí)現(xiàn)菜單左邊抽屜樣式的動(dòng)畫(huà)效果,監(jiān)聽(tīng)滾動(dòng)回調(diào)的方法onScrollChanged() 這個(gè)就非常簡(jiǎn)單了一句話(huà)就搞定,效果就不看了一起看終極效果吧
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
// l 是 當(dāng)前滾動(dòng)的x距離 在滾動(dòng)的時(shí)候會(huì)不斷反復(fù)的回調(diào)這個(gè)方法
Log.e(TAG,l+"");
mMenuView.setTranslationX(l*0.8f);
}
2.7. 實(shí)現(xiàn)菜單右邊菜單的陰影透明度效果,這個(gè)打算在主頁(yè)面內(nèi)容布局上面加一層陰影,用ImageView即可,那么我們的內(nèi)容View就需要換了
/**
* description:
* 仿QQ5.0主頁(yè)面?zhèn)然淖远╒iew
* Created by 曾輝 on 2016/11/1.
* QQ:240336124
* Email: 240336124@qq.com
* Version:1.0
*/
public class SlidingMenu extends HorizontalScrollView {
private static final String TAG = "HorizontalScrollView";
private Context mContext;
// 4.給菜單和內(nèi)容View指定寬高 - 左邊菜單View
private View mMenuView;
// 4.給菜單和內(nèi)容View指定寬高 - 菜單的寬度
private int mMenuWidth;
// 5.3 手勢(shì)處理類(lèi) 主要用來(lái)處理手勢(shì)快速滑動(dòng)
private GestureDetector mGestureDetector;
// 5.3 菜單是否打開(kāi)
private boolean mMenuIsOpen = false;
// 7(4). 主頁(yè)面內(nèi)容View的布局包括陰影ImageView
private ViewGroup mContentView;
// 7.給內(nèi)容添加陰影效果 - 陰影的ImageView
private ImageView mShadowIv;
public SlidingMenu(Context context) {
this(context, null);
}
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//4.1 計(jì)算左邊菜單的寬度
//4.1.1 獲取自定義的右邊留出的寬度
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenu);
float rightPadding = array.getDimension(
R.styleable.SlidingMenu_rightPadding, dip2px(50));
// 4.1.2 計(jì)算菜單的寬度 = 屏幕的寬度 - 自定義右邊留出的寬度
mMenuWidth = (int) (getScreenWidth() - rightPadding);
array.recycle();
// 5.3 實(shí)例化手勢(shì)處理類(lèi)
mGestureDetector = new GestureDetector(context,new GestureListener());
this.mContext = context;
}
/**
* 把dip 轉(zhuǎn)成像素
*/
private float dip2px(int dip) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 4.2 指定菜單和內(nèi)容View的寬度
// 4.2.1.獲取根View也就是外層的LinearLayout
ViewGroup container = (ViewGroup) this.getChildAt(0);
int containerChildCount = container.getChildCount();
if (containerChildCount > 2) {
// 里面只允許放置兩個(gè)布局 一個(gè)是Menu(菜單布局) 一個(gè)是Content(主頁(yè)內(nèi)容布局)
throw new IllegalStateException("SlidingMenu 根布局LinearLayout下面只允許兩個(gè)布局,菜單布局和主頁(yè)內(nèi)容布局");
}
// 4.2.2.獲取菜單和內(nèi)容布局
mMenuView = container.getChildAt(0);
// 7.給內(nèi)容添加陰影效果
// 7.1 先new一個(gè)主內(nèi)容布局用來(lái)放 陰影和LinearLayout原來(lái)的內(nèi)容布局
mContentView = new FrameLayout(mContext);
ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
// 7.2 獲取原來(lái)的內(nèi)容布局,并把原來(lái)的內(nèi)容布局從LinearLayout中異常
View oldContentView = container.getChildAt(1);
container.removeView(oldContentView);
// 7.3 把原來(lái)的內(nèi)容View 和 陰影加到我們新創(chuàng)建的內(nèi)容布局中
mContentView.addView(oldContentView);
// 7.3.1 創(chuàng)建陰影ImageView
mShadowIv = new ImageView(mContext);
mShadowIv.setBackgroundColor(Color.parseColor("#99000000"));
mContentView.addView(mShadowIv);
// 7.4 把包含陰影的新的內(nèi)容View 添加到 LinearLayout中
container.addView(mContentView);
// 4.2.3.指定內(nèi)容和菜單布局的寬度
// 4.2.3.1 菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
mMenuView.getLayoutParams().width = mMenuWidth;
// 4.2.3.2 內(nèi)容的寬度 = 屏幕的寬度
mContentView.getLayoutParams().width = getScreenWidth();
}
/**
* 5.處理手指抬起和快速滑動(dòng)切換菜單
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
// 5.3 處理手指快速滑動(dòng)
if(mGestureDetector.onTouchEvent(ev)){
return mGestureDetector.onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
// 5.1 手指抬起獲取滾動(dòng)的位置
int currentScrollX = getScrollX();
if (currentScrollX > mMenuWidth / 2) {
// 5.1.1 關(guān)閉菜單
closeMenu();
} else {
// 5.1.2 打開(kāi)菜單
openMenu();
}
return false;
}
return super.onTouchEvent(ev);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
// l 是 當(dāng)前滾動(dòng)的x距離 在滾動(dòng)的時(shí)候會(huì)不斷反復(fù)的回調(diào)這個(gè)方法
Log.e(TAG,l+"");
// 6. 實(shí)現(xiàn)菜單左邊抽屜樣式的動(dòng)畫(huà)效果
mMenuView.setTranslationX(l*0.8f);
// 7.給內(nèi)容添加陰影效果 - 計(jì)算梯度值
float gradientValue = l * 1f / mMenuWidth;// 這是 1 - 0 變化的值
// 7.給內(nèi)容添加陰影效果 - 給陰影的View指定透明度 0 - 1 變化的值
float shadowAlpha = 1 - gradientValue;
mShadowIv.setAlpha(shadowAlpha);
}
/**
* 5.1.2 打開(kāi)菜單
*/
private void openMenu() {
smoothScrollTo(0, 0);
mMenuIsOpen = true;
}
/**
* 5.1.1 關(guān)閉菜單
*/
private void closeMenu() {
smoothScrollTo(mMenuWidth, 0);
mMenuIsOpen = false;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// 布局指定后會(huì)從新擺放子布局,當(dāng)其擺放完畢后,讓菜單滾動(dòng)到不可見(jiàn)狀態(tài)
if (changed) {
scrollTo(mMenuWidth, 0);
}
}
/**
* 獲取屏幕的寬度
*/
public int getScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
return dm.widthPixels;
}
/**
* 5.3 處理手指快速滑動(dòng)
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// 當(dāng)手指快速滑動(dòng)時(shí)候回調(diào)的方法
Log.e(TAG,velocityX+"");
// 5.3.1 如果菜單打開(kāi) 并且是向左快速滑動(dòng) 切換菜單的狀態(tài)
if(mMenuIsOpen){
if(velocityX<0){
toggleMenu();
return true;
}
}else{
// 5.3.2 如果菜單關(guān)閉 并且是向右快速滑動(dòng) 切換菜單的狀態(tài)
if(velocityX>0){
toggleMenu();
return true;
}
}
return false;
}
}
/**
* 切換菜單的狀態(tài)
*/
private void toggleMenu() {
if(mMenuIsOpen){
closeMenu();
}else{
openMenu();
}
}
}
我們來(lái)看一下最后的效果吧,最終代碼量不是很多啦,需要的請(qǐng)下載源碼,如果是實(shí)現(xiàn)QQ5.0或是酷狗的側(cè)滑效果可以自己改改。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android高仿QQ6.0側(cè)滑刪除實(shí)例代碼
- Android使用ViewDragHelper實(shí)現(xiàn)仿QQ6.0側(cè)滑界面(一)
- Android使用ViewDragHelper實(shí)現(xiàn)QQ6.X最新版本側(cè)滑界面效果實(shí)例代碼
- Android滑動(dòng)優(yōu)化高仿QQ6.0側(cè)滑菜單(滑動(dòng)優(yōu)化)
- Android使用DrawerLayout實(shí)現(xiàn)仿QQ雙向側(cè)滑菜單
- 基于Android實(shí)現(xiàn)仿QQ5.0側(cè)滑
- Android基于ViewDragHelper仿QQ5.0側(cè)滑界面效果
- Android程序開(kāi)發(fā)之使用Design包實(shí)現(xiàn)QQ動(dòng)畫(huà)側(cè)滑效果和滑動(dòng)菜單導(dǎo)航
- Android自定義view系列之99.99%實(shí)現(xiàn)QQ側(cè)滑刪除效果實(shí)例代碼詳解
- Android自定義布局實(shí)現(xiàn)仿qq側(cè)滑部分代碼
相關(guān)文章
Android手勢(shì)密碼的實(shí)現(xiàn)
這篇文章主要介紹了Android手勢(shì)密碼的實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2016-04-04
Android利用GridView實(shí)現(xiàn)單選功能
這篇文章主要為大家詳細(xì)介紹了Android利用GridView實(shí)現(xiàn)單選功能的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
sqlite查詢(xún)結(jié)果在listview中展示的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇sqlite查詢(xún)結(jié)果在listview中展示的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
Android studio實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Android studio實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
android 修改launcher行數(shù)和列數(shù)的方法
這篇文章主要介紹了android 修改launcher行數(shù)和列數(shù)的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-07-07
Android IPC機(jī)制利用Messenger實(shí)現(xiàn)跨進(jìn)程通信
這篇文章主要介紹了Android IPC機(jī)制中 Messager 實(shí)現(xiàn)跨進(jìn)程通信的知識(shí),對(duì)Android學(xué)習(xí)通信知識(shí)非常重要,需要的同學(xué)可以參考下2016-07-07
基于Android開(kāi)發(fā)支持表情的實(shí)現(xiàn)詳解
本篇文章是對(duì)在Android開(kāi)發(fā)中支持表情的實(shí)現(xiàn)代碼進(jìn)行了介紹。需要的朋友參考下2013-05-05

