美文网首页Android知识
自定义日历控件

自定义日历控件

作者: stefanJi | 来源:发表于2017-05-28 23:27 被阅读0次

    Android自定义日历控件(继承系统控件实现)

    主要步骤

    1. 编写布局
    2. 继承LinearLayout设置子控件
    3. 设置数据
    4. 继承TextView实现有圆圈背景的TextView
    5. 添加Attribute
    6. 添加长按事件

    1.编写布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <RelativeLayout
            android:id="@+id/header"
            android:layout_width="match_parent"
            android:layout_height="30dp">
    
            <ImageView
                android:id="@+id/btn_pre"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:src="@mipmap/ic_launcher" />
    
            <ImageView
                android:id="@+id/btn_next"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_alignParentEnd="true"
                android:layout_alignParentRight="true"
                android:src="@mipmap/ic_launcher" />
    
            <TextView
                android:id="@+id/txtData"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_centerInParent="true"
                android:gravity="center"
                android:text="@string/app_name" />
    
        </RelativeLayout>
    
        <LinearLayout
            android:id="@+id/week_header"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:orientation="horizontal">
    
            <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="1" />
    
            <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="2" />
    
            <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="3" />
    
            <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="4" />
    
            <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="5" />
    
            <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="6" />
    
            <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="7" />
        </LinearLayout>
    
        <GridView
            android:id="@+id/grid"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:numColumns="7" />
    
    </LinearLayout>
    

    2.继承LinearLayout设置子控件

    public class CalendarView extends LinearLayout {
        private ImageView btnPre;
        private ImageView btnNext;
        private TextView txtData;
        private GridView gridView;
        private Calendar calendar = Calendar.getInstance();
        private String displayFormat;
        public NewViewListener viewListener;
    
        public void setViewListener(NewViewListener viewListener) {
            this.viewListener = viewListener;
        }
    
        public CalendarView(Context context) {
            super(context);
        }
    
        public CalendarView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            initControl(context, attrs);
        }
    
        public CalendarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initControl(context, attrs);
        }
    
        private void initControl(Context context, AttributeSet attributeSet) {
            bindControl(context);
            bindControlEvent();
            setAttribute(attributeSet);
            renderCalender();
        }
    
        private void bindControl(Context context) {
            LayoutInflater.from(context).inflate(R.layout.new_view, this);
            btnNext = (ImageView) findViewById(R.id.btn_next);
            btnPre = (ImageView) findViewById(R.id.btn_pre);
            txtData = (TextView) findViewById(R.id.txtData);
            gridView = (GridView) findViewById(R.id.grid);
        }
    
        private void bindControlEvent() {
            btnNext.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    calendar.add(Calendar.MONTH, +1);
                    renderCalender();
                }
            });
            btnPre.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    calendar.add(Calendar.MONTH, -1);
                    renderCalender();
                }
            });
        }
     }
    

    3.设置数据

    private void renderCalender() {
    
            //返回"月+日"格式的数据
            SimpleDateFormat sdf = new SimpleDateFormat(displayFormat, Locale.CHINA);
            txtData.setText(sdf.format(calendar.getTime()));
    
            ArrayList<Date> cells = new ArrayList<>();
    
            Calendar calendar2 = (Calendar) this.calendar.clone();
            //设置月的第一天为1
            calendar2.set(Calendar.DAY_OF_MONTH, 1);
    
            //获取当前周数的前一天
            int prevDays = calendar2.get(Calendar.DAY_OF_WEEK) - 1;
            //运算日历加上加前一周
            calendar2.add(Calendar.DAY_OF_MONTH, -prevDays);
    
            int maxCellCount = 6 * 7;
    
            //将日历里的日期数据添加到ArrayList
            while (cells.size() < maxCellCount) {
                cells.add(calendar2.getTime());
                calendar2.add(Calendar.DAY_OF_MONTH, 1);
            }
    
            gridView.setAdapter(new MyAdapter(getContext(), cells));
            gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
                @Override
                public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                    if (viewListener == null) {
                        return false;
                    } else {
                        viewListener.onItemLongPress((Date) parent.getItemAtPosition(position));
                        return true;
                    }
                }
            });
        }
    
        private class MyAdapter extends ArrayAdapter<Date> {
    
            LayoutInflater layoutInflater;
    
            MyAdapter(@NonNull Context context, ArrayList<Date> dates) {
                super(context, R.layout.calendar_text_day, dates);
                layoutInflater = LayoutInflater.from(context);
            }
    
            @NonNull
            @Override
            public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
                Date date = getItem(position);
                if (convertView == null) {
                    convertView = layoutInflater.inflate(R.layout.calendar_text_day, parent, false);
                }
                int day = date.getDate();
                ((CalendarTextView) convertView).setText(String.valueOf(day));
                Date now = new Date();
    
                boolean isSameMonth = false;
                if (date.getMonth() == now.getMonth()) {
                    isSameMonth = true;
                }
    
                if (isSameMonth) {
                    ((CalendarTextView) convertView).setTextColor(Color.DKGRAY);
                }
    
                if (now.getDate() == date.getDate() && now.getMonth() == date.getMonth() && now.getYear() == date.getYear()) {
                    ((CalendarTextView) convertView).setNow(true);
                }
                return convertView;
            }
        }
    

    4. 圆圈背景TextView

    public class CalendarTextView extends AppCompatTextView {
        private Paint paint;
        private boolean isNow = false;
    
        public void setNow(boolean now) {
            isNow = now;
        }
    
        public CalendarTextView(Context context) {
            super(context);
        }
    
        public CalendarTextView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            initControl();
        }
    
        public CalendarTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initControl();
        }
    
    
        private void initControl() {
            paint = new Paint();
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(Color.RED);
            paint.setStrokeWidth(2);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (isNow) {
                canvas.translate(getWidth() / 2, getHeight() / 2);
                canvas.drawCircle(0, 0, getWidth() / 2, paint);
                setTextColor(Color.RED);
            }
        }
    }
    

    5.添加Attribute

    1. 添加attrs文件

      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <declare-styleable name="CalendarView">
              <attr name="dateFormat" format="string" />
          </declare-styleable>
      </resources>
      
    2. 将Arrtibute参数设置到控件

      private void setAttribute(AttributeSet attributeSet) {
          TypedArray ta = getContext().obtainStyledAttributes(attributeSet, R.styleable.CalendarView);
          try {
              displayFormat = ta.getString(R.styleable.CalendarView_dateFormat);
              if (displayFormat == null) {
                  displayFormat = "MMM yyy";
              }
          } finally {
              ta.recycle();
          }
      }
      
    3. 在布局中加入命名空间引入

      <com.example.jiyang.newview.CalendarView xmlns:CalendarView="http://schemas.android.com/apk/res/com.example.jiyang.newview"
          android:id="@+id/newView"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:padding="10dp"
          CalendarView:dateFormat="MMMM yyyy" />
      

    6.添加长按事件

    1. 定义长按接口

      public interface NewViewListener {
          void onItemLongPress(Date day);
      }
      
    2. CalenderView中调用

    gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
    if (viewListener == null) {
    return false;
    } else {
    viewListener.onItemLongPress((Date) parent.getItemAtPosition(position));
    return true;
    }
    }
    });
    ```

    1. Activity中实现接口方法

    @Override
    public void onItemLongPress(Date day) {
    DateFormat df = SimpleDateFormat.getDateInstance();
    Toast.makeText(this, df.format(day), Toast.LENGTH_SHORT).show();
    }
    ```

    总结

    • Attribute的使用

      1. 定义attrs文件
      2. 注意命名空间。自定义View不能使用app:,而要使用xmlns:自定义一个命名空间
      3. TypedArray放入try{}finaly{}中,要在finally中释放TypedArray typedArray.recycle()
    • 通过继承布局实现自定义View时,加载布局
      需要LayoutInflater.from(context).inflate(R.layout.new_view, this);不能LayoutInflater.from(context).inflate(R.layout.new_view, this,false);

    相关文章

      网友评论

        本文标题:自定义日历控件

        本文链接:https://www.haomeiwen.com/subject/tzxkfxtx.html