Custom view - Labeled Calendar view

Keywords: github less git

Old people are apt to be irritable. Immediately above, I'm as grumpy as I am.

Functions:
    1. There will be a blue half-arc sign on that day.
    2. If you sign in one day, the picture of "little paw" will be displayed.
    3. If you click on a certain day, it will show the gray circle selected state.
    4. There can be different small color dots at the bottom of each day to indicate the attendance status of the day (let the server buddies count, don't cross the data)

This is a simple custom control. Generally speaking, it has no technical content (although I did it for two days before I got it out), but I still hope to share it. Otherwise, it would be a waste to do something for two days without forcibly installing b.

Not much nonsense, irritable like me, go up.

Let's start with the train of thought.
    Step 1: Prepare Paint (mPaintLine), Word PaintTv, Small Dot Point, Background PaintBg)
    Step 2: Analyse the required data bean s, and then draw OnDraw
    Step 3: Deal with click events.
    Step 4: Optimize the layout (OnMeasure).

The first step is not to say, do not understand from the basis of customized view.
Let me just say that the brush mPaintTv must set the drawing point as the center point mPaintTv.setTextAlign(Paint.Align.CENTER);

The second step is to analyze:
Six states are naturally needed:

public String earlyStatus;     //Whether to leave early (0:1:1:1)
public String singleStatus;           //Is there a card missing (0: no card missing 1: card missing)
public String workStatus;         //Whether absenteeism (0: no absenteeism 1: absenteeism)
public String approvingStatus;         //Whether to ask for leave (0: no leave 1: leave)
public String lateStatus;           //Are you late (0: not late 1: late)
public String outStatus;        //Are you in the field (0: not in the field 1: already in the field)

Of course, the check-in status is public String status; // check-in or not (0: no check-in to 1: checked-in)

For convenience, add a day date public String day; // date

