欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义recyclerView实现时光轴效果

程序员文章站 2022-03-02 15:13:01
时光轴效果在很多app上都有出现,例如淘宝中快递的跟踪,本文将使用recyclerview实现时光轴效果,我们会到自定义控件,首先先看一下效果图:接下来是步骤分析1自定义属性这个大家应该都了解了,根据...

时光轴效果在很多app上都有出现,例如淘宝中快递的跟踪,本文将使用recyclerview实现时光轴效果,我们会到自定义控件,首先先看一下效果图:

Android自定义recyclerView实现时光轴效果

接下来是步骤分析

1自定义属性

这个大家应该都了解了,根据我们之前的分析,直接在attrs.xml中进行声明

<declare-styleable name="timeline">
    <attr name="beginline" format="reference|color"></attr>
    <attr name="endline" format="reference|color"></attr>
    <attr name="linewidth" format="dimension"></attr>
    <attr name="timelineimage" format="color|reference"></attr>
    <attr name="timelineimagesize" format="dimension"></attr>
</declare-styleable>

进行一下各个属性的声明

•   beginline:开始的线条

•   endline:下面的线条

•   linewidth:线条的宽度

•   timelineimage:中间的圆形

•  timelineimagesize:中间的圆形的大小,这里默认他的宽高一致

2.自定义timeline继承view,构造方法如下

private int linewidth;
private drawable mbeginline;
private drawable mendline;
private drawable mtimelineimage;
private int mtimelineimagesize;
 
public timeline(context context) {
    this(context,null);
}
 
public timeline(context context, attributeset attrs) {
    this(context,attrs,0);
}
 
public timeline(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
    typedarray a = context.obtainstyledattributes(attrs, r.styleable.timeline);
    linewidth = a.getdimensionpixeloffset(r.styleable.timeline_linewidth,15);
    mbeginline = a.getdrawable(r.styleable.timeline_beginline);
    mendline = a.getdrawable(r.styleable.timeline_endline);
    mtimelineimage = a.getdrawable(r.styleable.timeline_timelineimage);
    mtimelineimagesize = a.getdimensionpixelsize(r.styleable.timeline_timelineimagesize,25);
    a.recycle();
 
}

3.复写onmeasure方法

我们都知道自定义控件,一般需要重写onmeasure,ondraw,onlayout方法,这里onmeasure需要对wrap_content的情况进行特殊处理,具体原因请看源码

protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
        super.onmeasure(widthmeasurespec, heightmeasurespec);
        int w = timelinemarkersize + getpaddingleft() + getpaddingright();
        int h = timelinemarkersize + getpaddingtop() + getpaddingbottom();
        int widthsize = resolvesizeandstate(w, widthmeasurespec, 0);
        int heightsize = resolvesizeandstate(h, heightmeasurespec, 0);
       
        int widthspecmode = measurespec.getmode(widthmeasurespec);
        int widthspecsize = measurespec.getsize(widthmeasurespec);
        int heightspecmode = measurespec.getmode(heightmeasurespec);
        int heightspecsize = measurespec.getsize(heightmeasurespec);
 
        // 处理宽高都为 wrap_content 的情况
        if (widthspecmode == measurespec.at_most && heightspecmode == measurespec.at_most) {
            setmeasureddimension(default_width, default_height);
        }
        // 处理宽为 wrap_content 的情况
        else if (widthspecmode == measurespec.at_most) {
            setmeasureddimension(default_width, widthsize);
        }
        // 处理高为 wrap_content 的情况
        else if (heightspecmode == measurespec.at_most) {
            setmeasureddimension(heightsize, default_height);
        }
    }

看过view源码的同学应该知道,在控件进行测量的时候,需要根据

specmode来进行具体的操作,view中提供了resolvesizeandstate方法来进行判断,该方法源码如下:

public static int resolvesizeandstate(int size, int measurespec, int childmeasuredstate) {
        int result = size;
        int specmode = measurespec.getmode(measurespec);
        int specsize =  measurespec.getsize(measurespec);
        switch (specmode) {
        case measurespec.unspecified:
            result = size;
            break;
        case measurespec.at_most:
            if (specsize < size) {
                result = specsize | measured_state_too_small;
            } else {
                result = size;
            }
            break;
        case measurespec.exactly:
            result = specsize;
            break;
        }
        return result | (childmeasuredstate&measured_state_mask);
    }

