您当前的位置: 首页 >  android

Kevin-Dev

暂无认证

  • 2浏览

    0关注

    544博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Android 自定义 View】--> 单列时间轴(一)

Kevin-Dev 发布时间:2020-01-10 22:40:07 ,浏览量:2

不断学习,做更好的自己!💪

视频号CSDN简书欢迎打开微信,关注我的视频号:KevinDev点我点我 效果图

01.png

基类代码 适配器

BaseAdapter.java

/**
 * Created on 2021/7/16 14:50
 *
 * @author Gong Youqiang
 */
@SuppressWarnings("ALL")
public abstract class BaseAdapter extends RecyclerView.Adapter
        implements View.OnClickListener,View.OnLongClickListener,IAdapterProxy{

    // 数据集合
    protected List mDataList;
    // 监听器
    private AdapterListener adapterListener;

    public BaseAdapter() {
        this(null);
    }

    public BaseAdapter(AdapterListener adapterListener) {
        this(new ArrayList(),adapterListener);
    }

    public BaseAdapter(List mDataList, AdapterListener adapterListener) {
        this.mDataList = mDataList;
        this.adapterListener = adapterListener;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // 创建ViewHolder
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View root = inflater.inflate(viewType,parent,false);
        ViewHolder viewHolder = onCreateViewHolder(root,viewType);

        // 基础的操作
        root.setTag(R.id.recycler_view,viewHolder);
        root.setOnClickListener(this);
        root.setOnLongClickListener(this);
        doWithRoot(viewHolder,root);

        return viewHolder;
    }

    protected void doWithRoot(ViewHolder viewHolder,View root){

    }

    /**
     *  实际的创建ViewHolder的方法
     */
    public abstract ViewHolder onCreateViewHolder(View root, int viewType);

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // 设置不能进行重复绘制
        //holder.setIsRecyclable(false);
        // TODO 对多数据进行测试,查看哪里出了问题
        // 绑定数据
        Data data = mDataList.get(position);
        holder.bind(data);
    }

    @Override
    public int getItemViewType(int position) {
        Data data = mDataList.get(position);
        return getItemLayout(data,position);
    }

    /**
     * 得到子布局的ID 适合多种子布局的情况下使用
     */
    public abstract int getItemLayout(Data data,int position);

    @Override
    public int getItemCount() {
        return mDataList.size();
    }

    @Override
    public void onClick(View v) {
        ViewHolder holder = (ViewHolder) v.getTag(R.id.recycler_view);
        if(holder != null){
            if(adapterListener == null)
                return;
            int pos = holder.getAdapterPosition();
            adapterListener.onItemClick(holder,mDataList.get(pos));
        }
    }

    @Override
    public boolean onLongClick(View v) {
        ViewHolder holder = (ViewHolder) v.getTag(R.id.recycler_view);
        if(holder != null){
            if(adapterListener != null){
                int pos = holder.getAdapterPosition();
                adapterListener.onItemLongClick(holder,mDataList.get(pos));
                return true;
            }
        }
        return false;
    }

    /**
     *  得到数据
     */
    public List getItems() {
        return mDataList;
    }

    /**
     * 新增一个数据
     */
    public void add(Data data){
        mDataList.add(data);
        notifyItemInserted(mDataList.size() -1 );
    }

    /**
     * 新增所有的数据
     */
    public void addAllData(Collection datas){
        int start = mDataList.size();
        mDataList.addAll(datas);
        notifyItemRangeChanged(start,datas.size());
    }

    /**
     *  新增所有的数组数据
     */
    public void addAllData(Data... datas){
        int start = mDataList.size();
        mDataList.addAll(Arrays.asList(datas));
        notifyItemRangeChanged(start,datas.length);
    }

    /**
     * 删除所有的数据
     */
    public void remove(){
        mDataList.clear();
        notifyDataSetChanged();
    }

    /**
     * 替换数据
     */
    public void replace(Collection datas){
        mDataList.clear();
        mDataList.addAll(datas);
        notifyDataSetChanged();
    }

    public void setAdapterListener(AdapterListener listener){
        this.adapterListener = listener;
    }


    /*
     * 适配器的监听器
     */
    public interface AdapterListener{
        // 单击的时候
        void onItemClick(ViewHolder holder, Data data);
        // 长按的时候
        void onItemLongClick(ViewHolder holder, Data data);
    }

    /**
     *  自定义的ViewHolder
     */
    public static abstract class ViewHolder extends RecyclerView.ViewHolder{
        protected Data mData;

        public ViewHolder(View itemView) {
            super(itemView);
        }

        public void bind(Data data){
            mData = data;
            onBind(data);
        }

        /**
         * 实现数据的绑定
         */
        protected abstract void onBind(Data data);
    }

    public abstract static class AdapterListenerImpl implements AdapterListener{
        @Override
        public void onItemClick(ViewHolder holder,Data data) {

        }

        @Override
        public void onItemLongClick(ViewHolder holder,Data data) {

        }
    }
}

IAdapterProxy.java

