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

Android史上最强分割线全攻略

程序员文章站 2023-03-28 09:48:56
android史上最强分割线全攻略。说实话,分割线这个东西,真的太难太难了!!! 难在何处?难在用最对的方式去实现它! 1.view——管理代码不便,绘制开销大。非常非常不好...

android史上最强分割线全攻略。说实话,分割线这个东西,真的太难太难了!!!

难在何处?难在用最对的方式去实现它!

1.view——管理代码不便,绘制开销大。非常非常不好

2.图片——修改不便,增加apk体积。非常非常不好

3.java代码——使用不便,增加代码量。非常非常不好

4.layer-list通过覆盖实现——过度绘制,修改不便。非常非常不好

5.shape——设置成background会自动填充,无法实现!

6.自定义控件——做模块化的时候,需要移植这个库。不太好

7.不充满的底部分割线的这种情况,实现起来更难——又一限制条件

所以他难

本人在探索了2个小时后,终于得出了结论

1.linearlayout——虽然background这个属性会自动填充,不能用,但是他有pider属性!

2.其他布局——自定义控件

3.recyclerview——itemdecoration

下面我将给出这些情况的代码,你可以直接拷去用。

代码原理就不解释了,太简单了。有问题留言。

linearlayout充满的底部分割线(其他方向的分割线也一样)




    
    


效果

Android史上最强分割线全攻略

linearlayout不充满的底部分割线






    
    


效果

Android史上最强分割线全攻略

其他布局:网上开源库很多,不过我不太喜欢用别人的库,所以自己实现了一个(实现了主流的relativelayout和framelayout)

attrs



    
        线到起始端的margin
        线到末端的margin
        线的宽度
        线的颜色
        线的位置:left、top、right、bottom
    

border relative layout

package com.example.filemanager;

import android.content.context;
import android.content.res.typedarray;
import android.graphics.canvas;
import android.graphics.color;
import android.graphics.paint;
import android.util.attributeset;
import android.util.log;
import android.widget.relativelayout;

// todo: 2018/5/3 如果你想多弄几条线 或者位置有特殊需求 自己改写呗!

public class borderrelativelayout extends relativelayout {

    private static final string tag = "xbh";

    public borderrelativelayout(context context, attributeset attrs) {
        super(context, attrs);

        //params

        typedarray ta = context.obtainstyledattributes(attrs, r.styleable.borderrelativelayout);

        startmargin = ta.getdimension(r.styleable.borderrelativelayout_margin_start, 0);
        endmargin = ta.getdimension(r.styleable.borderrelativelayout_margin_end, 0);
        width = ta.getdimension(r.styleable.borderrelativelayout_width, 0);
        int color = ta.getcolor(r.styleable.borderrelativelayout_color, color.gray);
        position = ta.getstring(r.styleable.borderrelativelayout_position);

        ta.recycle();

        //init paint

        paint.setcolor(color);
    }

    //拿到绘制坐标:position、margin、width

    private static final string left = "left";
    private static final string top = "top";
    private static final string right = "right";
    private static final string bottom = "bottom";

    private float startmargin;
    private float endmargin;
    private float width;
    private final string position;

    private float left;
    private float top;
    private float right;
    private float bottom;

    @override
    protected void onlayout(boolean changed, int l, int t, int r, int b) {
        super.onlayout(changed, l, t, r, b);

        int w = getwidth();
        int h = getheight();

        if (left.equals(position)) {
            left = 0;
            top = startmargin;
            right = width;
            bottom = h - endmargin;
        } else if (top.equals(position)) {
            left = startmargin;
            top = 0;
            right = w - endmargin;
            bottom = width;
        } else if (right.equals(position)) {
            left = w - width;
            top = startmargin;
            right = w;
            bottom = h - endmargin;
        } else if (bottom.equals(position)) {
            left = startmargin;
            top = h - width;
            right = w - endmargin;
            bottom = h;
        }
    }

    //

    private final paint paint = new paint();

    @override
    protected void dispatchdraw(canvas canvas) {
        super.dispatchdraw(canvas);

        canvas.drawrect(left, top, right, bottom, paint);
    }
}

使用


测试了下非常好用。

frame layout你只需要改个名,继承自frame layout即可。

recyclerview

列表的情况上面就不适用了,因为最后一个item我们不期望有线。

rv.additemdecoration(new divideritemdecoration(activity, divideritemdecoration.vertical));

这行代码就会为我们处理的很好。

效果如下

Android史上最强分割线全攻略

如果我们对他默认的样式不满意呢?

我们可以通过setdrawable的方式去设置我们drawable文件夹下自己定义的线。但是这导致的后果是我们最后一个item也有了线。所以我们需要自定义itemdecoration。

