android 日程安排view,RecyclerView 列表控件中简单实现时间线
時(shí)間
時(shí)間,時(shí)間,時(shí)間啊;走慢一點(diǎn)吧~
看見很多軟件中都有時(shí)間線的東西,貌似天氣啊,旅游啊什么的最多了;具體實(shí)現(xiàn)方式很多,在本篇文章中講解一種自定義View封裝的方式。
效果
先來(lái)看看效果。
分析
軟件中,可以看見前面的時(shí)間線也就是線條加上圓圈組成;當(dāng)然這里的圓圈與線條也都是可以隨意換成其他的,比如圖片等等。
當(dāng)然這里最簡(jiǎn)單的來(lái)說(shuō),是上面一個(gè)線條,然后一個(gè)圓圈,然后下面一個(gè)線條;上線條在第一條數(shù)據(jù)時(shí)不做顯示,下線條在最后一條數(shù)據(jù)時(shí)不做顯示。
這里自定義布局部分也就是把旁邊的線條與圓圈封裝到一起,并使用簡(jiǎn)單的方法來(lái)控制是否顯示。?當(dāng)封裝好了后,與旁邊的文字部分也就是水瓶方向的線性布局了,然后設(shè)置為每一個(gè)的RecyclerView 的Item的布局也就完成了。
控件
控件很簡(jiǎn)單,首先我們繼承View,取名為 TimeLineMarker 就OK。
Attrs 屬性
開始控件之前先準(zhǔn)備好需要的屬性。<?xml ?version="1.0"?encoding="utf-8"?>
在這里也就準(zhǔn)備了線條的大小、開始線條、結(jié)束線條、中間標(biāo)示部分及大小。
屬性與現(xiàn)實(shí)private?int?mMarkerSize?=?24;
private?int?mLineSize?=?12;
private?Drawable?mBeginLine;
private?Drawable?mEndLine;
private?Drawable?mMarkerDrawable;
@Override
protected?void?onDraw(Canvas?canvas)?{
if?(mBeginLine?!=?null)?{
mBeginLine.draw(canvas);
}
if?(mEndLine?!=?null)?{
mEndLine.draw(canvas);
}
if?(mMarkerDrawable?!=?null)?{
mMarkerDrawable.draw(canvas);
}
super.onDraw(canvas);
}
兩個(gè)大小屬性,3個(gè)具體的Drawable,然后在onDraw方法中進(jìn)行具體的顯示也就OK。
構(gòu)造與屬性初始化
在上面我們定義了屬性,在這里我們?cè)跇?gòu)造函數(shù)中獲取XML所設(shè)置的屬性。public?TimeLineMarker(Context?context)?{
this(context,?null);
}
public?TimeLineMarker(Context?context,?AttributeSet?attrs)?{
this(context,?attrs,?0);
}
public?TimeLineMarker(Context?context,?AttributeSet?attrs,?int?defStyle)?{
super(context,?attrs,?defStyle);
init(attrs);
}
private?void?init(AttributeSet?attrs)?{
//?Load?attributes
final?TypedArray?a?=?getContext().obtainStyledAttributes(
attrs,?R.styleable.TimeLineMarker,?0,?0);
mMarkerSize?=?a.getDimensionPixelSize(
R.styleable.TimeLineMarker_markerSize,
mMarkerSize);
mLineSize?=?a.getDimensionPixelSize(
R.styleable.TimeLineMarker_lineSize,
mLineSize);
mBeginLine?=?a.getDrawable(
R.styleable.TimeLineMarker_beginLine);
mEndLine?=?a.getDrawable(
R.styleable.TimeLineMarker_endLine);
mMarkerDrawable?=?a.getDrawable(
R.styleable.TimeLineMarker_marker);
a.recycle();
if?(mBeginLine?!=?null)
mBeginLine.setCallback(this);
if?(mEndLine?!=?null)
mEndLine.setCallback(this);
if?(mMarkerDrawable?!=?null)
mMarkerDrawable.setCallback(this);
}
Drawable 的位置與大小初始化
屬性啥的有了,具體的Drawable 也有了,要顯示的地方調(diào)用也是OK了;但是如果沒(méi)有進(jìn)行進(jìn)行具體的位置調(diào)整這一切也都沒(méi)有意義。@Override
protected?void?onSizeChanged(int?w,?int?h,?int?oldw,?int?oldh)?{
super.onSizeChanged(w,?h,?oldw,?oldh);
initDrawableSize();
}
private?void?initDrawableSize()?{
int?pLeft?=?getPaddingLeft();
int?pRight?=?getPaddingRight();
int?pTop?=?getPaddingTop();
int?pBottom?=?getPaddingBottom();
int?width?=?getWidth();
int?height?=?getHeight();
int?cWidth?=?width?-?pLeft?-?pRight;
int?cHeight?=?height?-?pTop?-?pBottom;
Rect?bounds;
if?(mMarkerDrawable?!=?null)?{
//?Size
int?markerSize?=?Math.min(mMarkerSize,?Math.min(cWidth,?cHeight));
mMarkerDrawable.setBounds(pLeft,?pTop,
pLeft?+?markerSize,?pTop?+?markerSize);
bounds?=?mMarkerDrawable.getBounds();
}?else?{
bounds?=?new?Rect(pLeft,?pTop,?pLeft?+?cWidth,?pTop?+?cHeight);
}
int?halfLineSize?=?mLineSize?>>?1;
int?lineLeft?=?bounds.centerX()?-?halfLineSize;
if?(mBeginLine?!=?null)?{
mBeginLine.setBounds(lineLeft,?0,?lineLeft?+?mLineSize,?bounds.top);
}
if?(mEndLine?!=?null)?{
mEndLine.setBounds(lineLeft,?bounds.bottom,?lineLeft?+?mLineSize,?height);
}
}
initDrawableSize 方法進(jìn)行具體的運(yùn)算,而運(yùn)算的時(shí)間點(diǎn)就是當(dāng)控件的大小改變(onSizeChanged)的時(shí)候。
在初始化中采用了一定的投機(jī)取巧;這里利用了上內(nèi)邊距與下內(nèi)邊距分別作為上線條與下線條的長(zhǎng)度;而線條與中間的標(biāo)識(shí)都采用了水平距中。
其他設(shè)置方法public?void?setLineSize(int?lineSize)?{
if?(mLineSize?!=?lineSize)?{
this.mLineSize?=?lineSize;
initDrawableSize();
invalidate();
}
}
public?void?setMarkerSize(int?markerSize)?{
if?(this.mMarkerSize?!=?markerSize)?{
mMarkerSize?=?markerSize;
initDrawableSize();
invalidate();
}
}
public?void?setBeginLine(Drawable?beginLine)?{
if?(this.mBeginLine?!=?beginLine)?{
this.mBeginLine?=?beginLine;
if?(mBeginLine?!=?null)?{
mBeginLine.setCallback(this);
}
initDrawableSize();
invalidate();
}
}
public?void?setEndLine(Drawable?endLine)?{
if?(this.mEndLine?!=?endLine)?{
this.mEndLine?=?endLine;
if?(mEndLine?!=?null)?{
mEndLine.setCallback(this);
}
initDrawableSize();
invalidate();
}
}
public?void?setMarkerDrawable(Drawable?markerDrawable)?{
if?(this.mMarkerDrawable?!=?markerDrawable)?{
this.mMarkerDrawable?=?markerDrawable;
if?(mMarkerDrawable?!=?null)?{
mMarkerDrawable.setCallback(this);
}
initDrawableSize();
invalidate();
}
}
在設(shè)置中,首先判斷是否更改,如果更改那么就更新并重新計(jì)算位置;隨后刷新界面。到這里,控件差不多準(zhǔn)備OK了,其中還有很多可以完善的地方,比如加上快捷設(shè)置顏色什么的,也可以加上大小計(jì)算的東西。同時(shí)還可以加上時(shí)間線是水瓶還是垂直等等。在這里就不累贅介紹哪些了。下面來(lái)看看如何使用。
使用
XML布局
ITEM布局item_time_line.xml<?xml ?version="1.0"?encoding="utf-8"?>
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="@dimen/lay_16"
android:paddingRight="@dimen/lay_16"
tools:ignore="MissingPrefix">
android:id="@+id/item_time_line_mark"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingBottom="@dimen/lay_16"
android:paddingLeft="@dimen/lay_4"
android:paddingRight="@dimen/lay_4"
android:paddingTop="@dimen/lay_16"
app:beginLine="@color/black_alpha_32"
app:endLine="@color/black_alpha_32"
app:lineSize="2dp"
app:marker="@drawable/ic_timeline_default_marker"
app:markerSize="24dp"?/>
android:id="@+id/item_time_line_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingBottom="@dimen/lay_16"
android:paddingLeft="@dimen/lay_4"
android:paddingRight="@dimen/lay_4"
android:paddingTop="@dimen/lay_16"
android:textColor="@color/grey_600"
android:textSize="@dimen/font_16"?/>
在這里我們之間使用順序布局,左邊是TimelIne控件,右邊是一個(gè)簡(jiǎn)單的字體控件,具體使用中可以細(xì)化一些。?在TImeLine控件中我們的Mark是使用的drawable/ic_timeline_default_marker;這個(gè)就是一個(gè)簡(jiǎn)單的圓圈而已;對(duì)于自己美化可以使用一張圖片代替或者更加復(fù)雜的布局;當(dāng)然上面的線條就更加簡(jiǎn)單了,就直接使用顏色代替。<?xml ?version="1.0"?encoding="utf-8"?>
android:shape="oval">
android:width="1dp"
android:color="@color/black_alpha_32"?/>
主界面XML RecyclerView
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
android:id="@+id/time_line_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:fadeScrollbars="true"
android:fadingEdge="none"
android:focusable="true"
android:focusableInTouchMode="true"
android:overScrollMode="never"
android:scrollbarSize="2dp"
android:scrollbarThumbVertical="@color/cyan_500"
android:scrollbars="vertical"?/>
在這里就是加上了一個(gè)RecyclerView 控件在主界面就OK。
Java代碼部分
在開始之前先來(lái)看看我們的文件具體有些神馬。
widget中就是具體的自定義控件,model是具體的數(shù)據(jù)模型,adapter部分,這里有一個(gè)Recyclerview的adapter文件,以及一個(gè)具體的Item TimeLineViewHolder,當(dāng)然在這里還定義了一個(gè)ItemType類,該類用來(lái)標(biāo)示每個(gè)Item的類型,比如頭部,第一個(gè),普通,最后一個(gè),底部等等。
TimeLineModel.javapackage?net.qiujuer.example.timeline.model;
/**
*?Created?by?qiujuer
*?on?15/8/23.
*/
public?class?TimeLineModel?{
private?String?name;
private?int?age;
public?TimeLineModel()?{
}
public?TimeLineModel(String?name,?int?age)?{
this.name?=?name;
this.age?=?age;
}
public?int?getAge()?{
return?age;
}
public?String?getName()?{
return?name;
}
public?void?setAge(int?age)?{
this.age?=?age;
}
public?void?setName(String?name)?{
this.name?=?name;
}
}
一個(gè)名字,一個(gè)年齡也就OK。
ItemType.javapackage?net.qiujuer.example.timeline.adapter;
/**
*?Created?by?qiujuer
*?on?15/8/23.
*/
public?class?ItemType?{
public?final?static?int?NORMAL?=?0;
public?final?static?int?HEADER?=?1;
public?final?static?int?FOOTER?=?2;
public?final?static?int?START?=?4;
public?final?static?int?END?=?8;
public?final?static?int?ATOM?=?16;
}
分別定義了幾個(gè)靜態(tài)值,分別代表普通、頭部、底部、開始、結(jié)束、原子;當(dāng)然其中有些可以不用定義。
TimeLineViewHolder.javapackage?net.qiujuer.example.timeline.adapter;
import?android.support.v7.widget.RecyclerView;
import?android.view.View;
import?android.widget.TextView;
import?net.qiujuer.example.timeline.R;
import?net.qiujuer.example.timeline.model.TimeLineModel;
import?net.qiujuer.example.timeline.widget.TimeLineMarker;
/**
*?Created?by?qiujuer
*?on?15/8/23.
*/
public?class?TimeLineViewHolder?extends?RecyclerView.ViewHolder?{
private?TextView?mName;
public?TimeLineViewHolder(View?itemView,?int?type)?{
super(itemView);
mName?=?(TextView)?itemView.findViewById(R.id.item_time_line_txt);
TimeLineMarker?mMarker?=?(TimeLineMarker)?itemView.findViewById(R.id.item_time_line_mark);
if?(type?==?ItemType.ATOM)?{
mMarker.setBeginLine(null);
mMarker.setEndLine(null);
}?else?if?(type?==?ItemType.START)?{
mMarker.setBeginLine(null);
}?else?if?(type?==?ItemType.END)?{
mMarker.setEndLine(null);
}
}
public?void?setData(TimeLineModel?data)?{
mName.setText("Name:"?+?data.getName()?+?"?Age:"?+?data.getAge());
}
}
該文件為RecyclerView 的Adapter中每個(gè)Item需要實(shí)現(xiàn)的Holder類。?在該類中,我們?cè)跇?gòu)造函數(shù)中需要傳入一個(gè)根View同時(shí)傳入一個(gè)當(dāng)然item的狀態(tài)。?隨后使用find….找到控件,在這里我們把TextView保存起來(lái),而TimeLineView找到后直接進(jìn)行初始化設(shè)置。?根據(jù)傳入的ItemType來(lái)判斷是否是第一個(gè),最后一個(gè),以及原子;然后設(shè)置TimeLineView的屬性。?在下面的setData方法中我們顯示具體的Model數(shù)據(jù)。
TimeLineAdapter.java
適配器部分,我們需要做的工作是;根據(jù)具體的數(shù)據(jù)渲染上對(duì)應(yīng)的界面就OK。package?net.qiujuer.example.timeline.adapter;
import?android.support.v7.widget.RecyclerView;
import?android.view.LayoutInflater;
import?android.view.View;
import?android.view.ViewGroup;
import?net.qiujuer.example.timeline.R;
import?net.qiujuer.example.timeline.model.TimeLineModel;
import?java.util.List;
/**
*?Created?by?qiujuer
*?on?15/8/23.
*/
public?class?TimeLineAdapter?extends?RecyclerView.Adapter?{
private?List?mDataSet;
public?TimeLineAdapter(List?models)?{
mDataSet?=?models;
}
@Override
public?int?getItemViewType(int?position)?{
final?int?size?=?mDataSet.size()?-?1;
if?(size?==?0)
return?ItemType.ATOM;
else?if?(position?==?0)
return?ItemType.START;
else?if?(position?==?size)
return?ItemType.END;
else?return?ItemType.NORMAL;
}
@Override
public?TimeLineViewHolder?onCreateViewHolder(ViewGroup?viewGroup,?int?viewType)?{
//?Create?a?new?view.
View?v?=?LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_time_line,?viewGroup,?false);
return?new?TimeLineViewHolder(v,?viewType);
}
@Override
public?void?onBindViewHolder(TimeLineViewHolder?timeLineViewHolder,?int?i)?{
timeLineViewHolder.setData(mDataSet.get(i));
}
@Override
public?int?getItemCount()?{
return?mDataSet.size();
}
}在這里需要著重說(shuō)一下:我復(fù)寫了getItemViewType方法;在該方法中我們需要設(shè)置對(duì)應(yīng)的Item的類型;在這里傳入的是item的坐標(biāo),需要返回的是item的具體狀態(tài),該狀態(tài)標(biāo)示是int類型;在這里我使用的是ItemType的靜態(tài)屬性。
該方法會(huì)在調(diào)用onCreateViewHolder方法之前調(diào)用;而onCreateViewHolder方法中的第二個(gè)參數(shù)int值也就是從getItemViewType之中來(lái);所以我們可以在這里進(jìn)行對(duì)應(yīng)的數(shù)據(jù)狀態(tài)標(biāo)示。
而在onCreateViewHolder方法中我們返回一個(gè):TimeLineViewHolder就OK,隨后在onBindViewHolder方法中進(jìn)行數(shù)據(jù)初始化操作。
MainActivity.java
上面所有都準(zhǔn)備好了,下面就進(jìn)行具體的顯示。?在這里就只貼出核心代碼了;篇幅也是有些長(zhǎng)。private?RecyclerView?mRecycler;
@Override
protected?void?onCreate(Bundle?savedInstanceState)?{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecycler?=?(RecyclerView)?findViewById(R.id.time_line_recycler);
initRecycler();
}
private?void?initRecycler()?{
LinearLayoutManager?layoutManager?=?new?LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
TimeLineAdapter?adapter?=?new?TimeLineAdapter(getData());
mRecycler.setLayoutManager(layoutManager);
mRecycler.setAdapter(adapter);
}
private?List?getData()?{
List?models?=?new?ArrayList();
models.add(new?TimeLineModel("XiaoMing",?21));
models.add(new?TimeLineModel("XiaoFang",?20));
models.add(new?TimeLineModel("XiaoHua",?25));
models.add(new?TimeLineModel("XiaoA",?22));
models.add(new?TimeLineModel("XiaoNiu",?23));
return?models;
}
在這里就是傻瓜的操作了,流程就是準(zhǔn)備好對(duì)應(yīng)的數(shù)據(jù),裝進(jìn)Adapter,準(zhǔn)備好對(duì)應(yīng)的布局方式,然后都設(shè)置到RecyclerView中就OK。
效果
來(lái)看看具體的效果:
效果雖然簡(jiǎn)單,但是也算是五臟具全;其中無(wú)非就是控件的自定義。這個(gè)自定義是可以擴(kuò)展的,大家可以擴(kuò)展為水平方向試試。
代碼
寫在最后
文章的開始截屏來(lái)源于:最近沒(méi)事兒搗鼓了一個(gè)APP[UPMiss],一個(gè)簡(jiǎn)單的生日,紀(jì)念日提醒軟件;歡迎大家嘗鮮。
{UPMiss} 思念你的夏天?下載地址:百度?這個(gè)審核有問(wèn)題,明明沒(méi)有支付的東西,結(jié)果說(shuō)有支付的SDK存在,不得不說(shuō)百度的自動(dòng)審核有很大漏洞。
豌豆莢?新版2.0還在審核中!
========================================================?作者:qiujuer
總結(jié)
以上是生活随笔為你收集整理的android 日程安排view,RecyclerView 列表控件中简单实现时间线的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: NumPy (6)-结构化数据类型数组
- 下一篇: numpy(5)-astype