/**
 * Created on 2021/7/16 14:52
 *
 * @author Gong Youqiang
 */
public interface IAdapterProxy{
    void addAllData(Collection dataList);
    void setAdapterListener(BaseAdapter.AdapterListener listener);
}
2. 数据

ITimeItem.java

/**
 * Created on 2021/7/16 14:56
 *  时间轴数据需要实现的接口
 * @author Gong Youqiang
 */
public interface ITimeItem {
    /**
     * 构建绘制的标题
     * @return 标题
     */
    String getTitle();

    /**
     * 用户绘制原点的颜色
     * @return 颜色
     */
    int getColor();

    /**
     * 图片的资源文件
     * @return drawable的资源地址
     */
    int getResource();
}
3. ItemDecoration

TimeLine.java

/**
 * Created on 2021/7/16 14:54
 *
 * @author Gong Youqiang
 */
public abstract class TimeLine extends RecyclerView.ItemDecoration {
    // 标题
    public static final int FLAG_TITLE_POS_NONE = 0X0001;
    public static final int FLAG_TITLE_TYPE_TOP = 0x0002;
    public static final int FLAG_TITLE_TYPE_LEFT = 0x0004;
    public static final int FLAG_TITLE_DRAW_BG = 0x0008;

    public static final int FLAG_SAME_TITLE_HIDE = 0x0100;

    // 时间线
    public static final int FLAG_LINE_DIVIDE = 0x0010;
    public static final int FLAG_LINE_CONSISTENT = 0x0020;
    public static final int FLAG_LINE_BEGIN_TO_END= 0x0040;
    // 时间点
    public static final int FLAG_DOT_RES = 0x1000;
    public static final int FLAG_DOT_DRAW = 0x2000;


    protected Context mContext;
    protected List timeItems;
    // 标题放置的类型
    protected int mFlag;
    // 上次的标题

    // 标题分两种,
    // 1. 上方
    // 2. 左侧
    protected int mTitleColor;
    protected int mTopOffset;
    protected int mLeftOffset;
    protected Paint mTextPaint;
    protected int mTitleFontSize;
    protected int mBgColor;
    protected Paint mBgPaint;

    // 线
    protected int mLineColor;
    protected Paint mLinePaint;
    protected int mLineOffset;
    protected int mLineWidth;

    // 点
    protected Paint mDotPaint;

    public TimeLine(Config config) {
        mContext = config.context;
        this.timeItems = config.timeItems;
        this.mFlag = config.flag;

        // 标题
        this.mTitleColor = config.titleColor;
        if ((mFlag & FLAG_TITLE_TYPE_TOP) != 0) {
            mTopOffset = DisplayUtils.dip2px(config.titleOffset);
        } else if ((mFlag & FLAG_TITLE_TYPE_LEFT) != 0) {
            mLeftOffset = DisplayUtils.dip2px(config.titleOffset);
        }
        this.mTitleFontSize = DisplayUtils.sp2px(mContext, config.titleFontSize);
        this.mBgColor = config.bgColor;

        // 时间线
        this.mLineColor = config.lineColor;
        this.mLineOffset = DisplayUtils.dip2px(config.lineOffset);
        this.mLineWidth = DisplayUtils.dip2px(config.lineWidth);

        init();
    }

    private void init() {
        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        mLinePaint.setColor(mLineColor);

        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        mTextPaint.setTextSize(mTitleFontSize);
        mTextPaint.setColor(mTitleColor);
        mBgPaint = new Paint();
        mBgPaint.setColor(mBgColor);

        mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
    }


    /**
     * 更新部分数据
     */
    public void addItems(List items){
        this.timeItems.addAll(items);
    }

    /**
     * 更新全部数据
     * @param items 数据
     */
    public void replace(List items){
        this.timeItems = items;
    }

    /**
     * 清除数据
     */
    public void remove(){
        this.timeItems.clear();
    }

    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(c, parent, state);

        // 兼容4.0硬件加速无效
        parent.setLayerType(View.LAYER_TYPE_SOFTWARE,mDotPaint);

        int childCount = parent.getChildCount();
        if (childCount == 0)
            return;

