Android实现自定义测滑栏

先上图,这是右侧                                       

       

  这是顶部 

  

左边和下边的都类似,就不放图了,支持简单的手势和代码控制

在搞作业的时候需要这样的一个侧边栏功能,能够推挤主屏幕,不是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........需要的话自行解决吧,东西比较简陋,有什么问题可以留言建议,我再调整调整,一起学习学习

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章