转载请标明出处:

http://blog.csdn.net/xmxkf/article/details/54020386

本文出自:【openXu的博客】

目录:

  应项目需求,需要做一个日历控件,效果图如下:

    

  接到需求后,没有立即查找是否有相关开源日历控件可用、系统日历控件是否能满足 ,第一反应就是这个控件该怎么画?谁叫咱自定义控件技术牛逼呢O(∩_∩)O哈哈~(开个玩笑,不要这样子严肃→广告时间:要想达到哥的高度,请认真学习自定义控件系列博客喔)。言归正传,如图,上部分是自定义日历的效果图,下面是系统自带CalendarView的效果,这两个控件的相关功能需求元素图上都有标注。系统自带的日历控件能够左右滑动切换月份,效果还是挺酷的(这是我在自定义控件完毕之后才发现的),结果就后悔了,这么酷干嘛还要自定义啊?

  自定义当然不是为了装逼呐,请认真看需求,我们需要在日期下面显示任务完成情况,当日被切换之后需要标注为灰色圆圈背景,这些都是自带日历控件不可达到的,当然,我们也可以继承系统CalendarView然后试着修改;那为什么不选择开源的日历控件呢?如果我们不会自定义控件或者时间很紧,开源的当然是首选,谁叫我闲的慌,开源的控件也会有些问题,有的可能不够完善很多bug,比较完善的可能内容太多,如果要抽取有用的内容还是需要花一定时间,如果整个库工程都弄过来会造成大量的代码冗余。另外一点很重要,任务情况需要从服务器上获取,当切换日期之后,日历控件下方要显示那天的任务详情(可能需要请求数据),这么多问题如果去修改开源库工程工作量不一定比自定义小。综上,还是自定义更适合我,整个日历控件500多行代码就搞定(包括注释、接口、各种变量),后面如果项目需求有变动,改动起来也是so easy! 当然,日常开发中,自带控件能搞定的尽量就用系统自带的。下面我们一起看看这个控件是怎样实现的。

1、分析

    

  怎样自定义这个日历控件呢?可能我们第一反应是GridView+组合控件的方式,GridView用来展示下面日期部分,这种方式实现起来相对比较容易,但是核心的内容(获取某月的天数、具体展示在什么位置)还是得自己做,这种方式代码量也不少,另外这样做也会加大系统性能开销,GridView中同时显示30来个item,item里面还嵌套子控件,之前我们讲控件填充、测量时讲过尽量减少布局的嵌套,这样会造成过多的遍历,一不小心又装逼了,现在的手机那么牛逼,这么点工作量跟我谈什么性能?

  第二种就是通过自定义View完全绘制出来,只需要一个类搞定。其实绘制很简单,拿到一个画笔(Paint),我们就能画天画地画美女,爱画什么画什么,不需要有品位、不需要艺术功底,比起拿2B铅笔作画简单多了。

  如果要绘制出这个控件,我们首先要得到某个月的所有天数(从1号开始….)、1号是星期几(从什么位置开始展示),有了这两个数据,我们就能得到第一行从哪里开始绘制,能绘制多少天,最后一行能绘制多少天,其他中间的都是绘制7天;接下来需要绘制当前日期和被选中日期的背景,其实就是在绘制日期时先判断下日期是不是当前日期,如果是就给他先画一个背景,被选择的也是一样。我们先看看获取日期的算法:

