自定义控件其实很简单1/12

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究!

炮兵镇楼

自定义View,很多初学Android的童鞋听到这么一句话绝逼是一脸膜拜!因为在很多初学者眼里,能够自己去画一个View绝逼是一件很屌很Cool的事!但是,同样而言,自定义View对初学者来说却往往可望而不可及,可望是因为看了很多自定义View的源码好像并不难,有些自定义View甚至不足百行代码,不可及呢是因为即便看了很多文章很多类似的源码依然写不出一个霸气的View来。这时会有很多前辈告诉你多看看View类的源码,看看View类里是如何去处理这些绘制逻辑的,如果你去看了我只能说你是个很好学很有求知欲的孩纸,了解原理是好事,但是并非凡事都要去刨根问底的!如果你做Android开发必须要把Android全部源码弄懂,我只能呵呵了!你还不如去写一个系统实在对吧!同样的道理,写一个自定义View你非要去花巨量时间研究各类源码是不值得提倡的,当然哥没有否定追究原理的意义所在,只是对于一个普通的开发者你没有必要去深究一些不该值得你关心的东西,特别是一个有良好面向对象思维的猿。举个生活中简单的例子,大家都用过吹风,吹风一般都会提供三个档位:关、冷风、热风对吧,你去买吹风人家只会告诉你这吹风三个档位分别是什么功能,我相信没有哪个傻逼买吹风的会把吹风拆开、电机写下来一个一个地跟你解说那是啥玩意吧!同样的,我们自定义View其实Android已经提供了大量类似吹风档位的方法,你只管在里面做你想做的事情就可,至于Android本身内部是如何实现的,你压根不用去管!用官方文档的原话来说就是:Just do you things!初学者不懂如何去自定义View并非是不懂其原理,而是不懂这些类似“档位”的方法!

好了,扯了这么多废话!我们还是先步入正题,来看看究竟自定义View是如何实现的!在Android中自定义一个View类并定是直接继承View类或者View类的子类比如TextView、Button等等,这里呢我们也依葫芦画瓢直接继承View自定义一个View的子类CustomView:

[java] view plaincopyprint?

  1. public class CustomView extends View {
  2. }

在View类中没有提供无参的构造方法,这时我们的IDE会提示我们你得明确地声明一个和带有父类一样签名列表的构造方法:

这时我们点击“Add constructor CustomView(Context context)”,IDE就会自动为我们生成一个带有Context类型签名的构造方法:

[java] view plaincopyprint?

  1. public class CustomView extends View {
  2. public CustomView(Context context) {
  3. super(context);
  4. }
  5. }

Context是什么你不用管,只管记住它包含了许多各种不同的信息穿梭于Android中各类组件、控件等等之间,说得不恰当点就是一个装满信息的信使,Android需要它从里面获取需要的信息。

这样我们就定义了一个属于自己的自定义View,我们尝试将它添加到Activity:

[java] view plaincopyprint?

  1. public class MainActivity extends Activity {
  2. private LinearLayout llRoot;// 界面的根布局
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. llRoot = (LinearLayout) findViewById(R.id.main_root_ll);
  8. llRoot.addView(new CustomView(this));
  9. }
  10. }

运行后发现什么也没有,空的!因为我们的CustomView本来就什么都没有!但是添加到我们的界面后没有什么问题对吧!Perfect!那我们再直接在xml文档中引用它呢:

[html] view plaincopyprint?

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:id="@+id/main_root_ll"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical" >
  6. <com.sigestudio.customviewdemo.views.CustomView
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent" />
  9. </LinearLayout>

这时我们还原Activity中的代码:

[java] view plaincopyprint?

  1. public class MainActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. }
  7. }

再次运行后发现IDE报错了:

大致意思是无法解析我们的CustomView类找不到方法,为什么呢?我们在xml文件引用我们的CustomView类时为其指定了两个android自带的两个属性:layout_width和layout_height,当我们需要使用类似的属性(比如更多的什么id啊、padding啊、margin啊之类)时必须在自定义View的构造方法中添加一个AttributeSet类型的签名来解析这些属性:

