一个简单的轨迹动画

动态轨迹绘制

一、前言

        下半年公司业务忙成doge,堆了成吨想些的博客都没时间去写,趁着年假写个博客乐呵一下。祝新年大吉

二、需求描述

        年中那会儿基友的公司给他了一张只有一条曲线的图,想让他按照曲线的轨迹动态展示整个曲线,然而基友忙于把妹,一个馒头的代价收买了我,于是帮他写了代码。先看下实现效果                     

         上原始图(原始图的轨迹曲线是白色的,其他部分是透明的,这里为了便于观察,我将背景调为黑色)

          

三、分析实现

        讲道理,刚拿到这个图片的时候我也有点懵逼,一个毫无规律的曲线很显然不能通过简单的方程式去描述点的具体位置,甚至想说设计直接给个动画岂不美滋滋...... 

        吐槽归吐槽,需求还是要实现的,而且这个需求是有不少常见的方法可以进行实现的,比如上下两层图片保持一直,上层不断从左到右将原图纵向像素清除,然后将设计给的图片也按照从左到友的顺去绘制上去。不过这种方法的可扩展性太差,底层的图片发生改变(滑动、变色)上层也需要进行配合。

        这里使用方法是:将图片中有色像素在图片上的位置按照比例映射到要绘制的View中。

        比如,原图大小是200*100,要去做展示的View的尺寸大小是450*300,某点在原图中位于x100,y20的位置,那么它在view中的位置应该为225,60。也就是说将该点在途中的位置比上原图对应尺寸然后在乘以View的尺寸即可。

        轨迹的获取可以通过获取全部的像素点颜色,因为透明的必然不是轨迹,所以判断Alpha值即可,又因为轨迹是一条线,当我们使用Path连点的时候为了尽量减少不必要点的连接,我这里通过平均值的方式将曲线的宽度降为1。

        由于点的位置是按照尺寸比例计算的,所以无论要展示的图片尺寸有多奇葩,都可以完美适配上去

        

四、实现代码

public class TestView extends View implements ViewTreeObserver.OnGlobalLayoutListener{
    private Paint paint = new Paint();
    private Bitmap bitmap;
    private int[]local;
    boolean enter = false;
    int count;
    ViewTreeObserver observer;
    Canvas canvas;
    float lastX = 0;
    float lastY = 0;

    public TestView(Context context) {
        super(context);
    }

    public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        observer = this.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(this);
        getImageFromAssetsFile("target.png");//读取图片
    }

    public TestView(Context context, AttributeSet attrs) {
        super(context, attrs);
        observer = this.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(this);
        getImageFromAssetsFile("target.png");
    }

    private void getImageFromAssetsFile(String fileName) {
        AssetManager am = getResources().getAssets();
        try {
            InputStream is = am.open(fileName);
            bitmap = BitmapFactory.decodeStream(is);
            is.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }


    private void filterColor(){
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        int[]pixels = new int[w*h];
        local = new int[w];//以原图宽为单位,保存点在原图Y轴上的位置
        bitmap.getPixels(pixels,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
        for(int i = 0; i < w; i++){
            int lenght = 0;
            int total = 0;
            for(int j = 0;j<h;j++){
                    if (bitmap.getPixel(i, j) !=0) {
                        lenght++;
                        total+=j;
                    }
            }

            System.out.println(lenght+"");
            if(lenght<15||((float)i/(float) w)<0.85)
                local[i] = total/lenght;
            else
                    break;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(enter) {
            paint.setAntiAlias(true);                       //设置画笔为无锯齿
            paint.setColor(Color.WHITE);                    //设置画笔颜色
            canvas.drawColor(Color.TRANSPARENT);                  //透明背景
            paint.setStrokeWidth((float) 6.0);              //线宽
            paint.setStyle(Paint.Style.STROKE);

            Path path = new Path();
            path.moveTo(0, (((float) getMeasuredHeight()-25) / (float)bitmap.getHeight()) * (float) local[0]+7);
            if (count <= bitmap.getWidth()) {
                if(count<bitmap.getWidth())
                    count++;
                for (int i = 1; i < count - 1; i++) {
                    if(local[i]>0) {
                        path.lineTo((getMeasuredWidth() / (float) bitmap.getWidth()) * (float) i, (((float) getMeasuredHeight()-25) / (float) bitmap.getHeight()) * (float) local[i]+7);
                        lastX = (getMeasuredWidth() / (float) bitmap.getWidth()) * (float) i;
                        lastY = (((float) getMeasuredHeight()-25 )/ (float) bitmap.getHeight()) * (float) local[i];
                    }
                }
            }
            if(count<bitmap.getWidth())
                postInvalidate();
            else {
                count = 0;
                postInvalidate();
            }
                canvas.drawPath(path, paint);
                paint.setStyle(Paint.Style.FILL);
                canvas.drawCircle(lastX, lastY+7, 7, paint);
//            }
        }else{
            this.canvas = canvas;
        }
    }

    @Override
    public void onGlobalLayout() {
        filterColor();
        enter = true;
        postInvalidate();
    }
}
当然了,这个代码也只是给基友交差用的,还有不少地方能做优化,比如:将图片读取放在子线程、对图片大小进行判断避免OOM、大图加载图片压缩......

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值