編輯:關於Android編程
1.概述
最近一直都在帶實習生做項目,發現自己好久沒有寫博客了,這幾天更新會比較頻繁,今天玩QQ的時候發現QQ主頁菜單滑動效果早就變了,實在忍不住晚上就來實現一下了!

2.實現
2.1. 實現的方式多種多樣
2.1.1 自定義ViewGroup ,處理其onTouch事件
2.1.2 FrameLayout + 手勢處理類GestureDetector
2.2.3 使用Google自帶的DrawerLayout 對其進行修改
2.2.4 繼承自水平滾動HorizontalScrollView
大家可以看一下這個http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0909/6612.html,這種方式繼承自ViewGroup,個人覺得還行但是還是比較繁瑣要處理的東西也比較多,那麼我就用最後一種2.2.4的方式實現,有人說直接去網上下載一個源代碼就可以了,這我就只能GG了。
2.3. 自定義SlidingMenu extends HorizontalScrollView 然後寫好布局文件這個和ScrollView的用法一樣,只不過是橫向滾動的
/**
* description:
* 仿QQ5.0主頁面側滑的自定View
* 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. 運行起來之後發現布局不對,完全亂了明明都是match_parent可是就是不行那麼我們就需要用代碼指定菜單和內容的寬度:
菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
內容的寬度 = 屏幕的寬度
/**
* description:
* 仿QQ5.0主頁面側滑的自定View
* 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));
// 計算菜單的寬度 = 屏幕的寬度 - 自定義右邊留出的寬度
mMenuWidth = (int) (getScreenWidth() - rightPadding);
array.recycle();
}
/**
* 把dip 轉成像素
*/
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){
// 裡面只允許放置兩個布局 一個是Menu(菜單布局) 一個是Content(主頁內容布局)
throw new IllegalStateException("SlidingMenu 根布局LinearLayout下面只允許兩個布局,菜單布局和主頁內容布局");
}
// 2.獲取菜單和內容布局
mMenuView = container.getChildAt(0);
mContentView = container.getChildAt(1);
// 3.指定內容和菜單布局的寬度
// 3.1 菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
mMenuView.getLayoutParams().width = mMenuWidth;
// 3.2 內容的寬度 = 屏幕的寬度
mContentView.getLayoutParams().width = getScreenWidth();
}
/**
* 獲取屏幕的寬度
*/
public int getScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
return dm.widthPixels;
}
}
目前的效果就是可以滑動,並且菜單和主頁面內容的布局寬度正常

2.5 接下來一開始就讓菜單滑動到關閉狀態,手指滑動抬起判斷菜單打開和關閉並做相應的處理 onLayout() onTouch() smoothScrollTo(),當手指快速的時候切換菜單的狀態利用GestureDetector 手勢處理類:
/**
* description:
* 仿QQ5.0主頁面側滑的自定View
* 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;
// 手勢處理類 主要用來處理手勢快速滑動
private GestureDetector mGestureDetector;
// 菜單是否打開
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));
// 計算菜單的寬度 = 屏幕的寬度 - 自定義右邊留出的寬度
mMenuWidth = (int) (getScreenWidth() - rightPadding);
array.recycle();
// 實例化手勢處理類
mGestureDetector = new GestureDetector(context,new GestureListener());
}
/**
* 把dip 轉成像素
*/
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) {
// 裡面只允許放置兩個布局 一個是Menu(菜單布局) 一個是Content(主頁內容布局)
throw new IllegalStateException("SlidingMenu 根布局LinearLayout下面只允許兩個布局,菜單布局和主頁內容布局");
}
// 2.獲取菜單和內容布局
mMenuView = container.getChildAt(0);
mContentView = container.getChildAt(1);
// 3.指定內容和菜單布局的寬度
// 3.1 菜單的寬度 = 屏幕的寬度 - 自定義的右邊留出的寬度
mMenuView.getLayoutParams().width = mMenuWidth;
// 3.2 內容的寬度 = 屏幕的寬度
mContentView.getLayoutParams().width = getScreenWidth();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// 處理手指快速滑動
if(mGestureDetector.onTouchEvent(ev)){
return mGestureDetector.onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
// 手指抬起獲取滾動的位置
int currentScrollX = getScrollX();
if (currentScrollX > mMenuWidth / 2) {
// 關閉菜單
closeMenu();
} else {
// 打開菜單
openMenu();
}
return false;
}
return super.onTouchEvent(ev);
}
/**
* 打開菜單
*/
private void openMenu() {
smoothScrollTo(0, 0);
mMenuIsOpen = true;
}
/**
* 關閉菜單
*/
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);
// 布局指定後會從新擺放子布局,當其擺放完畢後,讓菜單滾動到不可見狀態
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) {
// 當手指快速滑動時候回調的方法
Log.e("TAG",velocityX+"");
// 如果菜單打開 並且是向左快速滑動 切換菜單的狀態
if(mMenuIsOpen){
if(velocityX<-500){
toggleMenu();
return true;
}
}else{
// 如果菜單關閉 並且是向右快速滑動 切換菜單的狀態
if(velocityX>500){
toggleMenu();
return true;
}
}
return false;
}
}
/**
* 切換菜單的狀態
*/
private void toggleMenu() {
if(mMenuIsOpen){
closeMenu();
}else{
openMenu();
}
}
}
到了這一步之後我們就可以切換菜單了,並且處理了手指快速滑動,迫不及待的看下效果

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
Android實現第三方登錄的上拉展開,下拉隱藏,下拉隱藏示例
Android的UI和交互是很重要的一部分,直接影響到用戶對軟件的體驗。隨著項目經驗的積累,發現Android中動畫的運用越來越重要。本篇文章抽出了項目登錄界面中實現的第
Android自定義ActionBar實例
本文實例講述了Android自定義ActionBar的實現方法。分享給大家供大家參考。具體實現方法如下:Android 3.0及以上已經有了ActionBar的API,可
Android Menu
OptionsMenu(選項菜單)1.重寫Activity的onCreateOptionsMenu(Menu menu)方法,在該方法裡調用Menu對象的方法來添加菜單項
Android編程中的消息機制實例詳解
本文實例講述了Android編程中的消息機制。分享給大家供大家參考,具體如下:在分析Android消息機制之前,我們先來看一段代碼:public class MainAc