[java] view plaincopyprint?

  1. public class CustomView extends View {
  2. public CustomView(Context context) {
  3. super(context);
  4. }
  5. public CustomView(Context context, AttributeSet attrs) {
  6. super(context, attrs);
  7. }
  8. }

再次运行发现一切又恢复了正常。现在我们来往我们的View里画点东西,毕竟自定义View总得有点什么才行对吧!Android给我们提供了一个onDraw(Canvas canvas)方法来让我们绘制自己想要的东西:

[java] view plaincopyprint?

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. super.onDraw(canvas);
  4. }

我们想要画些什么直接在这个方法里面画即可,在现实世界中,我们画画需要两样东西:笔(或者任何能涂画的东西)和纸(或者任何能被画的东西),同样地,Android也给我们提供了这两样东西:Paint和Canvas,一个是画笔而另一个呢当然是画布啦~~,我们可以看到在onDraw方法中,画布Canvas作为签名被传递进来,也就是说这个画布是Android为我们准备好的,不需要你去管,当然你也可以自定义一张画布在上面绘制自己的东西并将其传递给父类,但是一般我们不建议这样去做!有人会问这画布是怎么来的?在这里我不想跟大家深究其原理,否则长篇大论也过于繁琐打击各位菜鸟哥的学习兴趣。但是我可以这样跟大家说,如果在一张大的画布(界面)上面有各种各样小的画布(界面中的各种控件),那么这些小的画布该如何确定其大小呢?自己去想哈哈!
草!又跑题了!

画布有了,差一支画笔,简单!我们new一个呗!程序猿的好处就在万事万物都可以自己new!女朋友也能自己new,随便new!!~~~:

[java] view plaincopyprint?

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. super.onDraw(canvas);
  4. Paint paint = new Paint();
  5. paint.setAntiAlias(true);
  6. }

实例化了一个Paint对象后我们为其设置了抗锯齿(一种让图像边缘显得更圆滑光泽动感的碉堡算法):setAntiAlias(true),但是我们发现这是IDE又警告了!!!说什么“Avoid object allocations during draw/layout operations (preallocate and reuse instead)”:


Why?Why?说白了就是不建议你在draw或者layout的过程中去实例化对象!为啥?因为draw或layout的过程有可能是一个频繁重复执行的过程,我们知道new是需要分配内存空间的,如果在一个频繁重复的过程中去大量地new对象内存爆不爆我不知道,但是浪费内存那是肯定的!所以Android不建议我们在这两个过程中去实例化对象。既然都这样说了我们就改改呗:

[java] view plaincopyprint?

  1. public class CustomView extends View {
  2. private Paint mPaint;
  3. public CustomView(Context context) {
  4. this(context, null);
  5. }
  6. public CustomView(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. // 初始化画笔
  9. initPaint();
  10. }
  11. /**
  12. * 初始化画笔
  13. */
  14. private void initPaint() {
  15. // 实例化画笔并打开抗锯齿
  16. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  17. }
  18. @Override
  19. protected void onDraw(Canvas canvas) {
  20. super.onDraw(canvas);
  21. }
  22. }

现实世界中,我们画画的画笔是多种多样的,有马克笔、铅笔、圆珠笔、毛笔、水彩笔、荧光笔等等等等……而这些笔的属性也各自不同,像铅笔按照炭颗粒的粗糙度可以分为2B、3B、4B、5B、HB当然还有SB,而水彩笔也有各种不同的颜色,马克笔就更霸气了不说了!同样地在Android的画笔里,现实有的它也有,没有的它还有!我们可以用Paint的各种setter方法来设置各种不同的属性,比如setColor()设置画笔颜色,setStrokeWidth()设置描边线条,setStyle()设置画笔的样式:


Paint集成了所有“画”的属性,而Canvas则定义了所有要画的东西,我们可以通过Canvas下的各类drawXXX方法绘制各种不同的东西,比如绘制一个圆drawCircle(),绘制一个圆弧drawArc(),绘制一张位图drawBitmap()等等等:


