自定义view-仿网易音乐播放的按键

上图:

上代码:

package com.example.testview;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
import android.widget.TextView;

import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;

@SuppressLint("NewApi")
public class MusicView extends LinearLayout {

    public MusicView(Context context, AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        // TODO Auto-generated constructor stub
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public MusicView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub
    }

    public MusicView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public MusicView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    private void initView(Context mContext) {
        this.setClickable(true);
        initLine(mContext);
    }

    int lineNum = 4;
    int lineW = 5;
    TextView[] lines;
    LinearLayout.LayoutParams[] lps;
    int lineBgNomal = 0xFF000000;
    int lineBgClick = 0xFFFFFFFF;

    private void initLine(Context mContext) {
        if (lines == null) {
            lines = new TextView[lineNum];
            lps = new LinearLayout.LayoutParams[lineNum];
            for (int i = 0; i < lines.length; i++) {
                lines[i] = new TextView(mContext);
                lines[i].setBackgroundColor(lineBgNomal);
                lps[i] = new LinearLayout.LayoutParams(lineW,
                        LinearLayout.LayoutParams.MATCH_PARENT);
                lps[i].weight = 1;
            }
        }
    }

    private void setLineBg(int color) {
        if (lines != null)
            for (int i = 0; i < lines.length; i++) {
                lines[i].setBackgroundColor(color);
            }
    }

    int mHeight;
    int mWidth;

    boolean isNeedRelayoutLines = true;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int height = getMeasuredHeight();
        int width = getMeasuredWidth();
        if (height > 0) {
            mHeight = height;
        }
        if (width > 0) {
            mWidth = width;
        }
        if (isNeedRelayoutLines && mWidth > 0 && isShowToWindow) {

            int lPad = getPaddingLeft();
            int rPad = getPaddingRight();
            float perW = (mWidth - (lPad + rPad)) * 1.0f / lineNum;
            int marginW = (int) ((perW - lineW) / 2);
            for (int i = 0; i < lines.length; i++) {
                lps[i].leftMargin = lps[i].rightMargin = marginW;
                addView(lines[i], lps[i]);
            }
            isNeedRelayoutLines = false;
            invalidate();

            startMoveAnimation();
        }
    }

    boolean isShowToWindow = false;

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        isShowToWindow = true;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        isShowToWindow = false;
        isNeedRelayoutLines = true;
    }

    boolean isTouched = false;

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        final int action = MotionEventCompat.getActionMasked(event);

        Log.e("mytag", "<<" + action);
        
        switch (action) {
        case MotionEvent.ACTION_DOWN:

            if (!isTouched) {
                isTouched = true;
                setLineBg(lineBgClick);
                invalidate();
            }

            break;

        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            if (isTouched) {
                isTouched = false;
                setLineBg(lineBgNomal);
                invalidate();
            }
            break;
        }

        return super.onTouchEvent(event);
    }

    // ----------------
    // move
    // ----------------
    private void setLineMovePercent(TextView line, float percent) {
        int topPad = getPaddingTop();
        int bottomPad = getPaddingBottom();
        int LineHeight = mHeight - (topPad + bottomPad);
        int margintop = (int) ((1 - percent) * LineHeight);
        LinearLayout.LayoutParams ll = (LayoutParams) line.getLayoutParams();
        ll.topMargin = margintop;
        line.setLayoutParams(ll);
    }

    class LineMoveAnimationRunable implements Runnable {

        long startTime;
        long curTime;
        long durationTime;
        float startPercent;
        float toPercent;
        TextView line;
        MoveAnimationEndListener mMoveAnimationEndListener;

        public LineMoveAnimationRunable(TextView line, float startPercent,
                float toPercent, long durationTime,
                MoveAnimationEndListener mMoveAnimationEndListener) {
            this.durationTime = durationTime;
            this.startPercent = startPercent;
            this.toPercent = toPercent;
            this.line = line;
            this.mMoveAnimationEndListener = mMoveAnimationEndListener;
        }

        @Override
        public void run() {

            if (startTime == 0) {
                startTime = System.currentTimeMillis();
            }

            curTime = System.currentTimeMillis();

            float percent = (curTime - startTime) * 1.0f / durationTime;

            LinearInterpolator linearInterpolator = new LinearInterpolator();

            float dpercent = linearInterpolator.getInterpolation(percent);

            float mpercent = startPercent;

//            Log.e("mytag", "<<" + dpercent);

            if (startPercent < toPercent) {// 增加

                mpercent = startPercent + dpercent;

                if (mpercent >= toPercent) {
                    mpercent = toPercent;
                }
            } else {

                mpercent = startPercent - dpercent;

                if (mpercent <= toPercent) {
                    mpercent = toPercent;
                }
            }

            if (!isShowToWindow) {
                return;
            }

            setLineMovePercent(line, mpercent);
            invalidate();

            if (mpercent == toPercent) {
                if (mMoveAnimationEndListener != null) {
                    mMoveAnimationEndListener.AnimationEnd(line, startPercent,
                            toPercent, durationTime);
                }
                return;
            } else {
                line.postDelayed(this, 50L);
            }
        }
    }

    interface MoveAnimationEndListener {
        public void AnimationEnd(TextView line, float startPercent,
                float toPercent, long durationTime);
    }

    public void startMoveAnimation() {
        // 0 2 0.5 -1
        // 1 3 1-0.5
        MoveAnimationEndListener listener = new MoveAnimationEndListener() {
            @Override
            public void AnimationEnd(TextView line, float startPercent,
                    float toPercent, long durationTime) {
                LineMoveAnimationRunable moveRunnable = new LineMoveAnimationRunable(
                        line, toPercent, startPercent, durationTime, this);
                line.postDelayed(moveRunnable, 200L);
            }
        };

        for (int i = 0; i < lines.length; i++) {
            if (i % 2 == 0) {
                LineMoveAnimationRunable moveRunnable = new LineMoveAnimationRunable(
                        lines[i], 0.5f, 1.0f, 1000L, listener);
                lines[i].post(moveRunnable);
            } else {
                LineMoveAnimationRunable moveRunnable = new LineMoveAnimationRunable(
                        lines[i], 1f, 0.5f, 1000L, listener);
                lines[i].post(moveRunnable);
            }
        }
    }
}