Then you start drawing directly (here's the point):

  • Draw the top horizontal line
canvasWidth = getWidth();
canvas.drawLine(0,0, canvasWidth,0,mPaintLine);
  • Drawing Weekly Texts
for (int i = 0; i < weekStr.length; i++) {
            canvas.drawText(weekStr[i], viewMargin + mPaintTv.measureText("11")/2 +
                    i * (canvasWidth - viewMargin*2 - mPaintTv.measureText("11")) / 6, posweekStrY, mPaintTv);
        }

Abscissa:
Because mPaintTv draws the point at the center of the text, the x coordinate of the word "day" is one-half of the length of the word "day", plus the viewMargin of the font distance from the boundary, and how to calculate the abscissa coordinate of the word drawn in the next week?
The remaining distance is divided equally into six parts
(The total width of the control - about viewMargin - ("half of the distance of the word"day"+"half of the distance of the word "six")/ 6

Longitudinal coordinates:
If you don't know the drawing principle of textview, the drawing of ordinates is very confusing. So, go to you, portal.- Drawing coordinate principle of textview
(Figure 1: paint rendering of textview is based not on the bottom line, but on baseline, that is to say,
canvas.drawText("text", x,y,paintTv); y refers to the Y axis coordinates where baseline is located.

At the same time, lead, ascent and descent can be obtained by mPaintTv.getFontMetrics(), but it should be noted here that the values of lead and ascent are negative because they are based on baseline and above baseline.

The ordinates are equal to:
posweekStrY = lineTextDest - (mPaintTv.getFontMetrics().ascent + mPaintTv.getFontMetrics().leading);

  • Drawing the number of days in a month
    First of all, make sure that the first day of the month is the day of the week (representing several places that need not be drawn before the first day). Draw a total of dayNums+ firstDayOfWeek, and do not draw Text before the first DayOfweek.

Calculate ordinates:

posMonthDayY = poslineBottomY + lineTextDest - (mPaintTv.getFontMetrics().ascent + mPaintTv.getFontMetrics().leading)
                        + (j/7) * (mPaintTv.getTextSize() + lineTextDest + 2 * pointRadius + pointMarginTop);

Calculate abscissa:

posMonthDayX = viewMargin + mPaintTv.measureText("11") / 2
                        + (j%7) * (canvasWidth - viewMargin * 2 - mPaintTv.measureText("11")) / 6;

Draw text:

         canvas.drawText(calendarBeanList.get(j - firstDayOfWeek).day,
                        posMonthDayX, posMonthDayY, mPaintTv);
  • Draw background map
    Compute the plotted area:
    - 6 and + 6 are designed to make the drawing area of the picture slightly larger than that of the text and look good (I attach great importance to aesthetics).
RectF rect = new RectF((float) (posMonthDayX - mPaintTv.measureText("11") / 2)-6,
                        (float) (posMonthDayY - (-mPaintTv.getFontMetrics().ascent - mPaintTv.getFontMetrics().leading))-6,
                        (float) (posMonthDayX + mPaintTv.measureText("11") / 2)+6,
                        (float) (posMonthDayY + mPaintTv.getFontMetrics().descent)+6
                );
  • Draw a dot
int paintCount = calendarBeanList.get(j-firstDayOfWeek).getPaintColors().size();
//Draw a dot
for (int i = 0; i < paintCount; i++) {
    mPaintPoint.setColor(Color.parseColor(calendarBeanList.get(j-firstDayOfWeek).getPaintColors().get(i)));
    if (paintCount%2 == 0){
        canvas.drawCircle(posMonthDayX -(2*i-1)*(dotMargin/2+pointRadius)
        , posMonthDayY +pointRadius+pointMarginTop,pointRadius,mPaintPoint);
    }else {
        canvas.drawCircle(posMonthDayX -((int)Math.pow(-1,i+1)*(i+1)/2)*(dotMargin+2*pointRadius), posMonthDayY +pointRadius+pointMarginTop,pointRadius,mPaintPoint);
    }
}


There are two kinds:
Odd Points: First draw the dot coordinates, then the first dot on the left of the dot, then the first dot on the right of the dot, then the second dot on the left of the dot, and then draw the second dot on the right of the dot... (Math.pow(-1,i+1) controls that the left side of the dot is negative and the right side is positive.)
Even-numbered dots: first draw the left side on the right side, then draw the left two, in turn.

  • Click Event Judgment
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        int action = event.getAction();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                //Determine the date of the click
                for (int j = 0; j < dayNums+ firstDayOfWeek; j++) {
                    if (j >= firstDayOfWeek){
                        float posMonthDayY = poslineBottomY + lineTextDest - (mPaintTv.getFontMetrics().ascent + mPaintTv.getFontMetrics().leading)
                                + (j/7) * (mPaintTv.getTextSize() + lineTextDest + 2 * pointRadius + pointMarginTop);
                        float posMonthDayX = viewMargin + mPaintTv.measureText("11") / 2
                                + (j%7) * (canvasWidth - viewMargin * 2 - mPaintTv.measureText("11")) / 6;
                        if (x > posMonthDayX - mPaintTv.measureText("11") / 2-6 &&
                                x < posMonthDayX + mPaintTv.measureText("11") / 2+6 &&
                                y > posMonthDayY - (-mPaintTv.getFontMetrics().ascent - mPaintTv.getFontMetrics().leading)-6 &&
                                y < posMonthDayY + mPaintTv.getFontMetrics().descent+6){
                            //Only those less than the current date can be clicked

                            if (currentYear>chooseYear ||
                                    (currentYear == chooseYear && currentMonth>chooseMonth) ||
                                    (currentYear == chooseYear && currentMonth == chooseMonth && currentDay >= j - firstDayOfWeek + 1)) {

                                setChooseYMD(chooseYear, chooseMonth, j - firstDayOfWeek + 1);
                                if (iOnClickDateListener != null){
                                    iOnClickDateListener.onClickDate(chooseYear,chooseMonth,j - firstDayOfWeek + 1);
                                }else {
                                    new RuntimeException("iOnClickDateListener == null");
                                }
                            }
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:

                break;
        }
        return true;

    }

It's simple. Feel it for yourself.

Code has been submitted to git and csdn, need to download, if there are any problems, please do tell me.

GitHub address: https://github.com/SuperChandler/CustomCalendar View

Posted by s0c0 on Thu, 27 Jun 2019 17:44:52 -0700