时光轴最大的作用就是把过去的事物系统化、完整化、精确化。时间轴能够让用户更直观的看到,我的这一刻在做什么,那一刻做过什么,依据时间顺序,把一方面或多方面的时间足迹事件串联起来,形成相对完整的记录体系,再运用图文的形式呈现给用户;页面简单,表现形式特别,一直以来受到广大用户的欢迎。
首先来看效果图:
这是模拟一个横向的快递信息时光轴,接下来看看我们怎么实现的把。
首先我们先自定义一个横向的ListView类,然后MainActivity中的ListView继承他,来实现横向展示ListView。
package com.example.mytimeaxis;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;
import java.util.LinkedList;
import java.util.Queue;
public class HorizontalListView extends AdapterView {
public boolean mAlwaysOverrideTouch = true;
protected ListAdapter mAdapter;
private int mLeftViewIndex = -1;
private int mRightViewIndex = 0;
protected int mCurrentX;
protected int mNextX;
private int mMaxX = Integer.MAX_VALUE;
private int mDisplayOffset = 0;
protected Scroller mScroller;
private GestureDetector mGesture;
private Queue mRemovedViewQueue = new LinkedList();
private OnItemSelectedListener mOnItemSelected;
private OnItemClickListener mOnItemClicked;
private OnItemLongClickListener mOnItemLongClicked;
private boolean mDataChanged = false;
public HorizontalListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private synchronized void initView() {
mLeftViewIndex = -1;
mRightViewIndex = 0;
mDisplayOffset = 0;
mCurrentX = 0;
mNextX = 0;
mMaxX = Integer.MAX_VALUE;
mScroller = new Scroller(getContext());
mGesture = new GestureDetector(getContext(), mOnGesture);
}
@Override
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
mOnItemSelected = listener;
}
@Override
public void setOnItemClickListener(AdapterView.OnItemClickListener listener){
mOnItemClicked = listener;
}
@Override
public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {
mOnItemLongClicked = listener;
}
private DataSetObserver mDataObserver = new DataSetObserver() {
@Override
public void onChanged() {
synchronized(HorizontalListView.this){
mDataChanged = true;
}
invalidate();
requestLayout();
}
@Override
public void onInvalidated() {
reset();
invalidate();
requestLayout();
}
};
@Override
public ListAdapter getAdapter() {
return mAdapter;
}
@Override
public View getSelectedView() {
//TODO: implement
return null;
}
@Override
public void setAdapter(ListAdapter adapter) {
if(mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataObserver);
}
mAdapter = adapter;
mAdapter.registerDataSetObserver(mDataObserver);
reset();
}
private synchronized void reset(){
initView();
removeAllViewsInLayout();
requestLayout();
}
@Override
public void setSelection(int position) {
//TODO: implement
}
private void addAndMeasureChild(final View child, int viewPos) {
LayoutParams params = child.getLayoutParams();
if(params == null) {
params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
}
addViewInLayout(child, viewPos, params, true);
child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
}
@Override
protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(mAdapter == null){
return;
}
if(mDataChanged){
int oldCurrentX = mCurrentX;
initView();
removeAllViewsInLayout();
mNextX = oldCurrentX;
mDataChanged = false;
}
if(mScroller.computeScrollOffset()){
int scrollx = mScroller.getCurrX();
mNextX = scrollx;
}
if(mNextX <= 0){
mNextX = 0;
mScroller.forceFinished(true);
}
if(mNextX >= mMaxX) {
mNextX = mMaxX;
mScroller.forceFinished(true);
}
int dx = mCurrentX - mNextX;
removeNonVisibleItems(dx);
fillList(dx);
positionItems(dx);
mCurrentX = mNextX;
if(!mScroller.isFinished()){
post(new Runnable(){
@Override
public void run() {
requestLayout();
}
});
}
}
private void fillList(final int dx) {
int edge = 0;
View child = getChildAt(getChildCount()-1);
if(child != null) {
edge = child.getRight();
}
fillListRight(edge, dx);
edge = 0;
child = getChildAt(0);
if(child != null) {
edge = child.getLeft();
}
fillListLeft(edge, dx);
}
private void fillListRight(int rightEdge, final int dx) {
while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {
View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, -1);
rightEdge += child.getMeasuredWidth();
if(mRightViewIndex == mAdapter.getCount()-1) {
mMaxX = mCurrentX + rightEdge - getWidth();
}
if (mMaxX < 0) {
mMaxX = 0;
}
mRightViewIndex++;
}
}
private void fillListLeft(int leftEdge, final int dx) {
while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {
View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, 0);
leftEdge -= child.getMeasuredWidth();
mLeftViewIndex--;
mDisplayOffset -= child.getMeasuredWidth();
}
}
private void removeNonVisibleItems(final int dx) {
View child = getChildAt(0);
while(child != null && child.getRight() + dx <= 0) {
mDisplayOffset += child.getMeasuredWidth();
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mLeftViewIndex++;
child = getChildAt(0);
}
child = getChildAt(getChildCount()-1);
while(child != null && child.getLeft() + dx >= getWidth()) {
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mRightViewIndex--;
child = getChildAt(getChildCount()-1);
}
}
private void positionItems(final int dx) {
if(getChildCount() > 0){
mDisplayOffset += dx;
int left = mDisplayOffset;
for(int i=0;i
MainActivity布局展示:
MainActivity代码:
package com.example.mytimeaxis;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
//一个横向的ListView
List datas = new ArrayList();
private HorizontalListView mlv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
initAdapter();
}
private void initAdapter() {
MyAdapter adapter = new MyAdapter((ArrayList) datas, this);
mlv.setAdapter(adapter);
}
/**
* 这里用虚拟数据实现,仅供参考
*/
private void initData() {
// TODO Auto-generated method stub
Dates item1 = new Dates();
item1.setTitle("提交订单");
item1.setTime("03-14 08:13");
item1.setStatu(1); //设置状态标记1 ,0
Dates item2 = new Dates();
item2.setTitle("已支付");
item2.setTime("03-14 22:32");
item2.setStatu(1);
Dates item3 = new Dates();
item3.setTitle("商品出库");
item3.setTime("03-15 00:33");
item3.setStatu(0);
Dates item4 = new Dates();
item4.setTitle("已签收");
item4.setTime("03-15 15:55");
item4.setStatu(0);
datas.add(item1);
datas.add(item2);
datas.add(item3);
datas.add(item4);
}
private void initView() {
mlv = (HorizontalListView) findViewById(R.id.mlv);
}
}
接下来一个Dates 实体类。
package com.example.mytimeaxis;
/**
* Created by Administrator on 2018/5/28.
*/
public class Dates {
private String time;
private String title;
private int statu;
public Dates() {
}
@Override
public String toString() {
return "Dates{" +
"time='" + time + '\'' +
", title='" + title + '\'' +
", statu=" + statu +
'}';
}
public int getStatu() {
return statu;
}
public void setStatu(int statu) {
this.statu = statu;
}
public Dates(String time, String title, int statu) {
this.time = time;
this.title = title;
this.statu = statu;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Dates(String time, String title) {
this.time = time;
this.title = title;
}
}
最重要的是ListView适配器代码istView:
package com.example.mytimeaxis;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
public class MyAdapter extends BaseAdapter {
private ArrayList datas;
Context context;
public MyAdapter(ArrayList dates, Context context) {
super();
this.datas = dates;
this.context = context;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return datas.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
Item item=null;
if (convertView == null) {
item = new Item();
convertView = LayoutInflater.from(context).inflate(R.layout.item_view2, null);
item.time = (TextView) convertView.findViewById(R.id.show_time);
item.title = (TextView) convertView.findViewById(R.id.show_title);
item.lineNorma = (View) convertView.findViewById(R.id.line_normal);
item.lineHiLight = (View) convertView.findViewById(R.id.line_highlight);
item.image = (ImageView) convertView.findViewById(R.id.image);
convertView.setTag(item);
} else {
item = (Item) convertView.getTag();
}
//根据数据状态对视图做不同的操作
if (datas.get(position).getStatu() == 1) {
item.lineHiLight.setVisibility(View.VISIBLE);
item.image.setImageResource(R.drawable.report_checked);
item.time.setVisibility(View.VISIBLE);
}
item.time.setText(datas.get(position).getTime());
item.title.setText(datas.get(position).getTitle());
//这里在起始位置,就不显示“轴”了
if (position == 0) {
item.lineNorma.setVisibility(View.INVISIBLE);
item.lineHiLight.setVisibility(View.INVISIBLE);
}
return convertView;
}
private class Item {
TextView time, title;
View lineNorma, lineHiLight;
ImageView image;
}
}
适配器item_view2布局代码:
时光轴的效果就这样实现了,大家可以参考下。