溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

自定義Android側(cè)滑菜單控件

發(fā)布時間:2020-07-03 21:16:18 來源:網(wǎng)絡(luò) 閱讀:833 作者:2273167391 欄目:移動開發(fā)

package com.tenghu.sideslipmenu.view;


import android.content.Context;

import android.os.AsyncTask;

import android.util.AttributeSet;

import android.util.DisplayMetrics;

import android.view.MotionEvent;

import android.view.VelocityTracker;

import android.view.View;

import android.view.WindowManager;

import android.widget.LinearLayout;


/**

 * Created by Arvin_Li on 2014/11/18.

 */

public class SideslipMenuView extends LinearLayout {

    //滾動顯示和隱藏menu時,手指滑動需要達到的速度

    private static final int SNAP_VELOCITY = 200;

    private View mMenu;//菜單布局

    private View mContent;//主內(nèi)容布局

    private int mScreenWidth;//屏幕寬度

    private int mMenuWidth;//菜單寬度

    //menu最多可以滑動到的左邊緣,值由menu布局的寬度來定

    private int leftEdge;

    //menu最多可以滑到的有邊緣,值恒為0

    private int rightEdge = 0;

    //menu完全顯示時,留給content的寬度值

    private int toRightPaddingWidth = 50;

    //menu布局的參數(shù),通過此參數(shù)來更改leftMargin的值

    private LinearLayout.LayoutParams menuParams;

    //記錄手指按下的橫坐標

    private float xDown;

    //記錄手指抬起的橫坐標

    private float xUp;

    //記錄手指移動的橫坐標

    private float xMove;

    //當前menu是顯示還是隱藏,只有menu完全顯示和隱藏才會改變該值,滑動時不會改變

    private boolean isMenuVisible;

    //用于計算手指滑動的速度

    private VelocityTracker mVelocityTracker;

    private boolean once;


    public SideslipMenuView(Context context) {

        super(context);

        init(context);

    }


    public SideslipMenuView(Context context, AttributeSet attrs) {

        super(context, attrs);

        init(context);

    }


    /**

     * 初始化

     */

    private void init(Context context) {

        //獲取WindowManager對象

        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        DisplayMetrics displayMetrics = new DisplayMetrics();

        windowManager.getDefaultDisplay().getMetrics(displayMetrics);

        mScreenWidth = displayMetrics.widthPixels;//獲取屏幕寬度

    }


