SeniorUI1003_PathMeasure实现两种加载框

本文详细介绍SeniorUI中的两种动画实现方式,一是通过PathMeasure和Animation结合,实现笑脸与外部进度框的动态显示;二是利用PathMeasure获取路径长度,结合ValueAnimator实现动态路径绘制,展示动画效果的原理与核心代码。

SeniorUI_目录
SeniorUI1001_PathMeasure语法

一、效果图

在这里插入图片描述

在这里插入图片描述

二 PrinPrinciple

效果一:
  • 分成笑脸和外部进度框两个部分
  • 笑脸:画圆和Path的贝塞尔线,利用Animation不断重复修改高度
  • 外部进度框:先画一个完全的矩形(白色),通过点击事件利用Animation获取一个进度值,通过进度值和PathMesure获取一部分Path,绘制
效果二:
  • 定义一个Circle的Path

  • 利用一个Animation获取一个进度值

  • 通过PathMesure在path 分别取start和stop,
    stop值:stop = mLength * mAnimatorValue,

    start值:stop值减去一个值(这个值从0 到Path一半,再到0)
    start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * mLength))

三 Core Code

效果一:

public class LoadingView extends View {
    /**
     * 左眼距离左边的距离(控件宽度*EYE_PERCENT_W),
     * 右眼距离右边的距离(控件宽度*EYE_PERCENT_W)
     */
    private static final float EYE_PERCENT_W = 0.35F;
    /**
     *眼睛距离top的距离(控件的高度*EYE_PERCENT_H)
     */
    private static final float EYE_PERCENT_H = 0.38F;
    /**
     * 嘴巴左边跟右边距离top的距离(控件的高度*MOUCH_PERCENT_H)
     */
    private static final float MOUCH_PERCENT_H = 0.55F;
    /**
     * 嘴巴中间距离top的距离(控件的高度*MOUCH_PERCENT_H2)
     */
    private static final float MOUCH_PERCENT_H2 = 0.7F;
    /**
     * 嘴巴左边跟右边距离边缘的位置(控件宽度*MOUCH_PERCENT_W)
     */
    private static final float MOUCH_PERCENT_W = 0.23F;
    /**
     * 眼睛跟嘴巴摆动的区间范围
     */
    private static final float DURATION_AREA = 0.15F;
    /**
     * 眼睛跟嘴巴摆动的动画
     */
    Animation mAmin =new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            float offset=interpolatedTime*DURATION_AREA;
            mMouchH=MOUCH_PERCENT_H+offset;
            mMouchH2=MOUCH_PERCENT_H2+offset;
            mEyesH=EYE_PERCENT_H+offset;
            postInvalidate();
        }
    };
    private Paint reachedPaint;
    private Paint unreachedPaint;
    private Path reachedPath;
    private Path unreachedPath;
    private Path mouthPath=new Path();

    private float mProgress=0.1f;
    private float lineWidth=dp2px(2);

    private float mRadius;

    private float mMouchH=MOUCH_PERCENT_H;

    private float mMouchH2=MOUCH_PERCENT_H2;

    private float mEyesH=EYE_PERCENT_H;

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

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

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

    private void startAni() {
        mAmin.setDuration(500);
        mAmin.setRepeatCount(Animation.INFINITE);
        mAmin.setRepeatMode(Animation.REVERSE);
        startAnimation(mAmin);
    }

    private void initView() {
        reachedPaint=new Paint(Paint.ANTI_ALIAS_FLAG| Paint.DITHER_FLAG);
        reachedPaint.setStyle(Paint.Style.STROKE);
        reachedPaint.setStrokeWidth(lineWidth);
        reachedPaint.setColor(Color.WHITE);
        reachedPaint.setStrokeJoin(Paint.Join.ROUND);
        reachedPaint.setStrokeCap(Paint.Cap.ROUND);


        unreachedPaint=new Paint(reachedPaint);
        unreachedPaint.setColor(Color.GRAY);
    }
    private boolean isStart=true;
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if(isStart){
            startAni();
            isStart=false;
        }
        mRadius=getWidth()/7F/2;
        if(unreachedPath==null){
            unreachedPath=new Path();
        }
        unreachedPath.addRoundRect(new RectF(lineWidth,lineWidth,w-lineWidth,h-lineWidth),w/6,w/6, Path.Direction.CCW);
        if(reachedPath==null){
            reachedPath=new Path();
        }
        reachedPath.addRoundRect(new RectF(lineWidth,lineWidth,w-lineWidth,h-lineWidth),w/6,w/6, Path.Direction.CW);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.TRANSPARENT);
        canvas.save();
        //draw face
        drawFace(canvas);
        //drawreached rect
        drawReachedRect(canvas);
        canvas.restore();
    }

    /**
     * draw face
     */
    private void drawFace(Canvas canvas) {
        unreachedPaint.setStyle(Paint.Style.FILL);
        //画左边的眼睛
        canvas.drawCircle(getWidth()*EYE_PERCENT_W,getHeight()*mEyesH-mRadius,mRadius,unreachedPaint);
        //画右边的眼睛
        canvas.drawCircle(getWidth()*(1-EYE_PERCENT_W),getHeight()*mEyesH-mRadius,mRadius,unreachedPaint);
        mouthPath.reset();
        //画嘴巴
        mouthPath.moveTo(getWidth()*MOUCH_PERCENT_W,getHeight()*mMouchH);
        mouthPath.quadTo(getWidth()/2,getHeight()*mMouchH2,getWidth()*(1-MOUCH_PERCENT_W),getHeight()*mMouchH);
        unreachedPaint.setStyle(Paint.Style.STROKE);
        canvas.drawPath(mouthPath,unreachedPaint);
    }

    private void drawReachedRect(Canvas canvas) {
        unreachedPaint.setStyle(Paint.Style.STROKE);
        canvas.drawPath(unreachedPath,unreachedPaint);
        PathMeasure measure=new PathMeasure(reachedPath,false);
        float length = measure.getLength();
        //获取当前path长度
        float currLength=length*mProgress;
        Path path=new Path();
        /**
         * 因为uc的起始位置是在顶部的位置,而我们的path的起始位置是在左下的位置,
         * 所以我们需要加上一个length*1/3f偏移量
         */
        measure.getSegment(length*1/3f,currLength+length*1/3f,path,true);
        canvas.drawPath(path,reachedPaint);
        /**
         * 当mProgress>=2/3f的时候,也就是回到了起点的时候,我们得截取两段path了
         * 一段是1/3的位置到2/3
         * 一段是0到1/3的位置
         */
        if(mProgress>=2/3f){
            Path path2=new Path();
            measure.getSegment(0,length*(mProgress-2/3f),path2,true);
            canvas.drawPath(path2,reachedPaint);
        }

    }

    public float dp2px(float dpValue){
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpValue,getResources().getDisplayMetrics());
    }
    public void setProgress(float progress){
        Log.d("TAG",""+progress);
        if(progress<mProgress){
            return;
        }
        this.mProgress=progress;

        postInvalidate();

    }

    public void loadComplete() {
        mAmin.cancel();
        clearAnimation();
        setVisibility(View.GONE);
    }
}

