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)有寫博客了,這幾天更新會(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ì)處理類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 然后寫好布局文件這個(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ì)處理類:
/**
* 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)處理手勢(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ì)處理類
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)畫效果,監(jiān)聽(tīng)滾動(dòng)回調(diào)的方法onScrollChanged() 這個(gè)就非常簡(jiǎn)單了一句話就搞定,效果就不看了一起看終極效果吧
@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)處理手勢(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ì)處理類
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)畫效果
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)畫側(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查詢結(jié)果在listview中展示的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇sqlite查詢結(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)有寫博客了,這幾天更新會(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ì)處理類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 然后寫好布局文件這個(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ì)處理類:
/** * 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)處理手勢(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ì)處理類 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)畫效果,監(jiān)聽(tīng)滾動(dòng)回調(diào)的方法onScrollChanged() 這個(gè)就非常簡(jiǎn)單了一句話就搞定,效果就不看了一起看終極效果吧
@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)處理手勢(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ì)處理類 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)畫效果 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)畫側(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-04Android利用GridView實(shí)現(xiàn)單選功能
這篇文章主要為大家詳細(xì)介紹了Android利用GridView實(shí)現(xiàn)單選功能的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02sqlite查詢結(jié)果在listview中展示的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇sqlite查詢結(jié)果在listview中展示的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04Android studio實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Android studio實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03android 修改launcher行數(shù)和列數(shù)的方法
這篇文章主要介紹了android 修改launcher行數(shù)和列數(shù)的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-07-07Android 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