    @Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (!once) {

            mMenu = getChildAt(0);//獲取菜單布局

            mContent = getChildAt(1);//獲取內(nèi)容布局

            menuParams = (LayoutParams) mMenu.getLayoutParams();//獲取菜單參數(shù)

            mMenuWidth = menuParams.width = mScreenWidth - toRightPaddingWidth;//設(shè)置菜單寬度

            //左邊緣的值賦值為menu寬度的負數(shù)

            leftEdge = -menuParams.width;

            //menu的leftMargin設(shè)置為左邊緣的值,默認菜單不可見

            menuParams.leftMargin = leftEdge;

            mContent.getLayoutParams().width = mScreenWidth;//設(shè)置內(nèi)容寬度

            once = true;

        }

    }


    @Override

    public boolean onTouchEvent(MotionEvent event) {

        createVelocityTracker(event);//創(chuàng)建VelocityTracker對象

        switch (event.getAction()) {

            //手指按下

            case MotionEvent.ACTION_DOWN:

                xDown = event.getRawX();//記錄橫坐標

                break;

            //手指移動

            case MotionEvent.ACTION_MOVE:

                //手指移動時,對比按下的橫坐標,計算出移動的距離來調(diào)整menu的leftMargin值,從而顯示和隱藏menu

                xMove = event.getRawX();

                //計算獲取到距離

                int distanceX = (int) (xMove - xDown);

                if (isMenuVisible) {

                    menuParams.leftMargin = distanceX;

                } else {

                    menuParams.leftMargin = leftEdge + distanceX;

                }


                //如果菜單的左邊界小于了可以滑動的左邊界

                if (menuParams.leftMargin <= leftEdge) {

                    //將可以滑動的左邊界賦值給菜單左邊界

                    menuParams.leftMargin = leftEdge;

                } else if (menuParams.leftMargin >= rightEdge) {

                    menuParams.leftMargin = rightEdge;

                }

                mMenu.setLayoutParams(menuParams);//設(shè)置菜單參數(shù)

                break;

            //手指抬起

            case MotionEvent.ACTION_UP:

                //手指抬起時,進行判斷當前手勢的意圖,從而決定是滾動到menu界面,還是滾動到content界面

                xUp = event.getRawX();

                if (wantToShowMenu()) {

                    if (shouldScrollToMenu()) {

                        scrollToMenu();

                    } else {

                        scrollToContent();

                    }

                } else if (wantToShowContent()) {

                    if (shouldScrollToContent()) {

                        scrollToContent();

                    } else {

                        scrollToMenu();

                    }

                }

                //手指抬起是回收VelocityTracker對象

                recycleVelocityTracker();

                break;

        }

        return true;

    }


    /**

     * 創(chuàng)建VelocityTracker對象,并將觸摸content界面的滑動時間加入到VelocityTracker中

     *

     * @param event

     */

    private void createVelocityTracker(MotionEvent event) {

        if (null == mVelocityTracker) {

            mVelocityTracker = VelocityTracker.obtain();//初始化VelocityTracker

        }

        mVelocityTracker.addMovement(event);//添加界面滑動事件

    }


    /**

     * 判斷當前手勢的意圖是不是想顯示content,如果手指移動的距離是負數(shù),且當前menu是可見的,則認為當前手勢是想要顯示content。

     *

     * @return

     */

    private boolean wantToShowContent() {

        return xUp - xDown < 0 && isMenuVisible;

    }


    /**

     * 判斷當前手勢的意圖是不是想顯示menu,如果手指移動的距離是正數(shù),且當前menu是不可見,則認為當前手勢是顯示menu

     *

     * @return

     */

    private boolean wantToShowMenu() {

        return xUp - xDown > 0 && !isMenuVisible;

    }


    /**

     * 判斷是否應(yīng)該滾動將menu展示出來,如果手指移動距離大于屏幕的1/2,或者手指移動速度大于SNAP_VELOCITY,就認為應(yīng)該滾動將menu展示出來。

     *

     * @return 如果應(yīng)該滾動將menu展示出來返回true,否則返回false。

     */

    private boolean shouldScrollToMenu() {

        return xUp - xDown > mMenuWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;

    }


    /**

     * 判斷是否應(yīng)該滾動將content展示出來,如果手指移動距離加上menuPadding大于屏幕的1/2,或者手指移動速度大于SNAP_VELOCITY, 就認為應(yīng)該滾動將content展示出來。

     *

     * @return 如果應(yīng)該滾動將content展示出來返回true,否則返回false。

     */

    private boolean shouldScrollToContent() {

        //這里菜單狀態(tài)為顯示,如果要菜單隱藏,那么手勢是從右到左滑動,這里手指按下的橫坐標就會比抬起時的橫坐標小

        return xDown - xUp + toRightPaddingWidth > mMenuWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;

    }


    /**

     * 獲取手指在content上滑動的速度

     *

     * @return 滑動速度,以每秒鐘移動了多少像素為單位

     */

    private int getScrollVelocity() {

        mVelocityTracker.computeCurrentVelocity(1000);

        int velocity = (int) mVelocityTracker.getXVelocity();//獲取x方向的速度值

        return Math.abs(velocity);

    }


    /**

     * 回收VelocityTracker對象

     */

    private void recycleVelocityTracker() {

        if (null != mVelocityTracker) {

            mVelocityTracker.recycle();

            mVelocityTracker = null;

        }

    }


    /**

     * 將屏幕滾動到menu界面,設(shè)置滾動速度為30

     */

    private void scrollToMenu() {

        isMenuVisible = true;

        new ScrollTask().execute(30);

    }


    /**

     * 將屏幕滾動到content界面,設(shè)置滾動速度為-30

     */

    private void scrollToContent() {

        isMenuVisible = false;

        new ScrollTask().execute(-30);

    }


    /**

     * 創(chuàng)建滾動任務(wù)類

     */

    class ScrollTask extends AsyncTask<Integer, Integer, Integer> {

        @Override

        protected Integer doInBackground(Integer... speed) {

            int leftMargin = menuParams.leftMargin;

            //根據(jù)傳入的速度來滾動界面,當滾動到達左邊界或右邊界時跳出循環(huán)

            while (true) {

                leftMargin = leftMargin + speed[0];

                if (leftMargin > rightEdge) {

                    leftMargin = rightEdge;

                    break;

                }

                if (leftMargin < leftEdge) {

                    leftMargin = leftEdge;

                    break;

                }

                //更新任務(wù)進度,會把值傳入到onProgressUpdate()方法中進行UI的更新

                publishProgress(leftMargin);

                //為了要有滾動效果產(chǎn)生,每次循環(huán)使線程睡眠20毫秒

                sleep(20);

            }

            return leftMargin;

        }


        /**

         * 這里的Intege參數(shù)對應(yīng)AsyncTask中的第二個參數(shù)

         * 在doInBackground方法當中,,每次調(diào)用publishProgress方法都會觸發(fā)onProgressUpdate執(zhí)行

         * onProgressUpdate是在UI線程中執(zhí)行,所有可以對UI空間進行操作

         *

         * @param leftMargin

         */

        @Override

        protected void onProgressUpdate(Integer... leftMargin) {

            menuParams.leftMargin = leftMargin[0];

            mMenu.setLayoutParams(menuParams);

        }


        /**

         * 執(zhí)行異步結(jié)束,接收doInBackground()方法的返回值,接收到的返回值對應(yīng)AsyncTask<Integer, Integer, Integer> 第3個參數(shù)

         *

         * @param leftMargin

         */

        @Override

        protected void onPostExecute(Integer leftMargin) {

            menuParams.leftMargin = leftMargin;

            mMenu.setLayoutParams(menuParams);

        }

    }


    /**

     * 是當前線程睡眠指定的毫秒數(shù)

     *

     * @param millis

     */

    private void sleep(long millis) {

        try {

            Thread.sleep(millis);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

以上就是整個自定義側(cè)滑菜單控件的代碼,在布局文件直接使用即可如下:

<?xml version="1.0" encoding="utf-8"?>

<com.tenghu.sideslipmenu.view.SideslipMenuView xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:background="@drawable/bg_01">


    <include layout="@layout/left_menu" />


    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:background="@drawable/bg_02"

        android:orientation="vertical">

       

    </LinearLayout>

</com.tenghu.sideslipmenu.view.SideslipMenuView>

其中include引用的就是一個菜單布局文件,如下:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:gravity="center_vertical">


    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_marginLeft="30dp"

        android:orientation="vertical">


        <RelativeLayout

            android:layout_width="wrap_content"

            android:layout_height="50dp"

            android:layout_marginBottom="10dp">


            <ImageView

                android:id="@+id/iv_img_01"

                android:layout_width="50dp"

                android:layout_height="match_parent"

                android:src="@drawable/app_01" />


            <TextView

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                android:layout_marginLeft="10dp"

                android:layout_toRightOf="@id/iv_img_01"

                android:gravity="center_vertical"

                android:text="第一個Item" />

        </RelativeLayout>


        <RelativeLayout

            android:layout_width="wrap_content"

            android:layout_height="50dp"

            android:layout_marginBottom="10dp">


            <ImageView

                android:id="@+id/iv_img_02"

                android:layout_width="50dp"

                android:layout_height="match_parent"

                android:src="@drawable/app_02" />


            <TextView

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                android:layout_marginLeft="10dp"

                android:layout_toRightOf="@id/iv_img_02"

                android:gravity="center_vertical"

                android:text="第二個Item" />

        </RelativeLayout>


        <RelativeLayout

            android:layout_width="wrap_content"

            android:layout_height="50dp"

            android:layout_marginBottom="10dp">


            <ImageView

                android:id="@+id/iv_img_03"

                android:layout_width="50dp"

                android:layout_height="match_parent"

                android:src="@drawable/app_03" />


            <TextView

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                android:layout_marginLeft="10dp"

                android:layout_toRightOf="@id/iv_img_03"

                android:gravity="center_vertical"

                android:text="第三個Item" />

        </RelativeLayout>


        <RelativeLayout

            android:layout_width="wrap_content"

            android:layout_height="50dp"

            android:layout_marginBottom="10dp">


            <ImageView

                android:id="@+id/iv_img_04"

                android:layout_width="50dp"

                android:layout_height="match_parent"

                android:src="@drawable/app_04" />


            <TextView

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                android:layout_marginLeft="10dp"

                android:layout_toRightOf="@id/iv_img_04"

                android:gravity="center_vertical"

                android:text="第四個Item" />

        </RelativeLayout>


        <RelativeLayout

            android:layout_width="wrap_content"

            android:layout_height="50dp"

            android:layout_marginBottom="10dp">


            <ImageView

                android:id="@+id/iv_img_05"

                android:layout_width="50dp"

                android:layout_height="match_parent"

                android:src="@drawable/app_05" />


            <TextView

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                android:layout_marginLeft="10dp"

                android:layout_toRightOf="@id/iv_img_05"

                android:gravity="center_vertical"

                android:text="第五個Item" />

        </RelativeLayout>


    </LinearLayout>

</RelativeLayout>

其中的菜單可以使用ListView去布局,這里做測試就沒有去使用了

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI