View的滑動與彈性滑動(改變View的參數+屬性動畫)

自定義控件系列:
秒懂OnMeasure
秒懂OnLayout
讓自定義ViewGroup裏的子控件支持Margin
讓自定義ViewGroup支持Padding
自定義ViewGroup的一個綜合實踐 FlowLayout
onDraw
最簡單的自定義View:SwitchView

這次做一個SwitchView,需要使用下面的幾個知識點:

知識點:

自定義View不是每次都要重寫onMeasure和onLayout,爲了避免手寫onMeasure和onLayout,不要直接繼承ViewGroup,而是繼承LinearLayout、FrameLayout、RelativeLayout這種已經重寫過了onMeasure和onLayout的佈局

  1. 如何獲取控件的寬高

    我們知道經常因爲控件還沒加載出來就去獲取寬高,那麼得到的寬高爲0,使用view.post()是獲取寬高最簡單的方式之一(爲什麼呢,看了源碼後,巴拉巴拉…),當然還有其他很多方法。

    post(new Runnable() {
                @Override
                public void run() {
                    mWidth = getMeasuredWidth();
                    mHeight = getMeasuredHeight();
                }
            });
    
  2. 如何動態地添加一個子控件

    我們經常需要通過代碼來添加子控件,而不是寫到xml裏,當然是addView了
    addView(View child) 把子控件添加到末尾
    void addView(View child, int index)把子控件添加到指定位置
    addView(View child, int width, int height)把子控件添加到末尾,併爲子控件指定寬高
    addView(View child, LayoutParams params)把子控件添加到末尾,子控件的寬高、margin、gravity等等所有xml裏以“layout_xxx"開頭的屬性全部封裝在LayoutParams裏
    addView(View child, int index, LayoutParams params)把子控件添加到指定位置,子控件的寬高、margin、gravity等等所有xml裏以“layout_xxx"開頭的屬性全部封裝在LayoutParams裏
    上面的方法都是間接或直接調用了這個方法

    可見LayoutParams也很重要

    1. 不同的ViewGroup都有自己專屬的LayoutParams,對應xml裏“layout_xxx"的所有屬性,我們如果想使用這些屬性,必須通過設置子控件的LayoutParams來實現
      下圖是LinearLayout的Layout_開頭的屬性在這裏插入圖片描述

    下圖是RelativeLayut以Layout_開頭的屬性

    在這裏插入圖片描述

    舉個例子:在RelativeLayout裏動態添加子控件

     		rl = (RelativeLayout) findViewById(R.id.rl);
            
            TextView textView = new TextView(this);
            textView.setText("我是一個textview");
            textView.setBackgroundColor(Color.RED);
            textView.setId(R.id.textview);//動態設置id,見https://www.cnblogs.com/codingblock/p/5090441.html
            RelativeLayout.LayoutParams lp1 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            lp1.leftMargin = 10;
            lp1.setMargins(100, 10, 10, 10);
            lp1.addRule(RelativeLayout.CENTER_IN_PARENT);
            textView.setLayoutParams(lp1);
            rl.addView(textView);
    
    
            ImageView imageView = new ImageView(this);
            imageView.setImageResource(R.mipmap.ic_launcher);
            RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            //★注意:這裏new的是RelativeLayout.LayoutParams,因爲你在爲RelativeLayout添加子控件
            lp2.addRule(RelativeLayout.BELOW, R.id.textview);
            rl.addView(imageView, lp2);
    
    

    在這裏插入圖片描述

  3. 重寫onTouchEvent,返回true,表示當前的view要處理事件(事件分發會詳細說這個方法)

  4. 如何讓view滑動起來

    1. 通過改變一個view的LayoutParams屬性值(這裏使用了margin),來實現改變view的位置
    2. 還可以通過scrollBy來實現,下篇博客舉這個例子
  5. 如何實現彈性滑動,即鬆手後view自己平滑地移動

    1. 屬性動畫逐漸地改變一個屬性,達到平滑移動效果
    2. scroller可以實現平滑移動

應用

效果

一個開關,滑塊隨着手指滑動,鬆手後,判斷滑動位置是不是過半,過半的話平滑移動到最右側,沒過半平滑移動到最左側
在這裏插入圖片描述

代碼

package com.view.custom.dosometest.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;

/**
 * 描述當前版本功能
 *
 * @Project: DoSomeTest
 * @author: cjx
 * @date: 2019-12-01 10:06  星期日
 */
public class SwitchView extends LinearLayout {


    private ImageView mImageView;
    private LayoutParams mLayoutParams;
    private int mSliderHeight;
    private int mSliderWidth;
    private int mWidth;
    private int mHeight;

    public SwitchView(Context context) {
        super(context);
        init(context);
    }

    public SwitchView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public SwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }


    private void init(Context context) {
        setBackgroundColor(Color.GRAY);

        post(new Runnable() {
            @Override
            public void run() {
                addSlider();//因爲這裏需要用到控件的寬高,所以寫到post裏
            }
        });

    }

    private void addSlider() {
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();

        mSliderWidth = mWidth / 2;
        mSliderHeight = mHeight;

		// 注意導包LayoutParams是LinearLayout.LayoutParams
        mLayoutParams = new LayoutParams(mSliderWidth, mSliderHeight);
        mImageView = new ImageView(getContext());
        mImageView.setBackgroundColor(Color.CYAN);
        mImageView.setLayoutParams(mLayoutParams);
        addView(mImageView);
    }


    float mLastX;

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                mLastX = x;
                break;

            case MotionEvent.ACTION_MOVE:
                float deltaX = x - mLastX;

                // 防止滑塊滑出邊界,矯正deltaX
                if (mLayoutParams.leftMargin + mSliderWidth + deltaX > mWidth) {
                    deltaX = mWidth - mLayoutParams.leftMargin - mSliderWidth;
                } else if (mLayoutParams.leftMargin + deltaX < 0) {
                    deltaX = -mLayoutParams.leftMargin;
                }
				// 動態改變這個控件的左邊距,實現控件的滑動
                mLayoutParams.leftMargin += (int) deltaX;
                //別忘了通過setLayoutParams,使你的改動生效
                mImageView.setLayoutParams(mLayoutParams);
                mLastX = x;
                break;


            case MotionEvent.ACTION_UP:
				// 鬆手後,通過屬性動畫改變margin實現控件的平滑移動
                if (mLayoutParams.leftMargin > mSliderWidth / 2) {
                    smoothChangeLeftMargin(mLayoutParams.leftMargin, mSliderWidth);

                } else {
                    smoothChangeLeftMargin(mLayoutParams.leftMargin, 0);

                }

                break;
        }
        return true;
    }

    /**
     * 使用屬性動畫平滑地過度leftMargin
     * @param start
     * @param end
     */
    private void smoothChangeLeftMargin(int start, int end) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(start, end);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mLayoutParams.leftMargin = (int) animation.getAnimatedValue();
                mImageView.setLayoutParams(mLayoutParams);

            }
        });
        valueAnimator.setDuration(300);
        valueAnimator.start();

    }
}

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