既然初步了解了Paint和Canvas,我们不妨就尝试在我们的画布上绘制一点东西,比如一个圆环?我们先来设置好画笔的属性:

[java] view plaincopyprint?

  1. /**
  2. * 初始化画笔
  3. */
  4. private void initPaint() {
  5. // 实例化画笔并打开抗锯齿
  6. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  7. /*
  8. * 设置画笔样式为描边,圆环嘛……当然不能填充不然就么意思了
  9. *
  10. * 画笔样式分三种:
  11. * 1.Paint.Style.STROKE:描边
  12. * 2.Paint.Style.FILL_AND_STROKE:描边并填充
  13. * 3.Paint.Style.FILL:填充
  14. */
  15. mPaint.setStyle(Paint.Style.STROKE);
  16. // 设置画笔颜色为浅灰色
  17. mPaint.setColor(Color.LTGRAY);
  18. /*
  19. * 设置描边的粗细,单位:像素px
  20. * 注意:当setStrokeWidth(0)的时候描边宽度并不为0而是只占一个像素
  21. */
  22. mPaint.setStrokeWidth(10);
  23. }

然后在我们的onDraw方法中绘制Cricle即可:

[java] view plaincopyprint?

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. super.onDraw(canvas);
  4. // 绘制圆环
  5. canvas.drawCircle(MeasureUtil.getScreenSize((Activity) mContext)[0] / 2, MeasureUtil.getScreenSize((Activity) mContext)[1] / 2, 200, mPaint);
  6. }

这里要注意哦!drawCircle表示绘制的是圆形,但是在我们的画笔样式设置为描边后其绘制出来的就是一个圆环!其中drawCircle的前两个参数表示圆心的XY坐标,这里我们用到了一个工具类获取屏幕尺寸以便将其圆心设置在屏幕中心位置,第三个参数是圆的半径,第四个参数则为我们的画笔!

这里有一点要注意:在Android中设置数字类型的参数时如果没有特别的说明,参数的单位一般都为px像素。

好了,我们来运行下我们的Demo看看结果:

一个灰常漂亮的圆环展现在我们眼前!怎么样是不是很爽,这算是我们写的第一个View,当然这只是第一步,虽然只是一小步,但必定会是影响人类进步的一大步!……Fuck!

不过一个简单地画一个圆恐怕难以满足各位的胃口对吧,那我们尝试让它动起来?比如让它的半径从小到大地不断变化,那怎么实现好呢?大家如果了解动画的原理就会知道,一个动画是由无数张连贯的图片构成的,这些图片之间快速地切换再加上我们眼睛的视觉暂留给我们造成了在“动”的假象。那么原理有了实现就很简单了,我们不断地改变圆环的半径并且重新去画并展示不就成了?同样地,在Android中提供了一个叫invalidate()的方法来让我们重绘我们的View。现在我们重新构造一下我们的代码,添加一个int型的成员变量作为半径值的引用,再提供一个setter方法对外设置半径值,并在设置了该值后调用invalidate()方法重绘View:

[java] view plaincopyprint?

  1. public class CustomView extends View {
  2. private Paint mPaint;// 画笔
  3. private Context mContext;// 上下文环境引用
  4. private int radiu;// 圆环半径
  5. public CustomView(Context context) {
  6. this(context, null);
  7. }
  8. public CustomView(Context context, AttributeSet attrs) {
  9. super(context, attrs);
  10. mContext = context;
  11. // 初始化画笔
  12. initPaint();
  13. }
  14. /**
  15. * 初始化画笔
  16. */
  17. private void initPaint() {
  18. // 实例化画笔并打开抗锯齿
  19. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  20. /*
  21. * 设置画笔样式为描边,圆环嘛……当然不能填充不然就么意思了
  22. *
  23. * 画笔样式分三种:
  24. * 1.Paint.Style.STROKE:描边
  25. * 2.Paint.Style.FILL_AND_STROKE:描边并填充
  26. * 3.Paint.Style.FILL:填充
  27. */
  28. mPaint.setStyle(Paint.Style.STROKE);
  29. // 设置画笔颜色为浅灰色
  30. mPaint.setColor(Color.LTGRAY);
  31. /*
  32. * 设置描边的粗细,单位:像素px
  33. * 注意:当setStrokeWidth(0)的时候描边宽度并不为0而是只占一个像素
  34. */
  35. mPaint.setStrokeWidth(10);
  36. }
  37. @Override
  38. protected void onDraw(Canvas canvas) {
  39. super.onDraw(canvas);
  40. // 绘制圆环
  41. canvas.drawCircle(MeasureUtil.getScreenSize((Activity) mContext)[0] / 2, MeasureUtil.getScreenSize((Activity) mContext)[1] / 2, radiu, mPaint);
  42. }
  43. public synchronized void setRadiu(int radiu) {
  44. this.radiu = radiu;
  45. // 重绘
  46. invalidate();
  47. }
  48. }

那么OK,我们在Activity中开一个线程,通过Handler来定时间断地设置半径的值并刷新界面:

[java] view plaincopyprint?

  1. public class MainActivity extends Activity {
  2. private CustomView mCustomView;// 我们的自定义View
  3. private int radiu;// 半径值
  4. @SuppressLint("HandlerLeak")
  5. private Handler mHandler = new Handler() {
  6. @Override
  7. public void handleMessage(Message msg) {
  8. // 设置自定义View的半径值
  9. mCustomView.setRadiu(radiu);
  10. }
  11. };
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.activity_main);
  16. // 获取控件
  17. mCustomView = (CustomView) findViewById(R.id.main_cv);
  18. /*
  19. * 开线程
  20. */
  21. new Thread(new Runnable() {
  22. @Override
  23. public void run() {
  24. /*
  25. * 确保线程不断执行不断刷新界面
  26. */
  27. while (true) {
  28. try {
  29. /*
  30. * 如果半径小于200则自加否则大于200后重置半径值以实现往复
  31. */
  32. if (radiu <= 200) {
  33. radiu += 10;
  34. // 发消息给Handler处理
  35. mHandler.obtainMessage().sendToTarget();
  36. } else {
  37. radiu = 0;
  38. }
  39. // 每执行一次暂停40毫秒
  40. Thread.sleep(40);
  41. } catch (InterruptedException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. }
  46. }).start();
  47. }
  48. @Override
  49. protected void onDestroy() {
  50. super.onDestroy();
  51. // 界面销毁后清除Handler的引用
  52. mHandler.removeCallbacksAndMessages(null);
  53. }
  54. }

运行后的效果我就不演示了,项目源码会共享。

但是有一个问题,这么一个类似进度条的效果我还要在Activity中处理一些逻辑多不科学!浪费代码啊!还要Handler来传递信息,Fuck!就不能在自定义View中一次性搞定吗?答案是肯定的,我们修改下CustomView的代码让其实现Runnable接口,这样就爽多了:

[java] view plaincopyprint?

  1. public class CustomView extends View implements Runnable {
  2. private Paint mPaint;// 画笔
  3. private Context mContext;// 上下文环境引用
  4. private int radiu;// 圆环半径
  5. public CustomView(Context context) {
  6. this(context, null);
  7. }
  8. public CustomView(Context context, AttributeSet attrs) {
  9. super(context, attrs);
  10. mContext = context;
  11. // 初始化画笔
  12. initPaint();
  13. }
  14. /**
  15. * 初始化画笔
  16. */
  17. private void initPaint() {
  18. // 实例化画笔并打开抗锯齿
  19. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  20. /*
  21. * 设置画笔样式为描边,圆环嘛……当然不能填充不然就么意思了
  22. *
  23. * 画笔样式分三种:
  24. * 1.Paint.Style.STROKE:描边
  25. * 2.Paint.Style.FILL_AND_STROKE:描边并填充
  26. * 3.Paint.Style.FILL:填充
  27. */
  28. mPaint.setStyle(Paint.Style.STROKE);
  29. // 设置画笔颜色为浅灰色
  30. mPaint.setColor(Color.LTGRAY);
  31. /*
  32. * 设置描边的粗细,单位:像素px
  33. * 注意:当setStrokeWidth(0)的时候描边宽度并不为0而是只占一个像素
  34. */
  35. mPaint.setStrokeWidth(10);
  36. }
  37. @Override
  38. protected void onDraw(Canvas canvas) {
  39. super.onDraw(canvas);
  40. // 绘制圆环
  41. canvas.drawCircle(MeasureUtil.getScreenSize((Activity) mContext)[0] / 2, MeasureUtil.getScreenSize((Activity) mContext)[1] / 2, radiu, mPaint);
  42. }
  43. @Override
  44. public void run() {
  45. /*
  46. * 确保线程不断执行不断刷新界面
  47. */
  48. while (true) {
  49. try {
  50. /*
  51. * 如果半径小于200则自加否则大于200后重置半径值以实现往复
  52. */
  53. if (radiu <= 200) {
  54. radiu += 10;
  55. // 刷新View
  56. invalidate();
  57. } else {
  58. radiu = 0;
  59. }
  60. // 每执行一次暂停40毫秒
  61. Thread.sleep(40);
  62. } catch (InterruptedException e) {
  63. e.printStackTrace();
  64. }
  65. }
  66. }
  67. }

而我们的Activity呢也能摆脱繁琐的代码逻辑:

[java] view plaincopyprint?

  1. public class MainActivity extends Activity {
  2. private CustomView mCustomView;// 我们的自定义View
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. // 获取控件
  8. mCustomView = (CustomView) findViewById(R.id.main_cv);
  9. /*
  10. * 开线程
  11. */
  12. new Thread(mCustomView).start();
  13. }
  14. }

运行一下看看呗!肏!!!报错了:


Why!因为我们在非UI线程中更新了UI!而在Android中非UI线程是不能直接更新UI的,怎么办?用Handler?NO!Android给我们提供了一个更便捷的方法:postInvalidate();用它替代我们原来的invalidate()即可:

[java] view plaincopyprint?

  1. @Override
  2. public void run() {
  3. /*
  4. * 确保线程不断执行不断刷新界面
  5. */
  6. while (true) {
  7. try {
  8. /*
  9. * 如果半径小于200则自加否则大于200后重置半径值以实现往复
  10. */
  11. if (radiu <= 200) {
  12. radiu += 10;
  13. // 刷新View
  14. postInvalidate();
  15. } else {
  16. radiu = 0;
  17. }
  18. // 每执行一次暂停40毫秒
  19. Thread.sleep(40);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }

运行效果不变。

源码地址:传送门

温馨提示:自定义控件其实很简单系列文章每周一、周四更新一篇~

下集精彩预告:Paint为我们提供了大量的setter方法去设置画笔的属性,而Canvas呢也提供了大量的drawXXX方法去告诉我们能画些什么,那么小伙伴们知道这些方法是怎么用的又能带给我们怎样炫酷的效果呢?锁定本台敬请关注:自定义控件其实很简单1/6

自定义控件其实很简单1/6已更新

时间: 2024-12-08 13:36:39

自定义控件其实很简单1/12的相关文章

自定义控件其实很简单7/12

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 要在数量上统计中国菜的品种,在地域上毫无争议地划分菜系,在今天,是一件几乎不可能完成的事--Cut----抱歉--忘吃药了,再来一遍.如果非要对自定义控件的流程进行一个简单的划分,我会尝试将其分为三大部分:控件的绘制.控件的测量和控件的交互行为.前面我们用了六节的篇幅和一个翻页的例子来对控件的绘制有了一个全新的认识但是我们所做出的所

自定义控件其实很简单5/12

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 最近龙体欠安,很多任务都堆着,虽说如此,依然没有停下学习的步伐,虽然偶尔还会有点头痛,但是孤依旧在学习……自赞一个~ 在1/3中我们结束了全部的Paint方法学习还略带地说了下Matri的简单用法,这两节呢,我们将甩掉第二个陌生又熟悉的情妇:Canvas.Canvas从我们该系列教程的第一节起就嘚啵嘚啵个没完没了,几乎每个View都

自定义控件其实很简单2/3

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 又要开始鸡冻人心的一刻了有木有!有木有鸡冻! = = --通过上一节的讲解呢我们大致对Android测量控件有个初步的了解,而此后呢也有不少盆友Q小窗我问了不少问题,不过其实这些问题大多都不是问题,至于到底是不是问题呢,还要等我研究下究竟可不可以把这些问题归为问题--稍等.我吃个药先.大多数盆友的反应是在对控件测量的具体方法还不是很

自定义控件其实很简单3/4

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 隐约雷鸣 阴霾天空 但盼风雨来 能留你在此 隐约雷鸣 阴霾天空 即使天无雨 我亦留此地 上一节我们细致地.猥琐地.小心翼翼地.犹如丝滑般抚摸.啊不,是讲解了如何去测量一个布局控件,再次强调,如我之前多次强调那样,控件的测量必须要逻辑缜密严谨,尽量少地避免出现较大的逻辑错误.在整个系列撰写的过程中,有N^N个朋友曾多次不间断地小窗我问

自定义控件其实很简单1/4

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 上一回关羽操刀怒砍秦桧子龙拼命相救,岂料刘备这狗贼耍赖以张飞为祭品特殊召唤黑暗大法师消灭了场上所有逗逼,霎时间血流成河,鲜红的血液与冰冷的大地融合交汇在一起焕发出血液的煞气……那么,问题来了,请问这是使用了哪种PorterDuffXfermode? 在上一节的最后一个Example中我们做了一个橡皮擦的View,但是这个View虽然

自定义控件其实很简单1/6

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 上一节我们粗略地讲了下如何去实现我们的View并概述了View形成动画的基本原理,这一节我们紧跟上一节的步伐来深挖如何去绘制更复杂的View! 通过上一节的学习我们了解到什么是画布Canvas什么是画笔Paint,并且学习了如何去设置画笔的属性如何在画布上画一个圆,然而,画笔的属性并非仅仅就设置个颜色.大小那么简单而画布呢肯定也不单

自定义控件其实很简单1/2

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 年关将至事情巨多,最近因为安排蓄谋已已久的旅行事宜很久没更我们的系列教程,约莫着有一个月了,这事情多起来啊闲都闲不下来~~那么我们闲话少说,来看看这一节我们的重点,上一节因为之前从未涉及Canvas的clipXXX方法所以我们优先对其做了一定的介绍并顺带将Path类的方法做了一个小结,如我之前所说Canvas方法可以分为几类,cli

自定义控件其实很简单1/3

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 前几天身子骨出了点小毛病不爽,再加上CSDN抽风也木有更新,现在补上之前漏掉的1/3 上一节结尾的时候我们说到,Paint类中我们还有一个方法没讲 [java] view plaincopyprint? setShader(Shader shader) 这个方法呢其实也没有什么特别的,那么为什么我们要把它单独分离出来讲那么异类呢?难

【结果很简单,过程很艰辛】记阿里云Ons消息队列服务填坑过程

Maybe 这个问题很简单,因为解决方法是非常简单,但填坑过程会把人逼疯,在阿里云ONS工作人员.同事和朋友的协助下,经过一天的调试和瞎捣鼓,终于解决了这个坑,把问题记下来,也许更多人在碰到类似问题的时候,会开放思路.当然不得不说,Ons的.NET接口还很不完善,甚至没有独立在Windos 2008/2012服务器测试过,希望官方加把力. 1.阿里云ONS介绍 ONS(Open Notification Service)即开放消息服务,是基于阿里开源消息中间件MetaQ(RocketMQ)打造的