效果二:

public class LoadingView1 extends View {
    private Path mPath;
    private Paint mPaint;
    private PathMeasure mPathMeasure;
    private float mAnimatorValue;
    private Path mDst;
    private float mLength;

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

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

    }
    public LoadingView1(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPathMeasure = new PathMeasure();
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPath = new Path();
        mPath.addCircle(400, 400, 100, Path.Direction.CW);
        mPathMeasure.setPath(mPath, true);
        mLength = mPathMeasure.getLength();
        mDst = new Path();

        final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mAnimatorValue = (float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.setDuration(2000);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.start();
    }



    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mDst.reset();
        // 硬件加速的BUG
        mDst.lineTo(0,0);
        /*float stop = mLength * mAnimatorValue;
        mPathMeasure.getSegment(0, stop, mDst, true);*/

        float stop = mLength * mAnimatorValue;
        float start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * mLength));
        mPathMeasure.getSegment(start, stop, mDst, true);

        canvas.drawPath(mDst, mPaint);
    }
}

四 Demo

LoadingView
LoadingView1

【EI复现】基于深度强化学习的微能源网能量管理与优化策略研究(Python代码实现)内容概要:本文围绕“基于深度强化学习的微能源网能量管理与优化策略”展开研究,重点利用深度Q网络(DQN)等深度强化学习算法对微能源网中的能量调度进行建模与优化,旨在应对可再生能源出力波动、负荷变化及运行成本等问题。文中结合Python代码实现,构建了包含光伏、储能、负荷等元素的微能源网模型,通过强化学习智能体动态决策能量分配策略,实现经济性、稳定性和能效的多重优化目标,并可能与其他优化算法进行对比分析以验证有效性。研究属于电力系统与人工智能交叉领域,具有较强的工程应用背景和学术参考价值。; 适合人群:具备一定Python编程基础和机器学习基础知识,从事电力系统、能源互联网、智能优化等相关方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①学习如何将深度强化学习应用于微能源网的能量管理;②掌握DQN等算法在实际能源系统调度中的建模与实现方法;③为相关课题研究或项目开发提供代码参考和技术思路。; 阅读建议:建议读者结合提供的Python代码进行实践操作,理解环境建模、状态空间、动作空间及奖励函数的设计逻辑,同时可扩展学习其他强化学习算法在能源系统中的应用。
皮肤烧伤识别作为医学与智能技术交叉的前沿课题,近年来在深度学习方法推动下取得了显著进展。该技术体系借助卷积神经网络等先进模型,实现了对烧伤区域特征的高效提取与分类判别,为临床诊疗决策提供了重要参考依据。本研究项目系统整合了算法设计、数据处理及模型部署等关键环节,形成了一套完整的可操作性方案。 在技术实现层面,首先需要构建具有代表性的烧伤图像数据库,涵盖不同损伤程度及愈合阶段的临床样本。通过对原始图像进行标准化校正、对比度增强等预处理操作,有效提升后续特征学习的稳定性。网络架构设计需充分考虑皮肤病变的区域特性,通过多层卷积与池化操作的组合,逐步抽象出具有判别力的烧伤特征表示。 模型优化过程中采用自适应学习率调整策略,结合交叉熵损失函数与梯度下降算法,确保参数收敛的稳定性。为防止过拟合现象,引入数据扩增技术与正则化约束,增强模型的泛化能力。性能验证阶段采用精确率、召回率等多维度指标,在独立测试集上全面评估模型对不同烧伤类型的识别效能。 经过充分验证的识别系统可集成至医疗诊断平台,通过规范化接口实现与现有医疗设备的无缝对接。实际部署前需进行多中心临床验证,确保系统在不同操作环境下的稳定表现。该技术方案的实施将显著缩短烧伤评估时间,为临床医师提供客观量化的辅助诊断依据,进而优化治疗方案制定流程。 本项目的突出特点在于将理论研究与工程实践有机结合,既包含前沿的深度学习算法探索,又提供了完整的产业化实施路径。通过模块化的设计思路,使得医疗专业人员能够快速掌握核心技术方法,推动智能诊断技术在烧伤外科领域的实际应用。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值