android自定义View基础系列一(模仿365日历PC版加载动画)

前言:

前段时间公司业务不忙,每天无所事事,明明知道有很多要学的东西,却又不知道从何做起,感觉很不踏实,于是想管理规划下自己的时间。精挑细选下,看中了365日历,每天安排好时间,每个时间段学什么做什么,做起事来有条不紊,感觉充实多了。建议小伙伴们也要选择一种合适的时间管理工具,管理规划好时间,fighting。sorry,跑题了。

今天刷新日历,由于网络一时不畅,看到了加载动画,打算做个练练手,附一张静态图看效果:


就四个小球旋转,往里往外晃动。

概要:

本文都是一些绘图的基本知识,主要会涉及到绘图里面的一个旋转画布的概念,可以自己去了解下相关内容,文中不会做太详细的介绍。

正文:

首先分析一下需要自定义的属性:小球的半径radiu,四个球的颜色值,每个球偏移中心轴的距离。

我们先自定义好属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyViewAttrs">
        <!--小圆半径-->
        <attr name="radiu" format="integer"></attr>
        <!--四个圆的颜色-->
        <attr name="color_one" format="color"></attr>
        <attr name="color_two" format="color"></attr>
        <attr name="color_three" format="color"></attr>
        <attr name="color_four" format="color"></attr>
        <!--偏移中心点的距离-->
        <attr name="offset" format="integer"></attr>
    </declare-styleable>
</resources>


自定义一个View,获取对应的属性:

/*小圆半径*/
    private int radiu;

    /*四个球颜色值*/
    private int color_one;
    private int color_two;
    private int color_three;
    private int color_four;

    /*偏移中心轴距离*/
    private int offset;

    //绘图工具
    private Paint mPaint;

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

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

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MyViewAttrs,defStyleAttr,0);
        radiu = ta.getInt(R.styleable.MyViewAttrs_radiu,30);
        color_one = ta.getColor(R.styleable.MyViewAttrs_color_one, Color.BLACK);
        color_two = ta.getColor(R.styleable.MyViewAttrs_color_two, Color.BLACK);
        color_three = ta.getColor(R.styleable.MyViewAttrs_color_three, Color.BLACK);
        color_four = ta.getColor(R.styleable.MyViewAttrs_color_four, Color.BLACK);
        offset = ta.getInt(R.styleable.MyViewAttrs_offset,50);
        ta.recycle();

        mPaint = new Paint();
        mPaint.setStrokeWidth(3);
        mPaint.setAntiAlias(true);
    }


然后将自定义的VIew引入到我们的布局文件中,设置对应的属性值:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res/demo.bys.com.testmyview4"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <demo.bys.com.testmyview4.MyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        custom:radiu="60"
        custom:color_one="#FF7F00"
        custom:color_two="#FFFF00"
        custom:color_three="#63B8FF"
        custom:color_four="#0000EE"
        custom:offset="100"/>

</LinearLayout>

我们还需要在onMeasure中测量View的宽高,以便确定中心点的位置:

//View的宽高
private int mWidth,mHeight;
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if(widthMode == MeasureSpec.EXACTLY){
            mWidth = widthSize;
        }else{
            mWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,400,getResources().getDisplayMetrics());
        }

        if(heightMode == MeasureSpec.EXACTLY){
            mHeight = heightSize;
        }else{
            mHeight = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,400,getResources().getDisplayMetrics());
        }

        setMeasuredDimension(mWidth,mHeight);
    }


好了,到这里我们的准备工作已经完成,我们把View的中心点作为x,y轴的中心轴,四个球分别在x轴正负,y轴正负偏移相同距离。首先我们先绘制静态的小球:

/*四个球*/
            mPaint.setColor(color_one);
            canvas.drawCircle(mWidth/2-offset,mHeight/2,radiu,mPaint);

            mPaint.setColor(color_two);
            canvas.drawCircle(mWidth/2+offset,mHeight/2,radiu,mPaint);

            mPaint.setColor(color_three);
            canvas.drawCircle(mWidth/2,mHeight/2-offset,radiu,mPaint);

            mPaint.setColor(color_four);
            canvas.drawCircle(mWidth/2,mHeight/2+offset,radiu,mPaint);
效果如图:



现在我们在静态小圆的基础上实现圆偏移的动态效果,定义偏移值,和偏移判断:

/*增还是减*/
    private boolean isChanging = false;

    /*变化的值*/
    private int offsetChange;

/*四个球*/
        mPaint.setColor(color_one);
        canvas.drawCircle(mWidth/2-offsetChange,mHeight/2,radiu,mPaint);

        mPaint.setColor(color_two);
        canvas.drawCircle(mWidth/2+offsetChange,mHeight/2,radiu,mPaint);

        mPaint.setColor(color_three);
        canvas.drawCircle(mWidth/2,mHeight/2-offsetChange,radiu,mPaint);

        mPaint.setColor(color_four);
        canvas.drawCircle(mWidth/2,mHeight/2+offsetChange,radiu,mPaint);


        if(isChanging){//递减
            if(offsetChange > offset){
                offsetChange --;
            }else{
                isChanging = false;
            }
        }else{//递加
            if(offsetChange < offset + 30){
                offsetChange ++;
            }else{
                isChanging = true;
            }
        }

        postInvalidateDelayed(30);
偏移变化范围定为30,判断递增,递减,将小球的偏移值换成变化中的偏移值。此时就实现了小球动态远离/靠近的效果,没有动态图,就不贴了。

下面我们在添加画布旋转的效果,这里设置一个变化比例:

/*旋转的比例值*/
    private int percent = 0;
这里我们2度一叠加:
percent+=2;
canvas.rotate(percent,mWidth/2,mHeight/2);
最终实现了效果,在linux系统动态效果图不好搞,只能上个静态图,后面会附上源码:

附上View的完整代码:

public class MyView extends View {

    /*小圆半径*/
    private int radiu;

    /*四个球颜色值*/
    private int color_one;
    private int color_two;
    private int color_three;
    private int color_four;

    /*偏移中心轴距离*/
    private int offset;

    //绘图工具
    private Paint mPaint;

    //View的宽高
    private int mWidth,mHeight;

    /*增还是减*/
    private boolean isChanging = false;

    /*变化的值*/
    private int offsetChange;

    /*旋转的比例值*/
    private int percent = 0;


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

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

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MyViewAttrs,defStyleAttr,0);
        radiu = ta.getInt(R.styleable.MyViewAttrs_radiu,30);
        color_one = ta.getColor(R.styleable.MyViewAttrs_color_one, Color.BLACK);
        color_two = ta.getColor(R.styleable.MyViewAttrs_color_two, Color.BLACK);
        color_three = ta.getColor(R.styleable.MyViewAttrs_color_three, Color.BLACK);
        color_four = ta.getColor(R.styleable.MyViewAttrs_color_four, Color.BLACK);
        offset = ta.getInt(R.styleable.MyViewAttrs_offset,50);
        ta.recycle();

        mPaint = new Paint();
        mPaint.setStrokeWidth(3);
        mPaint.setAntiAlias(true);
        offsetChange = offset;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if(widthMode == MeasureSpec.EXACTLY){
            mWidth = widthSize;
        }else{
            mWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,400,getResources().getDisplayMetrics());
        }

        if(heightMode == MeasureSpec.EXACTLY){
            mHeight = heightSize;
        }else{
            mHeight = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,400,getResources().getDisplayMetrics());
        }

        setMeasuredDimension(mWidth,mHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        percent+=2;
        canvas.rotate(percent,mWidth/2,mHeight/2);

       /*四个球*/
        mPaint.setColor(color_one);
        canvas.drawCircle(mWidth/2-offsetChange,mHeight/2,radiu,mPaint);

        mPaint.setColor(color_two);
        canvas.drawCircle(mWidth/2+offsetChange,mHeight/2,radiu,mPaint);

        mPaint.setColor(color_three);
        canvas.drawCircle(mWidth/2,mHeight/2-offsetChange,radiu,mPaint);

        mPaint.setColor(color_four);
        canvas.drawCircle(mWidth/2,mHeight/2+offsetChange,radiu,mPaint);


        if(isChanging){//递减
            if(offsetChange > offset){
                offsetChange --;
            }else{
                isChanging = false;
            }
        }else{//递加
            if(offsetChange < offset + 30){
                offsetChange ++;
            }else{
                isChanging = true;
            }
        }


        postInvalidateDelayed(40);
    }


}