遗留问题:如上 每个line是单独的线程  可优化为四个line setMargin之后统一invalidate 

自定义控件总结 

几个相关函数:

1.构造函数:   初始化view ,获取自定义属性

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RefreshView);
        final int type = a.getInteger(R.styleable.RefreshView_type, STYLE_SUN);

2.onMeasure函数 中获取measure后的宽高

int height = getMeasuredHeight();
int width = getMeasuredWidth();

调用子view的 measure函数,

MeasureSpec.EXACTLY 父类指定大小 
AT_MOST 子类尽可能大
UNSPECIFIED 未指定大小
widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingRight() - getPaddingLeft(), MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY);
        mTarget.measure(widthMeasureSpec, heightMeasureSpec);
        mRefreshView.measure(widthMeasureSpec, heightMeasureSpec);

3.onLayout函数布局 调用 子view的layout 函数布局

 protected void onLayout(boolean changed, int l, int t, int r, int b) {
      ... ...
        int height = getMeasuredHeight();
        int width = getMeasuredWidth();
        int left = getLeft();
        int top = getTop();
        int right = getRight();
        int bottom = getBottom();

        int lpad = getPaddingLeft();
        int rpad = getPaddingRight();
        int tpad = getPaddingTop();
        int bpad = getPaddingBottom();

        mTarget.layout(left + lpad, top + tpad+ mCurrentOffsetTop, right-rpad, bottom-bpad + mCurrentOffsetTop);
        mRefreshView.layout(left, top, left + width - right, top + height - bottom);
    }

4.onInterceptTouchEvent 拦截touch事件  主要做状态的改变检测  

 onTouchEvent 主要touch逻辑操作 计算滑动距离等 改变子类view 需调用 this.validate or  this. postvalidate

  改变自身需要父类控件重新布局需要调用 requireLayout

5.动画相关 如 拖拽完成后的动画复位 Animation类 或者 Runnable类

     mAnimateToStartPosition.reset();
        mAnimateToStartPosition.setDuration(animationDuration);
        mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator);
        mAnimateToStartPosition.setAnimationListener(mToStartListener);
        mRefreshView.clearAnimation();
        mRefreshView.startAnimation(mAnimateToStartPosition);

  

private final Animation mAnimateToStartPosition = new Animation() {
        @Override
        public void applyTransformation(float interpolatedTime, Transformation t) {
            moveToStart(interpolatedTime);
        }
    };

  interpolateTime 时间所对应的差值点

 

posted on 2015-03-16 18:34  wjw334  阅读(302)  评论(0编辑  收藏  举报

导航