IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Android动画专题之:属性动画中的插值器:AccelerateDecelerateInterpolator 加速插值器 -> 正文阅读

[游戏开发]Android动画专题之:属性动画中的插值器:AccelerateDecelerateInterpolator 加速插值器

1: AccelerateInterpolator加速插值器

1.1 :Android系统资源 ID : @android:anim/accelerate_interpolator 加速插值器

表示:起始速度是零,速度越来越快,加速运动

1.2 :加速插值器源码:

public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    public AccelerateDecelerateInterpolator() {
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
    }

    
    // 主要通过此函数,来得到 x 和 y 轴的 速率关系曲线
    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
    }
}

2: 效果图

?2.1 :效果图实现分解

重点是两部分功能实现:绘制轨迹曲线和实现加速动画

2.1.1 绘制轨迹曲线

直接在 自定义View的 onDraw(Canvas canvas) 函数中,通过 Path这个工具类,结合轨迹点坐标集合,可以绘制出轨迹曲线。

 TimeInterpolatorView.java 

 // 绘制轨迹 Path工具
 private Path mDataPath = new Path();

 // 轨迹坐标集合
 private final List<PointF> mLineDataList = new ArrayList<>();

 // 点坐标封装
 PointF

 @Override
 protected void onDraw(Canvas canvas) {

      // 绘制轨迹

      mDataPath.reset();
      float width = mEachItemWidth * GRID_INTERVAL_COUNT;
      
      // 构建 路径,并选出最高和最低的point
      for (int i = 0; i < mLineDataList.size(); i++) {
            PointF curPoint = mLineDataList.get(i);
            if (i == 0) {
                mDataPath.moveTo(curPoint.x * width, -curPoint.y * width);
            } else {
                mDataPath.lineTo(curPoint.x * width, -curPoint.y * width);
            }
      }

 }

2.1.2 :生成轨迹曲线的动画

动画的生成我们分解为:X轴动画,Y轴动画,动画集

animatorSet.play(mAnimator).with(mXAnimator);

X轴动画:线性动画

初始化:mXAnimator = ValueAnimator.ofFloat(0f, 1f);

添加线性插值器 :
mXAnimator.setInterpolator(new LinearInterpolator());
mXAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float x = animation.getAnimatedFraction();
        float y = mInterpolator.getInterpolation(x);

        curPoint.x = x;
        curPoint.y = y;

        Log.i("zincTest", "onAnimationUpdate: [" + x + "," + y + "】");

        mTimeInterpolatorView.setCurPoint(curPoint);
    }
});

Y轴动画

mAnimator = ObjectAnimator.ofFloat(animView, "y", start, end);
float mInterpolator = AccelerateDecelerateInterpolator.getInterpolation();
mAnimator.setInterpolator(mInterpolator);

动画集

animatorSet = new AnimatorSet();
animatorSet.play(mAnimator).with(mXAnimator);
animatorSet.setDuration(duration);
animatorSet.start();

3:源码示例

/**
 * @author yuhongwen
 * @date 创建时间:2022/04/06
 * @description 插值器的坐标显示
 */
public class TimeInterpolatorView extends View {
 
    // 外边距
    private static final int PADDING = dpToPx(5f);
    // 字体大小
    private static final int TEXT_SIZE = dpToPx(8f);
    // 点的半径
    private static final int CUR_POINT_RADIUS = dpToPx(4.5f);
 
    // X、Y 轴色
    private static final int COORDINATION_LINE_COLOR = Color.BLACK;
    // 网格线色
    private static final int GRID_LINE_COLOR = Color.LTGRAY;
    // 数据线色
    private static final int DATA_LINE_COLOR = Color.parseColor("#DB001B");
    // 当前点的色
    private static final int CUR_POINT_COLOR = Color.parseColor("#DC143C");
    // 默认的最低点
    private static final PointF DEFAULT_MIN_POINT = new PointF(0, 0);
    // 默认的最高点
    private static final PointF DEFAULT_MAX_POINT = new PointF(0, 1);
    // 10个间隔
    private static final int GRID_INTERVAL_COUNT = 10;
    // 每个间隔的跨幅
    private static final float GRID_INTERVAL_LENGTH = 0.1f;
 
