在项目中经常会用到分割线,很多应用的“我的”模块,都会用到分割线,那么怎么优雅的实现这种很简单的效果呢?
实现它一般最简单最麻烦的方法可能就是Linearlayout然后里面每个子view之间用一个view,然后设置背景,然后高度设置为1px了,其实,最初我也这么搞过,但是效率实在是低呀,后来知道了Linearlayout自己就有实现分割线的属性了,就是一个divider属性,它指向的是一个drawable文件了,既然是drawable,那么可定制化的东西就很多了,然后showDividers是指这个分割线显示的位置,同样可能用到的一个属性就是dividerPadding了,它是指代分割线距离边界的距离,距离两边都有哦,说的这些大家也都知道。
以前这么用也没什么问题,后来ui设计师又要仿照ios了,ios的分割线一般都是距离左边有一段距离,而对于右边则是顶边的,那么单纯的设置dividerPadding是不可以的,有的同学说,我可以直接给该linearlayout设置一个paddingLeft呀,这样子也没问题,可是要是要加点击效果呢?设置了paddingLeft后,点击效果背景变化也会距离左边一段距离呀。那怎么办?难道又要回到设置通过设置空view然后设置背景的老路了吗?其实此刻想的是要是Android提供一个dividerPaddingLeft该多好呀。可是现实是人家没提供。
然后就去看看linearlayout中关于dividerPadding到底是怎么处理的吧,或许我们可以自定义下呢,首先是可以看到通过mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);通过这个属性获取到xml中设置的dividerPadding的值,然后看到用到的地方就是两个地方:
void drawHorizontalDivider(Canvas canvas, int top) {
mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
mDivider.draw(canvas);
}
可以看到一个是绘制竖直方向上的分割线,一个则是绘制水平方向上的分割线的,其实我们要是能把这两个方法给改了,那就不达到了我们变相设置dividerPaddingLeft的效果了嘛,其实我们只需要将第一个方法drawHorizontalDivider改为如下方式就好了,
void drawHorizontalDivider(Canvas canvas, int top) {
mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, getWidth() - getPaddingRight(), top + mDividerHeight);
mDivider.draw(canvas);
}
只修要将mDivider的setBounds的第三个设置right方向距离的参数改下就好了。然后我们的目标明确了,接下来就是实现了,哈哈哈。
看了下,linearlayout的代码还是蛮多的,都有2000行了,一般咱们都是继承LinearLayout,然后重写里面的方法就好了,对于我们就是重写,drawHorizontalDivider和drawVerticalDivider两个方法了,但是它俩不是public的,故而无法被重写了。所以只能想想别的方法了,然后就想到了鸿洋大神之前写过一篇兼容android3.0以下实现linearlayout的分隔线的文章,原文地址是:http://blog.csdn.net/lmj623565791/article/details/42407923,我就在这个基础上进行了修改;请看下面代码:
/**
* 能够设置left或者right的dividerpadding的线性布局
* Created by hexiaopang on 2017/8/14 14:50.
*/
public class CustomerDividerPaddingLinearLayout extends LinearLayout {
private static final int[] LL = new int[]{ //
android.R.attr.divider,//
android.R.attr.showDividers,//
android.R.attr.dividerPadding //
};
private static final int LL_DIVIDER = 0;
private static final int LL_SHOW_DIVIDER = 1;
private static final int LL_DIVIDER_PADDING = 2;
/**
* android:dividers
*/
private Drawable mDivider;
/**
* 对应:android:showDividers
*/
private int mShowDividers;
/**
* 对应:android:dividerPadding
*/
private int mDividerPadding;
/**
* 自定义的只有左侧有的dividerPaddingLeft
*/
private int mDividerPaddingLeft;
/**
* 自定义的只有右侧有的dividerPaddingRight
*/
private int mDividerPaddingRight = 0;
private int mDividerWidth;
private int mDividerHeight;
public CustomerDividerPaddingLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, LL);
setDividerDrawable(a.getDrawable(CustomerDividerPaddingLinearLayout.LL_DIVIDER));
mDividerPadding = a.getDimensionPixelSize(LL_DIVIDER_PADDING, 0);
mDividerPaddingLeft = mDividerPadding;
mShowDividers = a.getInteger(LL_SHOW_DIVIDER, SHOW_DIVIDER_NONE);
a.recycle();
}
/**
* 设置分隔元素,初始化宽高等
*/
public void setDividerDrawable(Drawable divider) {
if (divider == mDivider) {
return;
}
mDivider = divider;
if (divider != null) {
mDividerWidth = divider.getIntrinsicWidth();
mDividerHeight = divider.getIntrinsicHeight();
} else {
mDividerWidth = 0;
mDividerHeight = 0;
}
setWillNotDraw(divider == null);
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//将分隔元素的宽高转化为对应的margin
setChildrenDivider();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 将分隔元素的宽高转化为对应的margin
*/
protected void setChildrenDivider() {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
//遍历每个子View
View child = getChildAt(i);
//拿到索引
final int index = indexOfChild(child);
//方向
final int orientation = getOrientation();
final LayoutParams params = (LayoutParams) child.getLayoutParams();
//判断是否需要在子View左边绘制分隔
if (hasDividerBeforeChildAt(index)) {
if (orientation == VERTICAL) {
//如果需要,则设置topMargin为分隔元素的高度(垂直时)
params.topMargin = mDividerHeight;
} else {
//如果需要,则设置leftMargin为分隔元素的宽度(水平时)
params.leftMargin = mDividerWidth;
}
}
}
}
/**
* 判断是否需要在子View左边绘制分隔
*/
public boolean hasDividerBeforeChildAt(int childIndex) {
if (childIndex == 0 || childIndex == getChildCount()) {
return false;
}
if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
boolean hasVisibleViewBefore = false;
for (int i = childIndex - 1; i >= 0; i--) {
//当前index的前一个元素不为GONE则认为需要
if (getChildAt(i).getVisibility() != GONE) {
hasVisibleViewBefore = true;
break;
}
}
return hasVisibleViewBefore;
}
return false;
}
@Override
protected void onDraw(Canvas canvas) {
if (mDivider != null) {
if (getOrientation() == VERTICAL) {
//绘制垂直方向的divider
drawDividersVertical(canvas);
} else {
//绘制水平方向的divider
drawDividersHorizontal(canvas);
}
}
super.onDraw(canvas);
}
/**
* 绘制水平方向的divider
* @param canvas
*/
private void drawDividersHorizontal(Canvas canvas) {
final int count = getChildCount();
//遍历所有的子View
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child != null && child.getVisibility() != GONE) {
//如果需要绘制divider
if (hasDividerBeforeChildAt(i)) {
final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child
.getLayoutParams();
//得到开始的位置,getLeft为当前View的左侧,而左侧有margin,所以之差为divider绘制的开始区域
final int left = child.getLeft() - lp.leftMargin/*
* -
* mDividerWidth
*/;
//绘制divider
drawVerticalDivider(canvas, left);
}
}
}
}
/**
* 绘制divider,根据left,水平方向绘制
* @param canvas
* @param left
*/
public void drawVerticalDivider(Canvas canvas, int left) {
//设置divider的范围
mDivider.setBounds(left, getPaddingTop() + mDividerPaddingLeft, left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPaddingRight);
//绘制
mDivider.draw(canvas);
}
private void drawDividersVertical(Canvas canvas) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child != null && child.getVisibility() != GONE) {
if (hasDividerBeforeChildAt(i)) {
final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child
.getLayoutParams();
final int top = child.getTop() - lp.topMargin/*
* -
* mDividerHeight
*/;
drawHorizontalDivider(canvas, top);
}
}
}
}
private void drawHorizontalDivider(Canvas canvas, int top) {
mDivider.setBounds(getPaddingLeft() + mDividerPaddingLeft, top, getWidth() - getPaddingRight() - mDividerPaddingRight, top + mDividerHeight);
mDivider.draw(canvas);
}
//设置dividerpaddingleft
public void setDividerPaddingLeft(int dividerPaddingLeft){
this.mDividerPaddingLeft = dividerPaddingLeft;
}
//设置dividerpaddingright
public void setDividerPaddingRight(int dividerPaddingRight){
this.mDividerPaddingRight = dividerPaddingRight;
}
}
其实,我改的主要也就是我上面说的那两个方法了,并且提供了两个方法setDividerPaddingLeft和setDividerPaddingRight了, 用于设置分割线的paddingleft和paddingright。