4.ondraw方法

@override
protected void ondraw(canvas canvas) {
    super.ondraw(canvas);
    if (mbeginline != null) {
        mbeginline.draw(canvas);
    }
    if (mendline != null) {
        mendline.draw(canvas);
    }
 
    if (mtimelineimage!=null){
        mtimelineimage.draw(canvas);
    }
}

5.onsizechange

@override
protected void onsizechanged(int w, int h, int oldw, int oldh) {
    int paddingleft = getpaddingleft();
    int paddingright = getpaddingright();
    int paddingtop = getpaddingtop();
    int paddingbottom = getpaddingbottom();
    //父容器的宽高
    int width = getwidth();
    int height = getheight();
 
    int childwidth = width - paddingleft - paddingright;
    int childheight = height - paddingtop - paddingbottom;
 
    mtimelineimagesize = math.min(mtimelineimagesize, math.min(childheight, childwidth));
    if (mtimelineimage != null) {
        mtimelineimage.setbounds(paddingleft, paddingtop, paddingleft + mtimelineimagesize, paddingtop + mtimelineimagesize);
        bounds = mtimelineimage.getbounds();
    } else {
        bounds = new rect(paddingleft, paddingtop, paddingleft + childwidth, paddingtop + childheight);
    }
 
    if (mbeginline != null) {
        int lineleft = mtimelineimage.getbounds().centerx() - (linewidth >> 1);
        mbeginline.setbounds(lineleft, 0, lineleft + linewidth, mtimelineimage.getbounds().top);
    }
    if (mendline != null) {
        int lineleft = mtimelineimage.getbounds().centerx() - (linewidth >> 1);
        mendline.setbounds(lineleft, mtimelineimage.getbounds().bottom, lineleft + linewidth, height);
 
    }
}

这里需要说明的是,我们的mbeginline的长度,其实是我们自定义控件的paddingtop高度,同理mendline的长度是paddingbottom高度,所以我们在使用这个控件时,一般都会设置paddingtop和paddingbottom

6.使用timeline控件

以下是recyclerview中一个item的布局,多个item拼接起来就是一条时光轴,这里需要说明的是,我们的 linearlayout使用的高度模式是wrap_content,这里我的textview设置了android:paddingtop="30dp",如果不对textview设置android:paddingtop,会发现timelineview控件是看不见的,这是由于父控件wrap_content,那么父控件包裹textview的内容,那么父控件的高度就是textview的高度,这样timelineview设置了android:paddingtop="34dp",这个高度是大于父控件的高度的,所以就看不到timelineview了,除非我们给linearlayout的android:layout_height="wrap_content",修改成固定的高度

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:paddingleft="16dp"
    android:paddingright="16dp">
 
    <com.example.jikeyoujikeyou.timelinedemo2.timelineview
        android:id="@+id/timelineview"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:clickable="true"
        android:focusable="true"
        android:focusableintouchmode="true"
        android:paddingbottom="8dp"
        android:paddingleft="4dp"
        android:paddingright="4dp"
        android:paddingtop="34dp"
        app:beginline="#ff0000"
        app:endline="#ff0000"
        app:linewidth="3dp"
        app:timelinemarker="@drawable/timeline_marker"
        app:timelinemarkersize="24dp" />
 
 
  <textview
        style="@style/base.textappearance.appcompat.title"
        android:id="@+id/timelinename"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:paddingtop="30dp"
        android:singleline="true"
        android:text="name"
        android:textcolor="@color/grey_700"
        android:textsize="16sp" />
</linearlayout>

7.最后就是recyclerview的使用

recyclerview的使用大家应该都很熟悉了,无非就是设置adapter,viewholder等,这里不再赘述,还有一点需要强调的是itemviewtype有四种情况,第一个,最后一个,中间,还有只有一个四种情况情况,根据这几种情况,有选择设置mbeginline与 mendline是否进行绘制

timelineadapter代码:

package com.example.jikeyoujikeyou.timelinedemo;
 
import android.annotation.targetapi;
import android.content.context;
import android.graphics.drawable.drawable;
import android.os.build;
import android.support.v7.widget.recyclerview;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
import android.widget.textview;
 
