美文网首页Androidandroid自定义控件Android自定义控件
自定义的签到打卡头像重叠排列ViewGroup

自定义的签到打卡头像重叠排列ViewGroup

作者: 砂砾han | 来源:发表于2017-06-13 16:51 被阅读264次

先看看几种展示效果图:

效果展示.png

之前在实现一个打卡功能的时候,设计图上要求展示打卡用户的头像,只是这些头像不是正常的从左至右排排坐,如示例图上的3、4排的效果,而是1所展示的效果。如果只是像3或者4这样展示,我们可以用RecyclerView轻松实现。


思路

分析下1这种效果的特殊之处,从左至右都是左边的图片压在右边图片的上方,看起来它是从右往左layout的,最容易想到的实现方法有两种:

  • 自定义ViewGroup,重写layout方法,本文就是采用这种方式;
  • 利用RecyclerView的自定义LayoutManager来实现,大致思路是继承RecyclerView.LayoutManager,重写onLayoutChildren来实现,有兴趣的同学可以自己动手试试。
    下面结合代码看看自定义ViewGroup的实现方式。

具体实现

我们的主要工作是measure、layout、setData,首先考虑下我们需要自定义哪些属性便于拓展

<declare-styleable name="CoverView">
        <attr name="coverWidth" format="dimension" />
        <attr name="itemDia" format="dimension" />
        <attr name="display_style">
            <enum name="left_to_right" value="0" />
            <enum name="right_to_left" value="1" />
        </attr>
    </declare-styleable>

这里我定义了三个,两个图片重叠的宽度(coverWidth)、每张图片的直径(itemDia)、展示风格(display_style)、这里定义了两种:left_to_right从左至右顺序摆放,即2、3、4所对应的样式;right_to_left刚好相反,示例中1的效果。

  • measure过程,因为设计只让在限定区域内显示几个完整的头像,所以我们需要根据测量的宽度计算出能显示的最大图片数maxShowCounts,从而知道最终需要展示的是哪些数据。
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (data == null || data.isEmpty()) return;
        int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingRight() - getPaddingLeft();
        //根据图片的直径,重叠宽度和父控件允许的最大宽度计算出能显示的最大图片数
        int maxShowCounts = (width - coverWidth) / (itemDia - coverWidth);
        showData.clear();
        if (maxShowCounts < data.size()) {
            for (int i = 0; i < maxShowCounts; i++) {
                showData.add(data.get(i));
            }
        } else {
            showData.addAll(data);
        }
        showCount = showData.size();
        int totalWidth = MeasureSpec.getSize(widthMeasureSpec);
        setMeasuredDimension(totalWidth, itemDia + getPaddingTop() + getPaddingBottom());
    }
  • layout过程,最核心的过程,但其实代码没有几行……
@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (showData == null || adapter == null) return;
        if (displayStyle == STYLE_RIGHT_TO_LEFT) {
          //如果是right_to_left的效果我们需要将展示的list翻转一下
            Collections.reverse(showData);
        }
        for (int i = 0; i < showCount; i++) {
            ImageView childView = (ImageView) getChildAt(i);
            adapter.onDisplayImage(getContext(), childView, showData.get(i));
            int left;
            // 画个草图,很容易计算
            if (displayStyle == STYLE_RIGHT_TO_LEFT) {
                left = getPaddingLeft() + (itemDia - coverWidth) * (showCount - i - 1);
            } else {
                left = getPaddingLeft() + (itemDia - coverWidth) * i;
            }
            int right = left + itemDia;
            int top = getPaddingTop();
            int bottom = getPaddingTop() + itemDia;
            //辛辛苦苦只为这一下
            childView.layout(left, top, right, bottom);
        }
    }
  • 为CoverView填充数据,setData其实就是addview()然后requestLayout();
public void setData(List<T> list) {
        if (list == null || list.isEmpty()) {
            setVisibility(View.GONE);
            return;
        } else {
            setVisibility(View.VISIBLE);
        }
        data = list;
        for (int i = 0; i < data.size(); i++) {
            ImageView iv = getImageView(i);
            if (iv == null) {
                return;
            }
            addView(iv, generateDefaultLayoutParams());
        }
        requestLayout();
    }
  • 这里也需要一个adapter来作为数据与View的桥梁,通过adapter来展示图片和获取ImageView对象,当然还可以做很多其他的事情,例如设置监听…
public abstract class CoverAdapter<T> {

