广告
返回顶部
首页 > 资讯 > 移动开发 >Android自定义view实现日历打卡签到
  • 868
分享到

Android自定义view实现日历打卡签到

2024-04-02 19:04:59 868人浏览 八月长安
摘要

本文实例为大家分享了Android自定义view实现日历打卡签到的具体代码,供大家参考,具体内容如下 1.说明 自己写一个view实现每天签到的功能,设置背景图片 源码下载 2.效果

本文实例为大家分享了Android自定义view实现日历打卡签到的具体代码,供大家参考,具体内容如下

1.说明

自己写一个view实现每天签到的功能,设置背景图片

源码下载

2.效果图

3.主界面


package com.example.myapplication30;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
 
public class MainActivity extends AppCompatActivity {
    //参考网址:https://blog.csdn.net/Macaopark/article/details/102069775
    private TextView mTvDaySum;
    private TextView mTvMonth;
    private SignView mCvCalendar;
    private List<SignEntity> data;
    private Calendar calendar;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTvDaySum = findViewById(R.id.punch_tv_day_sum);
        mTvMonth = findViewById(R.id.punch_tv_month);
        mCvCalendar = findViewById(R.id.punch_cv_calendar);
    }
 
    @Override
    protected void onStart() {
        super.onStart();
        onReady();
    }
 
    @SuppressLint("SetTextI18n")
    private void onReady() {
          calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        //int date =  calendar.get(Calendar.DATE);
        int dayOfMonthToday = calendar.get(Calendar.DAY_OF_MONTH);
        List<SignDate> signDates = new ArrayList<>();
        signDates.add(new SignDate(2021, 5, 1, true));
        signDates.add(new SignDate(2021, 5, 2, true));
        signDates.add(new SignDate(2021, 5, 3, true));
        signDates.add(new SignDate(2021, 5, 4, true));
        signDates.add(new SignDate(2021, 5, 5, true));
 
        mTvDaySum.setText("本期连续登录\t"+signDates.size()+"\t天");
        mTvMonth.setText(year+"年"+getResources().getStringArray(R.array.month_array)[month]+"\t"+dayOfMonthToday+"日");
 
        data = new ArrayList<>();
        for (int i = 1; i <= dayOfMonthToday; i++) {
            SignEntity signEntity = new SignEntity();
            if (i == dayOfMonthToday) {
                signEntity.setDayType(2);
            } else {
                signEntity.setDayType(1);
            }
            for (int j = 0; j < signDates.size(); j++) {
                if (signDates.get(j).getDay() == i) {
                    signEntity.setDayType(0);
                    break;
                } else if (dayOfMonthToday == i) {
                    signEntity.setDayType(2);
                } else {
                    signEntity.setDayType(1);
                }
            }
            data.add(signEntity);
        }
 
        SignAdapter signAdapter = new SignAdapter(data);
        mCvCalendar.setAdapter(signAdapter);
    }
 
}

4.适配器


package com.example.myapplication30;
 
 
import java.util.List;
 

public class SignAdapter extends CalendarAdapter {
    private List<SignEntity> data;
 
    public SignAdapter(List<SignEntity> data) {
        this.data = data;
    }
 
    @Override
    public SignView.DayType getType(int dayOfMonth) {
        return SignView.DayType.valueOf(data.get(dayOfMonth - 1).getDayType());
    }
}

5.自定义view


package com.example.myapplication30;
 
 
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.canvas;
import android.graphics.Color;
import android.graphics.ComposeShader;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
 
 
import java.util.Calendar;
 

public class SignView extends View {
    private static final String[] WEEK_MARK = {"一", "二", "三", "四", "五", "六", "日"};
 
    private static final int MAX_COLUMN = 7;
    
    private static final int COLOR_MARKER_WEEKDAY = 0xFF999999;
    private static final int COLOR_MARKER_WEEKEND = 0xFF1B89CD;
    
    //private static final int COLOR_BACKGROUND_HIGHLIGHT = 0xFFFF0000;
    
    private static final int COLOR_BACKGROUND_NORMAL = 0xFF9C9C9C;
    
    private static final int COLOR_BACKGROUND_WAIT = 0xFFFE7471;
    
    private static final int COLOR_TEXT_HIGHLIGHT = 0xFFFFFFFF;
    
    private static final int COLOR_TEXT_NORMAL = 0xFF606060;
//    
//    private static final int COLOR_TEXT_DISABLED = 0xFFD4D4D4;
 
    private static final int MARKER_TEXT_SIZE = 40;
    private static final int CELL_TEXT_SIZE = 40;
 
