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........需要的話自行解決吧,東西比較簡陋,有什麼問題可以留言建議,我再調整調整,一起學習學習

 

 

 

 

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