/**设置月份*/
private void setMonth(String Month){
    //设置的月份(2017年01月)
    month = str2Date(Month);

    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new Date());
    //获取今天是多少号
    currentDay = calendar.get(Calendar.DAY_OF_MONTH);

    Date cM = str2Date(getMonthStr(new Date()));
    //判断是否为当月
    if(cM.getTime() == month.getTime()){
        isCurrentMonth = true;
        selectDay = currentDay;//当月默认选中当前日
    }else{
        isCurrentMonth = false;
        selectDay = 0;
    }
    Log.d(TAG, "设置月份:"+month+"   今天"+currentDay+"号, 是否为当前月:"+isCurrentMonth);
    calendar.setTime(month);
    dayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    //第一行1号显示在什么位置(星期几)
    firstIndex = calendar.get(Calendar.DAY_OF_WEEK)-1;
    lineNum = 1;
    //第一行能展示的天数
    firstLineNum = 7-firstIndex;
    lastLineNum = 0;
    int shengyu = dayOfMonth - firstLineNum;
    while (shengyu>7){
        lineNum ++;
        shengyu-=7;
    }
    if(shengyu>0){
        lineNum ++;
        lastLineNum = shengyu;
    }
    Log.i(TAG, getMonthStr(month)+"一共有"+dayOfMonth+"天,第一天的索引是:"+firstIndex+"   有"+lineNum+
            "行,第一行"+firstLineNum+"个,最后一行"+lastLineNum+"个");
}

2、自定义属性

  自定义属性相关的知识请参考Android自定义View(二、深入解析自定义属性),这里就不多说了,我们看看本控件都定义了那些属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomCalendar">
        <!--这四个颜色分别是月份、星期、日期、任务的背景色,只是方便调试测量时使用,正式试用时可配置透明色-->
        <attr name="mBgMonth" format="color" />
        <attr name="mBgWeek" format="color" />
        <attr name="mBgDay" format="color" />
        <attr name="mBgPre" format="color" />

        <attr name="mTextColorMonth" format="color" />           <!--标题字体颜色-->
        <attr name="mTextColorWeek" format="color" />            <!--星期字体颜色-->
        <attr name="mTextColorDay" format="color" />             <!--日期字体颜色-->
        <attr name="mTextColorPreFinish" format="color" />       <!--任务次数字体颜色-->
        <attr name="mTextColorPreUnFinish" format="color" />
        <attr name="mSelectTextColor" format="color" />          <!--选中日期字体颜色-->
        <attr name="mSelectBg" format="color" />                 <!--选中日期背景-->
        <attr name="mCurrentBg" format="color" />                <!--当天日期背景-->
        <attr name="mCurrentBgStrokeWidth" format="dimension" /> <!--当天日期背景虚线宽度-->
        <attr name="mCurrentBgDashPath" format="reference" />    <!--当天日期背景虚线数组-->

        <attr name="mTextSizeMonth" format="dimension" />        <!--标题字体大小-->
        <attr name="mTextSizeWeek" format="dimension" />         <!--星期字体大小-->
        <attr name="mTextSizeDay" format="dimension" />          <!--日期字体大小-->
        <attr name="mTextSizePre" format="dimension" />          <!--任务次数字体大小-->

        <attr name="mMonthRowL" format="reference" />            <!--月份箭头-->
        <attr name="mMonthRowR" format="reference" />            <!--月份箭头-->
        <attr name="mMonthRowSpac" format="dimension" />

        <attr name="mSelectRadius" format="dimension" />         <!--选中日期背景半径-->
        <attr name="mMonthSpac" format="dimension" />            <!--标题月份上下间隔-->
        <attr name="mLineSpac" format="dimension" />             <!--日期行间距-->
        <attr name="mTextSpac" format="dimension" />             <!--日期和任务次数字体上下间距-->
    </declare-styleable>
</resources>

3、onMeasure()

  得到需要绘制的数据之后,接下来就是重写onMeasure()方法了,这个控件需要多宽多高?宽度直接填充父窗体即可,总高度=月份高度+星期高度+日期高度,相应的数据在上面的算法中都得到了,请看下面分析图:

    

