美文网首页自定义控件Android进阶自定义View篇
Android自定义View:快递时间轴实现

Android自定义View:快递时间轴实现

作者: darryrzhong | 来源:发表于2019-03-27 23:58 被阅读32次

    前言

    • 在Android开发中,时间轴的 UI非常常见,如下图:


      TIM图片20190327232833.jpg
    • 储备知识:
      1.自定义view基础
      2.RecyclerView的使用
      3.自定义RecyclerView.ItemDecoration

    具体实现

    1.最终效果如下:


    TIM截图20190327231820.png

    2.实现思路

    • 使用RecyclerView,自定义RecyclerView.ItemDecoration
    • 复习ItemDecoration中getItemOffsets()方法,重写onDraw()方法
    • 实现RecyclerView.Adapter,绑定数据

    3.详细设计

    TIM截图20190327235039.png TIM截图20190327235010.png

    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就实现完成了.


    TIM截图20190327231820.png

    参考文章:

    Android 自定义View实战系列 :时间轴

    欢迎关注作者darryrzhong,更多干货等你来拿哟.

    请赏个小红心!因为你的鼓励是我写作的最大动力!

    更多精彩文章请关注

    相关文章

      网友评论

        本文标题:Android自定义View:快递时间轴实现

        本文链接:https://www.haomeiwen.com/subject/omwxbqtx.html