自定义 View | 时间轴

简介: 自定义 View | 时间轴

自定义时间轴


可配合 recyclerView 进行使用

动态的指点具体的位置

高度自定义,根据需求修改 View 即可

可设置默认 圆点 的选中,以及修改其位置

效果图如下:


0a2653c851af460fa595bd959398a8f1.png


public class LineCircle extends View {
    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Path path = new Path();
    /**
     * 线的颜色
     */
    private int mPathColor = Color.parseColor("#eeeeee");
    /**
     * 外圆的颜色
     */
    private int outerCircleColor = Color.parseColor("#96ccff");
    /**
     * 内圆的颜色
     */
    private int innerCircleColor = Color.parseColor("#2988e2");
    /**
     * 外圆半径
     */
    private int outRadius = Utils.dp2px(5f).intValue();
    /**
     * 内圆半径
     */
    private int inRadius = Utils.dp2px(2.5f).intValue();
    /**
     * 通过 {@link #add(int)} 添加位置的圆的半径
     */
    private int listRadius = Utils.dp2px(2.5f).intValue();
    /**
     * 是否为头部
     */
    private boolean mHead = false;
    /**
     * 是否发为尾部
     */
    private boolean mEnd = false;
    /**
     * 是否选中
     */
    private boolean isSelect = false;
    //偏移
    private int offsetY;
    /**
     * 默认的圆在高度上的百分比
     */
    private float mScale = 0.3f;
    /**
     * 原点 y 轴位置
     */
    private ArrayList<Integer> mList = new ArrayList<>();
    public LineCircle(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        int width = getWidth();
        // 默认圆的高度
        int scaleHeight;
        if (offsetY == 0) {
            scaleHeight = (int) (getHeight() * mScale);
        } else {
            scaleHeight = offsetY;
        }
        int center = width / 2;
        paint.setColor(mPathColor);
        paint.setStrokeWidth(Utils.dp2px(1f));
        path.moveTo(width / 2, 50);
        path.lineTo(width / 2, getHeight());
        //画线
        if (mHead) {
            canvas.drawLine(center, scaleHeight, center, getHeight(), paint);
        } else if (mEnd) {
            if (mList.size() > 0 && mList.get(mList.size() - 1) > scaleHeight) {
                canvas.drawLine(center, 0, center, mList.get(mList.size() - 1), paint);
            } else {
                canvas.drawLine(center, 0, center, scaleHeight, paint);
            }
        } else {
            canvas.drawLine(center, 0, center, getHeight(), paint);
        }
        //画圆
        if (isSelect) {
            paint.setColor(outerCircleColor);
            canvas.drawCircle(center, scaleHeight, outRadius, paint);
            paint.setColor(innerCircleColor);
            canvas.drawCircle(center, scaleHeight, inRadius, paint);
        } else {
            paint.setColor(mPathColor);
            canvas.drawCircle(center, scaleHeight, listRadius, paint);
        }
        if (mList.size() > 0) {
            for (int i = 0; i < mList.size(); i++) {
                paint.setColor(mPathColor);
                canvas.drawCircle(center, mList.get(i), listRadius, paint);
            }
        }
    }
    /**
     * 添加别的圆点
     * 需传入具体的高度,以像素为单位
     * <p>
     * 全部添加完成后需调用 {@link #addSave()} ,
     * 注意,需按顺序添加,从小往大
     * </p>
     *
     * @param offsetY 偏移
     */
    public void add(int offsetY) {
        mList.add(offsetY);
    }
    public void add(List<Integer> offsetY) {
        mList.clear();
        mList.addAll(offsetY);
    }
    /**
     * 添加完成后执行
     */
    public void addSave() {
        invalidate();
    }
    public void clear() {
        mList.clear();
    }
    /**
     * 是否为第一个
     */
    public void setHead(boolean head) {
        this.mHead = head;
        invalidate();
    }
    /**
     * 是否为最后一个
     *
     * @param end
     */
    public void setEnd(boolean end) {
        this.mEnd = end;
        invalidate();
    }
    /**
     * 设置线的颜色
     *
     * @param color
     */
    public void pathColor(int color) {
        this.mPathColor = color;
        invalidate();
    }
    /**
     * 设置圆高度的百分比
     *
     * @param mScale
     */
    public void setScale(float mScale) {
        this.mScale = mScale;
        invalidate();
    }
    /**
     * 是否选中默认的圆。
     * 高度使用  {@link #setSelect(boolean)} 进行设置
     *
     * @param isSelect
     */
    public void setSelect(boolean isSelect) {
        setSelect(isSelect, 0);
    }
    /**
     * 是否选中默认的圆,以及对应的位置
     *
     * @param isSelect
     */
    public void setSelect(boolean isSelect, int offsetY) {
        this.isSelect = isSelect;
        this.offsetY = offsetY;
        invalidate();
    }
}


布局如下:


<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <com.fengtong.core.ui.view.LineCircle
        android:id="@+id/record_line"
        android:layout_width="15dp"
        android:layout_height="match_parent"
        android:layout_marginStart="18dp" />
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="5dp">
        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/record_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/_666666"
            android:textSize="12sp"
            tools:ignore="HardcodedText"
            tools:text="2020-06-17" />
        <androidx.appcompat.widget.LinearLayoutCompat
            android:id="@+id/record_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/record_date"
            android:orientation="vertical"
            android:paddingBottom="20dp">
        </androidx.appcompat.widget.LinearLayoutCompat>
    </RelativeLayout>
