Android自定義View:快遞時間軸實現

NO IMAGE

前言

  • 在Android開發中,時間軸的 UI非常常見,如下圖:

    Android自定義View:快遞時間軸實現

  • 儲備知識:
    1.自定義view基礎
    2.RecyclerView的使用
    3.自定義RecyclerView.ItemDecoration

具體實現

1.最終效果如下:

Android自定義View:快遞時間軸實現

2.實現思路

  • 使用RecyclerView,自定義RecyclerView.ItemDecoration
  • 複習ItemDecoration中getItemOffsets()方法,重寫onDraw()方法
  • 實現RecyclerView.Adapter,綁定數據

3.詳細設計

Android自定義View:快遞時間軸實現

Android自定義View:快遞時間軸實現

4.具體實現

  • 引入RecyclerView依賴包
dependencies {
     ..........
    api 'com.android.support:recyclerview-v7:28.0.0'
}
  • 在佈局文件中使用
<RelativeLayout 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="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="horizontal"
        />


</RelativeLayout>
  • 設置item佈局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <TextView
        android:id="@+id/item_title"
        android:text="New Text"
        android:textSize="15sp"
        android:layout_marginLeft="30dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Text"
        android:layout_marginLeft="30dp"
        android:textSize="15sp"
        android:id="@+id/item_text"
        android:layout_below="@+id/item_title"
        />

</LinearLayout>
  • 實現RecyclerView.Adapter
public class MyAdapter extends RecyclerView.Adapter {
    private LayoutInflater inflater;
    private ArrayList<HashMap<String,Object>> listitem;

    //構造函數,傳入數據
    public MyAdapter(Context context,ArrayList<HashMap<String, Object>> listitem) {
        this.inflater = LayoutInflater.from(context);
        this.listitem = listitem;
    }

    class ViewHolder extends RecyclerView.ViewHolder{
        private TextView title,text;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            title = itemView.findViewById(R.id.item_title);
            text = itemView.findViewById(R.id.item_text);
        }

        public TextView getTitle() {
            return title;
        }

        public TextView getText() {
            return text;
        }


    }



    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        return new ViewHolder(inflater.inflate(R.layout.list_cell,null));
        //綁定item佈局
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
          //綁定數據到ViewHolder
        ViewHolder  vh = (ViewHolder) viewHolder;
        vh.title.setText((CharSequence) listitem.get(i).get("ItemTitle"));
        vh.text.setText((CharSequence) listitem.get(i).get("ItemText"));
    }

    @Override
    public int getItemCount() {
        return listitem.size();
    }
}
  • 自定義RecyclerView.ItemDecoration
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
//軸點畫筆
private final Paint mPaint;
//時分畫筆
private final Paint mPaint1;
//年月畫筆
private final Paint mPaint2;
//itemView 左 上 偏移量
private int itemView_leftinterval;
private int itemView_topintervarl;
//軸點半徑
private  int circle_radius;
private final Bitmap mIcon;
//在構造函數裡初始化需要屬性
public DividerItemDecoration(Context context){
mPaint = new Paint();
mPaint.setColor(Color.RED);//設置畫筆顏色為紅色
mPaint1 = new Paint();
mPaint1.setColor(Color.BLUE);
mPaint1.setTextSize(30);//設置繪製字體大小
mPaint2 = new Paint();
mPaint2.setColor(Color.BLUE);
mPaint2.setTextSize(15);
itemView_leftinterval = 200; //左偏移長度200
itemView_topintervarl = 50; //上偏移長度50
circle_radius = 10;//軸點半徑為10
mIcon = BitmapFactory.decodeResource(context.getResources(),R.mipmap.logo);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//設置itemview的左上偏移量,即為onDraw可繪製的區域
outRect.set(itemView_leftinterval,itemView_topintervarl,0,0);
}
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
//獲取RecyclerView的Child的個數
int childCount = parent.getChildCount();
//遍歷每個item,分別獲取他們的位置信息,然後在繪製對應的分割線
for (int i=0;i<childCount;i++){
View view = parent.getChildAt(i);//獲取每個item對象
/**
* 繪製軸點
*/
// 軸點 = 圓 = 圓心(x,y)
float centerX = view.getLeft() - itemView_leftinterval/3;
float centerY = view.getTop() - itemView_topintervarl+(itemView_topintervarl+view.getHeight()/2);
// 繪製軸點圓
//c.drawCircle(centerX,centerY,circle_radius,mPaint);
c.drawBitmap(mIcon,centerX-circle_radius,centerY-circle_radius,mPaint);
/**
* 繪製上半軸線
*/
// 上端點座標(x,y)
float upLine_up_x = centerX;
float upLine_up_y =view.getTop()-itemView_topintervarl;
// 下端點座標(x,y)
float upLine_down_x = centerX;
float upLine_down_y = centerY-circle_radius;
c.drawLine(upLine_up_x,upLine_up_y,upLine_down_x,upLine_down_y,mPaint);//繪製下半軸線
/**
* 繪製下半軸線
*/
// 上端點座標(x,y)
float bottomLine_up_x = centerX;
float bottom_up_y = centerY + circle_radius;
// 下端點座標(x,y)
float bottomLine_bottom_x = centerX;
float bottomLine_bottom_y = view.getBottom();
//繪製下半部軸線
c.drawLine(bottomLine_up_x, bottom_up_y, bottomLine_bottom_x, bottomLine_bottom_y, mPaint);
/**
* 繪製左邊時間文本
*/
int index =  parent.getChildAdapterPosition(view);
//繪製時間文本起始位置
float Text_x = view.getLeft()-itemView_leftinterval*5/6;
float Text_y = upLine_down_y;
//根據item位置設置時間
switch (index){
case 0:
//設置繪製日期
c.drawText("13:40",Text_x,Text_y,mPaint1);
c.drawText("2018.4.03",Text_x+5,Text_y+20,mPaint2);
break;
case 1:
//設置繪製日期
c.drawText("13:40",Text_x,Text_y,mPaint1);
c.drawText("2018.4.03",Text_x+5,Text_y+20,mPaint2);
break;
case 2:
//設置繪製日期
c.drawText("13:40",Text_x,Text_y,mPaint1);
c.drawText("2018.4.03",Text_x+5,Text_y+20,mPaint2);
break;
case 3:
//設置繪製日期
c.drawText("13:40",Text_x,Text_y,mPaint1);
c.drawText("2018.4.03",Text_x+5,Text_y+20,mPaint2);
break;
case 4:
//設置繪製日期
c.drawText("13:40",Text_x,Text_y,mPaint1);
c.drawText("2018.4.03",Text_x+5,Text_y+20,mPaint2);
break;
case 5:
//設置繪製日期
c.drawText("13:40",Text_x,Text_y,mPaint1);
c.drawText("2018.4.03",Text_x+5,Text_y+20,mPaint2);
break;
default:
c.drawText("已簽收",Text_x,Text_y,mPaint1);
}
}
}
}
  • 初始化數據,綁定RecyclerView