代码:

    /**计算相关常量,构造方法中调用*/
    private void initCompute(){
        mPaint = new Paint();
        bgPaint = new Paint();
        mPaint.setAntiAlias(true); //抗锯齿
        bgPaint.setAntiAlias(true); //抗锯齿

        map = new HashMap<>();

        //标题高度
        mPaint.setTextSize(mTextSizeMonth);
        titleHeight = FontUtil.getFontHeight(mPaint) + 2 * mMonthSpac;
        //星期高度
        mPaint.setTextSize(mTextSizeWeek);
        weekHeight = FontUtil.getFontHeight(mPaint);
        //日期高度
        mPaint.setTextSize(mTextSizeDay);
        dayHeight = FontUtil.getFontHeight(mPaint);
        //次数字体高度
        mPaint.setTextSize(mTextSizePre);
        preHeight = FontUtil.getFontHeight(mPaint);
        //每行高度 = 行间距 + 日期字体高度 + 字间距 + 次数字体高度
        oneHeight = mLineSpac + dayHeight + mTextSpac + preHeight;

        //默认当前月份
        String cDateStr = getMonthStr(new Date());
//        cDateStr = "2015年08月";
        setMonth(cDateStr);
    }

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //宽度 = 填充父窗体
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);   //获取宽的尺寸
    columnWidth = widthSize / 7;
    //高度 = 标题高度 + 星期高度 + 日期行数*每行高度
    float height = titleHeight + weekHeight + (lineNum * oneHeight);
    Log.v(TAG, "标题高度:"+titleHeight+" 星期高度:"+weekHeight+" 每行高度:"+oneHeight+
            " 行数:"+ lineNum + "  \n控件高度:"+height);
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            (int)height);

}

4、onDraw()

@Override
protected void onDraw(Canvas canvas) {
    drawMonth(canvas);
    drawWeek(canvas);
    drawDayAndPre(canvas);
}

①、绘制月份

private void drawMonth(Canvas canvas){
    //背景
    bgPaint.setColor(mBgMonth);
    RectF rect = new RectF(0, 0, getWidth(), titleHeight);
    canvas.drawRect(rect, bgPaint);
    //绘制月份
    mPaint.setTextSize(mTextSizeMonth);
    mPaint.setColor(mTextColorMonth);
    float textLen = FontUtil.getFontlength(mPaint, getMonthStr(month));
    float textStart = (getWidth() - textLen)/ 2;
    canvas.drawText(getMonthStr(month), textStart,
            mMonthSpac+FontUtil.getFontLeading(mPaint), mPaint);
    /*绘制左右箭头*/
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mMonthRowL);
    int h = bitmap.getHeight();
    rowWidth = bitmap.getWidth();
    //float left, float top
    rowLStart = (int)(textStart-2*mMonthRowSpac-rowWidth);
    canvas.drawBitmap(bitmap, rowLStart+mMonthRowSpac , (titleHeight - h)/2, new Paint());
    bitmap = BitmapFactory.decodeResource(getResources(), mMonthRowR);
    rowRStart = (int)(textStart+textLen);
    canvas.drawBitmap(bitmap, rowRStart+mMonthRowSpac, (titleHeight - h)/2, new Paint());
}

②、绘制星期

private String[] WEEK_STR = new String[]{"Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat", };
private void drawWeek(Canvas canvas){
    //背景
    bgPaint.setColor(mBgWeek);
    RectF rect = new RectF(0, titleHeight, getWidth(), titleHeight + weekHeight);
    canvas.drawRect(rect, bgPaint);
    //绘制星期:七天
    mPaint.setTextSize(mTextSizeWeek);
    mPaint.setColor(mTextColorWeek);
    for(int i = 0; i < WEEK_STR.length; i++){
        int len = (int)FontUtil.getFontlength(mPaint, WEEK_STR[i]);
        int x = i * columnWidth + (columnWidth - len)/2;
        canvas.drawText(WEEK_STR[i], x, titleHeight + FontUtil.getFontLeading(mPaint), mPaint);
    }
}

③、绘制日期及任务

private void drawDayAndPre(Canvas canvas){
        //某行开始绘制的Y坐标,第一行开始的坐标为标题高度+星期部分高度
        float top = titleHeight+weekHeight;
        //行
        for(int line = 0; line < lineNum; line++){
            if(line == 0){
                //第一行
                drawDayAndPre(canvas, top, firstLineNum, 0, firstIndex);
            }else if(line == lineNum-1){
                //最后一行
                top += oneHeight;
                drawDayAndPre(canvas, top, lastLineNum, firstLineNum+(line-1)*7, 0);
            }else{
                //满行
                top += oneHeight;
                drawDayAndPre(canvas, top, 7, firstLineNum+(line-1)*7, 0);
            }
        }
    }

    /**
     * 绘制某一行的日期
     * @param canvas
     * @param top 顶部坐标
     * @param count 此行需要绘制的日期数量(不一定都是7天)
     * @param overDay 已经绘制过的日期,从overDay+1开始绘制
     * @param startIndex 此行第一个日期的星期索引
     */
    private void drawDayAndPre(Canvas canvas, float top,
                               int count, int overDay, int startIndex){
//        Log.e(TAG, "总共"+dayOfMonth+"天  有"+lineNum+"行"+ "  已经画了"+overDay+"天,下面绘制:"+count+"天");
        //背景
        float topPre = top + mLineSpac + dayHeight;
        bgPaint.setColor(mBgDay);
        RectF rect = new RectF(0, top, getWidth(), topPre);
        canvas.drawRect(rect, bgPaint);

        bgPaint.setColor(mBgPre);
        rect = new RectF(0, topPre, getWidth(), topPre + mTextSpac + dayHeight);
        canvas.drawRect(rect, bgPaint);

        mPaint.setTextSize(mTextSizeDay);
        float dayTextLeading = FontUtil.getFontLeading(mPaint);
        mPaint.setTextSize(mTextSizePre);
        float preTextLeading = FontUtil.getFontLeading(mPaint);
//        Log.v(TAG, "当前日期:"+currentDay+"   选择日期:"+selectDay+"  是否为当前月:"+isCurrentMonth);
        for(int i = 0; i<count; i++){
            int left = (startIndex + i)*columnWidth;
            int day = (overDay+i+1);

            mPaint.setTextSize(mTextSizeDay);

            //如果是当前月,当天日期需要做处理
            if(isCurrentMonth && currentDay == day){
                mPaint.setColor(mTextColorDay);
                bgPaint.setColor(mCurrentBg);
                bgPaint.setStyle(Paint.Style.STROKE);  //空心
                PathEffect effect = new DashPathEffect(mCurrentBgDashPath, 1);
                bgPaint.setPathEffect(effect);   //设置画笔曲线间隔
                bgPaint.setStrokeWidth(mCurrentBgStrokeWidth);       //画笔宽度
                //绘制空心圆背景
                canvas.drawCircle(left+columnWidth/2, top + mLineSpac +dayHeight/2,
                        mSelectRadius-mCurrentBgStrokeWidth, bgPaint);
            }
            //绘制完后将画笔还原,避免脏笔
            bgPaint.setPathEffect(null);
            bgPaint.setStrokeWidth(0);
            bgPaint.setStyle(Paint.Style.FILL);

            //选中的日期,如果是本月,选中日期正好是当天日期,下面的背景会覆盖上面绘制的虚线背景
            if(selectDay == day){
                //选中的日期字体白色,橙色背景
                mPaint.setColor(mSelectTextColor);
                bgPaint.setColor(mSelectBg);
                //绘制橙色圆背景,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象;
                canvas.drawCircle(left+columnWidth/2, top + mLineSpac +dayHeight/2, mSelectRadius, bgPaint);
            }else{
                mPaint.setColor(mTextColorDay);
            }

            int len = (int)FontUtil.getFontlength(mPaint, day+"");
            int x = left + (columnWidth - len)/2;
            canvas.drawText(day+"", x, top + mLineSpac + dayTextLeading, mPaint);

            //绘制次数
            mPaint.setTextSize(mTextSizePre);
            MainActivity.DayFinish finish = map.get(day);
            String preStr = "0/0";
            if(finish!=null){
                //区分完成未完成
                if(finish.finish >= finish.all) {
                    mPaint.setColor(mTextColorPreFinish);
                }else{
                    mPaint.setColor(mTextColorPreUnFinish);
                }
                preStr = finish.finish+"/"+finish.all;

            }else{
                mPaint.setColor(mTextColorPreUnFinish);
            }
            len = (int)FontUtil.getFontlength(mPaint, preStr);
            x = left + (columnWidth - len)/2;
            canvas.drawText(preStr, x, topPre + mTextSpac + preTextLeading, mPaint);
        }
    }

