转:http://blog.csdn.net/lmj623565791/article/details/42407923

1、概述

话说,随着Android SDK版本的升级,很多控件增加了新的属性方便我们的使用,比如LinearLayout中多了:divider、showDividers等,用于为其内部元素添加分隔;但是呢,这样的属性在较低版本的SDK中不能被支持,那么,我们在开发过程中,可能会出现这样的需求:将这个新的特性想办法做到尽可能的向下兼容。有人说,可以自己写个新的控件去实现,这样的确可以,但是会不会太霸气了点。难道就没有接地气一点的方式么?嗯,本文就是这样的一个目的,以一种较为接地气的方式,实现新的属性的向下兼容。

这样的情况在Android中肯定会很多,希望可以以此进行抛砖引玉,大家遇到类似的情况,提供一定的思路。这才是这篇博客的真正目的!

2、divider相关用法

为了保证简介性,这里就不讨论divider有多么多么好用神马的,因为不是我们的重点。当然了这里提供一篇divider的参考:grid-spacing-on-android(基本就是引出divider的用处,有兴趣的看下,本文的demo样子也将参考本链接)。

大家先看一个效果图:

如果要实现,这样的效果图,对于这3个Button大家会怎么做(主要看button):

简单嘛:一个水平的线性布局,内部三个Button的weight都为1,然后第二个Button设置leftMargin,rightMargin就可以了。

嗯,没问题,假设现在我有一个需求:经过某个操作Button3隐藏,然后让Button1和Button2按如下布局:

这样的感觉是不是不错,虽然少了一个,完全不影响美观;但是,如果按照上述的答案

“一个水平的线性布局,内部三个Button的weight都为1,然后第二个Button设置leftMargin,rightMargin就可以了”  Button2的右边会多出一个rightMargin 。

所以,这样的制作方式很明显不是最优秀的,最优秀的方案是,使用Linearlayout的divider、showDividers属性:

布局代码如下:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="wrap_content"
  4. android:padding="20dp"
  5. android:layout_margin="10dp"
  6. android:background="#22444444"
  7. android:orientation="vertical" >
  8. <TextView
  9. android:layout_width="match_parent"
  10. android:layout_height="128dp"
  11. android:background="@android:color/darker_gray"
  12. android:gravity="center"
  13. android:text="application_logo" />
  14. <LinearLayout
  15. android:id="@+id/buttons_container"
  16. android:layout_width="match_parent"
  17. android:layout_height="wrap_content"
  18. android:layout_marginTop="10dp"
  19. android:divider="@drawable/divider"
  20. android:orientation="horizontal"
  21. android:showDividers="middle" >
  22. <Button
  23. android:id="@+id/btn_first"
  24. android:layout_width="0dp"
  25. android:layout_height="wrap_content"
  26. android:layout_weight="1"
  27. android:background="#ff0000"
  28. android:text="button_1" />
  29. <Button
  30. android:id="@+id/btn_second"
  31. android:layout_width="0dp"
  32. android:layout_height="wrap_content"
  33. android:layout_weight="1"
  34. android:background="#00ff00"
  35. android:text="button_2" />
  36. <Button
  37. android:id="@+id/btn_third"
  38. android:layout_width="0dp"
  39. android:layout_height="wrap_content"
  40. android:layout_weight="1"
  41. android:background="#0000ff"
  42. android:text="button_3" />
  43. </LinearLayout>
  44. </LinearLayout>

其实核心就是放置Button的LinearLayout设置了 android:divider="@drawable/divider"和 android:showDividers="middle" ;

当然了,有人会说,我就是任性,我就用margin来实现,消失的时候,我显示去控制button的rightMargin为0也可以。嗯,是的,你不嫌麻烦的确没问题。那么现在问题又来了,我现在要求每个Button间的间隔是蓝色的,你怎么办?注意:我们这里的divider的值设置的是一个drawable噢~~没辙了吧。

本例的drawable(divider.xml):

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:shape="rectangle" >
  4. <size android:width="15dp" />
  5. <solid android:color="@android:color/transparent" />
  6. </shape>