    // 速率的数据
    private final List<PointF> mLineDataList = new ArrayList<>();
 
    // 坐标的画笔
    private Paint mLinePaint;
    // 速率的轨迹
    private Path mDataPath = new Path();
    // 字体画笔
    private Paint mTextPaint;
    // 点的笔
    private Paint mPointPaint;
 
    // 数据的最低点
    private PointF mMinPoint = DEFAULT_MIN_POINT;
    // 数据的最高点
    private PointF mMaxPoint = DEFAULT_MAX_POINT;
 
    // 视图的宽
    private float mViewWidth;
    // 视图的高
    private float mViewHeight;
    // 坐标的宽
    private float mWidth;
 
    // 坐标中每个下标 的宽度
    private float mEachItemWidth;
 
    private int mPositiveCount;
    private int mNegativeCount;
 
    // 当前的点
    private PointF mCurPoint;
 
    public TimeInterpolatorView(Context context) {
        this(context, null, 0);
    }
 
    public TimeInterpolatorView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
 
    public TimeInterpolatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }
 
    private void init(Context context) {
 
        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStyle(Paint.Style.STROKE);
 
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(TEXT_SIZE);
 
        mPointPaint = new Paint();
        mPointPaint.setAntiAlias(true);
        mPointPaint.setStyle(Paint.Style.FILL);
 
    }
 
    /**
     * 设置当前移动的点
     *
     * @param curPoint
     */
    public void setCurPoint(PointF curPoint) {
        this.mCurPoint = curPoint;
        invalidate();
    }
 
    public void setLineData(List<PointF> lineDataList) {
        mLineDataList.clear();
        mLineDataList.addAll(lineDataList);
 
        mMinPoint = DEFAULT_MIN_POINT;
        mMaxPoint = DEFAULT_MAX_POINT;
        // 构建 路径,并选出最高和最低的point
        for (int i = 0; i < mLineDataList.size(); i++) {
            PointF curPoint = mLineDataList.get(i);
 
            // 选最低点
            if (curPoint.y < mMinPoint.y) {
                mMinPoint = curPoint;
            }
 
            // 选最高点
            if (curPoint.y > mMaxPoint.y) {
                mMaxPoint = curPoint;
            }
 
        }
 
        calculateEachItemWidth();
 
        invalidate();
 
    }
 
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mViewWidth = w;
        mViewHeight = h;
 
        // 需要减去 padding 的宽度 和 字体的大小
        mWidth = Math.min(w, h) - 2 * PADDING - TEXT_SIZE;
 
        calculateEachItemWidth();
    }
 
    /**
     * 计算每个格子的大小
     */
    private void calculateEachItemWidth() {
 
        // 获取 y正半轴 的分割个数
        mPositiveCount = (int) Math.abs(Math.ceil(mMaxPoint.y / GRID_INTERVAL_LENGTH));
        // 获取 y负半轴 的分割个数
        mNegativeCount = (int) Math.abs(Math.floor(mMinPoint.y / GRID_INTERVAL_LENGTH));
 
        // 计算需要分割的数量,最少十个
        int intervalCount = mPositiveCount + mNegativeCount;
        intervalCount = Math.max(intervalCount, GRID_INTERVAL_COUNT);
 
        mEachItemWidth = mWidth / intervalCount;
 
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
 
        // 构建轨迹
        buildDataPath();
 
        canvas.save();
 
        // 移至原点
        moveToTheOrigin(canvas);
 
        // 画坐标
        drawCoordination(canvas);
        // 画网格
        drawGrid(canvas);
 
        // 画数据线
        drawDataLine(canvas);
 
        // 画下标
        drawText(canvas);
 
        // 画当前的点
        drawPoint(canvas);
 
        canvas.restore();
 
    }
 
    /**
     * 画点
     *
     * @param canvas
     */
    private void drawPoint(Canvas canvas) {
        if (mCurPoint == null) {
            return;
        }
 
        mPointPaint.setColor(CUR_POINT_COLOR);
        canvas.drawCircle(mCurPoint.x * mEachItemWidth * GRID_INTERVAL_COUNT,
                -mCurPoint.y * mEachItemWidth * GRID_INTERVAL_COUNT,
                CUR_POINT_RADIUS,
                mPointPaint);
    }
 
    /**
     * 画下标
     *
     * @param canvas
     */
    private void drawText(Canvas canvas) {
 
        canvas.drawText("0", -PADDING,
                0,
                mTextPaint);
 
        mTextPaint.setTextAlign(Paint.Align.RIGHT);
        for (int i = 1; i <= mPositiveCount; ++i) {
            if (i <= 10) {
                mTextPaint.setColor(COORDINATION_LINE_COLOR);
            } else {
                mTextPaint.setColor(DATA_LINE_COLOR);
            }
            canvas.drawText(getNumString(i * 0.1f), -PADDING / 2,
                    -i * mEachItemWidth,
                    mTextPaint);
        }
 
        mTextPaint.setColor(DATA_LINE_COLOR);
        for (int i = 1; i <= mNegativeCount; ++i) {
            canvas.drawText(getNumString(i * -0.1f), -PADDING / 2,
                    i * mEachItemWidth,
                    mTextPaint);
        }
 
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setColor(COORDINATION_LINE_COLOR);
        for (int i = 1; i <= GRID_INTERVAL_COUNT; ++i) {
            canvas.drawText(getNumString(i * 0.1f), i * mEachItemWidth,
                    PADDING / 2 + TEXT_SIZE,
                    mTextPaint);
        }
    }
 
    private String getNumString(float num) {
        return String.format("%.1f", num);
    }
 
    /**
     * 画网格线
     *
     * @param canvas
     */
    private void drawGrid(Canvas canvas) {
        mLinePaint.setStrokeWidth(dpToPx(0.5f));
        mLinePaint.setColor(GRID_LINE_COLOR);
 
        // 画y正轴横线
        for (int i = 1; i <= mPositiveCount; ++i) {
            canvas.drawLine(0,
                    -i * mEachItemWidth,
                    GRID_INTERVAL_COUNT * mEachItemWidth,
                    -i * mEachItemWidth,
                    mLinePaint);
        }
 
        // 画y负轴横线
        for (int i = 1; i <= mNegativeCount; ++i) {
            canvas.drawLine(0,
                    i * mEachItemWidth,
                    GRID_INTERVAL_COUNT * mEachItemWidth,
                    i * mEachItemWidth,
                    mLinePaint);
        }
 
        // 画x正轴竖线
        for (int i = 1; i <= GRID_INTERVAL_COUNT; ++i) {
            canvas.drawLine(i * mEachItemWidth,
                    -mPositiveCount * mEachItemWidth,
                    i * mEachItemWidth,
                    mNegativeCount * mEachItemWidth,
                    mLinePaint);
        }
 
 
    }
 
    /**
     * 画数据线
     *
     * @param canvas
     */
    private void drawDataLine(Canvas canvas) {
 
        mLinePaint.setStrokeWidth(dpToPx(1f));
        mLinePaint.setColor(DATA_LINE_COLOR);
 
        canvas.drawPath(mDataPath, mLinePaint);
 
    }
 
    /**
     * 画 x、y 轴
     *
     * @param canvas
     */
    private void drawCoordination(Canvas canvas) {
 
        mLinePaint.setStrokeWidth(dpToPx(1f));
        mLinePaint.setColor(COORDINATION_LINE_COLOR);
 
        // 画 y 轴
        canvas.drawLine(0,
                -mPositiveCount * mEachItemWidth,
                0,
                mNegativeCount * mEachItemWidth,
                mLinePaint);
 
        // 画 x 轴
        canvas.drawLine(0,
                0,
                GRID_INTERVAL_COUNT * mEachItemWidth,
                0,
                mLinePaint);
 
    }
 
    /**
     * 构建数据路径
     */
    private void buildDataPath() {
        mDataPath.reset();
        float width = mEachItemWidth * GRID_INTERVAL_COUNT;
        // 构建 路径,并选出最高和最低的point
        for (int i = 0; i < mLineDataList.size(); i++) {
            PointF curPoint = mLineDataList.get(i);
            if (i == 0) {
                mDataPath.moveTo(curPoint.x * width, -curPoint.y * width);
            } else {
                mDataPath.lineTo(curPoint.x * width, -curPoint.y * width);
            }
        }
    }
 
    /**
     * 将画布移至 原点
     */
    private void moveToTheOrigin(Canvas canvas) {
 
        float verHeight = mEachItemWidth * mPositiveCount;
 
        // 计算 横向移动距离
        float horPadding = mViewWidth - mEachItemWidth * GRID_INTERVAL_COUNT - 2 * PADDING;
        float verPadding = mViewHeight - mEachItemWidth * (mPositiveCount + mNegativeCount) - 2 * PADDING - TEXT_SIZE / 2;
 
        canvas.translate(horPadding / 2 + PADDING, verPadding / 2 + verHeight + PADDING);
 
    }
 
    /**
     * 转换 dp 至 px
     *
     * @param dpValue dp值
     * @return px值
     */
    protected static int dpToPx(float dpValue) {
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        return (int) (dpValue * metrics.density + 0.5f);
    }
 
    /**
     * 获取视图的宽
     *
     * @return 视图宽 - 左右的内边距
     */
    private float getViewEnableWidth() {
        return mViewWidth - PADDING * 2;
    }
 
    /**
     * 获取视图的高
     *
     * @return 视图高 - 上下的内边距
     */
    private float getViewEnableHeight() {
        return mViewHeight - PADDING * 2;
    }