public class MainActivity extends AppCompatActivity {
private ArrayList<HashMap<String, Object>> itemlist;
private RecyclerView rl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initData() {
itemlist = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> map1 = new HashMap<String, Object>();
HashMap<String, Object> map2 = new HashMap<String, Object>();
HashMap<String, Object> map3 = new HashMap<String, Object>();
HashMap<String, Object> map4 = new HashMap<String, Object>();
HashMap<String, Object> map5 = new HashMap<String, Object>();
HashMap<String, Object> map6 = new HashMap<String, Object>();
map1.put("ItemTitle", "中國廣州公司已發出");
map1.put("ItemText", "發件人:妙卡迪文化公司");
itemlist.add(map1);
map2.put("ItemTitle", "國際順豐已收入");
map2.put("ItemText", "等待中轉");
itemlist.add(map2);
map3.put("ItemTitle", "國際順豐轉件中");
map3.put("ItemText", "下一站中國");
itemlist.add(map3);
map4.put("ItemTitle", "中國順豐已收入");
map4.put("ItemText", "下一站江蘇理工大學");
itemlist.add(map4);
map5.put("ItemTitle", "中國順豐派件中");
map5.put("ItemText", "等待派件");
itemlist.add(map5);
map6.put("ItemTitle", "江蘇理工大學已簽收");
map6.put("ItemText", "收件人:darryrzhong");
itemlist.add(map6);
}
private void initView() {
rl = findViewById(R.id.my_recycler_view);
LinearLayoutManager manager = new LinearLayoutManager(this);
rl.setLayoutManager(manager);
//當知道Adapter內Item的改變不會影響RecyclerView寬高的時候,可以設置為true讓RecyclerView避免重新計算大小。
rl.setHasFixedSize(true);
rl.addItemDecoration(new DividerItemDecoration(this));//設置自定義分割線
MyAdapter adapter =  new MyAdapter(this,itemlist);
rl.setAdapter(adapter);
}
}

至此,自定義RecyclerView就實現完成了.

Android自定義View:快遞時間軸實現

參考文章:

Android 自定義View實戰系列 :時間軸

歡迎關注作者darryrzhong,更多幹貨等你來拿喲.

請賞個小紅心!因為你的鼓勵是我寫作的最大動力!

更多精彩文章請關注

相關文章

Android常用圖片加載庫介紹及對比

Android進階學習思維大綱

AndroidRxJava:基礎介紹與使用

AndroidRetrofit2.5.0使用基礎詳解