(解决方案学习自:https://www.jianshu.com/p/26b33e1181e3 自己重写了一个divideritemdecoration)

static class divideritemdecoration extends recyclerview.itemdecoration {
    public static final int horizontal = linearlayout.horizontal;
    public static final int vertical = linearlayout.vertical;
    private static final string tag = "divideritem";
    private static final int[] attrs = new int[]{android.r.attr.listdivider};
    private drawable mdivider;
    private boolean misshowbottomdivider;
    /**
     * current orientation. either {@link #horizontal} or {@link #vertical}.
     */
    private int morientation;
    private final rect mbounds = new rect();

    public divideritemdecoration(context context, int orientation) {
        misshowbottomdivider = false;
        final typedarray a = context.obtainstyledattributes(attrs);
        mdivider = a.getdrawable(0);
        if (mdivider == null) {
            log.w(tag, "@android:attr/listdivider was not set in the theme used for this "
                    + "divideritemdecoration. please set that attribute all call setdrawable()");
        }
        a.recycle();
        setorientation(orientation);
    }

    /**
     * sets the orientation for this pider. this should be called if
     * {@link recyclerview.layoutmanager} changes orientation.
     *
     * @param orientation {@link #horizontal} or {@link #vertical}
     */
    public void setorientation(int orientation) {
        if (orientation != horizontal && orientation != vertical) {
            throw new illegalargumentexception(
                    "invalid orientation. it should be either horizontal or vertical");
        }
        morientation = orientation;
    }

    /**
     * sets the {@link drawable} for this pider.
     *
     * @param drawable drawable that should be used as a pider.
     */
    public void setdrawable(@nonnull drawable drawable) {
        if (drawable == null) {
            throw new illegalargumentexception("drawable cannot be null.");
        }
        mdivider = drawable;
    }

    @override
    public void ondraw(canvas c, recyclerview parent, recyclerview.state state) {
        if (parent.getlayoutmanager() == null || mdivider == null) {
            return;
        }
        if (morientation == vertical) {
            drawvertical(c, parent, state);
        } else {
            drawhorizontal(c, parent, state);
        }
    }

    private void drawvertical(canvas canvas, recyclerview parent, recyclerview.state state) {
        canvas.save();
        final int left;
        final int right;
        //noinspection androidlintnewapi - newapi lint fails to handle overrides.
        if (parent.getcliptopadding()) {
            left = parent.getpaddingleft();
            right = parent.getwidth() - parent.getpaddingright();
            canvas.cliprect(left, parent.getpaddingtop(), right,
                    parent.getheight() - parent.getpaddingbottom());
        } else {
            left = 0;
            right = parent.getwidth();
        }

        final int childcount = parent.getchildcount();
        final int lastposition = state.getitemcount() - 1;
        for (int i = 0; i < childcount; i++) {
            final view child = parent.getchildat(i);
            final int childrealposition = parent.getchildadapterposition(child);
            //misshowbottomdivider false的时候不绘制最后一个view的pider
            if (misshowbottomdivider || childrealposition < lastposition) {
                parent.getdecoratedboundswithmargins(child, mbounds);
                final int bottom = mbounds.bottom + math.round(child.gettranslationy());
                final int top = bottom - mdivider.getintrinsicheight();
                mdivider.setbounds(left, top, right, bottom);
                mdivider.draw(canvas);
            }
        }
        canvas.restore();
    }

    private void drawhorizontal(canvas canvas, recyclerview parent, recyclerview.state state) {
        canvas.save();
        final int top;
        final int bottom;
        //noinspection androidlintnewapi - newapi lint fails to handle overrides.
        if (parent.getcliptopadding()) {
            top = parent.getpaddingtop();
            bottom = parent.getheight() - parent.getpaddingbottom();
            canvas.cliprect(parent.getpaddingleft(), top,
                    parent.getwidth() - parent.getpaddingright(), bottom);
        } else {
            top = 0;
            bottom = parent.getheight();
        }

        final int childcount = parent.getchildcount();
        final int lastposition = state.getitemcount() - 1;
        for (int i = 0; i < childcount; i++) {
            final view child = parent.getchildat(i);
            final int childrealposition = parent.getchildadapterposition(child);
            //misshowbottomdivider false的时候不绘制最后一个view的pider
            if (misshowbottomdivider || childrealposition < lastposition) {
                parent.getlayoutmanager().getdecoratedboundswithmargins(child, mbounds);
                final int right = mbounds.right + math.round(child.gettranslationx());
                final int left = right - mdivider.getintrinsicwidth();
                mdivider.setbounds(left, top, right, bottom);
                mdivider.draw(canvas);
            }
        }
        canvas.restore();
    }

    @override
    public void getitemoffsets(rect outrect, view view, recyclerview parent,
                               recyclerview.state state) {
        if (mdivider == null) {
            outrect.set(0, 0, 0, 0);
            return;
        }
        if (morientation == vertical) {
            //parent.getchildcount() 不能拿到item的总数
            //https://*.com/questions/29666598/android-recyclerview-finding-out-first
            // -and-last-view-on-itemdecoration
            int lastposition = state.getitemcount() - 1;
            int position = parent.getchildadapterposition(view);
            if (misshowbottomdivider || position < lastposition) {
                outrect.set(0, 0, 0, mdivider.getintrinsicheight());
            } else {
                outrect.set(0, 0, 0, 0);
            }
        } else {
            int lastposition = state.getitemcount() - 1;
            int position = parent.getchildadapterposition(view);
            if (misshowbottomdivider || position < lastposition) {
                outrect.set(0, 0, mdivider.getintrinsicwidth(), 0);
            } else {
                outrect.set(0, 0, 0, 0);
            }
        }
    }
}

自定义的样式




    
    

使用自定义的样式

//rv

recyclerview rv = (recyclerview) fvb(r.id.rv);
rv.setadapter(new adapter());
rv.setlayoutmanager(new linearlayoutmanager(activity));

divideritemdecoration decoration = new divideritemdecoration(activity, divideritemdecoration.vertical);
decoration.setdrawable(activity.getresources().getdrawable(r.drawable.border_bottom_not_full));
rv.additemdecoration(decoration);

大功告成。分割线的不同情形的最佳实现思路一直是让人头疼的地方,甚至在难以满足需求的时候忍不住直接用view。看了本文后,您可以开开心心地搞定分割线啦!