Android 自定義側(cè)滑菜單效果(ViewDragHelper)

吹牛皮

忙里偷閑在研究自定義View這一塊的東西,單純的使用觸摸事件加攔截事件等等的側(cè)滑功能還是寫過伏嗜,還沒用過ViewDragHelper來完成這個功能垮斯,所以就嘗試一下草戈!

ToolBar+DrawerLayout使用

一般來講坪创,如果要完成一個具有拖拽側(cè)滑的功能就必需要處理各種事件炕婶,比如onInterceptTouchEventOnTouchEvent,處理起來也不是很得心應(yīng)手莱预,出各種亂子的可能性都有柠掂!這個時候可以使用ViewDragHelper來輔助我們完成這些操作,Google用ViewDragHelper 封裝了對onInterceptTouchEventOnTouchEvent的處理依沮,也就是說Google已經(jīng)替我們寫好了邏輯涯贞,我們只需要設(shè)定好條條框框(比如邊界判斷等)就行了。


一悉抵、不扯有的沒的 回歸正題

UI什么的都是臨時搭的肩狂,很丑啊摘完,但是很溫柔姥饰!
↓↓↓先上效果圖↓↓↓

側(cè)滑.png


效果圖看完了繼續(xù)往下看。

二孝治、自定義View

/**
 * Created by Leogh on 2017/8/25.
 */

public class SwipeLayout1 extends LinearLayout {

    private ViewDragHelper mDragHelper = null;
    private View mDragView;
    private View mHideView;
    private int mDragSlop;//移動距離 小于這個距離就不觸發(fā)移動控件 恢復(fù)到當前位置
    private int mWidth;
    private int mHeight;
    private int mDragDistance;
    private final int STATE_CLOSE = 1001;
    private final int STATE_OPEN = 1002;
    private int mState = STATE_CLOSE;

    public SwipeLayout1(Context context) {
        this(context, null);
    }

