自定义控件:SlidingMenu,侧边栏,侧滑菜单
1. 項(xiàng)目概述
觀察如圖2-4 的完整項(xiàng)目中的效果界面,點(diǎn)擊標(biāo)題欄的左上角會彈出側(cè)邊欄,再次點(diǎn)擊時(shí)會關(guān)閉側(cè)邊欄,這種效果在很多手機(jī)應(yīng)用中使用,因此,我們有必要學(xué)會如何自定義一個(gè)具有側(cè)邊欄效果的控件。
2. 布局界面UI
在本章中,主界面為MainActivity.java,具體代碼如文件所示:res/layout/activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent" ><com.itheima.slidmenudemo.view.SlidMenu android:id="@+id/slidmenu"android:layout_width="match_parent"android:layout_height="match_parent" ><!-- 當(dāng)前為左邊菜單界面索引為0 --><include layout="@layout/slidmenu_left" /><!-- 當(dāng)前為主界面索引為1 --><include layout="@layout/slidmenu_main"/></com.itheima.slidmenudemo.view.SlidMenu> </RelativeLayout>其中,slidmenu_left.xml 是側(cè)邊欄的布局文件,具體代碼如文件【2-7】所示:【文件2-7】res/layout/slidmenu_left.xml
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="240dp"android:layout_height="match_parent" ><LinearLayout android:layout_width="240dp"android:layout_height="match_parent"android:orientation="vertical"android:background="@drawable/menu_bg" ><ImageButton style="@style/tab_style"android:text="新聞"android:background="#33000000"android:drawableLeft="@drawable/tab_news" /><ImageButton style="@style/tab_style"android:text="訂閱"android:drawableLeft="@drawable/tab_read" /><TextView style="@style/tab_style"android:text="本地"android:drawableLeft="@drawable/tab_local" /><TextView style="@style/tab_style"android:text="跟帖"android:drawableLeft="@drawable/tab_ties" /><TextView style="@style/tab_style"android:text="圖片"android:drawableLeft="@drawable/tab_pics" /><TextView style="@style/tab_style"android:text="話題"android:drawableLeft="@drawable/tab_ugc" /><TextView style="@style/tab_style"android:text="投票"android:drawableLeft="@drawable/tab_vote" /><TextView style="@style/tab_style"android:text="聚合閱讀"android:drawableLeft="@drawable/tab_focus" /></LinearLayout> </ScrollView>上面的代碼中我們在styles.xml 文件中定義了一個(gè)樣式tab_style,它是用來修飾左邊側(cè)欄中每一個(gè)條目,詳細(xì)代碼如下所示。
<!-- tab 菜單界面的樣式--> <style name="tab_style"><item name="android:padding">5dp</item><item name="android:gravity">center_vertical</item><item name="android:drawablePadding">5dp</item><item name="android:layout_width">match_parent</item><item name="android:layout_height">wrap_content</item><item name="android:textSize">18sp</item><item name="android:background">@drawable/tab_bg</item><item name="android:textColor">#ffffff</item><item name="android:onClick">clickTab</item><item name="android:focusable">true</item><item name="android:clickable">true</item> </style>主界面的右邊slidmenu_main.xml 布局文件的代碼如文件【2-8】所示。【文件2-8】res/layout/slidmenu_left.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><LinearLayout android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@drawable/top_bar_bg"android:orientation="horizontal" ><ImageButton android:id="@+id/main_back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@null"android:src="@drawable/main_back" /><View android:layout_width="1dp"android:layout_height="match_parent"android:layout_margin="5dp"android:background="@drawable/top_bar_divider" /><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginLeft="10dp"android:text="黑馬新聞"android:textColor="#fff"android:textSize="24sp" /></LinearLayout><TextView android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:text="釣魚島是中國的!!!!\nXXX 是世界的"android:textColor="#000"android:textSize="24sp" /> </LinearLayout>3. 主界面業(yè)務(wù)邏輯
自定好控件之后,MainActivity,java 主界面的業(yè)務(wù)邏輯如下所示。activity_setting.xml 如文件【2-9】所示:【文件2-9】res/layout/setup_password_dialog.xml
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);ImageButton main_back = (ImageButton) findViewById(R.id.main_back);final SlidMenu slidmenu = (SlidMenu) findViewById(R.id.slidmenu);main_back.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {boolean currentView = slidmenu.isMenuShow();if(currentView){//當(dāng)前菜單界面顯示,就隱藏slidmenu.hideMenu();}else{slidmenu.showMenu();}}});}public void clickTab(View v){TextView tv = (TextView) v;Toast.makeText(getApplicationContext(), tv.getText(), 0).show();} }側(cè)邊欄在項(xiàng)目會經(jīng)常用到,這里我們將自定義一個(gè)側(cè)邊欄,具體代碼如文件【2-10】所示。【文件2-10】com/itheima/slidmenudemo/view/SlidMenu.java
public class SlidMenu extends ViewGroup {private int downX;private final int MAIN_VIEW = 0;// 主界面private final int MENU_VIEW = 1;// 左邊菜單界面private int currentView = MAIN_VIEW;// 記錄當(dāng)前界面,默認(rèn)為主界面private Scroller scroller;// 用來模擬數(shù)據(jù)private int touchSlop;public SlidMenu(Context context, AttributeSet attrs) {super(context, attrs);init();}public SlidMenu(Context context) {this(context, null);}@SuppressWarnings("deprecation")private void init() {scroller = new Scroller(getContext());touchSlop = ViewConfiguration.getTouchSlop();// 系統(tǒng)默認(rèn)你進(jìn)行了一個(gè)滑動操作的固定值}/*** widthMeasureSpec 屏幕的寬度heightMeasureSpec 屏幕的高度*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// TODO Auto-generated method stubsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);// 測量主界面View mainView = getChildAt(1);// 主界面// 主界面寬高:屏幕的寬度屏幕的高度mainView.measure(widthMeasureSpec, heightMeasureSpec);// 測量左邊菜單界面和主界面的寬高View menuView = getChildAt(0);// 左邊菜單界面menuView.measure(menuView.getLayoutParams().width, heightMeasureSpec);}// viewgroup 的排版方法/*** int l 左邊0 int t 上邊0 int r 右邊屏幕的寬度int b 下邊屏幕的高度*/@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {// 排版主界面左邊0, 高度0, 右邊屏幕寬度,下邊屏幕高度View mainView = getChildAt(1);mainView.layout(l, t, r, b);// 排版左邊菜單界面左邊-左邊菜單界面的寬度, 高度0 ,右邊0, 下邊屏幕高度View menuView = getChildAt(0);menuView.layout(-menuView.getMeasuredWidth(), t, 0, b);}// 按下的點(diǎn): downX = 100// 移動過后的點(diǎn): moveX = 150// 間距diffX = -50 =100 -150 = downX - moveX;// scrollby(diffX,0)// downX = moveX = 150@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:downX = (int) event.getX();break;case MotionEvent.ACTION_MOVE:int moveX = (int) event.getX();// 計(jì)算間距int diffX = downX - moveX;int scrollX = getScrollX();// 獲得當(dāng)前界面的左上角的X 值// 屏幕將要移動到的X 值int dff = scrollX + diffX;if (dff < -getChildAt(0).getMeasuredWidth()) {// 設(shè)置左邊界scrollTo(-getChildAt(0).getMeasuredWidth(), 0);} else if (dff > 0) {// 設(shè)置右邊界scrollTo(0, 0);} else {// 如果不超過邊界值,就要根據(jù)屏幕左上角的點(diǎn)的X 值,來計(jì)算位置scrollBy(diffX, 0);}downX = moveX;break;case MotionEvent.ACTION_UP:int center = -getChildAt(0).getMeasuredWidth() / 2;if (getScrollX() > center) {System.out.println("顯示主界面");currentView = MAIN_VIEW;} else {System.out.println("顯示左邊菜單界面");currentView = MENU_VIEW;}switchView();break;default:break;}return true;// 由我們自己來處理觸摸事件}// 根據(jù)當(dāng)前狀態(tài)值來切換切面private void switchView() {int startX = getScrollX();int dx = 0;if (currentView == MAIN_VIEW) {// scrollTo(0, 0);dx = 0 - startX;} else {// scrollTo(-getChildAt(0).getMeasuredWidth(), 0);dx = -getChildAt(0).getMeasuredWidth() - startX;}int duration = Math.abs(dx) * 10;if (duration > 1000) {duration = 1000;}scroller.startScroll(startX, 0, dx, 0, duration);invalidate();// scrollTo(scroller.getCurrX(), 0);}@Overridepublic void computeScroll() {if (scroller.computeScrollOffset()) {int currX = scroller.getCurrX();scrollTo(currX, 0);invalidate();}}// 判斷當(dāng)前是否是菜單界面,true 就是菜單界面public boolean isMenuShow() {return currentView == MENU_VIEW;}// 隱藏菜單界面public void hideMenu() {currentView = MAIN_VIEW;switchView();}// 顯示菜單界面public void showMenu() {currentView = MENU_VIEW;switchView();}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:downX = (int) ev.getX();break;case MotionEvent.ACTION_MOVE:int moveX = (int) ev.getX();int diff = moveX - downX;if (Math.abs(diff) > touchSlop) {return true;}break;case MotionEvent.ACTION_UP:break;default:break;}return super.onInterceptTouchEvent(ev);} }自定義好側(cè)邊欄之后,運(yùn)行程序,效果圖如圖2-5 所示。
4. ViewDragHelper實(shí)現(xiàn)SlidingMenu
package com.github.slidingmenu.viewdraghelper;import android.content.Context; import android.graphics.Color; import android.support.v4.view.ViewCompat; import android.support.v4.widget.ViewDragHelper; import android.support.v4.widget.ViewDragHelper.Callback; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout;public class SlideMenu2 extends FrameLayout{private String TAG = SlideMenu2.class.getSimpleName();public SlideMenu2(Context context, AttributeSet attrs) {super(context, attrs);init();}private ViewDragHelper viewDragHelper;private void init(){viewDragHelper = ViewDragHelper.create(this, callback);}private View menuView,mainView;private int menuWidth,menuHeight,mainWidth;private int dragRange;@Overrideprotected void onFinishInflate() {super.onFinishInflate();if(getChildCount()<2){throw new IllegalStateException("Your layout must has 2 children or more!");}menuView = getChildAt(0);mainView = getChildAt(1);setBackgroundColor(Color.BLACK);}// @Override // protected void onLayout(boolean changed, int left, int top, int right, // int bottom) { // super.onLayout(changed, left, top, right, bottom); // menuView.layout(left, top, right, bottom); // mainView.layout(left, top, right, bottom); // }@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);menuWidth = menuView.getMeasuredWidth();menuHeight = menuView.getMeasuredHeight();mainWidth = mainView.getMeasuredWidth();dragRange = (int) (menuWidth * 0.6); // ViewHelper.setScaleX(menuView, 0.5f); // ViewHelper.setScaleY(menuView, 0.5f); // ViewHelper.setTranslationX(menuView, -dragRange/2);}private int lastX,lastY;@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {int x = (int) ev.getX();int y = (int) ev.getY();if(mStatus==Status.Open && viewDragHelper.isViewUnder(mainView, x, y)){return true;} // Log.e(TAG, "onInterceptTouchEvent : "+);if(viewDragHelper.isViewUnder(mainView, x, y)){switch (ev.getAction()) {case MotionEvent.ACTION_MOVE:int deltaX = x - lastX;int deltaY = y - lastY;if(Math.abs(deltaX)>Math.abs(deltaY)*2) { // Log.e(TAG, "移動斜角太大,攔截事件");viewDragHelper.cancel();return true;}break;case MotionEvent.ACTION_UP:lastX = 0;lastY = 0;break;}lastX = x;lastY = y;}return viewDragHelper.shouldInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {int x = (int) event.getX();int y = (int) event.getY();if(mStatus==Status.Open && viewDragHelper.isViewUnder(mainView, x, y)){if(event.getAction()==MotionEvent.ACTION_UP){Log.e(TAG, "抬起");close();}}viewDragHelper.processTouchEvent(event);return true;}private Callback callback = new Callback() {@Overridepublic boolean tryCaptureView(View child, int pointerId) {return child==menuView || child==mainView;}@Overridepublic void onViewDragStateChanged(int state) {super.onViewDragStateChanged(state);}@Overridepublic void onViewPositionChanged(View changedView, int left, int top,int dx, int dy) {super.onViewPositionChanged(changedView, left, top, dx, dy); // Log.e(TAG, "onViewPositionChanged dx: "+dx);if(changedView==menuView){menuView.layout(0, 0, menuWidth, menuHeight);if(mainView.getLeft()>dragRange){mainView.layout(dragRange, 0, dragRange+mainWidth, mainView.getBottom());}else {mainView.layout(mainView.getLeft()+dx, 0, mainView.getRight()+dx, mainView.getBottom());}}float percent = mainView.getLeft()/(float)dragRange;excuteAnimation(percent);if(mainView.getLeft()==0 && mStatus != Status.Close){mStatus = Status.Close;if(onDragStatusChangeListener!=null ){onDragStatusChangeListener.onClose();}}else if (mainView.getLeft()==dragRange && mStatus != Status.Open) {mStatus = Status.Open;if(onDragStatusChangeListener!=null ){onDragStatusChangeListener.onOpen();}}else {if(onDragStatusChangeListener!=null){onDragStatusChangeListener.onDragging(percent);}}}private void excuteAnimation(float percent) {menuView.setScaleX(0.5f + 0.5f * percent);menuView.setScaleY(0.5f + 0.5f * percent);mainView.setScaleX(1 - percent * 0.2f);mainView.setScaleY( 1 - percent * 0.2f);menuView.setTranslationX( -dragRange / 2 + dragRange / 2 * percent);menuView.setAlpha(percent);getBackground().setAlpha((int) ((1 - percent) * 255));}@Overridepublic void onViewCaptured(View capturedChild, int activePointerId) {super.onViewCaptured(capturedChild, activePointerId);}@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {super.onViewReleased(releasedChild, xvel, yvel); // Log.e(TAG, "onViewReleased :"+(releasedChild==mainView));if(mainView.getLeft()>dragRange/2){open();}else {close();}}@Overridepublic int getViewHorizontalDragRange(View child) {return menuWidth;}@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {if(child==mainView){if(left<0)return 0;if(left>dragRange) return dragRange;}if(child==menuView){mainView.layout(mainView.getLeft()+dx, 0, mainView.getRight()+dx, mainView.getBottom());menuView.layout(0, 0, menuWidth, menuHeight);return 0;}return left;}};public void open(){viewDragHelper.smoothSlideViewTo(mainView, dragRange, 0);ViewCompat.postInvalidateOnAnimation(SlideMenu2.this);}public void close(){viewDragHelper.smoothSlideViewTo(mainView, 0, 0);ViewCompat.postInvalidateOnAnimation(SlideMenu2.this);}public void computeScroll() {if(viewDragHelper.continueSettling(true)){ViewCompat.postInvalidateOnAnimation(this);}}public OnDragStatusChangeListener getOnDragStatusChangeListener() {return onDragStatusChangeListener;}public void setOnDragStatusChangeListener(OnDragStatusChangeListener onDragStatusChangeListener) {this.onDragStatusChangeListener = onDragStatusChangeListener;}public Status mStatus = Status.Close;;public enum Status{Open,Close}private OnDragStatusChangeListener onDragStatusChangeListener;public interface OnDragStatusChangeListener{void onOpen();void onClose();void onDragging(float dragProgress);}}
代碼:https://github.com/JackChen1999/SlidingMenu
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的自定义控件:SlidingMenu,侧边栏,侧滑菜单的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Git常用命令和Git团队使用规范指南
- 下一篇: 自定义控件:视差特效