源码:

csdn地址:http://download.csdn.net/detail/liujibin1836591303/9723215



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="stylesheet" type="text/css" href="http://s.365rili.com/wannianlibaidu/css/wnl.css" /> </head> <body false"> <div id="middle"> <div class="cal_container" id="cal_container" > <div id='cal_body'> <div id='cal_funcbar'> <div id='funcbar_content'> <div id='prev_button'> </div> <div id='year_func' <div id='year_num'> </div> <span id='year_str'>年</span> </div> <div id='month_func' <div id='month_num'></div> <span id='month_str'>月</span> </div> <div id='next_button'></div> <div id='festival'>假期安排</div> <div id='today_button'>今天</div> <div id='cal_plusbutton' title="点击添加日程"></div> <div id='cal_365riliUser'> <div class='top_bar_text' id='365riliUserName'></div> <div class='top_bar_text' id='bd_login'>登录</div> </div> </div> </div> <div id='cal_down'> <div id='cal_downleft'> <div id='mainCal'></div> </div> <div left;'> <div id='cal_rightboard'> <div id='gregorianDayStr'></div> <div id='right_big_date'></div> <div id='gregorianDay'> <div id='dayafterorbefore'></div> </div> <div class='chinaDay' id='chinaDay'></div> <div class='chinaDay' id='chinaDay2'></div> <div class='chinaDay' id='chinaDay3'></div> <div id='YJdiv' class='YJdiv'> <div id='ylistblock' class='YJdiv'> <div id='ylistword' class='YJdiv'>宜</div> <div id='ylist' class='YJdiv'></div> </div> <div id='jlistblock' class='YJdiv'> <div id='jlistword' class='YJdiv'>忌</div> <div id='jlist' class='YJdiv'></div> </div> </div> </div> </div> </div> <div id='festival_detail_str'>1月22日至1月28日放假7天,1月21日(星期六)、1月29日(星期日)上班。</div> <div id='cal_topbar'> <div id='cal_topbar_top'> <a href='http://www.365rili.com/v3/sync2cell.htm?from=baidu' target='_blank'> <div id='logo-text' class='top_bar_text'>365日历手机下载</div> </a> <div id='cal_365riliTime'> <div class='top_bar_text' id='beijingtime'>北京时间</div> <div class='top_bar_text' id='top_bar_time'></div> </div> </div> </div> </div> </div> <div id='year_select' class='selecter'> <div id='year_select_selecter' class='selecter_table'></div> </div> <div id='month_select' class='selecter'> <div id='month_select_selecter' class='selecter_table'></div> </div> <div id='festival_select' class='selecter'> <div id='festival_select_selecter' class='selecter_table'></div> </div> <div id='plusDiv'></div> <div id='plusDivEle'> <div id='closePlus'>取消</div> <div id='popDivItem'> <div id='plusTime' class='popDivItem'></div> <div id='popHourSelect' class='popDivItem hourselect'> <div id='popHourSelectNumb' class='hourselect'>全天</div> <div class='popDownArrow hourselect'></div> <div <div id='popHourSelectList'></div> </div> <div id='popMinuteSelect' class='popDivItem minuteselect'> <div id='popMinuteSelectNumb' class='minuteselect'>0分</div> <div class='popDownArrow minuteselect'></div> <div <div id='popMinuteSelectList'></div> </div> </div> <div both;'></div> <textarea id='popTextarea' rows="4" cols="30"></textarea> <div id='plusDivCreate'>创建</div> </div> <div id='huangliDiv' class='YJdiv'> <div id='popDateStr' class='YJdiv'></div> <div id='popChineseStr' class='YJdiv'></div> <div class='clear'></div> <div id='popY' class='popYJ'> <div id='popYword' class='popWord'>宜</div> <div id='popYStr' class='popStr'></div> </div> <div id='popJ' class='popYJ'> <div id='popJword' class='popWord'>忌</div> <div id='popJStr' class='popStr'></div> </div> </div> <div id='taskHover'> <div id='taskList'></div> <a href="http://app.baidu.com/365richeng" target="_blank"> <div id='moretask'>更多日程</div> </a> </div> </div> [removed][removed] [removed][removed] [removed][removed] </body> </html>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值