前言
之前博客就写过关于日历的基本功能,无论是用QWidgets还是用QML。然而我一直想要实现像安卓日历的那种如图效果:下拉展开、上滑收住这种效果,但是因为没思路就搁置了,直到我遇到了Flickable。
Flickable很好用,也很常用,比如列表ListView就基于Flickable,有了它,可以显示更多的内容,还携带者一些动画效果,用法也简单,我真的是要吹爆它。
效果图
我这个在电脑端调试的,还没在手机端试过,按理说应该没多大问题。
知识点和代码
Flickable的一些知识点
先透个底,我对Flickable的属性和方法,通过帮助文档有一定的了解,但是有些具体怎么结合着用,我还是不清楚,比如:flickDeceleration减速度,这个与谁结合发挥作用?是不是与方法flick(xVelocity, qreal yVelocity)?但是我速度 、减速度的公式忘记了。。。
算了先说我知道的吧,上面的问题,有人会,告诉我一声,不然,不然我也不能怎么样。
Flickable有四个属性是必须要知道的,
width 和height :这个Flickable的宽和高,就是可视的那个框框的大小
contentWidth 和 contentHeight:这个是内容的宽和高,就是内容条的大小
还有需要注意的是:Flickable里面的项包括它自己不能用id来anchor,而用parent代替,不然你将会看到偏离你设计的界面。如果你嫌弃用parent来锚布局(anchor)麻烦,你可以在Flickable外面包一层Item,上层的项用id 进行锚布局,Flickable可管不住。
上面四个属性够平时用了,不过我这次为了控制Flickable滑动的位置(使其固定停到某个位置),我还用到了另外一属性:
contentY:内容滑动到的Y的值,比如向下滑动100px,contentY将增加100。
代码实现
我将整体分为三部分:年月显示头(CalendarHeader)、当前选中周(WeekRow)显示和日历(calendarItem)显示。年月显示简单,只需要获取当前Date的年月就可以了;我们可以看到当日历收起来时,才会显示选中周的情况,所以这个要根据日历的行为进行显示或隐藏;日历怎需要根据手势的滑动,进行显示或隐藏。
现在思路捋顺了,代码也就出来了,日历部分的代码可参考之前的博客,当前选中周的代码我也不提供了(用ListView实现的,很简单),整体的逻辑界面如下:
import QtQuick 2.12
Item {
id: myCal;
property bool isHide:false;
CalendarHeader{
id:header;
z:5;
anchors{left: parent.left; top: parent.top;right: parent.right;}
}
WeekRow{
id:weekRow;
z:5;
anchors{left: parent.left; right: parent.right; top:header.bottom;}
height: myCal.isHide? 77:35;
selectVisible: myCal.isHide;
}
Item{
id: calendarItem;
z:2;
anchors{left: parent.left; right: parent.right; top: weekRow.bottom; bottom: parent.bottom;}
Flickable{
id:myflick;
anchors.fill: parent;
contentHeight: 1000; contentWidth: parent.width;
MyCalendarWidget{
id:widget;
anchors{left: parent.left; right: parent.right; top: parent.top;}
onClicked: {
updateHeader();
}
}
Rectangle{
id:btn;
anchors{left: parent.left; right: parent.right; top: parent.top;
topMargin: widget.height;}
height: 10;
color: "#00000000";
property string downimg: "qrc:/res/down.png"
property string img: "qrc:/res/zk.png"
Image {
height: 15; width: 50;
anchors.centerIn: parent;
source: myCal.isHide ? btn.downimg : btn.img;
}
}
onFlickStarted: {
if(myCal.isHide)
{
myflick.contentY=0;
}
else
{
myflick.contentY=widget.height;
var data=packWeekData();
weekRow.updateSelectedWeek(data);
}
myCal.isHide=!myCal.isHide;
}
}
}
//将一周的数据以json的格式打包返回
function packWeekData()
{
var selectedDay=widget.selectedDate.getDate();
var arr=getWeekDateRangeByday();
var arr1=new Array(7);
for(var i=0; i<arr.length; i++)
{
var obj=new Object;
obj.day=arr[i].toString();
if(arr[i]===selectedDay)
obj.selected=true;
else
obj.selected=false;
arr1[i]=obj;
}
var jsonData=JSON.stringify(arr1);
return jsonData;
}
//获取选中日期的一周数据 数组方式返回
function getWeekDateRangeByday()
{
var myArray=new Array(7);
var date=widget.selectedDate;
var weekNum=date.getDay();
var sunDate=new Date(date);
sunDate.setDate(date.getDate()-weekNum);
myArray[0]=sunDate.getDate();
var dateVal=new Date(sunDate);
for(var i=1; i<=6; i++)
{
dateVal.setDate(sunDate.getDate()+i);
myArray[i]=dateVal.getDate();
}
return myArray;
}
function updateHeader()
{
var year=widget.selectedDate.getFullYear();
var month=widget.selectedDate.getMonth()+1;
header.yearTxt=year.toString();
header.monthTxt=month.toString()+"月";
}
Component.onCompleted: {
updateHeader();
}
}
结束语
在这个日历控件做出后,如果后续写个待办事项,计划啥日历框,方便多了。