Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義View ----QQ5.0左邊側滑 + 動畫

自定義View ----QQ5.0左邊側滑 + 動畫

編輯:關於Android編程

xml
activity_main.xml


    
        
            
                
            

            

            

        

        
            
                
            

            

            

        

    



2.自定義view
public class DragLayout extends FrameLayout

package com.guyulei.myqq50;

import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

/**
 * Created by Administrator on 2016/6/30 0030.
 * 1、為什麼不繼承 ViewGroup,因為繼承 ViewGroup 需要重寫 onMeasure()和實現 onLayout()方法,自己
 * 實現子 view 的測量和擺放,在這裡我們不需要自己去做測量和擺放,而 FrameLayout 已經對這兩個方法進
 * 行了具體實現,所以繼承 FrameLayout 更加簡單省事
 * 2、為什麼不繼承 RelativeLayout,因為這裡我們只需要層級關系,不需要相對關系,繼承 RelativeLayout
 * 界面效果是一樣的,但 RelativeLayout 對 FrameLayout 多了相對關系的計算,效率會低一些,所以選擇繼
 * 承 FrameLayout
 */
public class DragLayout extends FrameLayout {


    private ViewDragHelper mViewDragHelper;
    private LinearLayout   mLeftContent;
    private LinearLayout   mMainContent;
    private int            mWidth;
    private int            mHeight;
    private int            mRange;
    private int mLeftWidth;

    //狀態值
    public enum Status {
        Open,
        Close,
        Draging;
    }
    //默認狀態值
    private Status status = Status.Close;
    //監聽對象聲明
    private OnUpdataDragStateListener mOnUpdataDragStateListener;
    //對外接口
    public interface OnUpdataDragStateListener {
        void onOpen();
        void onClose();
        void onDragging(float percent);
    }

    //對外接口回調方法
    public void setOnUpdataDragStateListener(OnUpdataDragStateListener onUpdataDragStateListener) {
        this.mOnUpdataDragStateListener = onUpdataDragStateListener;
    }
    //串聯構造方法

    /**
     * 我們可以通過串連三個構造方法的方式實現只調用一次 init()方法
     * 這樣無論是代碼創建還是布局在 xml 中都能調用到我們的初始化代碼
     *
     * @param context
     */
    //代碼創建時調用
    public DragLayout(Context context) {
        this(context, null);
    }

    //布局在 xml 中,實例化時調用
    public DragLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //初始化
        //1.輔助類ViewDragHelper  forparent 2個拖拽控件的父控件 ,sensitivity子控件拖拽的敏感度
        //callback:事件回調
        mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
            @Override
            // tryCaptureView俘獲; 奪取; 奪得(嘗試奪取子控件)
            //返回值代表 子控件 是否可以被奪取
            public boolean tryCaptureView(View child, int pointerId) {
                return true;
            }

            //可通過計算測量水平或者垂直方向拖拽的范圍  返回>0即可
            @Override
            public int getViewHorizontalDragRange(View child) {
                return mRange;
            }