下面简单介绍下divider、showDividers、dividerPadding:

divider可以设置一个drawable作为元素间的间隔;

showDividers:可取值为:middle(子元素间)、beginning(第一个元素左边)、end(最后一个元素右边)、none;【关于垂直方向的类似】

dividerPadding:设置绘制间隔元素的上下padding。

很简单,大家自己动手做下实验就知道了。

好了,到此,我们简单介绍了divider等的好处以及使用方式。但是这么优雅的来实现元素间的间隔只有在3.0以上才被支持,那么3.0以下怎么办呢?

别怕,下面开始本文的重点,让divider兼容至3.0一下。

3、自定义LinearLayout

看了标题,大家认为又是自定义LinearLayout么~~

嗯,继承LinearLayout是肯定的,我们没有办法改变它的源码,但是可以通过继承去改变一些特性。

注意下:现在的目的是兼容至3.0以下:

首先看一个3.0以下的效果图,不然你说我骗你:

上面的布局文件在3.0以下显示就是这么个样子,完全无视间隔。

首先考虑一个问题,对于divider、showDividers 3.0以下的LinearLayout肯定无视呀,咋办呢?

我们实现个LinearLayout的子类,让它认识divider和showDividers~~~重视一下这里,这里就是我们向前迈进的一大步,以后遇到类似问题,都这么干。

1、识别高版本的属性

  1. public class IcsLinearLayout extends LinearLayout
  2. {
  3. private static final int[] LL = new int[]
  4. { //
  5. android.R.attr.divider,//
  6. android.R.attr.showDividers,//
  7. android.R.attr.dividerPadding //
  8. };
  9. private static final int LL_DIVIDER = 0;
  10. private static final int LL_SHOW_DIVIDER = 1;
  11. private static final int LL_DIVIDER_PADDING = 2;
  12. /**
  13. * android:dividers
  14. */
  15. private Drawable mDivider;
  16. /**
  17. * 对应:android:showDividers
  18. */
  19. private int mShowDividers;
  20. /**
  21. * 对应:android:dividerPadding
  22. */
  23. private int mDividerPadding;
  24. private int mDividerWidth;
  25. private int mDividerHeight;
  26. public IcsLinearLayout(Context context, AttributeSet attrs)
  27. {
  28. super(context, attrs);
  29. TypedArray a = context.obtainStyledAttributes(attrs, LL);
  30. setDividerDrawable(a.getDrawable(IcsLinearLayout.LL_DIVIDER));
  31. mDividerPadding = a.getDimensionPixelSize(LL_DIVIDER_PADDING, 0);
  32. mShowDividers = a.getInteger(LL_SHOW_DIVIDER, SHOW_DIVIDER_NONE);
  33. a.recycle();
  34. }
  35. /**
  36. * 设置分隔元素,初始化宽高等
  37. */
  38. public void setDividerDrawable(Drawable divider)
  39. {
  40. if (divider == mDivider)
  41. {
  42. return;
  43. }
  44. mDivider = divider;
  45. if (divider != null)
  46. {
  47. mDividerWidth = divider.getIntrinsicWidth();
  48. mDividerHeight = divider.getIntrinsicHeight();
  49. } else
  50. {
  51. mDividerWidth = 0;
  52. mDividerHeight = 0;
  53. }
  54. setWillNotDraw(divider == null);
  55. requestLayout();
  56. }

这里贴出了成员变量和我们的构造方法,成员变量中包含了3个属性对应的接收变量;然后我们在构造里面对这三个属性进行了获取并赋值给相应的属性;

这里大家肯定会困惑,我上面定义了一个整型数组,然后几个变量为数组下标,最后利用这个数组和下标在构造里面获取了值。是不是要问,你为什么这么写,你咋知道的?

嗯,这样,大家随便下载我之前包含自定义属性的文章,或者你自己写的:

这里我拿了Android BitmapShader 实战 实现圆形、圆角图片这个例子中的源代码,大家就不用下载了,看看我下面就明白了,我在这里例子中自定义了两个属性:type和border_radius,看看我们的R.java里面生成了什么样的代码:

  1. public static final int border_radius=0x7f010001;
  2. public static final int type=0x7f010000;
  3. public static final int[] RoundImageViewByShader = {
  4. 0x7f010000, 0x7f010001
  5. };
  6. public static final int RoundImageViewByShader_type = 0;
  7. public static final int RoundImageViewByShader_border_radius = 1;

看见木有,整型数组,下标;我们的android.R.attr.xxx对应于上面的常量。是不是和我们上例定义的一模一样~~

对,自定义属性怎么获取的,你照着模仿就是,无非现在的属性是android.R.attr.xxx而不是你自定义的,本质没区别。

好了,现在大家应该知道怎么获取高版本的属性了~~

2、onMeasure

获取到分隔元素以后,分隔元素肯定有宽和高,我们这里把分隔元素的宽和高转化为合适的margin

  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  3. {
  4. //将分隔元素的宽高转化为对应的margin
  5. setChildrenDivider();
  6. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  7. }
  8. /**
  9. * 将分隔元素的宽高转化为对应的margin
  10. */
  11. protected void setChildrenDivider()
  12. {
  13. final int count = getChildCount();
  14. for (int i = 0; i < count; i++)
  15. {
  16. //遍历每个子View
  17. View child = getChildAt(i);
  18. //拿到索引
  19. final int index = indexOfChild(child);
  20. //方向
  21. final int orientation = getOrientation();
  22. final LayoutParams params = (LayoutParams) child.getLayoutParams();
  23. //判断是否需要在子View左边绘制分隔
  24. if (hasDividerBeforeChildAt(index))
  25. {
  26. if (orientation == VERTICAL)
  27. {
  28. //如果需要,则设置topMargin为分隔元素的高度(垂直时)
  29. params.topMargin = mDividerHeight;
  30. } else
  31. {
  32. //如果需要,则设置leftMargin为分隔元素的宽度(水平时)
  33. params.leftMargin = mDividerWidth;
  34. }
  35. }
  36. }
  37. }
  38. /**
  39. * 判断是否需要在子View左边绘制分隔
  40. */
  41. public boolean hasDividerBeforeChildAt(int childIndex)
  42. {
  43. if (childIndex == 0 || childIndex == getChildCount())
  44. {
  45. return false;
  46. }
  47. if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0)
  48. {
  49. boolean hasVisibleViewBefore = false;
  50. for (int i = childIndex - 1; i >= 0; i--)
  51. {
  52. //当前index的前一个元素不为GONE则认为需要
  53. if (getChildAt(i).getVisibility() != GONE)
  54. {
  55. hasVisibleViewBefore = true;
  56. break;
  57. }
  58. }
  59. return hasVisibleViewBefore;
  60. }
  61. return false;
  62. }

onMeasure中,将divider的宽和高,根据mShowDividers的情况,设置给了合适的View的margin;

其实就是,将divider需要占据的地方,利用margin空出来,我们最后会在这个空的区域进行绘制divider,别忘了,我们的divider是个drawable。

3、onDraw

好了,既然已经通过margin把需要绘制的地方空出来了,那么下面就是绘制了~~~

  1. @Override
  2. protected void onDraw(Canvas canvas)
  3. {
  4. if (mDivider != null)
  5. {
  6. if (getOrientation() == VERTICAL)
  7. {
  8. //绘制垂直方向的divider
  9. drawDividersVertical(canvas);
  10. } else
  11. {
  12. //绘制水平方向的divider
  13. drawDividersHorizontal(canvas);
  14. }
  15. }
  16. super.onDraw(canvas);
  17. }
  18. /**
  19. * 绘制水平方向的divider
  20. * @param canvas
  21. */
  22. private void drawDividersHorizontal(Canvas canvas)
  23. {
  24. final int count = getChildCount();
  25. //遍历所有的子View
  26. for (int i = 0; i < count; i++)
  27. {
  28. final View child = getChildAt(i);
  29. if (child != null && child.getVisibility() != GONE)
  30. {
  31. //如果需要绘制divider
  32. if (hasDividerBeforeChildAt(i))
  33. {
  34. final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child
  35. .getLayoutParams();
  36. //得到开始的位置,getLeft为当前View的左侧,而左侧有margin,所以之差为divider绘制的开始区域
  37. final int left = child.getLeft() - lp.leftMargin/*
  38. * -
  39. * mDividerWidth
  40. */;
  41. //绘制divider
  42. drawVerticalDivider(canvas, left);
  43. }
  44. }
  45. }
  46. }
  47. /**
  48. * 绘制divider,根据left,水平方向绘制
  49. * @param canvas
  50. * @param left
  51. */
  52. public void drawVerticalDivider(Canvas canvas, int left)
  53. {
  54. //设置divider的范围
  55. mDivider.setBounds(left, getPaddingTop() + mDividerPadding, left
  56. + mDividerWidth, getHeight() - getPaddingBottom()
  57. - mDividerPadding);
  58. //绘制
  59. mDivider.draw(canvas);
  60. }