    public abstract void onDisplayImage(Context context, ImageView imageView, T t);

    public ImageView generateImageView(Context context) {
        //可以在这里更改Imageview的类型
        CircleImageView circleImageView = new CircleImageView(context);
//        ImageView imageView = new ImageView(context);
        circleImageView.setBorderColor(Color.parseColor("#dcdcdc"));
        circleImageView.setBorderWidth(4);
        return circleImageView;
    }
}

如何使用

贴下我们展示的示例图片代码:

public class MainActivity extends Activity {

    private CoverView coverView;
    private CoverView coverView2;
    private CoverView coverView3;
    private CoverView coverView4;

    private CoverAdapter<String> adapter;

    private List<String> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        coverView = (CoverView) findViewById(R.id.cover_view);
        coverView2 = (CoverView) findViewById(R.id.cover_view2);
        coverView3 = (CoverView) findViewById(R.id.cover_view3);
        coverView4 = (CoverView) findViewById(R.id.cover_view4);
        initData();

        adapter = new CoverAdapter<String>() {
            @Override
            public void onDisplayImage(Context context, ImageView imageView, String s) {
                Picasso.with(MainActivity.this).load(s).into(imageView);
            }
        };
        coverView.setAdapter(adapter);
        coverView.setData(list);

        coverView2.setAdapter(adapter);
        coverView2.setData(list);

        coverView3.setAdapter(adapter);
        coverView3.setData(list);

        coverView4.setAdapter(adapter);
        coverView4.setData(list);
    }

    private void initData() {
        list = new ArrayList<>();
        list.add("https://pic4.zhimg.com/02685b7a5f2d8cbf74e1fd1ae61d563b_xll.jpg");
        list.add("https://pic4.zhimg.com/fc04224598878080115ba387846eabc3_xll.jpg");
        list.add("https://pic3.zhimg.com/d1750bd47b514ad62af9497bbe5bb17e_xll.jpg");
        list.add("https://pic4.zhimg.com/da52c865cb6a472c3624a78490d9a3b7_xll.jpg");
        list.add("https://pic3.zhimg.com/0c149770fc2e16f4a89e6fc479272946_xll.jpg");
        list.add("https://pic1.zhimg.com/76903410e4831571e19a10f39717988c_xll.png");
        list.add("https://pic3.zhimg.com/33c6cf59163b3f17ca0c091a5c0d9272_xll.jpg");
        list.add("https://pic4.zhimg.com/52e093cbf96fd0d027136baf9b5cdcb3_xll.png");
        list.add("https://pic3.zhimg.com/f6dc1c1cecd7ba8f4c61c7c31847773e_xll.jpg");
    }
}

xml文件

 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="match_parent"
    android:background="#DFEFF0">

    <com.hdz.signview.CoverView
        android:id="@+id/cover_view"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentTop="true"
        android:layout_marginTop="40dp"
        android:background="#FEBFB3"
        android:paddingBottom="10dp"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:paddingTop="10dp"
        app:coverWidth="10dp"
        app:display_style="right_to_left"
        app:itemDia="50dp" />

    <com.hdz.signview.CoverView
        android:id="@+id/cover_view2"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/cover_view"
        android:layout_marginTop="20dp"
        android:background="#009378"
        android:paddingBottom="10dp"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:paddingTop="10dp"
        app:coverWidth="10dp"
        app:display_style="left_to_right"
        app:itemDia="50dp" />

    <com.hdz.signview.CoverView
        android:id="@+id/cover_view3"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/cover_view2"
        android:layout_marginTop="20dp"
        android:background="#009378"
        android:paddingBottom="10dp"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:paddingTop="10dp"
        app:coverWidth="0dp"
        app:display_style="left_to_right"
        app:itemDia="50dp" />

    <!-- coverWidth设置成负值的时候就分开啦-->
    <com.hdz.signview.CoverView
        android:id="@+id/cover_view4"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/cover_view3"
        android:layout_marginTop="20dp"
        android:background="#009378"
        android:paddingBottom="10dp"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:paddingTop="10dp"
        app:coverWidth="-10dp"
        app:display_style="left_to_right"
        app:itemDia="50dp" />
</RelativeLayout>

…………写个简书比造轮子的时间还要长…………
<p>
完整的demo已上传到我的GitHub ,有需要的可以去下载,有错误请在评论指出,共同学习进步。
<p>

相关文章

网友评论

本文标题:自定义的签到打卡头像重叠排列ViewGroup

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