    private static final int VERTICAL_SPACE = 51;
    private static final int VERTICAL_MARGIN = 62;
    private static final int HORIZONTAL_MARGIN = 39;
    private static final int CELL_SIZE = 80;
    private static final int WAIT_LINE_SIZE = 14;
 
    private int dayOfMonthToday;
    private int markerTextY;
    private int verticalCellTop;
    private int sumDayOfMonth;
    private int daysOfFirstWeek;
    private int horizontalSpace;
    private int deltaTextCellY;
    private int deltaTextMarkerY;
 
    private int verticalSpace;
    private int verticalMargin;
    private int horizontalMargin;
    private int cellSize;
    private int waitLineSize;
 
    private Path waitPath;
    private Rect waitRect;
    private Paint paintWeekday;
    private Paint paintWeekend;
    private Paint paintTextNormal;
    private Paint paintTextHighlight;
    private Paint paintBackgroundWait;
    private Paint paintBackgroundNormal;
    private Paint paintBackgroundHighlight;
    private CalendarAdapter adapter;
 
    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);
        initResolution();
        initPaint();
        initData();
    }
 
    private void initResolution() {
//        resolutionUtil = ResolutionUtil.getInstance();
//        verticalSpace = resolutionUtil.formatVertical(VERTICAL_SPACE);
//        verticalMargin = resolutionUtil.formatVertical(VERTICAL_MARGIN);
//        horizontalMargin = resolutionUtil.formatHorizontal(HORIZONTAL_MARGIN);
//        cellSize = resolutionUtil.formatVertical(CELL_SIZE);
//        waitLineSize = resolutionUtil.formatVertical(WAIT_LINE_SIZE);
        verticalSpace = VERTICAL_SPACE;
        verticalMargin = VERTICAL_MARGIN;
        horizontalMargin = HORIZONTAL_MARGIN;
        cellSize = CELL_SIZE;
        waitLineSize = WAIT_LINE_SIZE;
 
    }
 
    private void initPaint() {
//        int markerTextSize = resolutionUtil.formatVertical(MARKER_TEXT_SIZE);
//        int cellTextSize = resolutionUtil.formatVertical(CELL_TEXT_SIZE);
        int markerTextSize = MARKER_TEXT_SIZE;
        int cellTextSize = CELL_TEXT_SIZE;
 
        paintWeekday = new Paint();
        paintWeekday.setAntiAlias(true);
        paintWeekday.setColor(COLOR_MARKER_WEEKDAY);
        paintWeekday.setTextSize(markerTextSize);
        paintWeekday.setTextAlign(Paint.Align.CENTER);
 
        paintWeekend = new Paint();
        paintWeekend.setAntiAlias(true);
        paintWeekend.setColor(COLOR_MARKER_WEEKEND);
        paintWeekend.setTextSize(markerTextSize);
        paintWeekend.setTextAlign(Paint.Align.CENTER);
 
        paintTextNormal = new Paint();
        paintTextNormal.setAntiAlias(true);
        paintTextNormal.setColor(COLOR_TEXT_NORMAL);
        paintTextNormal.setTextSize(cellTextSize);
        paintTextNormal.setTextAlign(Paint.Align.CENTER);
 
        paintTextHighlight = new Paint();
        paintTextHighlight.setAntiAlias(true);
        paintTextHighlight.setColor(COLOR_TEXT_HIGHLIGHT);
        paintTextHighlight.setTextSize(cellTextSize);
        paintTextHighlight.setTextAlign(Paint.Align.CENTER);
 
        paintBackgroundWait = new Paint();
        paintBackgroundWait.setAntiAlias(true);
        paintBackgroundWait.setColor(COLOR_BACKGROUND_WAIT);
        paintBackgroundWait.setStrokeWidth(2);
        paintBackgroundWait.setStyle(Paint.Style.STROKE);
 
        paintBackgroundNormal = new Paint();
        paintBackgroundNormal.setAntiAlias(true);
        paintBackgroundNormal.setColor(COLOR_BACKGROUND_NORMAL);
        paintBackgroundNormal.setStrokeWidth(2);
        paintBackgroundNormal.setStyle(Paint.Style.STROKE);
 
        paintBackgroundHighlight = new Paint();
        paintBackgroundHighlight.setAntiAlias(true);
        paintBackgroundHighlight.setStrokeWidth(2);
        paintBackgroundHighlight.setStyle(Paint.Style.FILL);
        //颜色
        //paintBackgroundHighlight.setColor(COLOR_BACKGROUND_HIGHLIGHT);
        //多种颜色数组
        //int[] colors = {Color.RED,Color.GREEN,Color.BLUE,Color.YELLOW,Color.MAGENTA};
        //float[] position = {0f, 0.2f, 0.4f, 0.6f, 1.0f};
        //Shader shader1 = new LinearGradient(100,850,600,850,colors,position,Shader.TileMode.CLAMP);
        //paintBackgroundHighlight.setShader(shader1);
        //设置背景图片
       
 
 
    }
 
    private void initData() {
        Paint.FontMetricsInt fmiMarker = paintWeekday.getFontMetricsInt();
        deltaTextMarkerY = -(fmiMarker.bottom - fmiMarker.top) / 2 - fmiMarker.top;
        markerTextY = verticalMargin + cellSize / 2;
 
        Paint.FontMetricsInt fmiCell = paintTextNormal.getFontMetricsInt();
        deltaTextCellY = -(fmiCell.bottom - fmiCell.top) / 2 - fmiCell.top;
        verticalCellTop = verticalMargin + cellSize;
 
        Calendar calendartoday = Calendar.getInstance();
        dayOfMonthToday = calendarToday.get(Calendar.DAY_OF_MONTH);
        int dayOfWeek;
        sumDayOfMonth = calendarToday.getActualMaximum(Calendar.DAY_OF_MONTH);
 
        Calendar calendarFirstDay = Calendar.getInstance();
        calendarFirstDay.set(Calendar.DAY_OF_MONTH, 1);
        dayOfWeek = calendarFirstDay.get(Calendar.DAY_OF_WEEK);
        if (dayOfWeek == Calendar.SUNDAY) {
            dayOfWeek = 7;
        } else {
            dayOfWeek = dayOfWeek - 1;
        }
        daysOfFirstWeek = MAX_COLUMN - dayOfWeek + 1;
    }
 
    private void createWaitBackground(int topX, int topY) {
        waitPath = new Path();
        waitPath.moveTo(topX, topY + waitLineSize);
        waitPath.lineTo(topX, topY);
        waitPath.lineTo(topX + waitLineSize, topY);
 
        waitPath.moveTo(topX + cellSize - waitLineSize, topY + cellSize);
        waitPath.lineTo(topX + cellSize, topY + cellSize);
        waitPath.lineTo(topX + cellSize, topY + cellSize - waitLineSize);
 
        waitRect = new Rect(topX, topY, topX + cellSize, topY + cellSize);
    }
 
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        horizontalSpace = (w - MAX_COLUMN * cellSize - horizontalMargin * 2) / (MAX_COLUMN - 1);
    }
 
    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        drawWeekMark(canvas);
        drawCellsBackground(canvas);
        drawCells(canvas);
    }
 
    private void drawWeekMark(Canvas canvas) {
        int y = markerTextY + deltaTextMarkerY;
        for (int i = 0; i < 7; i++) {
            int x = horizontalMargin + i * (horizontalSpace + cellSize)
                    + cellSize / 2;
            if (i < 5) {
                canvas.drawText(WEEK_MARK[i], x, y, paintWeekday);
            } else {
                canvas.drawText(WEEK_MARK[i], x, y, paintWeekend);
            }
        }
    }
 
    private void drawCellsBackground(Canvas canvas) {
        for (int i = 1; i <= dayOfMonthToday; i++) {
            drawCellBackground(canvas, i, getColumnIndex(i), getRowIndex(i));
        }
    }
 
    
    private void drawCellBackground(Canvas canvas, int dayOfMonth, int column, int row) {
        int x = horizontalMargin + column * (horizontalSpace + cellSize)
                + cellSize / 2;
        int y = verticalCellTop + verticalSpace * (row + 1) + cellSize * row + cellSize / 2;
        if (adapter != null) {
            DayType dayType = adapter.getType(dayOfMonth);
            switch (dayType) {
                case WAITING:
                    if (waitPath == null) {
                        createWaitBackground(x - cellSize / 2, y - cellSize / 2);
                    }
                    canvas.drawPath(waitPath, paintBackgroundWait);
                    break;
                case SIGNED:
                 //     canvas.drawCircle(x, y, cellSize/2, paintBackgroundHighlight);
                //  canvas.drawRect(x - 60, y - 60, x + 60, y + 60, paintBackgroundHighlight);// 正方形
                   // Bitmap placeholder = BitmapFactory.decodeResource(getResources(), R.mipmap.purtest);
                 //   canvas.drawBitmap(placeholder,);wCircle(x, y, cellSize/2, paintBackgroundHighlight);
                    canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.small3),x-40, y-40 , paintBackgroundHighlight);
                    break;
                default:
                    canvas.drawCircle(x, y, cellSize / 2, paintBackgroundNormal);
                    break;
            }
        } else {
            canvas.drawCircle(x, y, cellSize / 2, paintBackgroundNormal);
        }
    }
 
    private void drawCells(Canvas canvas) {
        for (int i = 1; i <= sumDayOfMonth; i++) {
            drawCell(canvas, i, getColumnIndex(i), getRowIndex(i));
        }
    }
 
    
    private void drawCell(Canvas canvas, int dayOfMonth, int column, int row) {
        int x = horizontalMargin + column * (horizontalSpace + cellSize)
                + cellSize / 2;
        int y = verticalCellTop + verticalSpace * (row + 1) + cellSize * row + cellSize / 2
                + deltaTextCellY;
        if (adapter != null && dayOfMonth <= dayOfMonthToday) {
            DayType dayType = adapter.getType(dayOfMonth);
            Paint paint;
            switch (dayType) {
                case SIGNED:
                    paint = paintTextHighlight;
                    break;
                default:
                    paint = paintTextNormal;
                    break;
            }
            canvas.drawText(String.valueOf(dayOfMonth), x, y, paint);
        } else {
            canvas.drawText(String.valueOf(dayOfMonth), x, y, paintTextNormal);
        }
    }
 
    
    private int getColumnIndex(int dayOfMonth) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        if (dayOfWeek == Calendar.SUNDAY) {
            dayOfWeek = 6;
        } else {
            dayOfWeek = dayOfWeek - 2;
        }
        return dayOfWeek;
    }
 
    
    private int getRowIndex(int dayOfMonth) {
        float weight = (dayOfMonth - daysOfFirstWeek) / (MAX_COLUMN * 1f);
        double rowIndexDouble = Math.abs(Math.ceil(weight));
        return (int) rowIndexDouble;
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            float x = event.getX();
            float y = event.getY();
            if (waitPath != null) {
                if (adapter.getType(dayOfMonthToday).equals(DayType.WAITING)) {
                    if (x >= waitRect.left && y >= waitRect.top && x <= waitRect.right && y <= waitRect.bottom) {
                        if (onTodayClickListener != null) {
                            onTodayClickListener.onTodayClick();
                        }
                    }
                }
            }
        }
        return true;
    }
 
    public void setAdapter(CalendarAdapter adapter) {
        this.adapter = adapter;
        this.invalidate();
    }
 
    public int getDayOfMonthToday() {
        return dayOfMonthToday;
    }
 
    public void notifyDataSetChanged() {
        invalidate();
    }
 
    private OnTodayClickListener onTodayClickListener;
 
    public void setOnTodayClickListener(OnTodayClickListener onTodayClickListener) {
        this.onTodayClickListener = onTodayClickListener;
    }
 
    public interface OnTodayClickListener {
        void onTodayClick();
    }
 
    public enum DayType {
        
        SIGNED(0),
        
        UNSIGNED(1),
        
        WAITING(2),
        
        UNREACHABLE(3),
        
        DISABLED(4);
 
        private int value;
 
        DayType(int value) {
            this.value = value;
        }
 
        public int getValue() {
            return value;
        }
 
        public static DayType valueOf(int value) {
            switch (value) {
                case 0:
                    return SIGNED;
                case 1:
                    return UNSIGNED;
                case 2:
                    return WAITING;
                case 3:
                    return UNREACHABLE;
                case 4:
                    return DISABLED;
                default:
                    return DISABLED;
            }
        }
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: Android自定义view实现日历打卡签到

本文链接: https://www.lsjlt.com/news/125457.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android自定义view实现日历打卡签到
    本文实例为大家分享了Android自定义view实现日历打卡签到的具体代码,供大家参考,具体内容如下 1.说明 自己写一个view实现每天签到的功能,设置背景图片 源码下载 2.效果...
    99+
    2022-11-12
  • Android如何自定义view实现日历打卡签到
    这篇文章主要介绍Android如何自定义view实现日历打卡签到,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Android是什么Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备...
    99+
    2023-06-14
  • android自定义view之实现日历界面实例
    现在网上有很多自定义view实现日历的demo,今天讲一讲如何自己实现这个自定义view。 看一下最终效果图: 在这个自定义view中,我使用了各种奇技淫巧的方法来实现这个日...
    99+
    2022-06-06
    view 界面 Android
  • JavaScript自定义日历实现签到功能
    本文实例为大家分享了JavaScript自定义日历签到功能的具体代码,供大家参考,具体内容如下 先看下效果图 红色块为已签到的日期,样式可以随意更改,清晰明了,话不多说上代码: &...
    99+
    2022-11-13
  • Android实现自定义日历
    自定义日历控件,支持旧历、节气、日期标注、点击操作 (参考网络上的日历控件改写) 注:将下面的四张资源图片拷贝到所建包的下一个image目录中,如Calendar.java 所...
    99+
    2022-06-06
    自定义 Android
  • Android自定义实现日历控件
    本文实例为大家分享了Android自定义实现日历控件的具体代码,供大家参考,具体内容如下 1. Calendar类 2. 布局 创建calendar_layout.xml <...
    99+
    2022-11-12
  • Android自定义View实现标签流效果
    本文实例为大家分享了Android自定义View实现标签流效果的具体代码,供大家参考,具体内容如下 一、概述 Android自定义View实现标签流效果,一行放不下时会自动换行,用户...
    99+
    2022-11-13
  • Android Studio简单实现自定义日历
    本文实例为大家分享了Android Studio自定义日历的具体代码,供大家参考,具体内容如下 效果图: 目录树 1.DayBean.java用来存储每天的信息 package ...
    99+
    2022-11-13
  • Android自定义日历Calender代码实现
    产品要做签到功能,签到功能要基于一个日历来进行,所以就根据 要求自定义了一个日历 自定义控件相信做android都知道: (1)首先创建一个类,继承一个容器类或者是一个控...
    99+
    2022-06-06
    Android
  • Android自定义View实现打字机效果
    一、先来看看效果演示 二、实现原理: 这个其实不难实现,通过一个定时器不断调用TextView的setText就行了,在setText的时候播放打字的音效。 具体代码如下:...
    99+
    2022-06-06
    view 打字机 Android
  • Android如何自定义实现日历控件
    这篇文章主要介绍Android如何自定义实现日历控件,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!具体内容如下1. Calendar类2. 布局创建calendar_layout.xml<LinearLayou...
    99+
    2023-06-25
  • iOS自定义View实现卡片滑动
    本文实例为大家分享了iOS自定义View实现卡片滑动效果的具体代码,供大家参考,具体内容如下 说明 控件基于UIView封装完成,采用UIPanGestureRecognizer监听...
    99+
    2022-06-03
    iOS view 滑动
  • Android中怎么自定义View实现标签流效果
    本篇内容主要讲解“Android中怎么自定义View实现标签流效果”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Android中怎么自定义View实现标签流效果”吧!一、概述Android自定义...
    99+
    2023-06-29
  • Android自定义日历控件实例详解
    为什么要自定义控件 有时,原生控件不能满足我们对于外观和功能的需求,这时候可以自定义控件来定制外观或功能;有时,原生控件可以通过复杂的编码实现想要的功能,这时候可以自定义控件来...
    99+
    2022-06-06
    Android
  • Android自定义View实现验证码
    本文章是基于鸿洋的Android 自定义View (一) 的一些扩展,以及对Android自定义View构造函数详解里面内容的一些转载。 首先我们定义一个declare-sty...
    99+
    2022-06-06
    view 验证码 Android
  • java简单实现自定义日历
    本文实例为大家分享了java自定义日历的具体代码,供大家参考,具体内容如下 效果图: 源码: package com.example; import java.text.Simp...
    99+
    2022-11-13
  • Android如何实现自定义View中attrs.xml
    这篇文章主要为大家展示了“Android如何实现自定义View中attrs.xml”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Android如何实现自定义View中attrs.xml”这篇文章...
    99+
    2023-05-30
    android view attrs.xml
  • android自定义view实现钟表效果
    本文实例为大家分享了android view实现钟表的具体代码,供大家参考,具体内容如下 先看效果图: 自定义view大家肯定已经不陌生了,所以直接今天直接步入正题:如何利用...
    99+
    2022-06-06
    view Android
  • Android自定义View实现BMI指数条
    最近项目需要,需要做一个BMI指数的指示条,先上效果图: BMI指数从18到35,然后上面指示条的颜色会随着偏移量的变化而改变,数字显示当前的BMI指数,下面的BMI标准...
    99+
    2022-06-06
    view bmi Android
  • Android自定义View实现开关按钮
     前言:Android自定义View对于刚入门乃至工作几年的程序员来说都是非常恐惧的,但也是Android进阶学习的必经之路,平时项目中经常会有一些苛刻的需求,我们可...
    99+
    2022-06-06
    view 开关 按钮 Android
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作