自定义的签到View

平常都是用的系统的view,不过有时候需要自己去自定义,这次我也遇到要自定义签到view。

先来看下效果图


第一张是今天没签到的,点击今天就会签到。

主要实现类:     

SignUtil:用来获取日历的相关操作。
SignEntity:定义签到的type的实体类。
SignView:具体的绘制代码,自定义的view
我们先来看下 SignEntity里面的代码

public class SignEntity {
    private String day;//代表的日期
    private int dayType;//用来判断是否签到   0 表示已签到  1未签到   2 今天可以签到   3今天以后的日期 4超出当前月份的日期

    public String getDay() {
        return day;
    }

    public void setDay(String day) {
        this.day = day;
    }

    public int getDayType() {
        return dayType;
    }

    public void setDayType(int dayType) {
        this.dayType = dayType;
    }

}
SignUtil代码

public class SignUtil {
    /**
     * 获取当前天的下标
     */
    public int getTodayPostion(){
        int today=getToday();
        String s1= getWeek(1);//获取到星期
        int upWeek=getUpShowDay(s1);// 把星期转换成天数  上个月要显示几天
        return today+upWeek-1;
    }
    /**
     * 获取总共要显示的天数
     */
    public int getAllDay(){
        int day=getDay();
        String s1= getWeek(1);//这个月第一天是星期几  0代表 星期日
        String  s2=getWeek(day);//这个月最后一天是星期几
        int upWeek=getUpShowDay(s1);
        int nextWeek=getNextShowDay(s2);//把星期转换成天数
        return day+upWeek+nextWeek;
    }
    /**
     * 获取当前月份的天数
     */
    public int getDay() {
        Calendar a = Calendar.getInstance();
        a.set(Calendar.MONTH, getMonth());
        int maxDate = a.getActualMaximum(Calendar.DATE);
        return maxDate;
    }
    /**
     * 获取指定月份1号是星期几   用来确定上个月要在签到表里面站几个位置
     */
    public String getWeek(int day) {
        String str=getYear()+"-"+getMonth()+"-"+day;
        Date date=null;
        SimpleDateFormat dateFm = new SimpleDateFormat("yyyy-MM-dd");
        try {
            date=dateFm.parse(str);
            SimpleDateFormat sdf = new SimpleDateFormat("E");
            String s = sdf.format(date);
            return s;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return "";
    }
    /**
     * 上月要显示的天数
     */
    public int getUpShowDay(String s){
        int week=0;
        if (s.equals("周一")){
            week=1;
        }else if (s.equals("周二")){
            week=2;
        }else if (s.equals("周三")){
            week=3;
        }else if (s.equals("周四")){
            week=4;
        }else if (s.equals("周五")){
            week=5;
        }else if (s.equals("周六")){
            week=6;
        }else if (s.equals("周日")){
            week=0;
        }
        return week;
    }
    /**
     * 下月要显示的天数
     */
    public int getNextShowDay(String s){
        int week=0;
        if (s.equals("周一")){
            week=5;
        }else if (s.equals("周二")){
            week=4;
        }else if (s.equals("周三")){
            week=3;
        }else if (s.equals("周四")){
            week=2;
        }else if (s.equals("周五")){
            week=1;
        }else if (s.equals("周六")){
            week=0;
        }else if (s.equals("周日")){
            week=6;
        }
        return week;
    }
    /**
     * 获取指定月份的前一个月的天数
     */
    public int getUpDay() {
        Calendar a = Calendar.getInstance();
        a.set(Calendar.MONTH,getMonth()-1);
        int maxDate = a.getActualMaximum(Calendar.DATE);
        return maxDate;
    }
    /**
     * 获取当月
     */
    public int getMonth() {
        Calendar cal = Calendar.getInstance();
        return cal.get(Calendar.MONTH)+1;
    }
    /**
     * 获取当前年
     */
    public int getYear() {
        Calendar cal = Calendar.getInstance();
        return cal.get(Calendar.YEAR);
    }
    /**
     * 获取当前天
     */
    public int getToday(){
        Calendar cal = Calendar.getInstance();
        return cal.get(Calendar.DATE);
    }
    /**
     * 获取到我默认的数据    可以不用  然后自己写数据
     */
    public List<SignEntity> getListData(){
        List<SignEntity> listData = new ArrayList<>();
        int upDar = getUpDay();//上个月是多少天   用来计算 上个月要显示的时间
        int day = getDay();//当前月要显示的天数
        String s1= getWeek(1);//获取到星期
        String  s2=getWeek(day);
        int upWeek=getUpShowDay(s1);// 把星期转换成天数  上个月要显示几天
        int nextWeek=getNextShowDay(s2);//下个月要显示几天
        int today = getToday();//当前天
        Random ran = new Random();
        for (int i = 1; i <= getAllDay(); i++) {
            SignEntity entity = new SignEntity();
            if (i <= upWeek) {//上月的日期
                entity.setDay("" + (upDar - upWeek + i));//日期是从最后开始的显示的
                entity.setDayType(4);
            } else if (i <= day + upWeek) {//这个月的日期
                if (i < today+upWeek) {
                    entity.setDayType(ran.nextInt(10) % 2);//随机生成是否签到过
                } else if (i == today+upWeek) {
                    entity.setDayType(2);//只有今天是可以签到的
                } else {
                    entity.setDayType(3);
                }
                entity.setDay("" + (-upWeek + i));//日期从1开始的
            } else {//下个月的日期
                entity.setDay("" + (-(upWeek + day) + i));//日期是从1开始的
                entity.setDayType(4);
            }
            listData.add(entity);
        }
        return  listData;
    }
}
SignView的代码

public class SignView extends View {
    private int xian = getResources().getColor(R.color.sign_xian);//线的颜色
    private int c0 = getResources().getColor(R.color.sign_c0);//这个颜色对应  SignEntity 对应这个签到类的type
    private int c1 = getResources().getColor(R.color.sign_c1);
    private int c2 = getResources().getColor(R.color.sign_c2);
    private int c3 = getResources().getColor(R.color.sign_c3);
    private int c4 = getResources().getColor(R.color.sign_c4);