import java.util.arraylist;
import java.util.list;
import java.util.random;
 
/**
 * created by jikeyoujikeyou on 16/7/22.
 */
public class timelineadapter extends recyclerview.adapter<timelineadapter.viewholder> {
    private list<timelineitem> datas ;
 
   public timelineadapter(list<timelineitem> datas) {
        super();
        this.datas = datas;
    }
 
    @override
    public viewholder oncreateviewholder(viewgroup parent, int viewtype) {
        layoutinflater layoutinflater = layoutinflater.from(parent.getcontext());
        view view = layoutinflater.inflate(r.layout.item_timeline, null);
        return new viewholder(view, parent.getcontext(), viewtype);
    }
 
    @override
    public void onbindviewholder(viewholder holder, int position) {
        timelineitem timelineitem = datas.get(position);
        holder.tv_name.settext(timelineitem.gettimelinename());
    }
 
    @override
    public int getitemcount() {
        return datas.size();
    }
 
    @override
    public int getitemviewtype(int position) {
        int size = datas.size() - 1;
        if (size == 0) {
            return timelineitemtype.atom;
        } else if (position == 0) {
            return timelineitemtype.start;
        } else if (position == size) {
            return timelineitemtype.end;
        } else {
            return timelineitemtype.normal;
        }
 
    }
 
    class viewholder extends recyclerview.viewholder {
 
 
        private textview tv_name;
        private timeline timeline;
 
        public viewholder(view itemview, context context, int viewtype) {
            super(itemview);
            tv_name = (textview) itemview.findviewbyid(r.id.name);
            timeline = (timeline) itemview.findviewbyid(r.id.timelineview);
 
            drawable drawable = context.getresources().getdrawable(r.drawable.timeline_marker);
            drawable drawable2 = context.getresources().getdrawable(r.drawable.timeline_marker2);
            drawable drawable3 = context.getresources().getdrawable(r.drawable.timeline_marker3);
            drawable drawable4 = context.getresources().getdrawable(r.drawable.timeline_marker4);
            drawable drawable5 = context.getresources().getdrawable(r.drawable.timeline_marker5);
 
            random random = new random();
            final int i = random.nextint(5);
            final drawable drawables[] = {drawable, drawable2, drawable3, drawable4, drawable5};
 
            timeline.settimelineimage(drawables[i]);
 
            if (viewtype == timelineitemtype.start) {
                timeline.setbeginline(null);
 
            } else if (viewtype == timelineitemtype.end) {
                timeline.setendline(null);
            } else if (viewtype == timelineitemtype.atom) {
                timeline.setbeginline(null);
                timeline.setendline(null);
            }
        }
    }
 
    class timelineitemtype {
        //正常
        public final static int normal = 0;
        //开始
        public final static int start = 1;
        //结束
        public final static int end = 2;
        //只有一条数据,那么beginline和endline都没有
        public final static int atom = 3;
    }
 
}
mainactivity代码:

<pre name="code" class="java">public class mainactivity extends appcompatactivity {
 
    private list<timelineitem> mdatas;
 
    @override
    protected void oncreate(bundle savedinstancestate) {
        super.oncreate(savedinstancestate);
        setcontentview(r.layout.activity_main);
        initdata();
        recyclerview recyclerview = (recyclerview) findviewbyid(r.id.recyclerview);
        linearlayoutmanager linearlayoutmanager = new linearlayoutmanager(this);
        linearlayoutmanager.setorientation(linearlayoutmanager.vertical);
        recyclerview.setlayoutmanager(linearlayoutmanager);
        timelineadapter adapter = new timelineadapter(mdatas);
        recyclerview.setadapter(adapter);
 
 
    }
 
    private void initdata() {
        mdatas = new arraylist<>();
        mdatas.add(new timelineitem("爸爸生日"));
        mdatas.add(new timelineitem("妈妈生日"));
        mdatas.add(new timelineitem("姐姐生日"));
        mdatas.add(new timelineitem("女神生日"));
        mdatas.add(new timelineitem("前任生日"));
 
    }
}

运行项目,就会呈现本文一开始的效果。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

相关标签: Android 时光轴