        // 绘制处理
        // 1. 绘制标题
        drawTitle(c, parent);
        // 2. 绘制线
        drawVerticalLine(c, parent);
        // 3. 绘制点
        drawPoint(c, parent);
    }

    /**
     * 绘制标题
     */
    protected abstract void drawTitle(Canvas canvas, RecyclerView parent);

    /**
     * 绘制直线
     * @param c Canvas
     * @param parent RecyclerView
     */
    protected abstract void drawVerticalLine(Canvas c, RecyclerView parent);

    /**
     * 绘制点
     * @param c Canvas
     * @param parent RecyclerView
     */
    protected abstract void drawPoint(Canvas c, RecyclerView parent);


    public static class Config {
        Context context;
        List timeItems = new ArrayList();
        int flag = 0;
        // 标题
        int titleColor = Color.parseColor("#4e5864");
        int titleFontSize = 20;
        int bgColor;
        int titleOffset = 40;
        // 线
        int lineColor = Color.parseColor("#8d9ca9");
        int lineOffset = 30;
        int lineWidth = 1;
    }

    public static class Builder {
        private Config mConfig;

        public Builder(Context context) {
            this(context,new ArrayList());
        }

        public Builder(Context context,List timeItems) {
            this.mConfig = new Config();
            this.mConfig.context = context;
            this.mConfig.timeItems = timeItems;
        }

        /**
         * 设置标题
         *
         * @param titleColor 标题文本的颜色
         * @param fontSize   标题文本的大小 dp
         * @param bgColor    背景颜色
         */
        public Builder setTitle(int titleColor, int fontSize, int bgColor) {
            this.mConfig.titleColor = titleColor;
            this.mConfig.titleFontSize = fontSize;
            this.mConfig.bgColor = bgColor;
            this.mConfig.flag |= FLAG_TITLE_DRAW_BG;
            return this;
        }

        /**
         * 设置标题
         *
         * @param titleColor 标题文本的颜色
         * @param fontSize   标题文本的大小 dp
         */
        public Builder setTitle(int titleColor, int fontSize) {
            this.mConfig.titleColor = titleColor;
            this.mConfig.titleFontSize = fontSize;
            return this;
        }

        /**
         * 可以设置Title的位置,比如将标题设置在顶部或者将标题设置左边
         *
         * @param type        类型  FLAG_TITLE_POS_NONE/FLAG_TITLE_TYPE_TOP/FLAG_TITLE_TYPE_LEFT
         * @param titleOffset 偏移量
         */
        public Builder setTitleStyle(int type, int titleOffset) {
            this.mConfig.flag |= type;
            this.mConfig.titleOffset = titleOffset;
            return this;
        }

        /**
         * 启动隐藏相同标题
         */
        public Builder setSameTitleHide() {
            this.mConfig.flag |= FLAG_SAME_TITLE_HIDE;
            return this;
        }

        /**
         * @param type       type 时间线的类型
         * @param lineOffset 时间轴左边偏移的大小,右边也会偏移同样的大小
         */
        public Builder setLine(int type, int lineOffset, int lineColor) {
            return setLine(type, lineOffset, lineColor,1);
        }

        /**
         * @param type       type 时间线的类型
         * @param lineOffset 时间轴左边偏移的大小,右边也会偏移同样的大小
         */
        public Builder setLine(int type, int lineOffset, int lineColor,int lineWidth) {
            this.mConfig.flag |= type;
            this.mConfig.lineOffset = lineOffset;
            this.mConfig.lineColor = lineColor;
            this.mConfig.lineWidth = lineWidth;
            return this;
        }

        /**
         * 设置原点
         *
         * @param type   点的类型
         */
        public Builder setDot(int type) {
            this.mConfig.flag |= type;
            return this;
        }

        /**
         * 构建
         *
         * @param cls 构建的类
         * @return T
         */
        public TimeLine build(Class cls) {
            TimeLine t = null;
            try {
                Constructor con = cls.getConstructor(Config.class);
                t = con.newInstance(mConfig);
            } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return t;
        }

    }
}

SingleTimeLineDecoration.java

/**
 * Created on 2021/7/16 14:54
 *
 * @author Gong Youqiang
 */
public abstract class SingleTimeLineDecoration extends TimeLine {
    public SingleTimeLineDecoration(Config config) {
        super(config);
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
        int pos = params.getViewAdapterPosition();
        ITimeItem timeItem = timeItems.get(pos);

        if ((mFlag & FLAG_SAME_TITLE_HIDE) != 0) {
            if ((mFlag & FLAG_TITLE_TYPE_TOP) != 0) {
                if (pos == 0 || !timeItem.getTitle().equals(timeItems.get(pos - 1).getTitle())) {
                    outRect.set(mLineOffset + mLineWidth, mTopOffset, 0, 0);
                } else {
                    outRect.set(mLineOffset + mLineWidth, 0, 0, 0);
                }
            } else {
                outRect.set(mLineOffset + mLineWidth + mLeftOffset, 0, 0, 0);
            }
        } else {
            if ((mFlag & FLAG_TITLE_TYPE_TOP) != 0) {
                outRect.set(mLineOffset + mLineWidth, mTopOffset, 0, 0);
            } else {
                outRect.set(mLineOffset + mLineWidth + mLeftOffset, 0, 0, 0);
            }
        }
    }


    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(c, parent, state);

        int childCount = parent.getChildCount();
        if (childCount == 0)
            return;

        // 绘制处理
        // 1. 绘制标题
        drawTitle(c, parent);
        // 2. 绘制线
        drawVerticalLine(c, parent);
        // 3. 绘制点
        drawPoint(c, parent);
    }

    /**
     * 绘制标题
     */
    @Override
    protected void drawTitle(Canvas canvas, RecyclerView parent) {
        int childCount = parent.getChildCount();

        for (int i = 0; i             
关注
打赏
1658837700
查看更多评论
0.0426s