    private int cbj0 = getResources().getColor(R.color.sign_bj0);
    private int cbj2 = getResources().getColor(R.color.sign_bj2);
    private String[] mWeeks = {"日", "一", "二", "三", "四", "五", "六"};//这个显示要和我们的工具类里面星期对应的天数 的方法相同
    private int mMargin = 6;//默认二个格子之间的边距是 6
    private int mWidth = 60;//设置每个小控件的大小默认是 60
    private List<SignEntity> listData;
    private int size;//字体大小
    private Paint pWeek;//用来画上面的星期
    private Paint mPaint_Xian;//画笔  来画线
    private Rect waitRect;//今天签到的点击事件  的可点击范围
    private Rect mBound;//处理文字居中用的
    private Paint p0;//SignEntity 对应这个签到类的type
    private Paint p1;
    private Paint p2;
    private Paint p3;
    private Paint p4;
    private Paint bj0, bj1, bj2, bj3, bj4;
    private boolean is_off = true;//控制是否显示边框  默认显示

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

    public SignView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public SignView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initData();
        initPaint();
        initView(context);
    }

    private void initData() {
        size = 24;
    }

    private void initPaint() {
        p0 = new Paint();
        p1 = new Paint();
        p2 = new Paint();
        p3 = new Paint();
        p4 = new Paint();

        p0.setTextSize(size);
        p0.setColor(c0);
        p1.setTextSize(size);
        p1.setColor(c1);
        p2.setTextSize(size);
        p2.setColor(c2);
        p3.setTextSize(size);
        p3.setColor(c3);
        p4.setTextSize(size);
        p4.setColor(c4);

        bj0 = new Paint();
        bj1 = new Paint();
        bj2 = new Paint();
        bj3 = new Paint();
        bj4 = new Paint();

        bj0.setAntiAlias(true);
        bj0.setColor(cbj0);
        bj0.setStrokeWidth(2);
        bj0.setStyle(Paint.Style.FILL);

        bj2.setAntiAlias(true);
        bj2.setColor(cbj2);
        bj2.setStrokeWidth(1);
        bj2.setStyle(Paint.Style.STROKE);

        pWeek = new Paint();
        pWeek.setTextSize(24);
        pWeek.setColor(getResources().getColor(R.color.sidn_week));

        mBound = new Rect();

        mPaint_Xian = new Paint();
        mPaint_Xian.setStrokeWidth(1);
        mPaint_Xian.setColor(xian);
    }

