(请尊重劳动成果,如需转载,注明转载出处,谢谢)
因为项目最近改动listview的数据,要做成时间轴的样式。所以自己先动手做了一个demo。
所以,给大家分享一下我在项目中学习到的一个小知识:时间轴 TimeLine
【一】
技术要点:
①我使用的是ExpandableListView 来作为主体布局,代替常用的listview。
②group布局和child布局 要注意那个线line的位置,很可能会出现两根线没对齐,或者是由于动态的文本高度,导致两根线中间断开。(在编写xml布局的时候一定要留意如何解决这些细节问题)
【二】
废话少说,上代码。(demo源码下载 请移步:http://download.youkuaiyun.com/detail/haoqqjy/8906443)
①adapter
public class AgendaListAdapter extends BaseExpandableListAdapter {
private LayoutInflater inflater = null;
private List<TimeLineTitleBean> beanList;
public AgendaListAdapter(Context context, List<TimeLineTitleBean> list) {
this.beanList = list;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getGroupCount() {
return beanList.size();
}
@Override
public int getChildrenCount(int groupPosition) {
if (beanList.get(groupPosition).getContentList() == null)
return 0;
else
return beanList.get(groupPosition).getContentList().size();
}
@Override
public TimeLineTitleBean getGroup(int groupPosition) {
return beanList.get(groupPosition);
}
@Override
public TimeLineContentBean getChild(int groupPosition, int childPosition) {
return beanList.get(groupPosition).getContentList().get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
GroupViewHolder holder = new GroupViewHolder();
if (convertView == null) {
convertView = inflater.inflate(R.layout.timeline_left, null);
}
holder.titleTime = (TextView) convertView.findViewById(R.id.time);
holder.titleTime.setText(beanList.get(groupPosition).getTime());
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
ChildViewHolder holder = null;
TimeLineContentBean bean = (TimeLineContentBean) getChild(groupPosition, childPosition);
if (convertView != null) {
holder = (ChildViewHolder) convertView.getTag();
} else {
holder = new ChildViewHolder();
convertView = inflater.inflate(R.layout.timeline_content, null);
holder.detailTime = (TextView) convertView.findViewById(R.id.time);
holder.auther = (TextView) convertView.findViewById(R.id.auther);
holder.contactCount = (TextView) convertView.findViewById(R.id.contacts);
holder.content = (TextView) convertView.findViewById(R.id.content);
holder.back = (TextView) convertView.findViewById(R.id.back);
holder.thumbs = (TextView) convertView.findViewById(R.id.good);
holder.more = (TextView) convertView.findViewById(R.id.more);
}
holder.detailTime.setText(bean.getDetaiTime());
holder.auther.setText(bean.getAutherName());
holder.contactCount.setText("参与者" + bean.getContactCounts() + "人" + " - 10分钟前");
holder.content.setText(bean.getContent());
holder.thumbs.setText("赞(" + bean.getThumbs() + ")个");
holder.more.setText("更多");
holder.back.setText("回复(" + bean.getAnswer() + ")条");
convertView.setTag(holder);
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
private class GroupViewHolder {
public TextView titleTime;
}
private class ChildViewHolder {
public TextView detailTime;
public TextView auther;
public TextView contactCount;
public TextView back;
public TextView thumbs;
public TextView more;
public TextView content;
}
}
<?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:layout_gravity="center_vertical" >
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="15dp" >
<TextView
android:id="@+id/timeline"
android:layout_width="2dp"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:background="#9999"
/>
<TextView
android:id="@+id/time"
android:layout_width="140dp"
android:layout_height="wrap_content"
android:text="2015-7-13"
android:paddingBottom="5dp"
android:layout_gravity="center_vertical"
android:gravity="center"
android:background="@drawable/agenda_list_time"
/>
</FrameLayout>
</LinearLayout>
③child的xml
<?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:layout_gravity="center_horizontal"
android:orientation="horizontal" >
<LinearLayout
android:id="@+id/detaitime_layout"
android:layout_width="55dp"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:paddingLeft="15dp"
android:text="16:15"
android:textColor="#999999" >
</TextView>
</LinearLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="20dp" >
<TextView
android:id="@+id/timeline1"
android:layout_width="2dp"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:background="#9999" />
<ImageView
android:id="@+id/timeimage"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:src="@drawable/agenda0" />
</FrameLayout>
<LinearLayout
android:id="@+id/content_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:background="#eee"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:paddingTop="5dp" >
<TextView
android:id="@+id/auther"
android:layout_width="match_parent"
android:layout_height="20dp"
android:text="Sinya" />
<TextView
android:id="@+id/contacts"
android:layout_width="match_parent"
android:layout_height="20dp"
android:text="参与者 15人 提前10分钟" />
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="内容"
android:textColor="#999999" >
</TextView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#9999"
android:orientation="horizontal" >
<TextView
android:id="@+id/back"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="2"
android:gravity="center"
android:text="回复" />
<TextView
android:id="@+id/good"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="2"
android:gravity="center"
android:text="点赞" />
<TextView
android:id="@+id/more"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="1"
android:gravity="center"
android:text="更多" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
④titlebean
public class TimeLineTitleBean {
private String time;
private List<TimeLineContentBean> contentList;
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public List<TimeLineContentBean> getContentList() {
return contentList;
}
public void setContentList(List<TimeLineContentBean> contentList) {
this.contentList = contentList;
}
}
⑤文本bean
public class TimeLineContentBean {
private String detaiTime;
private String autherName;
private String contacts;
private int contactCounts;
private String content;
private int answer;
private int thumbs;
private String more;
public String getDetaiTime() {
return detaiTime;
}
public void setDetaiTime(String detaiTime) {
this.detaiTime = detaiTime;
}
public String getAutherName() {
return autherName;
}
public void setAutherName(String autherName) {
this.autherName = autherName;
}
public String getContacts() {
return contacts;
}
public void setContacts(String contacts) {
this.contacts = contacts;
}
public int getContactCounts() {
return contactCounts;
}
public void setContactCounts(int contactCounts) {
this.contactCounts = contactCounts;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getAnswer() {
return answer;
}
public void setAnswer(int answer) {
this.answer = answer;
}
public int getThumbs() {
return thumbs;
}
public void setThumbs(int thumbs) {
this.thumbs = thumbs;
}
public String getMore() {
return more;
}
public void setMore(String more) {
this.more = more;
}
}
public class MainActivity extends ActionBarActivity {
private List<TimeLineTitleBean> list;
private ExpandableListView expandlistView;
private AgendaListAdapter adapter;
private Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
expandlistView = (ExpandableListView) findViewById(R.id.expandlist);
putInitData();
adapter = new AgendaListAdapter(context, list);
expandlistView.setAdapter(adapter);
// 去掉默认带的箭头
expandlistView.setGroupIndicator(null);
// 遍历所有group,将所有项设置成默认展开
int groupCount = expandlistView.getCount();
for (int i = 0; i < groupCount; i++) {
expandlistView.expandGroup(i);
}
expandlistView.setOnGroupClickListener(new OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
return true;
}
});
}
/**
* 以下是模拟数据, 随便复制粘贴这几天的新闻<br>
* 实际的数据,就看项目需求了<br>
* 根据需求,改动bean的数据结构、xml的布局也是可以调整
*/
private void putInitData() {
list = new ArrayList<TimeLineTitleBean>();
TimeLineTitleBean time1 = new TimeLineTitleBean();
List<TimeLineContentBean> contentList1 = new ArrayList<TimeLineContentBean>();
TimeLineContentBean content1 = new TimeLineContentBean();
TimeLineContentBean content2 = new TimeLineContentBean();
TimeLineContentBean content3 = new TimeLineContentBean();
content1.setDetaiTime("14:52");
content1.setAnswer(14534);
content1.setThumbs(10);
content1.setContactCounts(4235);
content1.setAutherName("南都周刊");
content1.setContent("四川多地迎来高温天气,四川日报记者从四川省气象局官方网站获悉,当天成都、眉山、乐山、内江、遂宁、攀枝花、巴中等地最高气温达到33℃,达州、广安、南充、泸州、宜宾、资阳、自贡等地气温达35℃。位于遂宁市大英县的中国“死海”旅游度假区迎来避暑高峰,当天8000多人涌进游泳池避暑纳凉,现场犹如“下饺子”");
content2.setDetaiTime("09:12");
content2.setAnswer(15);
content2.setThumbs(0);
content2.setContacts("50");
content2.setAutherName("人民日报");
content2.setContent("国家互联网信息办公室12日发布通知要求,全面清理所有配资炒股的违法宣传广告信息,禁止任何机构和个人通过网络渠道发布此类违法宣传广告信息。");
content3.setDetaiTime("01:12");
content3.setAnswer(1523);
content3.setThumbs(2);
content3.setContactCounts(53);
content3.setAutherName("腾讯新闻");
content3.setContent("综合港媒报道,香港特首梁振英在立法会答问大会上多次炮轰泛民“拉布”(故意冗长演说阻挠政策审议),7月11日,他出席电台节目时直指香港最大的挑战非没钱、没能力,而是内部有很多阻力,批评目前立法会的秩序比台湾议会更差。他表示,目前无计划连任,会先做好经济、民生工作。");
contentList1.add(content1);
contentList1.add(content2);
contentList1.add(content3);
time1.setTime("2015-03-11");
time1.setContentList(contentList1);
list.add(time1);
TimeLineTitleBean time2 = new TimeLineTitleBean();
List<TimeLineContentBean> contentList2 = new ArrayList<TimeLineContentBean>();
TimeLineContentBean content4 = new TimeLineContentBean();
content4.setDetaiTime("14:52");
content4.setAnswer(0);
content4.setThumbs(546732);
content4.setContactCounts(0);
content4.setAutherName("网易新闻");
content4.setContent("综合港媒报道,香港特首梁振英在立法会答问大会上多次炮轰泛民“拉布”(故意冗长演说阻挠政策审议),7月11日,他出席电台节目时直指香港最大的挑战非没钱、没能力,而是内部有很多阻力,批评目前立法会的秩序比台湾议会更差。他表示,目前无计划连任,会先做好经济、民生工作。");
contentList2.add(content4);
time2.setTime("2015-07-11");
time2.setContentList(contentList2);
list.add(time2);
TimeLineTitleBean time3 = new TimeLineTitleBean();
List<TimeLineContentBean> contentList3 = new ArrayList<TimeLineContentBean>();
TimeLineContentBean content5 = new TimeLineContentBean();
TimeLineContentBean content6 = new TimeLineContentBean();
TimeLineContentBean content7 = new TimeLineContentBean();
content5.setDetaiTime("14:52");
content5.setAnswer(623);
content5.setThumbs(41);
content5.setContactCounts(59);
content5.setAutherName("凤凰网");
content5.setContent("1981年我在潜艇上工作过一年多,助民劳动中也帮助渔民在海上收割过海带,当时渔民用很粗的海带绳把海带苗夹在尼龙绳上,经常造成舰船螺旋桨受损,这是个很明显的道理。 二战中海军港口水下都有反潜网,潜艇艇首也有防雷网,70年代苏联一艘潜艇螺旋桨缠上渔网差点艇毁人亡。");
content6.setDetaiTime("14:12");
content6.setAnswer(3421);
content6.setThumbs(542);
content6.setContactCounts(886);
content6.setAutherName("新华社");
content6.setContent("中新网7月13日电 据中央纪委监察部网站消息,中纪委网站今日刊文《推动国有企业从严治党之一——全面从严治党 国企尤为紧迫 》,文章称对中管国有重要骨干企业专项巡视中发现有的企业以改革为名,打着建立现代企业制度的旗号,贱卖贵买、予取予求,侵吞国有资产如探囊取物。");
content7.setDetaiTime("04:52");
content7.setAnswer(16343);
content7.setThumbs(54);
content7.setContactCounts(658);
content7.setAutherName("BBC");
content7.setContent("近日,国内知名军事论坛发布了一组据称为国产“075型大型两栖攻击舰”的模型照片,从模型的照片中可以看到,该型两栖攻击舰采用直通甲板设计,具有4个直升机起降点,1台升降机,在机库下还设置有巨大的坞舱,可以搭载一定数量的国产气垫船。在整体外形上,这款据称为“075型”的两栖攻击舰与中国在在2012年阿布扎比防务展上展示的2.5万吨级出口两栖攻击舰非常相像,因此有分析认为出口型号有可能是解放军自用的075型两栖攻击舰的衍生产品。有军事专家分析,从模型上来看,该型两栖攻击舰未配置攻击性武器,表明其对海、反潜攻击任务主要由编队掩护舰艇担当,也可通过两栖攻击舰上搭载的武装直升机、反潜直升机担当对海、对陆、反潜攻击任务。");
contentList3.add(content5);
contentList3.add(content6);
contentList3.add(content7);
time3.setTime("2015-07-15");
time3.setContentList(contentList3);
list.add(time3);
}
}
【三】总结一下
这个时间轴布局介绍一下:
①title(我这里是用来标注时间,当然也可以用来标注其他的作为分割)
②line ,时间轴竖线,是由两个部分的xml串联在一起的哦(注意我开头时候说的,line对接的时候很可能出现的问题)
③文本部分。这个文本的布局可以任君修改,看需求咯。左侧的是时间,右侧是实际的文本部分,可以搭配图片等等。做成朋友圈更新、日志之类的都是没问题的(因为公司的项目需求就是这个,我做demo就是为了提前做好准备)
大家不要看了布局界面就被吓到了,其实用心去理解一下,动手做一做还是很简单的。
完整的代码,请移步到下载界面
http://download.youkuaiyun.com/detail/haoqqjy/8906443