            @Override
            //clamp緊緊抓住; 緊夾住(緊抓著俘獲的view(child)水平或者垂直移動位置)
            //dx 變化量(移動的差值 newleft-oldleft)
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                //dx累加  是  left
                if (child == mMainContent) {
                    //拖拽為主面板時
                    left = fixleft(left);
                }
                return left;
            }

            @Override
            //松手時  回調(Released釋放)
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                // xvel水平方向上的速度 左為-    右為+
                if (xvel == 0 && mMainContent.getLeft() > mRange / 2) {
                    open();
                } else if (xvel > 0) {
                    //有向右的速度
                    open();
                } else {
                    close();
                }
            }

            //位置發生移動  調用
            @Override
            public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
                if (changedView == mLeftContent) {
                    //控制底下的子控件不能移動(左面板)
                    mLeftWidth = mLeftContent.getMeasuredWidth();
                    mLeftContent.layout(0, 0, mLeftWidth, mHeight);
                    //左面板移動的距離 累加給主面板(移動左面板)
                    int oldLeft = mMainContent.getLeft();
                    int newLeft = oldLeft + dx;
                    //修正左邊的位置
                    newLeft = fixleft(newLeft);
                    mMainContent.layout(newLeft, 0, newLeft + mWidth, mHeight);
                }
                // dispatch派遣,調度; (迅速地)發出; 迅速處理
                dispatchEvent();
                invalidate();
            }
        });
    }

    //2  mViewDragHelper搶過觸摸事件自己決定應該怎麼處理
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    //3.mViewDragHelper搶過觸摸事件後,進行處理,返回值true
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

    //4.onFinishInflate()在控件 inflate 完成時會被調用查找子控件
    //可以通過 findViewById()的方式查找子控件  getChildAt(0)
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mLeftContent = (LinearLayout) getChildAt(0);
        mMainContent = (LinearLayout) getChildAt(1);
    }

    // 5.onSizeChanged()調用的次數比 onMeasure()少,在這裡我們在 onSizeChanged()方法中去獲取寬高
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //獲取整個控件的寬度和高度
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        //主界面拖拽的范圍(自定義)(0-mRange)(0 < left  mRange) {
            left = mRange;
        }
        return left;
    }

    //7.打開 smooth變平和,變緩和;Slide Mountain 斯來得山(凱次克來最高峰)
    //View child, int finalLeft, int finalTop
    private void open() {
        open(true);
    }

    private void open(boolean isSmooth) {
        int finalLeft = mRange;
        if (isSmooth) {
            //開啟平滑
            if (mViewDragHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)) {
                //重繪界面 Compat兼容性;  postInvalidateOnAnimation 動畫結束之後再重繪 防止丟幀
                ViewCompat.postInvalidateOnAnimation(this);
            } else {
                mMainContent.layout(finalLeft, 0, finalLeft + mWidth, mHeight);
            }
        }
    }

    //8. 關閉
    private void close() {
        close(true);
    }

    private void close(boolean isSmooth) {
        int finalLeft = 0;
        if (isSmooth) {
            //開啟平滑
            if (mViewDragHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)) {
                //重繪界面
                //invalidate();
                ViewCompat.postInvalidateOnAnimation(this);
            } else {
                mMainContent.layout(finalLeft, 0, finalLeft + mWidth, mHeight);
            }
        }
    }
    @Override
    public void computeScroll() {
        super.computeScroll();
        //維持動畫
        if (mViewDragHelper.continueSettling(true)) {
            //重繪
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    //9.加動畫  回調
    private void dispatchEvent() {
        //獲取 百分百 0.0f - 1.0f
        float percent = mMainContent.getLeft() * 1.0f / mRange;

        Status lastStatus = status;
        //狀態更新
        if (percent == 0) {
            status = Status.Close;
        } else if (percent == 1) {
            status = Status.Open;
        } else {
            status = Status.Draging;
        }
        //接口回調
        if (mOnUpdataDragStateListener != null) {
            mOnUpdataDragStateListener.onDragging(percent);
        }
        //
        if (lastStatus != status && mOnUpdataDragStateListener != null) {
            if (status == Status.Open) {
                mOnUpdataDragStateListener.onOpen();
            } else if (status == Status.Close) {
                mOnUpdataDragStateListener.onClose();
            }
        }


        //左面板 縮放  位移  透明的
        //0.0f-1.0f   ---0.5f-1.0f
        //0.5f + percent * (0.5f);
        mLeftContent.setScaleX(0.5f + percent * (0.5f));
        mLeftContent.setScaleY(0.5f + percent * (0.5f));
        //位移 evaluate(percent,-mw,0)
        mLeftContent.setTranslationX(evaluate(percent, -mWidth * 0.5f, 0f));
        //透明的evaluate(percent,0.2f,1.0f)
        mLeftContent.setAlpha(evaluate(percent, 0.2f, 1.0f));

        //主面板 縮放evaluate(percent,0.2f,1.0f)
        mMainContent.setScaleY(evaluate(percent, 1.0f, 0.8f));
        //背景亮度   濾波器Filter  估價,估值evaluate  門童; 搬運工人Porter欺騙; 把…改頭換面;Duff
        getBackground().setColorFilter((Integer) evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), PorterDuff.Mode.SRC_OVER);


    }

    public Object evaluateColor(float fraction, Object startValue, Object endValue) {
        int startInt = (Integer) startValue;
        int startA = (startInt >> 24) & 0xff;
        int startR = (startInt >> 16) & 0xff;
        int startG = (startInt >> 8) & 0xff;
        int startB = startInt & 0xff;
        int endInt = (Integer) endValue;
        int endA = (endInt >> 24) & 0xff;
        int endR = (endInt >> 16) & 0xff;
        int endG = (endInt >> 8) & 0xff;
        int endB = endInt & 0xff;
        return (int) ((startA + (int) (fraction * (endA - startA))) << 24) |
                (int) ((startR + (int) (fraction * (endR - startR))) << 16) |
                (int) ((startG + (int) (fraction * (endG - startG))) << 8) |
                (int) ((startB + (int) (fraction * (endB - startB))));
    }

    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

3.MainActivity
public class MainActivity extends Activity

package com.guyulei.myqq50;

import android.animation.ValueAnimator;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.CycleInterpolator;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import java.util.Random;

import utils.MyToast;

public class MainActivity extends Activity {

    private DragLayout mDragLayout;
    private ListView   mMainlistView;
    private ListView   mLeftlistView;
    private ImageView  mIvhead;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //主頁面imag對象
        mIvhead = (ImageView) findViewById(R.id.iv_head);
        mDragLayout = (DragLayout) findViewById(R.id.dl_dragLayout);
        mDragLayout.setOnUpdataDragStateListener(new DragLayout.OnUpdataDragStateListener() {
            @Override
            public void onOpen() {
                MyToast.showToast(getApplicationContext(), "onOpen");
                ////面板打開時,左面板上的 listview 隨機滑動到 0 到 50 間的某個位置
                mLeftlistView.smoothScrollToPosition(new Random().nextInt(20));
            }

            @Override
            public void onClose() {
                MyToast.showToast(getApplicationContext(), "onClose");
                ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f, 10f);
                valueAnimator.setDuration(1000);
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float value = (float) animation.getAnimatedValue();
                        mIvhead.setTranslationX(value);
                    }
                });
                //動畫插值器
                valueAnimator.setInterpolator(new CycleInterpolator(3));
                valueAnimator.start();
            }

            @Override
            public void onDragging(float percent) {
                MyToast.showToast(getApplicationContext(), "onDragging" + percent);
                //percent 0.0f -> 1.0f  => 1.0f -> 0.0f   ==> 1-percent
                mIvhead.setAlpha(1-percent);
            }
        });


        //
        mMainlistView = (ListView) findViewById(R.id.main_listView);
        //main數據
        String[] str_main = new String[]{"guyulei", "guyulei1", "guyulei2", "guyulei3", "guyulei4"
                , "guyulei5", "guyulei6", "guyulei7", "guyulei8", "guyulei9", "guyulei10",
                "guyulei", "guyulei1", "guyulei2", "guyulei3", "guyulei4"
                , "guyulei5", "guyulei6", "guyulei7", "guyulei8", "guyulei9", "guyulei10",
                "guyulei", "guyulei1", "guyulei2", "guyulei3", "guyulei4"
                , "guyulei5", "guyulei6", "guyulei7", "guyulei8", "guyulei9", "guyulei10"};

        //設置適配器:ArrayAdapter
        mMainlistView.setAdapter(new ArrayAdapter(getApplicationContext(), android.R.layout.simple_list_item_1, str_main) {
            @Override
            //重新getView  改變字體顏色
            public View getView(int position, View convertView, ViewGroup parent) {

                TextView view = (TextView) super.getView(position, convertView, parent);
                view.setTextColor(Color.BLUE);
                return view;
            }
        });

        //
        mLeftlistView = (ListView) findViewById(R.id.left_listView);
        String[] str_left = new String[]{"顧雨磊", "顧雨磊1", "顧雨磊2", "顧雨磊3", "顧雨磊4"
                , "顧雨磊5", "顧雨磊6", "顧雨磊7", "顧雨磊8", "顧雨磊9", "顧雨磊10", "顧雨磊11"
                , "顧雨磊12", "顧雨磊13", "顧雨磊14", "顧雨磊15", "顧雨磊16", "顧雨磊17", "顧雨磊18"
                , "顧雨磊19"};
        mLeftlistView.setAdapter(new ArrayAdapter(getApplicationContext(), android.R.layout.simple_list_item_1, str_left) {
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {

                TextView view = (TextView) super.getView(position, convertView, parent);
                view.setTextColor(Color.BLACK);
                return view;
            }
        });



    }
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved