編輯:關於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: [email protected] * 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: [email protected] * 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: [email protected] * 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: [email protected] * 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的UI和交互是很重要的一部分,直接影響到用戶對軟件的體驗。隨著項目經驗的積累,發現Android中動畫的運用越來越重要。本篇文章抽出了項目登錄界面中實現的第
本文實例講述了Android自定義ActionBar的實現方法。分享給大家供大家參考。具體實現方法如下:Android 3.0及以上已經有了ActionBar的API,可
OptionsMenu(選項菜單)1.重寫Activity的onCreateOptionsMenu(Menu menu)方法,在該方法裡調用Menu對象的方法來添加菜單項
本文實例講述了Android編程中的消息機制。分享給大家供大家參考,具體如下:在分析Android消息機制之前,我們先來看一段代碼:public class MainAc