    private void initView(Context context) {
        // 将DIP单位默认值转为PX
        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int ww = 0;
        int hh = 0;
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        if (widthMode == MeasureSpec.EXACTLY) {//
            width = widthSize;
            Log.i("gdx", "wws   " + width);
            ww = width;
        } else {
            width = 7 * mWidth;
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
            hh = height;
        } else {
            height = listData.size() / 7 * mWidth + mWidth;
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (listData.size() == 0) return;
        if (is_off) {
            mPaint_Xian.setStyle(Paint.Style.STROKE);
            canvas.drawRect(1, 0, getWidth(), getHeight() - 1, mPaint_Xian);
        }
        mDrawWeek(canvas);
        mDrawText(canvas);
    }

    private void mDrawWeek(Canvas canvas) {
        for (int i = 0; i < 7; i++) {
            pWeek.getTextBounds(mWeeks[i], 0, mWeeks[i].length(), mBound);
            float textWidth = pWeek.measureText(mWeeks[i]);//文字自己的宽度
            float startX = i * mWidth + mWidth / 2 - textWidth / 2;//宽度居中
            FontMetricsInt fm = pWeek.getFontMetricsInt();
            int startY = mWidth / 2 - fm.descent + (fm.bottom - fm.top) / 2;//高度居中  这个公式是网上找的
            canvas.drawText(mWeeks[i], startX, startY, pWeek);//画星期
        }
        mPaint_Xian.setStyle(Paint.Style.FILL);
        canvas.drawLine(0, mWidth, getWidth(), mWidth, mPaint_Xian);//横线
    }


    private void mDrawText(Canvas canvas) {
        int count = 0;//取listdata里面的数据
        for (int i = 1; i <= listData.size() / 7; i++) {
            for (int j = 0; j < 7; j++) {
                int x = j * mWidth + mWidth / 3;
                int y = i * mWidth + mWidth * 3 / 4;
                int bjX = j * mWidth + mWidth / 2;
                int bjY = i * mWidth + mWidth / 2;
                switch (listData.get(count).getDayType()) {
                    case 0:
                        canvas.drawCircle(bjX, bjY, mWidth / 2 - mMargin, bj0);//背景
                        mDrawTexts(canvas, listData.get(count).getDay(), j, i, count, p0);
                        break;
                    case 1:
                        mDrawTexts(canvas, listData.get(count).getDay(), j, i, count, p1);
                        break;
                    case 2:
                        waitRect = new Rect(j * mWidth, i * mWidth, j * mWidth + mWidth, i * mWidth + mWidth);
                        canvas.drawCircle(bjX, bjY, mWidth / 2 - mMargin, bj2);
                        mDrawTexts(canvas, listData.get(count).getDay(), j, i, count, p2);
                        break;
                    case 3:
                        mDrawTexts(canvas, listData.get(count).getDay(), j, i, count, p3);
                        break;
                    case 4:
                        mDrawTexts(canvas, listData.get(count).getDay(), j, i, count, p4);
                        break;
                }
                count++;
            }
        }
    }

    private void mDrawTexts(Canvas canvas, String str, int width, int height, int count, Paint p) {//画日期  并且居中
        p.getTextBounds(listData.get(count).getDay(), 0, listData.get(count).getDay().length(), mBound);
        float textWidth = p.measureText(listData.get(count).getDay());//文字自己的宽度
        float startX = width * mWidth + mWidth / 2 - textWidth / 2;//宽度居中   先加上前面有几个 mwidth 然后 + mwidth的一半 - 自己的宽度
        FontMetricsInt fm = p.getFontMetricsInt();
        int startY = height * mWidth + mWidth / 2 - fm.descent + (fm.bottom - fm.top) / 2;//高度居中  这个公式是网上找的   高度差不多也在这样的算法
        canvas.drawText(listData.get(count).getDay(), startX, startY, p);
    }

    public void setListData(List<SignEntity> listData) {
        if (listData != null)
            this.listData = listData;
        else
            this.listData = new ArrayList<>();
    }

    public void notifyDataSetChanged() {
        invalidate();
    }//重绘

    private OnTodayClickListener onTodayClickListener;

    public void setOnTodayClickListener(OnTodayClickListener onTodayClickListener) {
        this.onTodayClickListener = onTodayClickListener;
    }

    public interface OnTodayClickListener {
        void onTodayClick();
    }

    /**
     * 是否设置边框
     */
    public void setBober(boolean b) {
        is_off = b;
    }

    /**
     * 设置字体大小
     */
    public void setTextSize(int size) {
        this.size = size;
    }

    /**
     * 设置每个格子的大小
     */
    public void setWidthSize(int width) {//
        mWidth = width;
    }

    /**
     * 设置每个格子之间的边距
     */
    public void setWidthMargin(int margin) {
        mMargin = margin;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            float x = event.getX();
            float y = event.getY();
            SignUtil signUtil = new SignUtil();
            if (waitRect != null) {//用来给今天签到的
                if (listData.get(signUtil.getTodayPostion()).getDayType() == 2) {//必须要没签过到才可以
                    if (x >= waitRect.left && x <= waitRect.right && y <= waitRect.bottom && y >= waitRect.top) {//判断范围
                        if (onTodayClickListener != null) {
                            onTodayClickListener.onTodayClick();
                        }
                    }
                }
            }
        }
        return true;
    }
}
还有一个颜色配置

