时间都去哪了;时间你走慢一点吧

很多软件中都有时间线的东西,比如天气,计划,旅游等时间线最多了;具体实现方式很多,在本篇文章中讲解一种自定义View封装的方式实现时间线效果,PS:这也是面试中也时常会问到的知识点

1. 效果图

2. 实现分析

软件中,可以看见前面的时间线也就是线条加上圆圈组成;当然这里的圆圈与线条也都是可以随意换成其他的,比如图片等等。

当然这里最简单的来说,是上面一个线条,然后一个圆圈,然后下面一个线条;上线条在第一条数据时不做显示,下线条在最后一条数据时不做显示。

这里自定义布局部分也就是把旁边的线条与圆圈封装到一起,并使用简单的方法来控制是否显示。

当封装好了后,与旁边的文字部分也就是水瓶方向的线性布局了,然后设置为每一个的RecyclerView 的Item的布局也就完成了。

3. 自定义控件

控件很简单,首先我们继承View,取名为 TimeLineMarker 就OK。

3.1 自定义属性

开始控件之前先准备好需要的属性。

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="TimeLineMarker"><attr name="markerSize" format="dimension" /><attr name="marker" format="color|reference" /><attr name="beginLine" format="color|reference" /><attr name="endLine" format="color|reference" /><attr name="lineSize" format="dimension" /></declare-styleable>
</resources>

在这里也就准备了线条的大小、开始线条、结束线条、中间标示部分及大小。

3.2 属性与实现

  private int mMarkerSize = 24;private int mLineSize = 12;private Drawable mBeginLine;private Drawable mEndLine;private Drawable mMarkerDrawable;@Overrideprotected 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);}

两个大小属性,3个具体的Drawable,然后在onDraw方法中进行具体的显示也就OK。

3.3 构造与属性初始化

在上面我们定义了属性,在这里我们在构造函数中获取XML所设置的属性。

 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 attributesfinal 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);}

3.4 Drawable 的位置与大小初始化

属性啥的有了,具体的Drawable 也有了,要显示的地方调用也是OK了;但是如果没有进行进行具体的位置调整这一切也都没有意义。

    @Overrideprotected 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) {// Sizeint 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 方法进行具体的运算,而运算的时间点就是当控件的大小改变(onSizeChanged)的时候。
在初始化中采用了一定的投机取巧;这里利用了上内边距与下内边距分别作为上线条与下线条的长度;而线条与中间的标识都采用了水平距中。

3.5 其他设置方法

 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();}}

在设置中,首先判断是否更改,如果更改那么就更新并重新计算位置;随后刷新界面。

到这里,控件差不多准备OK了,其中还有很多可以完善的地方,比如加上快捷设置颜色什么的,也可以加上大小计算的东西。同时还可以加上时间线是水瓶还是垂直等等。在这里就不累赘介绍哪些了。下面来看看如何使用。

3.6 使用XML布局ITEM布局item_time_line.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"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"><net.qiujuer.example.timeline.widget.TimeLineMarker
        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" /><TextView
        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" /></LinearLayout>

在这里我们之间使用顺序布局,左边是TimelIne控件,右边是一个简单的字体控件,具体使用中可以细化一些。

在TImeLine控件中我们的Mark是使用的drawable/ic_timeline_default_marker;这个就是一个简单的圆圈而已;对于自己美化可以使用一张图片代替或者更加复杂的布局;当然上面的线条就更加简单了,就直接使用颜色代替。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="oval"><solid android:color="@color/cyan_500" /><stroke
        android:width="1dp"android:color="@color/black_alpha_32" />
</shape>

3.7 主界面XML RecyclerView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"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.support.v7.widget.RecyclerView
        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" /></RelativeLayout>

在这里就是加上了一个RecyclerView 控件在主界面就OK。

4. Java代码部分

在开始之前先来看看我们的文件具体有些神马。