public class TimeInterpolatorActivity extends AppCompatActivity implements TimeInterpolatorAdapter.ClickListener {
    // 取1000帧
    private static final int FRAME = 1000;

    private TimeInterpolatorView mTimeInterpolatorView;

    private final List<PointF> dataList = new ArrayList<>();
    private final List<TimeInterpolatorBean> interpolatorList = new ArrayList<>();

    private TimeInterpolator mInterpolator;

    private TextView tvRun;
    private View animView;
    private RecyclerView recycleView;
    private TextView tvDuration;
    private TextView tvStateInfo;

    private TimeInterpolatorAdapter mAdapter;

    private ObjectAnimator mAnimator;
    private ValueAnimator mXAnimator;

    private AnimatorSet animatorSet;

    private boolean isRunning;

    private PointF curPoint;

    private View getStartView() {
        return (View) findViewById(R.id.start_view);
    }

    private View getAnimView() {
        return (View) findViewById(R.id.anim_view);
    }

    private View getEndView() {
        return (View) findViewById(R.id.end_view);
    }

    private EditText getEtDuration() {
        return (EditText) findViewById(R.id.et_duration);
    }

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

        mTimeInterpolatorView = findViewById(R.id.time_interpolator_view);
        tvRun = findViewById(R.id.tv_run);
        animView = findViewById(R.id.anim_view);
        recycleView = findViewById(R.id.recycle_view);
        tvStateInfo = findViewById(R.id.tv_state_info);