为了代码的简短以及帮助大家的理解,这里没有贴出垂直方向的,水平方向的整个流程是完整的 。后面会贴出来垂直方向的绘制代码。

其实也比较简单,在onDraw里面判断方向,这里以水平为例:遍历所有的子View,如果发现需要在其前绘制divider的,则算出divider的开始的位置(child.getLeft() - lp.leftMargin),然后调用drawVerticalDivider(),设置divider范围,紧接着绘制出来。

垂直方向同理,就不赘述了,贴上代码:

  1. private void drawDividersVertical(Canvas canvas)
  2. {
  3. final int count = getChildCount();
  4. for (int i = 0; i < count; i++)
  5. {
  6. final View child = getChildAt(i);
  7. if (child != null && child.getVisibility() != GONE)
  8. {
  9. if (hasDividerBeforeChildAt(i))
  10. {
  11. final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child
  12. .getLayoutParams();
  13. final int top = child.getTop() - lp.topMargin/*
  14. * -
  15. * mDividerHeight
  16. */;
  17. drawHorizontalDivider(canvas, top);
  18. }
  19. }
  20. }
  21. }
  22. private void drawHorizontalDivider(Canvas canvas, int top)
  23. {
  24. mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, getWidth()
  25. - getPaddingRight() - mDividerPadding, top + mDividerHeight);
  26. mDivider.draw(canvas);
  27. }

代码说完了,下面干嘛呢?当然是测试了~~不测试怎么知道结果~~

4、测试

首先我们把布局文件中包含Button的Linelayout换成我们的com.zhy.view.IcsLinearLayout

在3.0以下机子上运行:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="wrap_content"
  4. android:padding="20dp"
  5. android:layout_margin="10dp"
  6. android:background="#22444444"
  7. android:orientation="vertical" >
  8. <TextView
  9. android:layout_width="match_parent"
  10. android:layout_height="128dp"
  11. android:background="@android:color/darker_gray"
  12. android:gravity="center"
  13. android:text="application_logo" />
  14. <com.zhy.view.IcsLinearLayout
  15. android:id="@+id/buttons_container"
  16. android:layout_width="match_parent"
  17. android:layout_height="wrap_content"
  18. android:layout_marginTop="10dp"
  19. android:divider="@drawable/divider"
  20. android:orientation="horizontal"
  21. android:showDividers="middle" >
  22. <Button
  23. android:id="@+id/btn_first"
  24. android:layout_width="0dp"
  25. android:layout_height="wrap_content"
  26. android:layout_weight="1"
  27. android:background="#ff0000"
  28. android:text="button_1" />
  29. <Button
  30. android:id="@+id/btn_second"
  31. android:layout_width="0dp"
  32. android:layout_height="wrap_content"
  33. android:layout_weight="1"
  34. android:background="#00ff00"
  35. android:text="button_2" />
  36. <Button
  37. android:id="@+id/btn_third"
  38. android:layout_width="0dp"
  39. android:layout_height="wrap_content"
  40. android:layout_weight="1"
  41. android:background="#0000ff"
  42. android:text="button_3" />
  43. </com.zhy.view.IcsLinearLayout>
  44. </LinearLayout>

效果图:

久违了~~我们的分隔~~可以看到在3.0以下机器完美实现~~~

but,别高兴太早,我们这么改,3.0以上机器是什么样子呢?

哈哈,是不是完美实现了间隔~~~

现在可以高兴了~~~

大家现在肯定有困惑,我擦,你在构造里面获取divider,然后在onDraw里面自己绘制了divider,大家都知道3.0以上是支持的呀,肯定也会绘制呀,你说没冲突谁信呀~~~!!!

5、答疑

1、为什么和3.0以上没有发生一些该有的冲突?

嗯,是的,3.0以上是支持的,为什么我们在onDraw里面自己绘制,然后调用super.onDraw竟然没有发生什么冲突?

原因很简单:我们看4.4LinearLayout的源码:

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. if (mDivider == null) {
  4. return;
  5. }

其实,源码中也是在onDraw里面去绘制divider,但是如果mDivider为null,就会return。之所以没有冲突,是因为我们前面的某个操作让其mDivider成员变量为null了~~

现在去LinearLayout的构造方法:

  1. public LinearLayout(Context context, AttributeSet attrs, int defStyle) {
  2. super(context, attrs, defStyle);
  3. ...
  4. setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));
  5. ...
  6. }
  7. public void setDividerDrawable(Drawable divider) {
  8. if (divider == mDivider) {
  9. return;
  10. }
  11. mDivider = divider;
  12. if (divider != null) {
  13. mDividerWidth = divider.getIntrinsicWidth();
  14. mDividerHeight = divider.getIntrinsicHeight();
  15. } else {
  16. mDividerWidth = 0;
  17. mDividerHeight = 0;
  18. }
  19. setWillNotDraw(divider == null);
  20. requestLayout();
  21. }

可以看到它在其构造中调用setDividerDrawable为其mDivider赋值,关键来了~~~~我们的自定义的LinearLayout复写了这个方法,也就是说,setDividerDrawable会调用子类的方法,这个父类的setDividerDrawable根本不会调用,从而导致mDivider为null了~~~

为null就对应了onDraw里面的绘制~~ok~解答完毕。

2、这篇博客怎么想到的?你咋知道代码这么写?

我相信这样的问题,很多人感兴趣,其实也算巧合,之前知道有divider这个属性;然后前段时间写Android 教你打造炫酷的ViewPagerIndicator 不仅仅是高仿MIUI 这篇博客的时候,特意去看了ViewPagerIndicator那个开源项目源码,发现了一个IcsLinearLayout这样的一个类,类似我们上面实现的,当然了,我做了一定的修改;于是乎,仔细研究了这了类,觉得很有必要写成博客,达到文章开头所叙述的的目的~其实大家有心的话,根据我们上述的代码,去看看LinearLayout源码中如何去绘制divider,你会发现代码基本是一样的(ps:你没发现问题1中的LinearLayout源码的setDividerDrawable和我们写的一模一样么~);

好了,到此整篇文章就结束了,还是那句话:”这样的情况在Android中肯定会很多,希望可以以此进行抛砖引玉,大家遇到类似的情况,提供一定的思路。这才是这篇博客的真正目的!“ 不要偷懒花点时间去敲一敲,看一看,想一想,你会发现里面还藏着很多东西,别怕浪费时间,我研究和写这篇博客的时间绝对超出你所学习这篇博客的时间~~

最后,欧来来~~

源码点击下载