widget中就是具体的自定义控件,model是具体的数据模型,adapter部分,这里有一个Recyclerview的adapter文件,以及一个具体的Item TimeLineViewHolder,当然在这里还定义了一个ItemType类,该类用来标示每个Item的类型,比如头部,第一个,普通,最后一个,底部等等。

4.1 TimeLineModel.java

package net.qiujuer.example.timeline.model;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;}
}

一个名字,一个年龄。

4.2 ItemType.java

package net.qiujuer.example.timeline.adapter;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;
}

分别定义了几个静态值,分别代表普通、头部、底部、开始、结束、原子;当然其中有些可以不用定义。

4.3 TimeLineViewHolder.java

package 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;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中每个Item需要实现的Holder类。
在该类中,我们在构造函数中需要传入一个根View同时传入一个当然item的状态。

随后使用find….找到控件,在这里我们把TextView保存起来,而TimeLineView找到后直接进行初始化设置。

根据传入的ItemType来判断是否是第一个,最后一个,以及原子;然后设置TimeLineView的属性。

在下面的setData方法中我们显示具体的Model数据。

4.4 TimeLineAdapter.java

适配器部分,我们需要做的工作是;根据具体的数据渲染上对应的界面。

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;public class TimeLineAdapter extends RecyclerView.Adapter<TimeLineViewHolder> {private List<TimeLineModel> mDataSet;public TimeLineAdapter(List<TimeLineModel> models) {mDataSet = models;}@Overridepublic 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;}@Overridepublic 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);}@Overridepublic void onBindViewHolder(TimeLineViewHolder timeLineViewHolder, int i) {timeLineViewHolder.setData(mDataSet.get(i));}@Overridepublic int getItemCount() {return mDataSet.size();}
}

在这里需要着重说一下:我复写了getItemViewType()方法;在该方法中我们需要设置对应的Item的类型;在这里传入的是item的坐标,需要返回的是item的具体状态,该状态标示是int类型;在这里我使用的是ItemType的静态属性。

该方法会在调用onCreateViewHolder方法之前调用;而onCreateViewHolder方法中的第二个参数int值也就是从getItemViewType之中来;所以我们可以在这里进行对应的数据状态标示。

而在onCreateViewHolder()方法中我们返回一个:TimeLineViewHolder就OK,随后在onBindViewHolder方法中进行数据初始化操作。

4.5 MainActivity.java

上面所有都准备好了,下面就进行具体的显示。 在这里就只贴出核心代码了;篇幅也是有些长。

  private RecyclerView mRecycler;@Overrideprotected 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<TimeLineModel> getData() {List<TimeLineModel> models = new ArrayList<TimeLineModel>();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;}

在这里就是傻瓜的操作了,流程就是准备好对应的数据,装进Adapter,准备好对应的布局方式,然后都设置到RecyclerView中就OK

效果虽然简单,但是也算是五脏具全;其中无非就是控件的自定义。这个自定义是可以扩展的,大家可以扩展为水平方向试试。

GitHub地址:https://github.com/qiujuer/BeFoot

5. UPMiss

GitHub地址:https://github.com/qiujuer/UPMiss

原文出处:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0825/3364.html

