当前位置:首页 > 谈天说地 > 正文内容

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

34资源网2022年01月24日 21:07346

时光轴效果在很多app上都有出现,例如淘宝中快递的跟踪,本文将使用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("前任生日"));
 
    }
}

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

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

看完文章,还可以用支付宝扫描下面的二维码领取一个支付宝红包,目前可领1-88元不等

支付宝红包二维码

除了扫码可以领取之外,大家还可以(复制 720087999 打开✔支付宝✔去搜索, h`o`n.g.包哪里来,动动手指就能领)。

看下图所示是好多参与这次活动领取红包的朋友:

支付宝红包

扫描二维码推送至手机访问。

版权声明:本文由34楼发布,如需转载请注明出处。

本文链接:https://www.34l.com/post/6250.html

分享给朋友:

相关文章

一千元如何创业?一千元创业项目有吗?
一千元如何创业?一千元创业项目有吗?

一千元如何创业?有的人想用一千元创业,说实话,现实当中一千元根本创不了业,你买器材都不够,更别提创业了。当然,如果你在网络上面,那还是有可能创业的。因为现在网上有很多可以投资1000元左右创业的项目。下面,小编就给大家分享三个这样的创业项目...

用侠肝义胆造句30句
用侠肝义胆造句30句

1、李大哥侠肝义胆,受到四邻的交口称赞。2、村民见二位神仙眷侣侠肝义胆,救民水火,却落得个这般凄惨的下场,当下无人不落下泪来。3、还有蛊族毒宗的唯一传人,药宗里的第一才女,一朝沦落风尘的倾城之貌,侠肝义胆的流氓与巾帼,皇家琴师,断袖城主,独...

用不言谢造句17句分享
用不言谢造句17句分享

1、大恩不言谢,以后有什么用得上我的,尽管说!2、人们都说大恩不言谢,又说滴水之恩当涌泉相报,谢还是不谢?好为难!那今儿就不谢天不谢地,只谢朋友,谢谢你风雨一路的陪伴。感恩节快乐!3、大恩不言谢,我就是结草衔环,也不足为报。4、大恩不言谢,...

联合国秘书长:新冠疫苗必须要成为全球公共产品
联合国秘书长:新冠疫苗必须要成为全球公共产品

当地时间3月11日,联合国启动“必须团结一致”(Only Together)公共宣传活动,呼吁新冠疫苗能够在联合国疫苗计划主导下,成为全球公共产品,向全球各地有需要的人提供。联合国秘书长古特雷斯在致辞中表示,新冠肺炎疫情引发了全球巨大的苦难...

融资丨「PPIO边缘云」完成亿元A1轮融资,刷新边缘云领域融资记录
融资丨「PPIO边缘云」完成亿元A1轮融资,刷新边缘云领域融资记录

创业邦获悉,近日,边缘云公司PPIO宣布完成过亿元A1轮融资,由创世伙伴、张江科投、磐霖资本等多家机构联合投资,Pre-A轮投资方蓝驰创投、沸点资本及华业天成继续追加投资,光源资本担任融资财务顾问。这是公司半年内再次获投资人支持,公司A2...

瑞幸喘口气,对手已林立
瑞幸喘口气,对手已林立

图源:摄图网 编者按:本文来自微信公众号螳螂观察(ID:TanglangFin),作者:易不二,创业邦经授权转载 年轻人从未停止消费小蓝杯。 今年夏天生椰拿铁被全网催货的盛景,与9天就突破270万杯销量的丝绒拿铁,都证明了瑞幸的生命力。...