Android史上最强分割线全攻略
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充满的底部分割线(其他方向的分割线也一样)
效果
linearlayout不充满的底部分割线
效果
其他布局:网上开源库很多,不过我不太喜欢用别人的库,所以自己实现了一个(实现了主流的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));
这行代码就会为我们处理的很好。
效果如下
如果我们对他默认的样式不满意呢?
我们可以通过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。看了本文后,您可以开开心心地搞定分割线啦!
上一篇: Vim中设置括号自动补全
下一篇: 我已经理解了并发和并行的区别