这部分完成之后,我们自定义日历的绘制工作就over了,下面我们看看效果图:

    

5、事件处理

  事件相关知识点也是自定义控件比较重要的内容,后面有空会详细介绍。下面我们看看这个控件需要处理那些事件。当点击箭头时需要增减月份,点击日期时需要置为选中。控件接受到事件之后,我要怎样知道点击的是箭头还是日期还是其他部位?只能通过事件的坐标计算了,如果在某个范围之内即可,在上面的分析图中,将控件划分成了很多小网格,这些小网格的坐标范围都是确定的(根据宽高等数据),事件发生后,只需要判断事件点坐标是否落入相应区域即可,然后边测试边修改一些细节问题,下面是事件处理先关的代码:

//焦点坐标
    private PointF focusPoint = new PointF();
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction() & MotionEvent.ACTION_MASK;
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                focusPoint.set(event.getX(), event.getY());
                touchFocusMove(focusPoint, false);
                break;
            case MotionEvent.ACTION_MOVE:
                focusPoint.set(event.getX(), event.getY());
                touchFocusMove(focusPoint, false);
                break;
            case MotionEvent.ACTION_OUTSIDE:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                focusPoint.set(event.getX(), event.getY());
                touchFocusMove(focusPoint, true);
                break;
        }
        return true;
    }

    /**焦点滑动*/
    public void touchFocusMove(final PointF point, boolean eventEnd) {
        Log.e(TAG, "点击坐标:("+point.x+" ,"+point.y+"),事件是否结束:"+eventEnd);
        /**标题和星期只有在事件结束后才响应*/
        if(point.y<=titleHeight){
            //事件在标题上
            if(eventEnd && listener!=null){
                if(point.x>=rowLStart && point.x<(rowLStart+2*mMonthRowSpac+rowWidth)){
                    Log.w(TAG, "点击左箭头");
                    listener.onLeftRowClick();
                }else if(point.x>rowRStart && point.x<(rowRStart + 2*mMonthRowSpac+rowWidth)){
                    Log.w(TAG, "点击右箭头");
                    listener.onRightRowClick();
                }else if(point.x>rowLStart && point.x <rowRStart){
                    listener.onTitleClick(getMonthStr(month), month);
                }
            }
        }else if(point.y<=(titleHeight+weekHeight)){
            //事件在星期部分
            if(eventEnd && listener!=null){
                //根据X坐标找到具体的焦点日期
                int xIndex = (int)point.x / columnWidth;
                Log.e(TAG, "列宽:"+columnWidth+"  x坐标余数:"+(point.x / columnWidth));
                if((point.x / columnWidth-xIndex)>0){
                    xIndex += 1;
                }
                if(listener!=null){
                    listener.onWeekClick(xIndex-1, WEEK_STR[xIndex-1]);
                }
            }
        }else{
            /**日期部分按下和滑动时重绘,只有在事件结束后才响应*/
            touchDay(point, eventEnd);
        }
    }

    //控制事件是否响应
    private boolean responseWhenEnd = false;
    /**事件点在 日期区域 范围内*/
    private void touchDay(final PointF point, boolean eventEnd){
        //根据Y坐标找到焦点行
        boolean availability = false;  //事件是否有效
        //日期部分
        float top = titleHeight+weekHeight+oneHeight;
        int foucsLine = 1;
        while(foucsLine<=lineNum){
            if(top>=point.y){
                availability = true;
                break;
            }
            top += oneHeight;
            foucsLine ++;
        }
        if(availability){
            //根据X坐标找到具体的焦点日期
            int xIndex = (int)point.x / columnWidth;
            if((point.x / columnWidth-xIndex)>0){
                xIndex += 1;
            }
//            Log.e(TAG, "列宽:"+columnWidth+"  x坐标余数:"+(point.x / columnWidth));
            if(xIndex<=0)
                xIndex = 1;   //避免调到上一行最后一个日期
            if(xIndex>7)
                xIndex = 7;   //避免调到下一行第一个日期
//            Log.e(TAG, "事件在日期部分,第"+foucsLine+"/"+lineNum+"行, "+xIndex+"列");
            if(foucsLine == 1){
                //第一行
                if(xIndex<=firstIndex){
                    Log.e(TAG, "点到开始空位了");
                    setSelectedDay(selectDay, true);
                }else{
                    setSelectedDay(xIndex-firstIndex, eventEnd);
                }
            }else if(foucsLine == lineNum){
                //最后一行
                if(xIndex>lastLineNum){
                    Log.e(TAG, "点到结尾空位了");
                    setSelectedDay(selectDay, true);
                }else{
                    setSelectedDay(firstLineNum + (foucsLine-2)*7+ xIndex, eventEnd);
                }
            }else{
                setSelectedDay(firstLineNum + (foucsLine-2)*7+ xIndex, eventEnd);
            }
        }else{
            //超出日期区域后,视为事件结束,响应最后一个选择日期的回调
            setSelectedDay(selectDay, true);
        }
    }
    /**设置选中的日期*/
    private void setSelectedDay(int day, boolean eventEnd){
        Log.w(TAG, "选中:"+day+"  事件是否结束"+eventEnd);
        selectDay = day;
        invalidate();
        if(listener!=null && eventEnd && responseWhenEnd && lastSelectDay!=selectDay) {
            lastSelectDay = selectDay;
            listener.onDayClick(selectDay, getMonthStr(month) + selectDay + "日", map.get(selectDay));
        }
        responseWhenEnd = !eventEnd;
    }