<color name="sidn_week">#1d3048</color>
<color name="sign_c0">#FFF</color>
<color name="sign_c1">#4d6687</color>
<color name="sign_c2">#ee4d4e</color>
<color name="sign_c3">#84B0E8</color>
<color name="sign_c4">#cdcfd1</color>

<color name="sign_bj0">#5d9ae5</color>
<color name="sign_bj2">#00CC00</color>

<color name="sign_xian" >#e1e1e1</color>
使用SignView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.qiang.demo.activity.MainActivity">


    <com.qiang.demo.ui.SignView
        android:id="@+id/signview1"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"   //这里有一个问题就是这个宽度只能设置成 wrap_content  高度也是
        android:layout_margin="10dp"
        android:layout_height="wrap_content" />

</RelativeLayout>
MainActivity使用
public class MainActivity extends Activity {

    private SignView signview;
    private List<SignEntity> listData;//初始化数据


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        signview = (SignView) findViewById(R.id.signview1);
        signview.setListData(listData);
        signview.setWidthSize(60);
//        signview.setWidthMargin(6);二个格子之间默认的边距
//        signview.setTextSize(); 字体大小
//        signview.setBober(true);  是否显示边框
        signview.setOnTodayClickListener(new SignView.OnTodayClickListener() {
            @Override
            public void onTodayClick() {
                SignUtil signUtil = new SignUtil();
                listData.get(signUtil.getTodayPostion()).setDayType(0);
                signview.notifyDataSetChanged();
                ;
            }
        });

    }

    private void initData() {
        SignUtil signUtil = new SignUtil();
        listData = signUtil.getListData();//我在工具类初始化的数据    可以根据自己的去修改
    }
}




里面注释很详细,可以根据自己的需求去修改




源码下载



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值