導(dǎo)語(yǔ)
有些時(shí)候看著別人家的自定義控件很酷炫。
別人家的控件
淘寶
美團(tuán)外賣(mài)
自己家的控件
做開(kāi)發(fā)的時(shí)候雷猪,難免產(chǎn)品會(huì)給出類(lèi)似的需求和設(shè)計(jì)次伶,看到這樣的物流狀態(tài)圖婉支,你第一時(shí)間會(huì)想到用什么方法去解決?當(dāng)然痢毒,解決的辦法N多種送矩,比如用ListView去實(shí)現(xiàn),或是用動(dòng)態(tài)布局去實(shí)現(xiàn)哪替,唯一需要解決的就是隱藏cell直接的間隔線(xiàn)栋荸,無(wú)縫連接每個(gè)cell之間的進(jìn)度線(xiàn),做一些布局上的巧妙處理凭舶∩慰椋看到類(lèi)似的物流狀態(tài)圖,我第一反應(yīng)總是想用自己的辦法去解決帅霜,腦子里浮現(xiàn)的是用一個(gè)自定義控件去搞定這件事情匆背,總覺(jué)得用ListView控件去實(shí)現(xiàn)這種數(shù)據(jù)量不大的界面表現(xiàn)有點(diǎn)大材小用。于是便有了下面的思路和解決辦法:
閱讀本文需要你了解這幾個(gè)知識(shí)點(diǎn):
1身冀、自定義View
2钝尸、手勢(shì)監(jiān)聽(tīng)
3、View滑動(dòng)
4搂根、適配器設(shè)計(jì)模式
思路整理
從界面呈現(xiàn)上
從整體來(lái)看珍促,界面上呈現(xiàn)的數(shù)據(jù)是一個(gè)數(shù)組或是List形式的數(shù)據(jù)封裝,然后根據(jù)數(shù)據(jù)的容量大小for循環(huán)動(dòng)態(tài)繪制剩愧,綁定相關(guān)數(shù)據(jù)到界面展示猪叙。
從一個(gè)cell局部來(lái)看,一個(gè)cell包含的內(nèi)容分6部分仁卷,分別是左上角的狀態(tài)提示點(diǎn)穴翩、左邊的狀態(tài)進(jìn)度線(xiàn)、cell的背景锦积、主文字區(qū)域芒帕、時(shí)間文字區(qū)域以及文字按鈕區(qū)域。
從其他來(lái)看充包,其實(shí)還包括每個(gè)cell之間的間隔距離副签、整個(gè)View和父控件的間隔距離遥椿、文字之間的距離、文字和cell間的距離等等淆储。
從邏輯上
根據(jù)for循環(huán)動(dòng)態(tài)填充界面元素冠场,最主要的邏輯是要計(jì)算每個(gè)元素的坐標(biāo)位置,然后根據(jù)坐標(biāo)位置繪制相關(guān)元素本砰。cell中某些元素的高度是給的定值碴裙,有些則是自適應(yīng)內(nèi)容高度,高度不定点额,這些高度值需要做累加處理舔株,才能準(zhǔn)確計(jì)算下個(gè)元素的坐標(biāo)位置。比如cell中的主文字高度是要根據(jù)文字字?jǐn)?shù)自適配的还棱,我們需要計(jì)算出文字的高度载慈。
左邊的物流狀態(tài)進(jìn)度線(xiàn)也會(huì)根據(jù)cell的高度繪制,其實(shí)這里我們可以換個(gè)角度去思考珍手,左邊的進(jìn)度線(xiàn)我們并不需要每一次for循環(huán)都去計(jì)算進(jìn)度線(xiàn)需要繪制的長(zhǎng)度或是計(jì)算繪制起點(diǎn)和終點(diǎn)的坐標(biāo)位置办铡,我們只需要得到物流狀態(tài)的第一個(gè)點(diǎn)和最后一個(gè)點(diǎn)的坐標(biāo)位置,然后用一條線(xiàn)連接即可琳要,這樣即可貫串中間的所有物流狀態(tài)點(diǎn)寡具。
從交互上
控件能承受的數(shù)據(jù)長(zhǎng)度是不定的,數(shù)據(jù)量偏大稚补,繪制肯定會(huì)超出屏幕童叠,這里需要做一個(gè)滑動(dòng);每個(gè)cell中的按鈕文字區(qū)域课幕,需要計(jì)算每個(gè)文字按鈕的有效觸摸區(qū)域厦坛,在觸摸事件結(jié)束的時(shí)候響應(yīng)監(jiān)聽(tīng)事件,模擬一個(gè)點(diǎn)擊事件撰豺。
從適配上
控件能接受的數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)肯定是多樣的粪般,這里需要為控件做一個(gè)數(shù)據(jù)適配器拼余,以適應(yīng)各種數(shù)據(jù)接口的數(shù)據(jù)源污桦。
運(yùn)行效果(刷新可看動(dòng)圖)
代碼實(shí)現(xiàn)
定義相關(guān)參數(shù)
<pre>
private int screenWidth;
private int screenHeight;
private int width, height; //當(dāng)前View寬高
private int viewWidth, viewHeight; //當(dāng)前View寬高
private int firstExpressCircleMarginLeft = DeviceUtils.dipToPx(getContext(), 16);
private int firstExpressCircleMarginTop = DeviceUtils.dipToPx(getContext(), 40);
private int expressCircleRadius = DeviceUtils.dipToPx(getContext(), 6);//物流狀態(tài)提示圈半徑
private int expressCircleCurrentRadius = DeviceUtils.dipToPx(getContext(), 3);//物流狀態(tài)提示圈半徑
private int expressCircleOuterRadius = DeviceUtils.dipToPx(getContext(), 8);//物流狀態(tài)提示圈外半徑
private int circleToTextMargin = DeviceUtils.dipToPx(getContext(), 12);//物流狀態(tài)提示圈到文字背景的距離
private int expressTextBackgroundWidth; //文字背景寬
private int expressTextMargin = DeviceUtils.dipToPx(getContext(), 8); //文字距離背景邊距
private int expressTextVecPadding = DeviceUtils.dipToPx(getContext(), 5); //每個(gè)物流信息豎直方向的間距
private int expressTextToTimeTextPadding = DeviceUtils.dipToPx(getContext(), 6); //物流文字距離時(shí)間文字的間距
private int expressButtonTextHeight; //按鈕文字高度
private boolean isTimeButtonVisible = false; //是否需要顯示時(shí)間和按鈕
private Paint expressCirclePaint; //物流狀態(tài)提示圈
private Paint expressTextBackgroundPaint; //文字背景
private TextPaint expressTextPaint; //文字
private TextPaint timeTextPaint; //時(shí)間文字
private TextPaint buttonTextPaint; //按鈕文字
private Paint bgPaint;
private Paint expressLinePaint;//物流線(xiàn)條
private int expressTextSize;//文字大小
private int expressTimeTextSize;//時(shí)間文字大小
private int contentDrawHeight; //根據(jù)內(nèi)容繪制的高度
</pre>
在res/values/attrs.xml中自定義控件相關(guān)屬性
<pre>
<declare-styleable name="ExpressView">
<attr name="firstExpressCircleMarginLeft" format="dimension">16</attr>
<attr name="firstExpressCircleMarginTop" format="dimension">16</attr>
<attr name="expressCircleRadius" format="dimension">6</attr>
<attr name="expressCircleOuterRadius" format="dimension">8</attr>
<attr name="circleToTextMargin" format="dimension">12</attr>
<attr name="expressTextMargin" format="dimension">8</attr>
<attr name="expressTextVecPadding" format="dimension">5</attr>
<attr name="expressTextSize" format="dimension">18</attr>
<attr name="expressTimeTextSize" format="dimension">14</attr>
<attr name="isTimeButtonVisible" format="boolean">false</attr>
<attr name="progressBarStyleHorizontal" format="reference" ></attr>
</declare-styleable>
</pre>
其中
firstExpressCircleMarginLeft 第一個(gè)物流狀態(tài)點(diǎn)距離父控件坐邊的間距
firstExpressCircleMarginTop 第一個(gè)物流狀態(tài)點(diǎn)距離父控件上邊的間距
expressCircleRadius 物流狀態(tài)點(diǎn)內(nèi)圈半徑
expressCircleOuterRadius 物流狀態(tài)點(diǎn)外圈半徑
circleToTextMargin 物流狀態(tài)提示圈到文字背景的距離
expressTextMargin 文字距離背景邊距
expressTextVecPadding 每個(gè)物流信息豎直方向的間距
expressTextSize 文字大小
expressTimeTextSize 時(shí)間文字大小
isTimeButtonVisible 是否顯示時(shí)間和文字按鈕
如果設(shè)置isTimeButtonVisible為false,界面顯示
初始化屬性值
<pre>
private void initTypeArray(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ExpressView);
firstExpressCircleMarginLeft = (int) array.getDimension(R.styleable.ExpressView_firstExpressCircleMarginLeft, 16);
firstExpressCircleMarginTop = (int) array.getDimension(R.styleable.ExpressView_firstExpressCircleMarginTop, 16);
expressCircleRadius = (int) array.getDimension(R.styleable.ExpressView_expressCircleRadius, 6);
expressCircleOuterRadius = (int) array.getDimension(R.styleable.ExpressView_expressCircleOuterRadius, 8);
circleToTextMargin = (int) array.getDimension(R.styleable.ExpressView_circleToTextMargin, 12);
expressTextMargin = (int) array.getDimension(R.styleable.ExpressView_expressTextMargin, 8);
expressTextVecPadding = (int) array.getDimension(R.styleable.ExpressView_expressTextVecPadding, 5);
expressTextSize = (int) array.getDimension(R.styleable.ExpressView_expressTextSize, 16);
expressTimeTextSize = (int) array.getDimension(R.styleable.ExpressView_expressTimeTextSize, 16);
isTimeButtonVisible = array.getBoolean(R.styleable.ExpressView_isTimeButtonVisible, false);
array.recycle();
buttonTopPositionMap = new HashMap<>();
buttonTopPositionMap.put(0, new Point(0, 0)); //設(shè)置初始值
}
</pre>
初始化畫(huà)筆
<pre>
private void initPaint(Context context) {
touchDistance = ViewConfiguration.get(context).getScaledTouchSlop();
screenHeight = DeviceUtils.getScreenHeight(context);
screenWidth = DeviceUtils.getScreenWidth(context);
//物流狀態(tài)提示圈
expressCirclePaint = new Paint();
expressCirclePaint.setColor(Color.parseColor("#969696"));
expressCirclePaint.setStyle(Paint.Style.FILL);
expressCirclePaint.setAntiAlias(true);
expressCirclePaint.setStrokeWidth(DeviceUtils.dipToPx(getContext(), 2));
expressTextBackgroundPaint = new Paint();
expressTextBackgroundPaint.setAntiAlias(true);
expressTextBackgroundPaint.setColor(Color.WHITE);
expressTextBackgroundPaint.setStyle(Paint.Style.FILL);
expressCirclePaint.setStrokeWidth(DeviceUtils.dipToPx(getContext(), 2));
expressTextPaint = new TextPaint();
expressTextPaint.setAntiAlias(true);
expressTextPaint.setColor(Color.BLACK);
expressTextPaint.setTextSize(expressTextSize);
expressTextPaint.setStyle(Paint.Style.FILL);
timeTextPaint = new TextPaint();
timeTextPaint.setAntiAlias(true);
timeTextPaint.setColor(Color.parseColor("#969696"));
timeTextPaint.setTextSize(expressTimeTextSize);
timeTextPaint.setStyle(Paint.Style.FILL);
buttonTextPaint = new TextPaint();
buttonTextPaint.setAntiAlias(true);
buttonTextPaint.setColor(Color.parseColor("#4682B4"));
buttonTextPaint.setTextSize(expressTextSize);
buttonTextPaint.setStyle(Paint.Style.FILL);
expressLinePaint = new Paint();
expressLinePaint.setAntiAlias(true);
expressLinePaint.setColor(Color.parseColor("#969696"));
expressLinePaint.setStyle(Paint.Style.FILL);
expressLinePaint.setStrokeWidth(DeviceUtils.dipToPx(getContext(), 1));
bgPaint = new Paint();
bgPaint.setAntiAlias(true);
bgPaint.setAlpha(30);
bgPaint.setColor(Color.parseColor("#969696"));
bgPaint.setStyle(Paint.Style.STROKE);
}
</pre>
這兩個(gè)方法的初始化都放在構(gòu)造方法里
<pre>
public ExpressView(Context context) {
super(context);
initPaint(context);
}
public ExpressView(Context context, AttributeSet attrs) {
super(context, attrs);
initTypeArray(context, attrs);
initPaint(context);
}
public ExpressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initTypeArray(context, attrs);
initPaint(context);
}
</pre>
重寫(xiě)onMeasure方法
<pre>
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
Log.e("ExpressView", "屏幕寬高 " + screenWidth + " " + screenHeight);
if (modeWidth == MeasureSpec.EXACTLY) {
viewWidth = sizeWidth;
Log.e("ExpressView", "精確測(cè)量寬 " + viewWidth);
} else {
viewWidth = width;
Log.e("ExpressView", "粗略測(cè)量寬 " + viewWidth);
}
if (modeHeight == MeasureSpec.EXACTLY) {
viewHeight = sizeHeight;
Log.e("ExpressView", "精確測(cè)量高 " + viewHeight);
} else {
viewHeight = height;
Log.e("ExpressView", "粗略測(cè)量高 " + viewHeight);
}
viewWidth = viewWidth > screenWidth ? screenWidth : viewWidth;
viewHeight = viewHeight > screenHeight ? screenHeight : viewHeight;
setMeasuredDimension(viewWidth, viewHeight);
expressTextBackgroundWidth = viewWidth - 2 * (firstExpressCircleMarginLeft - expressCircleRadius) - 2 * circleToTextMargin;
Log.e("ExpressView", "View寬度 " + viewWidth + "繪制的文字背景寬度 " + expressTextBackgroundWidth);
}
</pre>
重寫(xiě)onDraw方法
這里主要講解下cell中文字的繪制以及文字高度的獲取
用StaticLayout即可解決文字自動(dòng)換行以及獲取文字的高度匙监,具體使用方法如下:
<pre>
StaticLayout layout = new StaticLayout(text, expressTextPaint, expressTextBackgroundWidth - 2 * expressTextMargin, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);
int textHeight = layout.getHeight();//計(jì)算文字高度
layout.draw(canvas); //繪制到畫(huà)布
</pre>
StaticLayout中四個(gè)參數(shù)的意思:文字內(nèi)容凡橱、畫(huà)筆、文字寬度亭姥、對(duì)其方式稼钩、相對(duì)行間距、在基礎(chǔ)行距上添加值达罗、是否包含間距值坝撑。
重寫(xiě)onTouchEvent方法静秆,處理手勢(shì)監(jiān)聽(tīng)
定義一個(gè)HashMap集合,用于存儲(chǔ)文字按鈕坐標(biāo)值
<pre>
private Map<Integer, Point> buttonTopPositionMap; //用于存儲(chǔ)點(diǎn)擊按鈕左上角坐標(biāo)
private int firstButtonPositionY; //記錄第一個(gè)按鈕位置坐標(biāo)
</pre>
手勢(shì)監(jiān)聽(tīng)
<pre>
@Override
public boolean onTouchEvent(MotionEvent event) {
this.getParent().requestDisallowInterceptTouchEvent(true);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getY();
lastY = downY;
lastMoveY = downY;
isMoving = false;
break;
case MotionEvent.ACTION_MOVE:
downY = (int) event.getY();
int transY = downY - lastY;
int transMoveY = downY - lastMoveY; //每次手指按下到滑動(dòng)停止的滑動(dòng)距離
isMoving = Math.abs(transMoveY) > touchDistance ? true : false; //判定是否滑動(dòng)
Log.e("ExpressViewTouch", "當(dāng)前滑動(dòng)距離 " + Math.abs(transMoveY) + " 是否滑動(dòng) " + isMoving);
if (isMoving) {
transDistance += transY;
Log.e("ExpressViewOnScreen", "滑動(dòng)距離" + transDistance);
scrollBy(0, -transY);
}
lastY = downY;
break;
case MotionEvent.ACTION_UP:
if (isTimeButtonVisible) {
Log.e("ExpressViewTouch", "累計(jì)滑動(dòng)總距離 " + transDistance);
if (isMoving || (!isMoving && (buttonTopPositionMap.get(0).y == firstButtonPositionY))) {
Iterator<Map.Entry<Integer, Point>> it = buttonTopPositionMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Point> entry = it.next();
entry.getValue().y += transDistance;
}
}
Log.e("ExpressViewTouch", "手指離開(kāi)屏幕位置信息 " + JsonUtils.objectToString(buttonTopPositionMap, Map.class));
onActionUpEvent(isMoving, event, buttonTopPositionMap);
this.getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_CANCEL:
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return true;
}
</pre>
這里需要注意一點(diǎn)是巡李,每一次滑動(dòng)操作抚笔,文字按鈕的坐標(biāo)信息都會(huì)發(fā)生變動(dòng),需要隨滑動(dòng)過(guò)程隨時(shí)記錄最新的坐標(biāo)位置侨拦。
手指離開(kāi)屏幕需要檢驗(yàn)當(dāng)前點(diǎn)擊是否是一次有效點(diǎn)擊事件并處理點(diǎn)擊回調(diào)事件
<pre>
private void onActionUpEvent(boolean isMoving, MotionEvent event, Map<Integer, Point> maps) {
int touchX = (int) event.getX();
int touchY = (int) event.getY();
Iterator<Map.Entry<Integer, Point>> it = maps.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Point> entry = it.next();
if (!isMoving) { //只有點(diǎn)擊事件才檢驗(yàn)點(diǎn)擊是否有效并響應(yīng)點(diǎn)擊事件
if (isBooleanXY1(touchX, touchY, entry)) {
onExpressItemButtonClickListener.onExpressItemButtonClick(entry.getKey(), 0);
} else if (isBooleanXY2(touchX, touchY, entry)) {
onExpressItemButtonClickListener.onExpressItemButtonClick(entry.getKey(), 1);
}
}
}
}
</pre>
定義數(shù)據(jù)適配器
由于我們的物流狀態(tài)控件只需要四種數(shù)據(jù):主文字殊橙、時(shí)間文字、按鈕坐邊文字以及按鈕右邊文字狱从,所以要對(duì)數(shù)據(jù)源做適配處理膨蛮。
針對(duì)物流控件封裝好數(shù)據(jù)格式
<pre>
public class ExpressViewData {
private String content; //內(nèi)容
private String time; //時(shí)間
private String leftBtnText; //左按鈕文字
private String rightBtnText; //右按鈕文字
@Override
public String toString() {
return "ExpressViewData{" +
"content='" + content + '\'' +
", time='" + time + '\'' +
", leftBtnText='" + leftBtnText + '\'' +
", rightBtnText='" + rightBtnText + '\'' +
'}';
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getLeftBtnText() {
return leftBtnText;
}
public void setLeftBtnText(String leftBtnText) {
this.leftBtnText = leftBtnText;
}
public String getRightBtnText() {
return rightBtnText;
}
public void setRightBtnText(String rightBtnText) {
this.rightBtnText = rightBtnText;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
</pre>
定義一個(gè)抽象適配器類(lèi)
<pre>
public abstract class ExpressViewAdapter<T> {
private OnDataChangedListener onDataChangedListener;
private List<T> dataList;
public ExpressViewAdapter(List<T> dataList) {
this.dataList = dataList;
}
public int getCount(){
return dataList == null ? 0 : dataList.size();
}
public T getItem(int position){
return dataList == null ? null : dataList.get(position);
}
public abstract ExpressViewData bindData(ExpressView expressView, int position, T t);
public void notifyDataChanged(){
onDataChangedListener.onDataChanged();
}
public interface OnDataChangedListener {
void onDataChanged();
}
public void setOnDataChangedListener(OnDataChangedListener onDataChangedListener) {
this.onDataChangedListener = onDataChangedListener;
}
}
</pre>
數(shù)據(jù)適配具體實(shí)現(xiàn)
<pre>
adapter = new ExpressViewAdapter<ExpressMessageBean>(list) {
@Override
public ExpressViewData bindData(ExpressView expressView, int position, ExpressMessageBean expressMessageBean) {
ExpressViewData data = new ExpressViewData();
data.setContent(expressMessageBean.getOpContent());
data.setTime(expressMessageBean.getCreateTimeFormat());
data.setLeftBtnText(expressMessageBean.getFlowStateBtLeft());
data.setRightBtnText(expressMessageBean.getFlowStateBtRight());
return data;
}
};
</pre>
控件中具體取值
<pre>
ExpressViewData expressViewData = mAdapter.bindData(this, i, mAdapter.getItem(i));
</pre>
適配器的使用
<pre>
expressView.setAdapter(adapter);
adapter.notifyDataChanged();
</pre>
通過(guò)適配器,即可把任意的數(shù)據(jù)源做適配展示到物流狀態(tài)控件上季研。
源碼
ExpressView
<pre>
/**
- 物流狀態(tài)View
- Created by licheng on 19/2/17.
*/
public class ExpressView extends View implements ExpressViewAdapter.OnDataChangedListener {
private int screenWidth;
private int screenHeight;
private int width, height; //當(dāng)前View寬高
private int viewWidth, viewHeight; //當(dāng)前View寬高
private int firstExpressCircleMarginLeft = DeviceUtils.dipToPx(getContext(), 16);
private int firstExpressCircleMarginTop = DeviceUtils.dipToPx(getContext(), 40);
private int expressCircleRadius = DeviceUtils.dipToPx(getContext(), 6);//物流狀態(tài)提示圈半徑
private int expressCircleCurrentRadius = DeviceUtils.dipToPx(getContext(), 3);//物流狀態(tài)提示圈半徑
private int expressCircleOuterRadius = DeviceUtils.dipToPx(getContext(), 8);//物流狀態(tài)提示圈外半徑
private int circleToTextMargin = DeviceUtils.dipToPx(getContext(), 12);//物流狀態(tài)提示圈到文字背景的距離
private int expressTextBackgroundWidth; //文字背景寬
private int expressTextMargin = DeviceUtils.dipToPx(getContext(), 8); //文字距離背景邊距
private int expressTextVecPadding = DeviceUtils.dipToPx(getContext(), 5); //每個(gè)物流信息豎直方向的間距
private int expressTextToTimeTextPadding = DeviceUtils.dipToPx(getContext(), 6); //物流文字距離時(shí)間文字的間距
private int expressButtonTextHeight; //按鈕文字高度
private boolean isTimeButtonVisible = false; //是否需要顯示時(shí)間和按鈕
private Paint expressCirclePaint; //物流狀態(tài)提示圈
private Paint expressTextBackgroundPaint; //文字背景
private TextPaint expressTextPaint; //文字
private TextPaint timeTextPaint; //時(shí)間文字
private TextPaint buttonTextPaint; //按鈕文字
private Paint bgPaint;
private Paint expressLinePaint;//物流線(xiàn)條
private int expressTextSize;//文字大小
private int expressTimeTextSize;//時(shí)間文字大小
private int contentDrawHeight; //根據(jù)內(nèi)容繪制的高度
private Map<Integer, Point> buttonTopPositionMap; //用于存儲(chǔ)點(diǎn)擊按鈕左上角坐標(biāo)
private int firstButtonPositionY; //記錄第一個(gè)按鈕位置坐標(biāo)
// private int[] location = new int[2];
private ExpressViewAdapter mAdapter;
public ExpressView(Context context) {
super(context);
initPaint(context);
}
public ExpressView(Context context, AttributeSet attrs) {
super(context, attrs);
initTypeArray(context, attrs);
initPaint(context);
}
public ExpressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initTypeArray(context, attrs);
initPaint(context);
}
private void initTypeArray(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ExpressView);
firstExpressCircleMarginLeft = (int) array.getDimension(R.styleable.ExpressView_firstExpressCircleMarginLeft, 16);
firstExpressCircleMarginTop = (int) array.getDimension(R.styleable.ExpressView_firstExpressCircleMarginTop, 16);
expressCircleRadius = (int) array.getDimension(R.styleable.ExpressView_expressCircleRadius, 6);
expressCircleOuterRadius = (int) array.getDimension(R.styleable.ExpressView_expressCircleOuterRadius, 8);
circleToTextMargin = (int) array.getDimension(R.styleable.ExpressView_circleToTextMargin, 12);
expressTextMargin = (int) array.getDimension(R.styleable.ExpressView_expressTextMargin, 8);
expressTextVecPadding = (int) array.getDimension(R.styleable.ExpressView_expressTextVecPadding, 5);
expressTextSize = (int) array.getDimension(R.styleable.ExpressView_expressTextSize, 16);
expressTimeTextSize = (int) array.getDimension(R.styleable.ExpressView_expressTimeTextSize, 16);
isTimeButtonVisible = array.getBoolean(R.styleable.ExpressView_isTimeButtonVisible, false);
array.recycle();
buttonTopPositionMap = new HashMap<>();
buttonTopPositionMap.put(0, new Point(0, 0)); //設(shè)置初始值
}
private void initPaint(Context context) {
touchDistance = ViewConfiguration.get(context).getScaledTouchSlop();
screenHeight = DeviceUtils.getScreenHeight(context);
screenWidth = DeviceUtils.getScreenWidth(context);
//物流狀態(tài)提示圈
expressCirclePaint = new Paint();
expressCirclePaint.setColor(Color.parseColor("#969696"));
expressCirclePaint.setStyle(Paint.Style.FILL);
expressCirclePaint.setAntiAlias(true);
expressCirclePaint.setStrokeWidth(DeviceUtils.dipToPx(getContext(), 2));
expressTextBackgroundPaint = new Paint();
expressTextBackgroundPaint.setAntiAlias(true);
expressTextBackgroundPaint.setColor(Color.WHITE);
expressTextBackgroundPaint.setStyle(Paint.Style.FILL);
expressCirclePaint.setStrokeWidth(DeviceUtils.dipToPx(getContext(), 2));
expressTextPaint = new TextPaint();
expressTextPaint.setAntiAlias(true);
expressTextPaint.setColor(Color.BLACK);
expressTextPaint.setTextSize(expressTextSize);
expressTextPaint.setStyle(Paint.Style.FILL);
timeTextPaint = new TextPaint();
timeTextPaint.setAntiAlias(true);
timeTextPaint.setColor(Color.parseColor("#969696"));
timeTextPaint.setTextSize(expressTimeTextSize);
timeTextPaint.setStyle(Paint.Style.FILL);
buttonTextPaint = new TextPaint();
buttonTextPaint.setAntiAlias(true);
buttonTextPaint.setColor(Color.parseColor("#4682B4"));
buttonTextPaint.setTextSize(expressTextSize);
buttonTextPaint.setStyle(Paint.Style.FILL);
expressLinePaint = new Paint();
expressLinePaint.setAntiAlias(true);
expressLinePaint.setColor(Color.parseColor("#969696"));
expressLinePaint.setStyle(Paint.Style.FILL);
expressLinePaint.setStrokeWidth(DeviceUtils.dipToPx(getContext(), 1));
bgPaint = new Paint();
bgPaint.setAntiAlias(true);
bgPaint.setAlpha(30);
bgPaint.setColor(Color.parseColor("#969696"));
bgPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
width = w;
height = h;
Log.e("ExpressView", "當(dāng)前View的width " + width + " height " + height);
// getLocationOnScreen(location);
// Log.e("ExpressViewOnScreen", location[0] + " " + location[1]);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
Log.e("ExpressView", "屏幕寬高 " + screenWidth + " " + screenHeight);
if (modeWidth == MeasureSpec.EXACTLY) {
viewWidth = sizeWidth;
Log.e("ExpressView", "精確測(cè)量寬 " + viewWidth);
} else {
viewWidth = width;
Log.e("ExpressView", "粗略測(cè)量寬 " + viewWidth);
}
if (modeHeight == MeasureSpec.EXACTLY) {
viewHeight = sizeHeight;
Log.e("ExpressView", "精確測(cè)量高 " + viewHeight);
} else {
viewHeight = height;
Log.e("ExpressView", "粗略測(cè)量高 " + viewHeight);
}
viewWidth = viewWidth > screenWidth ? screenWidth : viewWidth;
viewHeight = viewHeight > screenHeight ? screenHeight : viewHeight;
setMeasuredDimension(viewWidth, viewHeight);
expressTextBackgroundWidth = viewWidth - 2 * (firstExpressCircleMarginLeft - expressCircleRadius) - 2 * circleToTextMargin;
Log.e("ExpressView", "View寬度 " + viewWidth + "繪制的文字背景寬度 " + expressTextBackgroundWidth);
}
@Override
protected void onDraw(Canvas canvas) {
int expressTextBgHeightSum = 0;
int firstCirclePoint = 0, lastCirclePoint = 0; //記錄第一個(gè)繪制和最后一個(gè)繪制的點(diǎn)位置
if (isAdapterNull()) {
for (int i = 0; i < mAdapter.getCount(); i++) {
//物流文字高度測(cè)量
ExpressViewData expressViewData = mAdapter.bindData(this, i, mAdapter.getItem(i));
String text = expressViewData.getContent();
StaticLayout layout = new StaticLayout(text, expressTextPaint, expressTextBackgroundWidth - 2 * expressTextMargin, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);
int textHeight = layout.getHeight();//計(jì)算文字高度
int expressTextBgHeight = textHeight + 2 * expressTextMargin;
//時(shí)間文字高度測(cè)量
String timeText = "2017-02-16 23:44:31";
StaticLayout timeLayout = new StaticLayout(timeText, timeTextPaint, (expressTextBackgroundWidth - 2 * expressTextMargin) / 2, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);
int timeTextHeight = timeLayout.getHeight();
String timeShowText = expressViewData.getTime();
//按鈕文字高度測(cè)量
String buttonLeft = expressViewData.getLeftBtnText();
String buttonRight = expressViewData.getRightBtnText();
String buttoTxt = "立即購(gòu)買(mǎi)";
StaticLayout buttonLayout = new StaticLayout(buttoTxt, buttonTextPaint, (expressTextBackgroundWidth - 2 * expressTextMargin) / 4, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);
int buttonTextHeight = buttonLayout.getHeight();
int buttonTextWidth = buttonLayout.getWidth();
expressButtonTextHeight = buttonTextHeight;
timeTextHeight = Math.max(timeTextHeight, buttonTextHeight);
if (!isTimeButtonVisible) {
timeTextHeight = 0;
expressTextToTimeTextPadding = 0;
}
//繪制提示圓圈
canvas.save();
if (i == 0) {
expressCirclePaint.setColor(Color.parseColor("#D2691E"));
canvas.drawCircle(firstExpressCircleMarginLeft,
firstExpressCircleMarginTop + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i,
expressCircleOuterRadius,
expressCirclePaint);
expressCirclePaint.setColor(Color.parseColor("#ffffff"));
canvas.drawCircle(firstExpressCircleMarginLeft,
firstExpressCircleMarginTop + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i,
expressCircleCurrentRadius,
expressCirclePaint);
} else {
expressCirclePaint.setColor(Color.parseColor("#969696"));
canvas.drawCircle(firstExpressCircleMarginLeft,
firstExpressCircleMarginTop + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i,
expressCircleRadius,
expressCirclePaint);
}
canvas.restore();
//獲取第一個(gè)提示點(diǎn)和最后一個(gè)提示點(diǎn)坐標(biāo)
if (i == 0)
firstCirclePoint = firstExpressCircleMarginTop + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i;
if (i == mAdapter.getCount() - 1)
lastCirclePoint = firstExpressCircleMarginTop + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i;
//繪制文字背景
canvas.save();
if (i == 0) {
canvas.translate(firstExpressCircleMarginLeft + circleToTextMargin + expressCircleRadius,
firstExpressCircleMarginTop + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i);
expressTextBackgroundPaint.setColor(Color.parseColor("#D2691E"));
canvas.drawRect(0, 0, expressTextBackgroundWidth, expressTextBgHeight + expressTextToTimeTextPadding + timeTextHeight, expressTextBackgroundPaint);
expressTextBackgroundPaint.setColor(Color.parseColor("#ffffff"));
canvas.drawRect(2, 2, expressTextBackgroundWidth - 2, expressTextBgHeight + expressTextToTimeTextPadding + timeTextHeight - 2, expressTextBackgroundPaint);
} else {
canvas.translate(firstExpressCircleMarginLeft + circleToTextMargin + expressCircleRadius,
firstExpressCircleMarginTop + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i);
expressTextBackgroundPaint.setColor(Color.parseColor("#ffffff"));
canvas.drawRect(0, 0, expressTextBackgroundWidth, expressTextBgHeight + expressTextToTimeTextPadding + timeTextHeight, expressTextBackgroundPaint);
}
if (i == mAdapter.getCount() - 1) { //記錄最后一個(gè)文字背景的坐標(biāo)位置
contentDrawHeight = firstExpressCircleMarginTop + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i + expressTextBgHeight + expressTextToTimeTextPadding + timeTextHeight + expressTextVecPadding;
Log.e("ExpressView", "最后一個(gè)文字背景坐標(biāo) " + contentDrawHeight);
}
canvas.restore();
//繪制物流文字
drawLeftButton(canvas, layout, firstExpressCircleMarginLeft + circleToTextMargin + expressCircleRadius + expressTextMargin, firstExpressCircleMarginTop + expressTextMargin + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i);
if (isTimeButtonVisible) {
//繪制時(shí)間文字
if (!StringUtils.isBlank(timeShowText)) {
StaticLayout tl = new StaticLayout(timeShowText, timeTextPaint, (expressTextBackgroundWidth - 2 * expressTextMargin) / 2, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);
drawLeftButton(canvas, tl, firstExpressCircleMarginLeft + circleToTextMargin + expressCircleRadius + expressTextMargin, firstExpressCircleMarginTop + expressTextMargin + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i + textHeight + expressTextToTimeTextPadding);
}
//繪制左邊按鈕
if (!StringUtils.isBlank(buttonLeft)) {
StaticLayout sll = new StaticLayout(buttonLeft, buttonTextPaint, (expressTextBackgroundWidth - 2 * expressTextMargin) / 4, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);
drawLeftButton(canvas, sll, firstExpressCircleMarginLeft + circleToTextMargin + expressCircleRadius + (expressTextBackgroundWidth - 2 * expressTextMargin) * 8 / 15, firstExpressCircleMarginTop + expressTextMargin + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i + textHeight + expressTextToTimeTextPadding);
}
//繪制右邊按鈕
if (!StringUtils.isBlank(buttonRight)) {
StaticLayout sll = new StaticLayout(buttonRight, buttonTextPaint, (expressTextBackgroundWidth - 2 * expressTextMargin) / 4, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);
drawRightButton(canvas, expressTextBgHeightSum, i, textHeight, sll, buttonTextWidth);
}
}
if (i == 0)
firstButtonPositionY = firstExpressCircleMarginTop + expressTextMargin + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i + textHeight + expressTextToTimeTextPadding; //記錄第一按鈕位置坐標(biāo)
//存儲(chǔ)左邊按鈕坐標(biāo)
Point point = new Point();
point.set(firstExpressCircleMarginLeft + circleToTextMargin + expressCircleRadius + (expressTextBackgroundWidth - 2 * expressTextMargin) * 8 / 15, firstExpressCircleMarginTop + expressTextMargin + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i + textHeight + expressTextToTimeTextPadding);
buttonTopPositionMap.put(i, point);
expressTextBgHeightSum += (expressTextBgHeight + timeTextHeight);
}
}
if (isAdapterNull()) {
canvas.save();
canvas.drawLine(firstExpressCircleMarginLeft, firstCirclePoint + expressCircleOuterRadius, firstExpressCircleMarginLeft, lastCirclePoint, expressLinePaint);
canvas.restore();
}
Log.e("ExpressView", "按鈕位置信息 " + JsonUtils.objectToString(buttonTopPositionMap, Map.class));
}
//繪制左邊邊按鈕
private void drawLeftButton(Canvas canvas, StaticLayout buttonLayout, int dx, int dy) {
canvas.save();
canvas.translate(dx, dy);
buttonLayout.draw(canvas);
canvas.restore();
}
//繪制右邊按鈕
private void drawRightButton(Canvas canvas, int expressTextBgHeightSum, int i, int textHeight, StaticLayout buttonLayout, int buttonTextWidth) {
drawLeftButton(canvas, buttonLayout, firstExpressCircleMarginLeft + circleToTextMargin + expressCircleRadius + (expressTextBackgroundWidth - 2 * expressTextMargin) * 8 / 15 + buttonTextWidth, firstExpressCircleMarginTop + expressTextMargin + expressTextBgHeightSum + (expressTextVecPadding + expressTextToTimeTextPadding) * i + textHeight + expressTextToTimeTextPadding);
}
/**
* 判斷適配器是否為null
*
* @return
*/
private boolean isAdapterNull() {
return null != mAdapter;
}
int downY = 0;
int lastY = 0;
int lastMoveY = 0;
int transDistance; //累計(jì)滑動(dòng)距離
int touchDistance; //系統(tǒng)判定滑動(dòng)的最小距離
boolean isMoving = false; //是否滑動(dòng)
@Override
public boolean onTouchEvent(MotionEvent event) {
this.getParent().requestDisallowInterceptTouchEvent(true);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getY();
lastY = downY;
lastMoveY = downY;
isMoving = false;
break;
case MotionEvent.ACTION_MOVE:
downY = (int) event.getY();
int transY = downY - lastY;
int transMoveY = downY - lastMoveY; //每次手指按下到滑動(dòng)停止的滑動(dòng)距離
isMoving = Math.abs(transMoveY) > touchDistance ? true : false; //判定是否滑動(dòng)
Log.e("ExpressViewTouch", "當(dāng)前滑動(dòng)距離 " + Math.abs(transMoveY) + " 是否滑動(dòng) " + isMoving);
if (isMoving) {
transDistance += transY;
Log.e("ExpressViewOnScreen", "滑動(dòng)距離" + transDistance);
scrollBy(0, -transY);
// if (Math.abs(transDistance) <= contentDrawHeight - screenHeight + location[1] && Math.abs(transDistance) > 0) {
// scrollBy(0, -transY);
// } else if (Math.abs(transDistance) == 0) {
// Log.e("ExpressViewOnScreen", "底部");
// } else {
// Log.e("ExpressViewOnScreen", "頂部");
// }
}
lastY = downY;
break;
case MotionEvent.ACTION_UP:
if (isTimeButtonVisible) {
Log.e("ExpressViewTouch", "累計(jì)滑動(dòng)總距離 " + transDistance);
if (isMoving || (!isMoving && (buttonTopPositionMap.get(0).y == firstButtonPositionY))) {
Iterator<Map.Entry<Integer, Point>> it = buttonTopPositionMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Point> entry = it.next();
entry.getValue().y += transDistance;
}
}
Log.e("ExpressViewTouch", "手指離開(kāi)屏幕位置信息 " + JsonUtils.objectToString(buttonTopPositionMap, Map.class));
onActionUpEvent(isMoving, event, buttonTopPositionMap);
this.getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_CANCEL:
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return true;
}
private void onActionUpEvent(boolean isMoving, MotionEvent event, Map<Integer, Point> maps) {
int touchX = (int) event.getX();
int touchY = (int) event.getY();
Iterator<Map.Entry<Integer, Point>> it = maps.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Point> entry = it.next();
if (!isMoving) { //只有點(diǎn)擊事件才檢驗(yàn)點(diǎn)擊是否有效并響應(yīng)點(diǎn)擊事件
if (isBooleanXY1(touchX, touchY, entry)) {
onExpressItemButtonClickListener.onExpressItemButtonClick(entry.getKey(), 0);
} else if (isBooleanXY2(touchX, touchY, entry)) {
onExpressItemButtonClickListener.onExpressItemButtonClick(entry.getKey(), 1);
}
}
}
}
public void setTimeButtonVisible(boolean timeButtonVisible) {
isTimeButtonVisible = timeButtonVisible;
}
private boolean isBooleanXY1(int touchX, int touchY, Map.Entry<Integer, Point> entry) {
return isBooleanX1(touchX, entry) && isBooleanY(touchY, entry);
}
private boolean isBooleanY(int touchY, Map.Entry<Integer, Point> entry) {
return touchY > entry.getValue().y && touchY < entry.getValue().y + expressButtonTextHeight;
}
private boolean isBooleanX1(int touchX, Map.Entry<Integer, Point> entry) {
return touchX > entry.getValue().x && touchX < entry.getValue().x + (expressTextBackgroundWidth - 2 * expressTextMargin) / 4;
}
private boolean isBooleanXY2(int touchX, int touchY, Map.Entry<Integer, Point> entry) {
return isBooleanX2(touchX, entry) && isBooleanY(touchY, entry);
}
private boolean isBooleanX2(int touchX, Map.Entry<Integer, Point> entry) {
return touchX > entry.getValue().x + (expressTextBackgroundWidth - 2 * expressTextMargin) / 4 && touchX < entry.getValue().x + 2 * ((expressTextBackgroundWidth - 2 * expressTextMargin) / 4);
}
@Override
public void onDataChanged() {
invalidate();
}
public interface OnExpressItemButtonClickListener {
void onExpressItemButtonClick(int position, int status);
}
private OnExpressItemButtonClickListener onExpressItemButtonClickListener;
public void setOnExpressItemButtonClickListener(OnExpressItemButtonClickListener onExpressItemButtonClickListener) {
this.onExpressItemButtonClickListener = onExpressItemButtonClickListener;
}
public void setAdapter(ExpressViewAdapter adapter){
mAdapter = adapter;
mAdapter.setOnDataChangedListener(this);
}
}
</pre>
ExpressViewAdapter
<pre>
/**
- Created by licheng on 19/3/17.
*/
public abstract class ExpressViewAdapter<T> {
private OnDataChangedListener onDataChangedListener;
private List<T> dataList;
public ExpressViewAdapter(List<T> dataList) {
this.dataList = dataList;
}
public int getCount(){
return dataList == null ? 0 : dataList.size();
}
public T getItem(int position){
return dataList == null ? null : dataList.get(position);
}
public abstract ExpressViewData bindData(ExpressView expressView, int position, T t);
public void notifyDataChanged(){
onDataChangedListener.onDataChanged();
}
public interface OnDataChangedListener {
void onDataChanged();
}
public void setOnDataChangedListener(OnDataChangedListener onDataChangedListener) {
this.onDataChangedListener = onDataChangedListener;
}
}
</pre>
ExpressViewData
<pre>
/**
- 物流控件數(shù)據(jù)接口
- Created by licheng on 19/3/17.
*/
public class ExpressViewData {
private String content; //內(nèi)容
private String time; //時(shí)間
private String leftBtnText; //左按鈕文字
private String rightBtnText; //右按鈕文字
@Override
public String toString() {
return "ExpressViewData{" +
"content='" + content + '\'' +
", time='" + time + '\'' +
", leftBtnText='" + leftBtnText + '\'' +
", rightBtnText='" + rightBtnText + '\'' +
'}';
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getLeftBtnText() {
return leftBtnText;
}
public void setLeftBtnText(String leftBtnText) {
this.leftBtnText = leftBtnText;
}
public String getRightBtnText() {
return rightBtnText;
}
public void setRightBtnText(String rightBtnText) {
this.rightBtnText = rightBtnText;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
</pre>
布局文件中的使用
<pre>
ExpressView
android:id="@+id/expressview"
android:layout_width="match_parent"
android:layout_height="match_parent"
express:circleToTextMargin="12dp"
express:expressCircleOuterRadius="8dp"
express:expressCircleRadius="6dp"
express:expressTextMargin="12dp"
express:expressTextSize="14sp"
express:expressTextVecPadding="5dp"
express:expressTimeTextSize="10sp"
express:firstExpressCircleMarginLeft="16dp"
express:firstExpressCircleMarginTop="16dp"
express:isTimeButtonVisible="true" />
</pre>
客戶(hù)端代碼
<pre>
//數(shù)據(jù)源
final List<ExpressMessageBean> list = new ArrayList<>();
ExpressMessageBean bean = new ExpressMessageBean();
bean.setFlowState(1);
bean.setFlowStateBtRight("購(gòu)買(mǎi)流程");
bean.setCreateTime(1487259871184l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259871184l));
bean.setOpContent("您已付款0.1200元敞葛,購(gòu)買(mǎi) 地下城與勇士/廣東區(qū)/廣東1區(qū)帳號(hào),請(qǐng)聯(lián)系賣(mài)家卡羅特將密保手機(jī)綁定您的手機(jī)號(hào) 18827065959");
list.add(bean);
bean = new ExpressMessageBean();
bean.setFlowState(2);
bean.setCreateTime(1487259991260l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259991260l));
bean.setOpContent("天空套 0.1200 1個(gè)-申請(qǐng)退款");
list.add(bean);
bean = new ExpressMessageBean();
bean.setFlowState(3);
bean.setCreateTime(1487259871184l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259871184l));
bean.setOpContent("您已付款0.1200元与涡,購(gòu)買(mǎi) 地下城與勇士/廣東區(qū)/廣東1區(qū)帳號(hào)制肮,請(qǐng)聯(lián)系賣(mài)家卡羅特將密保手機(jī)綁定您的手機(jī)號(hào) 18827065959");
list.add(bean);
bean = new ExpressMessageBean();
bean.setFlowState(4);
bean.setFlowStateBtLeft("同意退款"); //設(shè)置左右按鈕文字
bean.setFlowStateBtRight("拒絕退款");
bean.setCreateTime(1487259991260l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259991260l));
bean.setOpContent("天空套 0.1200 1個(gè)-申請(qǐng)退款");
list.add(bean);
bean = new ExpressMessageBean();
bean.setFlowState(5);
bean.setCreateTime(1487259871184l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259871184l));
bean.setOpContent("您已付款0.1200元,購(gòu)買(mǎi) 地下城與勇士/廣東區(qū)/廣東1區(qū)帳號(hào)递沪,請(qǐng)聯(lián)系賣(mài)家卡羅特將密保手機(jī)綁定您的手機(jī)號(hào) 18827065959");
list.add(bean);
bean = new ExpressMessageBean();
bean.setFlowState(6);
bean.setCreateTime(1487259991260l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259991260l));
bean.setOpContent("天空套 0.1200 1個(gè)-申請(qǐng)退款");
list.add(bean);
bean = new ExpressMessageBean();
bean.setFlowState(7);
bean.setCreateTime(1487259871184l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259871184l));
bean.setOpContent("您已付款0.1200元豺鼻,購(gòu)買(mǎi) 地下城與勇士/廣東區(qū)/廣東1區(qū)帳號(hào),請(qǐng)聯(lián)系賣(mài)家卡羅特將密保手機(jī)綁定您的手機(jī)號(hào) 18827065959");
list.add(bean);
bean = new ExpressMessageBean();
bean.setFlowState(1);
bean.setFlowStateBtRight("購(gòu)買(mǎi)流程"); //設(shè)置右按鈕文字
bean.setCreateTime(1487259991260l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259991260l));
bean.setOpContent("天空套 0.1200 1個(gè)-申請(qǐng)退款");
list.add(bean);
//數(shù)據(jù)源適配
adapter = new ExpressViewAdapter<ExpressMessageBean>(list) {
@Override
public ExpressViewData bindData(ExpressView expressView, int position, ExpressMessageBean expressMessageBean) {
ExpressViewData data = new ExpressViewData();
data.setContent(expressMessageBean.getOpContent());
data.setTime(expressMessageBean.getCreateTimeFormat());
data.setLeftBtnText(expressMessageBean.getFlowStateBtLeft());
data.setRightBtnText(expressMessageBean.getFlowStateBtRight());
return data;
}
};
expressView.setAdapter(adapter);
adapter.notifyDataChanged();
//延遲4秒添加2條數(shù)據(jù)
expressView.postDelayed(new Runnable() {
@Override
public void run() {
ExpressMessageBean bean = new ExpressMessageBean();
bean.setFlowState(1);
bean.setFlowStateBtRight("購(gòu)買(mǎi)流程"); //設(shè)置右按鈕文字
bean.setCreateTime(1487259991260l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259991260l));
bean.setOpContent("天空套 0.1200 1個(gè)-申請(qǐng)退款");
list.add(bean);
bean = new ExpressMessageBean();
bean.setFlowState(1);
bean.setFlowStateBtRight("購(gòu)買(mǎi)流程"); //設(shè)置右按鈕文字
bean.setCreateTime(1487259991260l);
bean.setCreateTimeFormat(TimeUtils.millis2String(1487259991260l));
bean.setOpContent("天空套 0.1200 1個(gè)-申請(qǐng)退款");
list.add(bean);
adapter.notifyDataChanged();
}
}, 4000);
//處理點(diǎn)擊事件
expressView.setOnExpressItemButtonClickListener(new ExpressView.OnExpressItemButtonClickListener() {
@Override
public void onExpressItemButtonClick(int position, int status) {
switch (list.get(position).getFlowState()){
case 1:
if(status == 1){ //購(gòu)買(mǎi)流程
ToastUtil.ToastBottow(TestActivity.this, list.get(position).getFlowStateBtRight());
}
break;
case 4:
if(status == 0) { //同意退款
ToastUtil.ToastBottow(TestActivity.this, list.get(position).getFlowStateBtLeft());
} else if(status == 1){ //拒絕退款
ToastUtil.ToastBottow(TestActivity.this, list.get(position).getFlowStateBtRight());
}
break;
default:
break;
}
}
});
</pre>
目前控件存在的問(wèn)題
1款慨、沒(méi)有處理滑動(dòng)沖突
2儒飒、沒(méi)有處理滑動(dòng)到頂部和到底部停止滑動(dòng)的邏輯
3、沒(méi)有實(shí)現(xiàn)彈性滑動(dòng)的效果
源碼地址:
https://github.com/xiaomanzijia/ExpressView
本文中物流狀態(tài)控件的實(shí)現(xiàn)僅供參考學(xué)習(xí)檩奠,用于實(shí)際項(xiàng)目還需優(yōu)化桩了。