最终效果如下:

    

  本篇博客讲解没有特别细致,但是关键的思路已经很清晰了,其实自定义控件也就那么会事儿,在之前自定义控件系列博客及案例中已经讲解的非常详细了;如果后面我再更新自定义系列文章也将侧重讲解思路,知识点不熟悉的还请移步自定义控件基础

源码下载:

注:没有积分的童鞋 请点赞留言索要代码

http://download.csdn.net/detail/u010163442/9728781

Android自定义View(CustomCalendar-定制日历控件)的更多相关文章

  1. Android 自定义View之自绘控件

    首先要提前声明一下,我对于自定义View的理解并不是很深,最近啃了几天guolin博主写的关于自定义View的博客,讲的非常棒,只不过涉及到源码和底层的一些东西,我自己就懵逼了,目前只是会了关于自定义 ...

  2. Android自己定义组件之日历控件-精美日历实现(内容、样式可扩展)

    需求 我们知道.Android系统本身有自带的日历控件,网络上也有非常多开源的日历控件资源.可是这些日历控件往往样式较单一.API较多.不易于在实际项目中扩展并实现出符合详细样式风格的,内容可定制的效 ...

  3. Android 自定义简易的方向盘操作控件

    最近在做一款交互性较为复杂的APP,需要开发一个方向操作控件.最终用自定义控件做了一个简单的版本. 这里我准备了两张素材图,作为方向盘被点击和没被点击的背景图.下面看看自定义的Wheel类 publi ...

  4. Android 4.0之后的日历控件拥挤的解决办法

    本意是想做成这个样子的控件: 发现使用datepicker之后,效果完全不同,把整个日历都显示出来了.非常拥挤. 在datepicker中加入android:calendarViewShown=&qu ...

  5. Android 一个日历控件的实现代码

    转载  2017-05-19   作者:Othershe   我要评论 本篇文章主要介绍了Android 一个日历控件的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看 ...

  6. android自定义View之NotePad出鞘记

    现在我们的手机上基本都会有一个记事本,用起来倒也还算方便,记事本这种东东,如果我想要自己实现,该怎么做呢?今天我们就通过自定义View的方式来自定义一个记事本.OK,废话不多说,先来看看效果图. 整个 ...

  7. android自定义view之---组合view

    最近工作比较轻松,没有什么事情干,于是进入高产模式(呃....高产似xx). 应该很多童鞋对自定义view这个东西比较抵触,可能是听网上说view比较难吧,其实自定义view并没有很难 自定义view ...

  8. Android 自定义View修炼-自定义弹幕效果View

    一.概述 现在有个很流行的效果就是弹幕效果,满屏幕的文字从右到左飘来飘去.看的眼花缭乱,看起来还蛮cool的 现在就是来实现这一的一个效果,大部分的都是从右向左移动漂移,本文的效果中也支持从左向右的漂 ...

  9. Android自定义View和控件之一-定制属于自己的UI

    照例,拿来主义.我的学习是基于下面的三篇blog.前两是基本的流程,第三篇里有比较细致的绘制相关的属性.第4篇介绍了如何减少布局层次来提高效率. 1. 教你搞定Android自定义View 2. 教你 ...