    public SwipeLayout1(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SwipeLayout1(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        //其中1.0f是敏感度參數(shù)參數(shù)越大越敏感列粪。第一個參數(shù)為this审磁,表示該類生成的對象,
        // 他是ViewDragHelper的拖動處理對象岂座,必須為ViewGroup态蒂。
        mDragHelper = ViewDragHelper.create(this, 1.0f, new CallBack());
        //48dp  是一個距離,表示滑動的時候费什,手的移動要大于這個距離才開始移動控件钾恢。如果小于這個距離就不觸發(fā)移動控件,
        // 如viewpager就是用這個距離來判斷用戶是否翻頁
        mDragSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(ViewConfiguration.get(getContext()));
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        mDragView.layout(getPaddingLeft(), getPaddingTop(), mWidth - getPaddingRight(), mHeight - getPaddingBottom());
        mHideView.layout(mWidth - getPaddingRight(), getPaddingTop(), mWidth - getPaddingRight() + mDragDistance, mHeight - getPaddingBottom());
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        try {
            mDragView = getChildAt(0);
            mHideView = getChildAt(1);
        } catch (Exception e) {
            throw new NullPointerException("必須有兩個子view");
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mWidth = w;
        mHeight = h;
        mDragDistance = mHideView.getMeasuredWidth();
    }

    /**
     * onInterceptTouchEvent中通過使用mDragger.shouldInterceptTouchEvent(event)來決定我們是否應(yīng)該攔截當前的事件鸳址。
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mDragHelper.shouldInterceptTouchEvent(ev);
    }

    /**
     * onTouchEvent中通過mDragger.processTouchEvent(event)處理事件瘩蚪。
     *
     * @param event
     * @return true 時間已消費(交給了mDragHelper)不往下傳遞
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDragHelper.processTouchEvent(event);
        return true;
    }

    /**
     * 這個計算滑動的函數(shù)computeScroll(),就是用于判斷滾動是否完成的稿黍。
     * 在computeScroll方法中判斷smoothSlideViewTo觸發(fā)的continueSettling(boolean)的返回值疹瘦,來動態(tài)刷新界面
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
//            postInvalidate();
        }
    }

    class CallBack extends ViewDragHelper.Callback {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == mDragView;
        }

        //拖拽的子View在所屬方向上移動的位置(這里是水平方向),child為拖拽的子View巡球,left為子view應(yīng)該到達的x坐標言沐,dx為挪動差值
        //return left
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            Log.e(TAG + "clampViewPositionHorizontal", left + "");
            //以下兩個判斷是防止越界(部分View被遮住)
            if (left > getPaddingLeft()) {//向右滑動時 超過了paddingLeft都返回這個值(保持原位)
                return getPaddingLeft();
            }
            if (left < getPaddingLeft() - mDragDistance) {//向左滑動 整個隱藏的View都滑出來了 超過了getPaddingLeft() - mDragDistance都返回這個值(保持原位)
                return getPaddingLeft() - mDragDistance;
            }
            return left;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return getPaddingTop();
        }

        //返回拖拽子View在相應(yīng)方向上可以被拖動的最遠距離酣栈,默認為0
        @Override
        public int getViewHorizontalDragRange(View child) {
            Log.e(TAG + "getViewHorizontalDragRange", mDragDistance + "");
            return mDragDistance;
        }

        //當前拖拽的view松手或者ACTION_CANCEL時調(diào)用险胰,xvel、yvel為離開屏幕時的速率
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            //getPaddingLeft() - mDragView.getLeft() → 控件不動時為0
            int getPaddingLeft = getPaddingLeft();
            int getmDragViewLeft = mDragView.getLeft();
            int temp = getPaddingLeft() - mDragView.getLeft();
            int tempdragSlop = mDragSlop;
            if (getPaddingLeft() - mDragView.getLeft() < mDragSlop) {//最終位置的判斷
                smoothSlideHide();
            } else {
                smoothSlideOpen();
            }
            ViewCompat.postInvalidateOnAnimation(SwipeLayout1.this);
//            postInvalidate();
        }

        //被拖拽的View位置變化時回調(diào)矿筝,changedView為位置變化的view鸯乃,left、top變化后的x跋涣、y坐標缨睡,dx、dy為新位置與舊位置的偏移量
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            Log.e(TAG + "onViewPositionChanged", dx + "");
            mHideView.layout(mHideView.getLeft() + dx, mHideView.getTop(), mHideView.getRight() + dx, mHideView.getBottom());
            ViewCompat.postInvalidateOnAnimation(SwipeLayout1.this);
//            postInvalidate();
        }
    }

    private void smoothSlideHide(){
        //smoothSlideViewTo方法某個View自動滾動到指定的位置陈辱,如果這個方法返回true奖年,那么在接下來動畫移動的每一幀中都會回調(diào)continueSettling(boolean)方法,直到結(jié)束
        mDragHelper.smoothSlideViewTo(getHideView(), mWidth - getPaddingRight(), getPaddingTop());
        mDragHelper.smoothSlideViewTo(getDragView(), getPaddingLeft(), getPaddingTop());
        mState = STATE_CLOSE;
    }

    private void smoothSlideOpen(){
        mDragHelper.smoothSlideViewTo(getHideView(), mWidth - getPaddingRight() - mDragDistance, getPaddingTop());
        mDragHelper.smoothSlideViewTo(getDragView(), getPaddingLeft() - mDragDistance, getPaddingTop());
        mState = STATE_OPEN;
    }

    public View getDragView() {
        if (getChildCount() == 0) return null;
        return getChildAt(0);
    }

    public View getHideView() {
        if (getChildCount() == 1) return null;
        return getChildAt(1);
    }

    private void setState(int state){
        this.mState = state;
    }

    private int getState(){
        return mState;
    }

    /**
     * 關(guān)閉滑動
     */
    public void close(){
        if (mState == STATE_OPEN){
            smoothSlideHide();
            ViewCompat.postInvalidateOnAnimation(SwipeLayout1.this);
        }
    }
}

好了沛贪,自定義view完成了陋守,代碼中注釋已經(jīng)一目了然了,都是用比較淺顯的話來表達(片面)利赋,只能說話糙理不糙水评,看得懂才是王道


二媚送、大致的UI布局

創(chuàng)建在res\layout文件夾下創(chuàng)建一個xml文件中燥,命名為item_swipelayout.xml。首先側(cè)滑我們不難看出只分為兩個部分塘偎,第一部分為內(nèi)容區(qū)域(可視部分)疗涉,第二部分為菜單區(qū)域(隱藏部分)拿霉。
所以自定view類SwipeLayout1中就要求在布局時要包含兩個子view(即兩個部分),具體布局如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context=".MainActivity">

    <com.sobergh.soberghalltest.itemslideview.SwipeLayout1
        android:id="@+id/sl"
        android:layout_width="match_parent"
        android:layout_height="70dp">

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/holo_blue_bright"
            android:gravity="center_vertical"
            android:text="老臘肉老臘肉老臘肉老臘肉老臘肉老臘肉"/>

        <LinearLayout
            android:layout_width="80dp"
            android:layout_height="match_parent"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_top"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@android:color/holo_orange_light"
                android:gravity="center"
                android:text="置頂"/>

            <TextView
                android:id="@+id/tv_delete"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@android:color/holo_green_light"
                android:gravity="center"
                android:text="刪除"/>

        </LinearLayout>
    </com.sobergh.soberghalltest.itemslideview.SwipeLayout1>
</RelativeLayout>

到這里咱扣,自定義效果就完成了绽淘,只需新建一個activty把布局文件item_swipelayout.xml加載一下就行了。好吧闹伪,還是寫一下沪铭,新建一個activity命名為SwipeLayoutActivity,如下:

public class SwipeLayoutActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.item_swipelayout);
    }
}