        isRunning = false;

        curPoint = new PointF(0, 0);

        buildInterpolatorList();
        createData();

        final float start = dpToPx(this, 35);
        final float end = getScreenHeight(this) - dpToPx(this, 35 + 50) - getStatusHeight(this);

        mAnimator = ObjectAnimator.ofFloat(animView, "y", start, end);
        mXAnimator = ValueAnimator.ofFloat(0f, 1f);
        animatorSet = new AnimatorSet();
        animatorSet.play(mAnimator).with(mXAnimator);

        mAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                updateState(false);
            }
        });

        mXAnimator.setInterpolator(new LinearInterpolator());
        mXAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float x = animation.getAnimatedFraction();
                float y = mInterpolator.getInterpolation(x);

                curPoint.x = x;
                curPoint.y = y;

                Log.i("zincTest", "onAnimationUpdate: [" + x + "," + y + "】");

                mTimeInterpolatorView.setCurPoint(curPoint);
            }
        });

        mTimeInterpolatorView.setCurPoint(curPoint);
        mTimeInterpolatorView.setLineData(dataList);
        
        // 点击开始----动画运作
        tvRun.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isRunning) {
                    Toast.makeText(TimeInterpolatorActivity.this, "动画正在进行中,请稍等", Toast.LENGTH_SHORT).show();
                    return;
                }

                updateState(true);
                String durationString = getEtDuration().getText().toString();
                long duration = TextUtils.isEmpty(durationString) ? 2000L : Long.parseLong(durationString);

                animatorSet.setDuration(duration);
                mAnimator.setInterpolator(mInterpolator);
                animatorSet.start();
            }
        });

        mAdapter = new TimeInterpolatorAdapter(this, interpolatorList);
        mAdapter.setListener(this);
        recycleView.setLayoutManager(new LinearLayoutManager(this));
        recycleView.setAdapter(mAdapter);

    }

    private void createData() {
        dataList.clear();
        for (float x = 0; x <= 1; x += 1.0f / FRAME) {
            float y = mInterpolator.getInterpolation(x);

            PointF pointF = new PointF(x, y);
            dataList.add(pointF);
        }
    }

    /**
     * 初始化插值器,需要的可以在这里添加自己的插值器
     */
    private void buildInterpolatorList() {
        interpolatorList.clear();
        interpolatorList.add(new TimeInterpolatorBean(true, "SpringInterpolator", new SpringInterpolator()));
        interpolatorList.add(new TimeInterpolatorBean(false, "AccelerateDecelerateInterpolator", new AccelerateDecelerateInterpolator()));
        interpolatorList.add(new TimeInterpolatorBean(false, "AccelerateInterpolator", new AccelerateInterpolator()));
        interpolatorList.add(new TimeInterpolatorBean(false, "AnticipateInterpolator", new AnticipateInterpolator()));
        interpolatorList.add(new TimeInterpolatorBean(false, "AnticipateOvershootInterpolator", new AnticipateOvershootInterpolator()));
        interpolatorList.add(new TimeInterpolatorBean(false, "BounceInterpolator", new BounceInterpolator()));
        interpolatorList.add(new TimeInterpolatorBean(false, "CycleInterpolator(1)", new CycleInterpolator(1)));
        interpolatorList.add(new TimeInterpolatorBean(false, "DecelerateInterpolator", new DecelerateInterpolator()));
        interpolatorList.add(new TimeInterpolatorBean(false, "LinearInterpolator", new LinearInterpolator()));
        interpolatorList.add(new TimeInterpolatorBean(false, "OvershootInterpolator", new OvershootInterpolator()));

        mInterpolator = interpolatorList.get(0).getTimeInterpolator();

    }

    public static float getScreenHeight(Context context) {
        DisplayMetrics metrics = new DisplayMetrics();
        ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metrics);
        return metrics.heightPixels;
    }

    public static float dpToPx(Context context, float dipValue) {
        float density = context.getResources().getDisplayMetrics().density;
        return dipValue * density + 0.5f;
    }

    public static float getStatusHeight(Context context) {
        int result = 0;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = context.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

    @Override
    public void onTimeInterpolatorClick(int position) {
        for (TimeInterpolatorBean bean : interpolatorList) {
            bean.setSelect(false);
        }

        interpolatorList.get(position).setSelect(true);

        mInterpolator = interpolatorList.get(position).getTimeInterpolator();

        mAdapter.notifyDataSetChanged();

        createData();
        mTimeInterpolatorView.setLineData(dataList);

        curPoint.x = 0;
        curPoint.y = 0;
        mTimeInterpolatorView.setCurPoint(curPoint);

    }

    private void updateState(boolean isRunning) {
        this.isRunning = isRunning;
        tvStateInfo.setText(isRunning ? "running" : "ready");
        tvStateInfo.setTextColor(isRunning ?
                Color.parseColor("#32CD32") :
                Color.parseColor("#1E90FF"));
    }

}

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-15 00:35:02  更:2022-04-15 00:37:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:36:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码