随机推荐

  1. Hive:动静态分区

    http://hugh-wangp.iteye.com/blog/1612268 http://blog.csdn.net/opensure/article/details/46537969 使用静态 ...

  2. Spring之事务管理

        事务管理对于企业应用至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.     就像银行的自助取款机,通常都能正常为客户服务,但是也难免遇到 ...

  3. MySQL 5.7 新特性之增强半同步复制

    1. 背景介绍 半同步复制 普通的replication,即mysql的异步复制,依靠mysql二进制日志也即binary log进行数据复制.比如两台机器,一台主机(master),另外一台是从机( ...

  4. ES6 new syntax of Rest and Spread Operators

    Rest and Spread Operators.md Why we need rest and spread operators? var showCollections = function(i ...

  5. 【温故而知新】HTTP 概述

    什么是 HTTP 官方解释是 "因特网的多媒体信使",通俗点说,就是个送信的.电话机出来之前,人与人(有一定距离)之间的沟通基本靠写信,然后由快递员送发.如果把 web 服务器和客 ...

  6. “百度杯”CTF比赛 九月场_再见CMS(齐博cms)

    题目在i春秋ctf大本营 又是一道cms的题,打开御剑一通乱扫,发现后台登录地址,访问一看妥妥的齐博cms 记得以前很久以前利用一个注入通用漏洞,这里我贴上链接,里面有原理与利用方法详细说明: 齐博c ...

  7. [POJ 2104]K-th Number【模板】(主席树)

    题目背景 这是个非常经典的主席树入门题——静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...

  8. [POI2006]OKR-Periods of Words

    题目描述 一个串是有限个小写字符的序列,特别的,一个空序列也可以是一个串. 一个串P是串A的前缀, 当且仅当存在串B, 使得 A = PB. 如果 P A 并且 P 不是一个空串,那么我们说 P 是A ...

  9. bzoj 3751: [NOIP2014]解方程

    Description 已知多项式方程: a0+a1x+a2x^2+...+an*x^n=0 求这个方程在[1,m]内的整数解(n和m均为正整数). 解题报告: 这题比较诡,看到高精度做不了,就要想到 ...

  10. hdu 1828 线段树扫描线(周长)

    Picture Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...