RecyclerView列表控件漂亮时间线实现相关推荐

  1. android 日程安排view,RecyclerView 列表控件中简单实现时间线

    时间 时间,时间,时间啊:走慢一点吧- 看见很多软件中都有时间线的东西,貌似天气啊,旅游啊什么的最多了:具体实现方式很多,在本篇文章中讲解一种自定义View封装的方式. 效果 先来看看效果. 分析 软 ...

  2. android 固定底部 布局_Android系统列表控件

    在android系统控件中,有多个控件可以展示列表数据. 一.ListView 该组件是android中最常用的一个UI组件,用于实现在屏幕上显示多个内容,以便于我们用手指进行滑动. ListView ...

  3. android listview添加数据_Android系统列表控件

    在android系统控件中,有多个控件可以展示列表数据. 一.ListView 该组件是android中最常用的一个UI组件,用于实现在屏幕上显示多个内容,以便于我们用手指进行滑动. ListView ...

  4. 安卓进阶系列-05列表控件(RecyclerView)的使用

    RecyclerView的使用 前言 在之前的博客中我介绍了ListView如何使用,当然那部分博客由于没有考虑基础的问题,写的跨度太大,后面我会全部重构的. ListView是一个滑动列表控件,随着 ...

  5. android tv 开发布局,Android TV开发总结(七)构建一个TV app中的剧集列表控件

    前言:剧集类控件,在TV app中非常常见,今天将介绍构建一个TV app中的剧集列表控件,此控件上传到我的Github:https://github.com/hejunlin2013/Episode ...

  6. Android ExpandableListView 展开列表控件(手机QQ好友列表)

    你是否觉得手机QQ上的好友列表那个控件非常棒?  不是.....   那也没关系, 学多一点知识对自己也有益无害. 那么我们就开始吧. 展开型列表控件, 原名ExpandableListView 是普 ...

  7. Adapter类型控件之ListView(列表控件)

    (一)概述 Android中的列表控件飞创灵活,可以自定义每一个列表项,实际上每一个列表项就是一个View,在Android定义了3个列表控件:ListView.ExpandableListView和 ...

  8. android横向列表控件,Android逆天控件:CircleListView(圆弧形列表)

    背景 近日设计师小姐姐创作能力爆棚,设计了一个狂拽酷炫的效果:星球环绕列表.如下图: image 在拿着板砖去沟通,且看到对方40米长的大刀后,愉快地确认出页面的如下特性: 需要实现一个可围绕圆弧(图 ...

  9. [翻译]Chameleon介绍(3) : 列表控件

    [原文]http://getben.com/archive/2007/01/11/introduction-to-chameleon-list-base-controls.aspx 这篇文章属于我的文 ...

最新文章

  1. 慌!年中总结完全没思路,这份安全汇报让你抄作业
  2. Delphi面向对象学习随笔六:接口
  3. OpenCV:透视变换
  4. MSSQL TCP/IP服务无法启动的解决方法
  5. pythonserial函数_python3.5 中serial模块的问题
  6. (转)被AI改变的风投模式:数据驱动使效率提高10倍
  7. mappedBy的具体使用及其含义
  8. 惠普m1005mfp说明书_惠普M1005使用说明
  9. 无线传感器网络技术原理及应用 许毅编著(转载请注明出处---https://www.cnblogs.com/qingl)...
  10. 国外服务器https协议,AriaNg使用Https/WebSocket(安全)协议连接Aria2的方法
  11. 逻辑电路是计算机学的吗,下列属于组合逻辑电路的是( )。
  12. python ccf题解 201409-1 相邻数对
  13. 如何登陆计算机服务器,电脑服务器怎样登陆
  14. 一般服务器监控什么项目,服务器中需要监控的项目
  15. 同一片天空共眠,同一个梦想奋斗《大抠车始歌》(1)
  16. 11. kafka重试机制解读
  17. php利用phpword读取word
  18. JavaScript奇淫技巧:反调试
  19. An internal error occurred during: Validating **.问题处理
  20. 没有权限打开服务器共享文件夹权限,Win 7无法访问共享文件夹,提示您可能没有权限使用网络资源。...

热门文章

  1. IBM X3650 M3服务器上RAID配置实战
  2. 光纤通信是如何接入网络的?
  3. Java之currenHashMap
  4. JAVA-retry 重试
  5. antd menu收缩时二级菜单不跟随的问题。
  6. 解决安装python第三方模块 '-mno-cygwin'报错的问题
  7. App_GlobalResources、App_LocalResources (理论篇)(
  8. SoapRpcMethodAttribute.OneWay属性
  9. 意法半导体STM32 ARM Cortex 32位微控制器
  10. 2020全国高中清华北大录取人数榜,各位学生快看!