先上图,这是右侧
这是顶部
左边和下边的都类似,就不放图了,支持简单的手势和代码控制
在搞作业的时候需要这样的一个侧边栏功能,能够推挤主屏幕,不是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........需要的话自行解决吧,东西比较简陋,有什么问题可以留言建议,我再调整调整,一起学习学习