Android 光芒四射的View

先看效果图:



下面一步步介绍如何实现:

首先,这是一个动态的效果,很消耗性能,所以我们用SurfaceView,而且我们要用到线程:

public class ShineView extends SurfaceView implements SurfaceHolder.Callback,Runnable{


还有SurfaceView相关的SurfaceHolder,及线程相关变量:

    private SurfaceHolder mHolder;
    private Thread thread;
    private boolean isRun;

    public ShineView(Context context) {
        super(context);
        mHolder=getHolder();
        mHolder.addCallback(this);
    }

    public ShineView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHolder=getHolder();
        mHolder.addCallback(this);
    }


我们要可以设置光源的位置,光源的背景色,光源的颜色,及光源的数量:

private float x,y;//光源坐标
private boolean hasInitXY;//光源是否初始化
private int lightSize=1;//光线数量
private int bgColor=Color.WHITE;
private int lightColor=Color.GREEN;

    /**
     * 设置背景颜色
     * @param bgColor 背景颜色
     */
    public void setBgColor(int bgColor) {
        this.bgColor = bgColor;
    }

    /**
     * 设置光线颜色
     * @param lightColor 光线颜色
     */
    public void setLightColor(int lightColor) {
        this.lightColor = lightColor;
    }

    /**
     * 设置光源在控件内的坐标位置X,Y。不设置,默认光源在控件的中心。
     * @param x 控件内x坐标
     * @param y 控件内y坐标
     */
    public void setLightXY(float x,float y){
        this.x=x;
        this.y=y;
        hasInitXY=true;
    }

我们默认如果不设置光源的坐标,光源就在中心,所以上面加上hasInitXY判断。


然后我们测量光源在控件内的位置,我们不允许光源超出控件外。我们还要加上一个偏移位置,计算光源不在中心时的情况:

private float detalX,detalY;
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if(x>getMeasuredWidth())x=getMeasuredWidth();
        if(x<0)x=0;
        if(y>getMeasuredHeight())y=getMeasuredHeight();
        if(y<0)y=0;

        float centerX=getMeasuredWidth()/2;
        float centerY=getMeasuredHeight()/2;

        if(hasInitXY){
            detalX=x-centerX;
            detalY=y-centerY;
        }else{
            detalX=0;
            detalY=0;
        }

    }

我们发出的光线其实是一个弧形,因此有光线射出时的角度startRange,弧度的角度大小sweepAngle:

private float startRange;//
private float sweepAngle=45;//角度

我们要在360度里平均分布光线数量,因此360/lightSize是平均分布的空间,也是从一条光线到另一条光线的角度。

我们为了后面用for循环绘制光线的方便,定义一个增量的角度:

private float addRange;//增量

而光线只能占分布空间的一半,不能占全部,否则就填满阴影看不到背景了。

我们也定义至少一条光线,因此设置光线数量的方法如下:

    /**
     * 设置光线数量
     * @param lightSize 光线数量
     */
    public void setLightSize(int lightSize){
        if(lightSize<1){
            lightSize=1;//最少一条光线
            sweepAngle=45;//弧度最大45
        }

        this.lightSize=lightSize;
        this.addRange=360/lightSize;
        this.sweepAngle=this.addRange/2;
    }

然后我们在SurfaceCreated方法里开线程绘图了:

    Paint p= null;
    RectF oval = null;
    @Override
    public void surfaceCreated(SurfaceHolder holder){
        p=new Paint();
        p.setColor(lightColor);
        oval= new RectF(-getWidth()+detalX, -getHeight()+detalY, getWidth()*2+detalX, getHeight()*2+detalY);

        isRun=true;
        thread=new Thread(this);
        thread.start();
    }


    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isRun=false;
    }

我们也定义光线可以顺时针或逆时针转动,默认是顺时针:

public static final int CLOCKWISE=1;//顺时针
public static final int ANTICLOCKWISE=2;//逆时针
private int rotationDirection=CLOCKWISE;
    /**
     * 设置转动方向。不设置,默认是顺时针。
     * @param rotationDirection 转动方向
     */
    public void setRotationDirection(int rotationDirection) {
        this.rotationDirection = rotationDirection;
    }

然后,我们在run()里不断循环绘图:

    @Override
    public void run() {
        while(isRun){
            Canvas canvas=mHolder.lockCanvas();
            canvas.drawColor(bgColor);
            for(int i=0;i<lightSize;i++){
                canvas.drawArc(oval, startRange+addRange*i, sweepAngle, true, p);
            }
            mHolder.unlockCanvasAndPost(canvas);

            SystemClock.sleep(50);//设置更小的数值可以加快速度
            if(rotationDirection==ANTICLOCKWISE){
                startRange-=1;
            }else{
                startRange+=1;
            }
            this.postInvalidate();
        }
    }

至此就完成了。


详细项目:

https://github.com/zhengjingle/ShineView


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值