先上圖,這是右側
這是頂部
左邊和下邊的都類似,就不放圖了,支持簡單的手勢和代碼控制
在搞作業的時候需要這樣的一個側邊欄功能,能夠推擠主屏幕,不是DrawerLayout那種,百度找了好久,只發現一種android近版本中給出的一個Slidingpanelayout的佈局,這個佈局效果和上圖這個類似,唯一不好的就是隻有左側邊欄,(或者我沒發現有其他設置)於是想到重寫這個Slidingpanelayout的例子,打開源代碼,發現裏面寫的太高深,看不懂。
如果Slidingpanelayout左側欄可以滿足就不用往下看了,在build.gradle文件導入包的地方加一行:
implementation 'androidx.slidingpanelayout:slidingpanelayout:1.0.0'
直接使用就好,它的佈局裏只能放兩個View,第一個是左側欄內容,第二個爲主頁面
源碼註釋開頭:
SlidingPaneLayout provides a horizontal, multi-pane layout for use at the top level of a UI. A left (or first) pane is treated as a content list or browser, subordinate to a primary detail view for displaying content.
SlidingPaneLayout提供了一個水平的、多窗格的佈局,用於UI的頂層。左(或第一個)窗格被視爲內容列表或瀏覽器,從屬於顯示內容的主要詳細視圖。
百度多次無果,決定自己造一個。
自定義的這個佈局比較簡陋,只實現了我需要的一點點功能,比如側邊欄展開讓主頁面變暗色,或者拖拽着邊框可以拉拽這樣的功能實現比較麻煩,也比較雞肋,需要的話可以自行嘗試添加。話不多說上代碼:
在styles.xml文件<resources></resources>中添加如下代碼:
<declare-styleable name="MySlidLayout" > <attr name="position" format="enum"> <enum name="top" value="1"/> <enum name="left" value="2"/> <enum name="right" value="3"/> <enum name="bottom" value="4"/> </attr> </declare-styleable>
然後java代碼:
import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateDecelerateInterpolator; public class MySideBarLayout extends ViewGroup { private int position = 3;//默認右側欄 private int move = 0;//記錄需要滑動的量 private boolean isopen = false;//打開狀態 private boolean ishead=false;//判斷滑動趨勢是否合格 private float lastX = 0;//記錄手指在屏幕中的X private float lastY = 0;//記錄手指在屏幕中的Y private float countmove = 0;//記錄累計滑動量X //實例化一個監聽 public MySideBarLayout(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySideBarLayout); position = typedArray.getInteger(R.styleable.MySideBarLayout_position, 3); typedArray.recycle(); } public void setOnChangeListener(OnChangeListener onChangeListener1){ onChangeListener=onChangeListener1; } //打開側邊欄 public void openBar() { if (!isopen) { onChangeListener.Onchanged(true); setMoveAnim(move, getChildAt(0)); setMoveAnim(move, getChildAt(1)); isopen = true; } } //關閉側邊欄 public void closeBar() { if (isopen) { onChangeListener.Onchanged(false); setMoveAnim(0, getChildAt(0)); setMoveAnim(0, getChildAt(1)); isopen = false; } } //返回側邊欄的狀態 public boolean isOpen() { return isopen; } //調用方法啓動側邊欄的動畫 private void setMoveAnim(float distance, View view) { ObjectAnimator ro; if (position == 1 || position == 4) { ro = ObjectAnimator.ofFloat(view, "translationY", distance).setDuration(300); } else { ro = ObjectAnimator.ofFloat(view, "translationX", distance).setDuration(300); } ro.setInterpolator(new AccelerateDecelerateInterpolator()); ro.start(); } //手指滑動側邊欄的動畫,與上面那個沒什麼區別,只是時間不一樣,手動的快一丟丟 private void setMoveAnimself(float distance, View view) { ObjectAnimator ro; if (position == 1 || position == 4) { ro = ObjectAnimator.ofFloat(view, "translationY", distance).setDuration(150); } else { ro = ObjectAnimator.ofFloat(view, "translationX", distance).setDuration(150); } ro.setInterpolator(new AccelerateDecelerateInterpolator()); ro.start(); } //根據子view確定佈局本身的大小 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) { int groupWidth = getMaxWidth(); int groupHeight = getTotalHeight(); setMeasuredDimension(groupWidth, groupHeight); } else if (widthMode == MeasureSpec.AT_MOST) { setMeasuredDimension(getMaxWidth(), height); } else if (heightMode == MeasureSpec.AT_MOST) { setMeasuredDimension(width, getTotalHeight()); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int pw = r - l; int ph = b - t; View v1 = getChildAt(0); v1.layout(0, 0, pw, ph); View v2 = getChildAt(1); int vw = v2.getMeasuredWidth(); int vh = v2.getMeasuredHeight(); if (position == 1) { move = vh; v2.layout(0, -vh, pw, 0); } else if (position == 2) { move = vw; v2.layout(-vw, 0, 0, ph); } else if (position == 3) { move = -vw; v2.layout(pw, 0, pw + vw, ph); } else if (position == 4) { move = -vh; v2.layout(0, ph, pw, ph + vh); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = event.getRawX(); lastY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: float vx = event.getRawX() - lastX; float vy = event.getRawY() - lastY; if (position == 1) { if(Math.abs(vy)>Math.abs(2*vx)) { if (isopen && vy < 0) { countmove = vy; ishead = true; } else if (!isopen && vy > 0) { countmove = vy; ishead = true; } } } else if (position == 2) { if(Math.abs(vx)>Math.abs(2*vy)) { if (isopen && vx < 0) { countmove = vx; ishead = true; } else if (!isopen && vx > 0) { countmove = vx; ishead = true; } } } else if (position == 3) { if(Math.abs(vx)>Math.abs(2*vy)) { if (isopen && vx > 0) { countmove = vx; ishead = true; } else if (!isopen && vx < 0) { countmove = vx; ishead = true; } } } else if (position == 4) { if(Math.abs(vy)>Math.abs(2*vx)) { if (isopen && vy > 0) { countmove = vy; ishead = true; } else if (!isopen && vy < 0) { countmove = vy; ishead = true; } } } break; case MotionEvent.ACTION_UP: if (Math.abs(countmove) > Math.abs(move) / 2 && !isopen&&ishead) { isopen = true; ishead=false; onChangeListener.Onchanged(true); setMoveAnimself(move, getChildAt(0)); setMoveAnimself(move, getChildAt(1)); } else if (Math.abs(countmove) > Math.abs(move) / 2 && isopen&&ishead) { isopen = false; ishead=false; onChangeListener.Onchanged(false); setMoveAnimself(0, getChildAt(0)); setMoveAnimself(0, getChildAt(1)); } break; } return true; } private int getMaxWidth() { int count = getChildCount(); int maxWidth = 0; for (int i = 0; i < count; i++) { int currentWidth = getChildAt(i).getMeasuredWidth(); if (maxWidth < currentWidth) { maxWidth = currentWidth; } } return maxWidth; } private int getTotalHeight() { int count = getChildCount(); int totalHeight = 0; for (int i = 0; i < count; i++) { totalHeight += getChildAt(i).getMeasuredHeight(); } return totalHeight; } private OnChangeListener onChangeListener=new OnChangeListener() { @Override public void Onchanged(boolean status) { } }; public interface OnChangeListener{ void Onchanged(boolean status); } }
使用方法:第一個view爲主頁面,第二個爲側頁面。
在<MySideBarLayout/>中調用
app:position="top"
提供四個選項 : top , left , right , bottom
<包名.MySideBarLayout android:id="@+id/sidelayout" android:layout_width="match_parent" android:layout_height="match_parent" app:position="top" > <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00cc66"> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="200dp" android:background="#ffff33"> </LinearLayout> </包名.MySideBarLayout>
java 代碼中我實現了幾個簡單的方法:
openBar()打開側欄
closeBar()關閉側欄
isOpen() 獲取側邊欄的狀態
setOnChangeListener(OnChangeListener onChangeListener1)設置側邊欄變化監聽
這裏注意監聽名,MySideBarLayout.OnChangeListener()是我在包內自定義的監聽
mysidebarlayout.setOnChangeListener(new MySideBarLayout.OnChangeListener() { @Override public void Onchanged(boolean status) { if(status){ textview.setText("側邊欄已打開"); }else{ textview.setText("側邊欄已關閉"); } } });
就這麼簡單就OK了,如果需要在這個佈局裏同時能滑動四個側邊欄,也可以實現的,重寫這個onlayout........需要的話自行解決吧,東西比較簡陋,有什麼問題可以留言建議,我再調整調整,一起學習學習