簡單粗暴的就可以運行了偏瓤,如果你要調(diào)用自定義View里面的關(guān)閉滑動的方法就需要進行findViewById的操作了伦意,然后調(diào)用即可。

還可以進行很多擴展硼补,比如應(yīng)用到ListView中驮肉,這一部分后面應(yīng)該會加上去,應(yīng)該在自定義view中加回調(diào)方法就行

不能做伸手黨已骇,所借鑒大神的地址:https://github.com/mzw1004


寫完离钝,可以開始打坐了!啦啦啦啦啦啦啦啦啦啦啦啦褪储,簡單粗暴卵渴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鲤竹,隨后出現(xiàn)的幾起案子浪读,更是在濱河造成了極大的恐慌,老刑警劉巖辛藻,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碘橘,死亡現(xiàn)場離奇詭異,居然都是意外死亡吱肌,警方通過查閱死者的電腦和手機痘拆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氮墨,“玉大人纺蛆,你說我怎么就攤上這事」婢荆” “怎么了桥氏?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長猛铅。 經(jīng)常有香客問我字支,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任祥款,我火速辦了婚禮,結(jié)果婚禮上月杉,老公的妹妹穿的比我還像新娘刃跛。我一直安慰自己,他們只是感情好苛萎,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布桨昙。 她就那樣靜靜地躺著,像睡著了一般腌歉。 火紅的嫁衣襯著肌膚如雪蛙酪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天翘盖,我揣著相機與錄音桂塞,去河邊找鬼。 笑死馍驯,一個胖子當著我的面吹牛阁危,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汰瘫,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼狂打,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了混弥?” 一聲冷哼從身側(cè)響起趴乡,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蝗拿,沒想到半個月后晾捏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡哀托,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年粟瞬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萤捆。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡裙品,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出俗或,到底是詐尸還是另有隱情市怎,我是刑警寧澤,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布辛慰,位于F島的核電站区匠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驰弄,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一麻汰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧戚篙,春花似錦五鲫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乱灵,卻和暖如春塑崖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痛倚。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工规婆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蝉稳。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓聋呢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親颠区。 傳聞我的和親對象是個殘疾皇子削锰,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,348評論 25 707
  • DrawerLayout是android support包新增的側(cè)滑菜單控件,在Android Studio中可以...
    Ihesong閱讀 4,456評論 0 3
  • 又與女兒起沖突了毕莱,總是抑制不住自己的性情器贩,著急,再急朋截。都說好孩子是夸出來的蛹稍,而我的確太吝嗇自己的表揚了,總是在苛求...
    綠塬閱讀 189評論 0 0
  • 今天5.8川藏線上第五天 昨天晚上之前腿傷那兩隊友大林和查派決定不去了 溫存哥 感冒還繼續(xù)待定 唉 沒辦法部服!今天就...
    jenffy閱讀 263評論 2 0
  • 曾經(jīng)年少愛追夢唆姐, 一心只想往前飛。 行遍千山和萬水廓八, 一路走來不能回奉芦。 世間哪有永遠的忘憂歲月。誰不曾渴望鮮衣怒馬...
    好風如水yt閱讀 404評論 1 2