Android _优雅实现元素间的分割线 (支持3.0以下)的更多相关文章

  1. Android 自定义控件 优雅实现元素间的分割线 (支持3.0以下)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/42407923 ,本文出自:[张鸿洋的博客] 1.概述 话说,随着Android ...

  2. 错觉-Info:视错觉与UI元素间的可能

    ylbtech-错觉-Info:视错觉与UI元素间的可能 1.返回顶部 1. 视觉原理在当下红火的机械视觉中是必不可少的,那在我们日常工作的UI产品设计中又有什么可能性的呢?今天,我从“视错觉”这个角 ...

  3. 转载>>去除inline-block元素间间距的N种方法《重》

    一.现象描述 真正意义上的inline-block水平呈现的元素间,换行显示或空格分隔的情况下会有间距,很简单的个例子: <input /> <input type="su ...

  4. cb49a_c++_STL_算法_对所有元素排序_sort_stable_sort

    cb49a_c++_STL_算法_对所有元素排序_sort_stable_sort sort(b,e) sort(b,e,p) stable_sort(b,e) stable_sort(b,e,p) ...

  5. 如何消除inline-block元素间间距问题(转)

    一.现象描述    真正意义上的inline-block水平呈现的元素间,换行显示或空格分隔的情况下会有间距,很简单的个例子: <input /> <input type=" ...

  6. 去除inline-block元素间间距的N种方法

    这篇文章发布于 2012年04月24日,星期二,22:38,归类于 css相关. 阅读 147771 次, 今日 52 次 by zhangxinxu from http://www.zhangxin ...

  7. [转]去除inline-block元素间间距的N种方法

    来自张鑫旭-鑫空间-鑫生活[http://www.zhangxinxu.com] 一.现象描述 真正意义上的inline-block水平呈现的元素间,换行显示或空格分隔的情况下会有间距,很简单的个例子 ...

  8. 去除UITableView中多余的分割线或者隐藏cell间的分割线

    一:去除tableView多余的分割线 首先,自定义一个方法 -(void)setExtraCellLineHidden: (UITableView *)tableView{    UIView *v ...

  9. 移动web在ios和android下点击元素出现阴影问题

     移动web开发经验总结 1.-webkit-tap-highlight-color:rgba(255,255,255,0)可以同时屏蔽ios和android下点击元素时出现的阴影.备注:transp ...

随机推荐

  1. [转载]ACM(访问控制模型),Security Identifiers(SID),Security Descriptors(安全描述符),ACL(访问控制列表),Access Tokens(访问令牌)

    对于<windows核心编程>中的只言片语无法驱散心中的疑惑.就让MSDN中的解释给我们一盏明灯吧.如果要很详细的介绍,还是到MSDN仔细的看吧,我只是大体用容易理解的语言描述一下. wi ...

  2. Putty使用公钥认证时,报错:Disconnected: No supported authentication methods available(server sent:public key) 问题的解决

    Putty使用公钥认证时,按照常规方法设置,一直报错:Disconnected: No supported authentication methods available (server sent: ...

  3. RMAN之一:快速入门

    1.数据导出基础 (1)创建datapump导出文件的目录对象并为相应用户授予权限. 出于安全考虑,不允许oracle用户直接在OS上进行文件的操作,而应通过directory对象指定. SQL> ...

  4. javabean对象自动赋值给另一个javabean对象

    方法1:把JavaBean的from的值自动set给to,省略了自己从from中get然后再set给to import java.beans.BeanInfo;import java.beans.In ...

  5. 运算符 - PHP手册笔记

    运算符优先级 每种编程语言都有运算符,运算符要学会灵活使用. 运算符拥有不同的优先级和结合方向. <?php var_dump(1 <= 1 == 1); // true var_dump ...

  6. Vue.js实现拼图游戏

    Vue.js实现拼图游戏 之前写过一篇<基于Vue.js的表格分页组件>的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/ ...

  7. 在Azure Cloud Service中部署Java Web App(1)

    Microsoft Azure是一个开放的,灵活的云平台,除了对自家的.Net平台有良好的支持外,对于各种开源的软件,语言,工具,框架都有着良好的支持,比如Java,Php,Python等等,你可以使 ...

  8. QT父子与QT对象delete

    原地址:http://www.qteverywhere.com/archives/437 很多C/C++初学者常犯的一个错误就是,使用malloc.new分配了一块内存却忘记释放,导致内存泄漏.Qt的 ...

  9. 可以供MFC调用的,QT实现的DLL(qtwinmigrate实现)

    MFC和QT的消息循环机制不同,所以,要让QT写的DLL可以供MFC调用,要做一点特殊的处理 #include <qmfcapp.h> #include <qwinwidget.h& ...

  10. 让Maven正确处理javac警告

    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.1:compile (default ...