</androidx.appcompat.widget.LinearLayoutCompat>


使用:


1,需要配合 recyclerView 使用。也可单独使用


2,调用 setSelect 方法设置是否选中默认的,以及默认圆的位置,可通过百分比,或者 具体的Y轴值来设置。


3,通过调用 add 方法添加其他的 圆点 ,需要传入Y 轴位置。


下面是在 recyclerView 中使用的:


//当前条目的类型数据
String tag = entity.getField(MultipleFields.TAG);
//当前条目的数据
String date = entity.getField(MultipleFields.DATE);
List<String> list = entity.getField(MultipleFields.LIST);
//拿到条目中对应的控件
AppCompatTextView dateTv = holder.getView(R.id.record_date);
LineCircle line = holder.getView(R.id.record_line);
LinearLayoutCompat layout = holder.getView(R.id.record_layout);
//设置值
dateTv.setText(date);
//添加dateTv 初始化完成的监听
dateTv.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
    // 计算dateTv 的位置
    int offsetY = dateTv.getTop() + (dateTv.getHeight() / 2);
    //设置默认圆的位置,以及选中。也可单独设置选中,通过 setScale 设置百分比,从 0 到 1
    line.setSelect(true, offsetY);
});
//判断条目是否为 第一个 或者最后一个
if (tag.equals("head")) {
    line.setHead(true);
} else if (tag.equals("end")) {
    line.setEnd(true);
}
//遍历数据,动态的创建 View 并进行添加的指定的 layout 中。
//然后计算 view 的位置,通过 add 方法进行设置。
//最后需调用 addSave
for (int i = 0; i < list.size(); i++) {
    AppCompatTextView textView = (AppCompatTextView) LayoutInflater.from(holder.itemView.getContext())
            .inflate(R.layout.layout_record_text, layout, false);
    textView.setText(list.get(i));
    layout.addView(textView);
    textView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
        //拿到中心点
        int top = textView.getTop() + layout.getTop() + (textView.getHeight() / 2);
        //绘制圆点
        line.add(top);
    });
    line.addSave();
}


2d65d23f6d4748949b924e4057485923.png

相关文章
|
XML Android开发 数据格式
控件布局(View)叠加效果
控件布局(View)叠加效果
|
Android开发
自定义 View | 画板
自定义 View | 画板
自定义 View | 画板
|
前端开发 容器
View的测量、布局和绘制过程中父View(当前View)和子View的先后顺序
View的测量、布局和绘制过程中,到底是先测量(布局、绘制)父View,还是先测量子View,这篇文章会从源码角度给出答案。
ViewPager如何区分自动切换和手势滑动切换
ViewPager是一个很常见的组件,不仅支持收拾滑动切换页面,我们还可以通过`viewPager.setCurrentItem(index)`来切换到指定的页面,那么他们如何区分呢? 我们知道ViewPager可以添加`ViewPager.OnPageChangeListener`监听器,可以监听切换的状态。通过观察`ViewPager.OnPageChangeListener#onPageScrollStateChanged(int state)`方法中state的输出,发现了手势切换和自动切换的规律。
|
小程序 前端开发 定位技术
【小程序】view视图,swiper轮播图,scroll-view滑动列表 (在线详细手册)
【小程序】view视图,swiper轮播图,scroll-view滑动列表 (在线详细手册)
【小程序】view视图,swiper轮播图,scroll-view滑动列表 (在线详细手册)
|
Android开发
Android开发 ListView(垂直滚动列表项视图)的简单使用
Android开发 ListView(垂直滚动列表项视图)的简单使用
348 0
Android开发 ListView(垂直滚动列表项视图)的简单使用
【JetPack】视图绑定 ( ViewBinding ) 各种应用 ( 视图绑定两种方式 | Activity 布局 | 对话框布局 | 自定义组件布局 | RecyclerView 列表布局 )
【JetPack】视图绑定 ( ViewBinding ) 各种应用 ( 视图绑定两种方式 | Activity 布局 | 对话框布局 | 自定义组件布局 | RecyclerView 列表布局 )
583 0
【JetPack】视图绑定 ( ViewBinding ) 各种应用 ( 视图绑定两种方式 | Activity 布局 | 对话框布局 | 自定义组件布局 | RecyclerView 列表布局 )
|
算法 iOS开发
iOS视图滚动的时候控制导航条标题及公告视图的alpha(显示与隐藏)
iOS视图滚动的时候控制导航条标题及公告视图的alpha(显示与隐藏)
242 0
iOS视图滚动的时候控制导航条标题及公告视图的alpha(显示与隐藏)
2-VIII--ViewPager滑动监听与自定义滑动特效
零、前言 [1]. 使用上文项目:1-VIII--ViewPager的基本使用 [2].对ViewPager的addOnPageChangeListener三个回调方法分析 [3].
1272 0
tablayout支持改变选中文字大小,支持左右滑动,支持viewpager,支持三角可移动指示器
TabLayout [简书地址] (https://www.jianshu.com/p/2c3f868266e8) 基于大神的FlycoTabLayout 传送地址和基本用法 用法和属性和这个库一样 效果图如下 Gif_20180828_142709.
2489 0