精华内容
下载资源
问答
  • 人类的智能是如何产生的

    千次阅读 多人点赞 2019-06-18 10:11:05
    智能的人类 ...而人类则不断通过基因突变来进化自身的组织,使得自身能够更加充分与外界接触并由此产生刺激,比如在触觉、视觉、听觉、嗅觉等等方面。更重要的是大脑还具有记忆功能,从而使得人类在与...

     

     

    智能的人类

            人类在外界的影响下进化,又凭借自己的意志来影响外界。
            一亿多年前体重是人类上百倍的恐龙虽然称霸了地球,但它们也仅仅是“低级动物”,并没有任何迹象标明恐龙发展出了高级智能。而人类则不断通过基因突变来进化自身的组织,使得自身能够更加充分与外界接触并由此产生刺激,比如在触觉、视觉、听觉、嗅觉等等方面。更重要的是大脑还具有记忆功能,从而使得人类在与外界环境的接触中知道了“自我”和“外界”,明确了人类个体的各部分与外界的物理边界。
            通过外界的各种刺激,人类大脑不断得到加强,脑容量也越来越大。大脑里面的神经网络变得越来越复杂,其复杂程度甚至比宇宙还复杂。大量的复杂神经网络通过互相通过突触连接在一起,从而才有条件产生了高级智能。

    生命的起源

            在生命刚开始时,没有什么大脑,甚至连神经都没有。远古时期地球没有任何生命,直到后来海洋里面出现核糖核酸(RNA)和蛋白质,从今天来看这些根本不像生命的物质,却是生命最原始的形态;

    展开全文
  • Java AWT/Swing实现规则窗体控件

    千次阅读 多人点赞 2019-05-09 05:27:28
    由于是自学,又是大专,没有科班的基础,所以不是很care算法数据结构,因为Java可以快速作出一个肉眼可以看到的UI,所以我选择了Java而不是C/C++,同时由于MFC这些微软的系统强相关,也就是说,同时放弃了VC++。...

    Oracle裁员,关注了下Oracle,但依旧并不喜欢这家公司,一直觉得它经营不好Java。就好像Microsoft经营不好Linux一样(很奇怪,但总觉得会有这么一天!) 不写Java已经十余年了,但最近还是想起一些点滴。花了一个半的夜晚,完成了此文。

    本文献给为我们送雨伞而把皮鞋?湿了的猛人。


    终于有机会重写这个话题了。我承诺读完本文,对Java如何实现不规则控件将会有非常清晰的认知。

    什么是自省?作为一个做技术的工程师,在以往学习和工作的过程中肯定曾经遇到过超级多的问题,其中有一些是棘手的,很难解决的,最终不了了之的问题。过了些时日以后,重新审视,看看以往的这些疑难问题现在是不是可以解决了,这就是一种自省的态度。即便是对于已经解决的问题,以及已经掌握的理论,重新审视看有没有更优的解法,有没有新的思想,这很必要。

    缘由

    2003年是我接触Java的第一年,2004年是我接触Java的第二年。

    由于是自学Java,又是大专,没有科班的基础,所以最初不是很care算法和数据结构,因为Java可以快速作出一个肉眼可以看到的GUI,所以我选择了Java而不是C/C++,同时由于MFC这些和微软的系统强相关,也就是说,同时放弃了VC++。

    但是直到2006年我都被一个问题所困扰,即:
    如何用Java实现一个不规则的窗体或者控件?

    之所以有这个问题,是因为我那时觉得像豪杰超级解霸这种播放器的 换肤 功能非常牛逼,比较有趣,如果说做GUI,当然不能少了这个。让人惊奇的是,一个播放器界面竟然可以是一个如下般非规则的样式:
    在这里插入图片描述
    这太帅了!我也想用Java做。

    但是Java的界面只能是那种四四方方的样子, 如何能把这种四四方方的界面裁剪成类似上面的样子 ,这是我进入这个这个行业以来 第一个要解决的问题。

    我当时使用的Java SDK版本是J2sdk 1.4.2版本,经典版本。

    2006年12月Java 1.6发布之前,网上根本搜不到答案,我自己也是有时间就折腾,几乎把Swing/AWT的API都撸遍了,也没有结果。最终,我是通过Eclipse的SWT解决了问题。

    Eclipse的SWT让很多有同样需求的人眼前一亮。我的第一份工作就是基于SWT开发的。

    是的Eclipse SWT有可以切割Java窗体和控件的API,具体例子我就不说了,网上一搜一大片。问题是,我想用标准的J2SE来做这些啊!

    后来,我是通过JNI的方式,调用自己写的本地C/C++库来完成切割窗体的。我也是因为要写这个本地C代码,从而 顺便 开始学习C语言编程。

    这种功能用本地代码写是很容易的,比如在微软的Windows系统,就可以用Windows API来做,而在Linux系统,则可以用基于X11的Gnome,KDE来完成,毕竟本地的GUI库必然要支持这些功能。

    然而Java作为其承诺的 一次编写,到处运行 的跨平台语言,不可能用同一个接口去兼容不同操作系统平台的不同GUI效果,所以Java也就根本就没有提供这样的功能。

    Java的公共API只能完成所有操作系统平台都支持的操作,并且还要保证达到同样的效果,至少不能差太多。我们知道Windows和UNIX/Linux的X11完全就不是一个东西,甚至都不是一类东西…

    Java实现不规则控件这件事

    这里还需要再PS一下。

    关于Java实现不规则控件这件事,很多人纠结过,实际上Java作为承诺的跨平台语言,它的优势根本就不在开发GUI程序,GUI这种是严重依赖本地系统的。

    虽然Eclipse的SWT曾经给人眼前一亮的的感觉,但它依然不是万金油。

    SWT不需要显式的进行JNI操作,不需要引入额外的包,处在集成开发环境Eclipse本身,直接拿来就能用。但是它只是将JNI隐藏了。说白了,SWT之所以可以做出和本地系统几乎一样的GUI控件,它所做的就是 将Win32 API,X11 API这种本地API封装成了Java的API ,是的,说白了就是一层封装,没什么大不了的。

    在Java SE 1.6之前,Java AWT/Swing要想做不规则控件,需要你自己去折腾JNI,而SWT替你把这一切封装好了,这才是它的唯一优势。

    这不,Java SE 1.6出来后,SWT的优势就不在了。因为它在Java内部做了同样的事情。


    使用Java SE 1.6如何实现不规则窗口

    到了2006年12月份,Sun放出了Java SE 1.6,带来了福音!

    Java SE 1.6提供的 com.sun.awt.AWTUtilities 和Eclipse SWT一样,封装了JNI操作,让程序员可以直接使用,就像不知道JNI这回事一样。

    在这之后,当我们再次搜索 如何用Java实现不规则窗体 这个话题时,答案就有了,但是基本都是同一个答案:
    通过com.sun.awt.AWTUtilities提供的方法来完成!

    比如:

    com.sun.awt.AWTUtilities.setWindowShape(Window window, Shape shape);
    

    只需要初始化一个Shape对象,就能实现一个任意形状的窗口,我以圆形窗口为例:

    import java.awt.*;
    import javax.swing.*;
    import java.awt.geom.Ellipse2D;
    
    public class CircleFrame extends JFrame {
    	public CircleFrame () {
    		super("Triangle Frame");
    		setSize(500,500);
    		setUndecorated(true);
    	}
    
        public static void main(String[] args) {
    	CircleFrame frame = new CircleFrame();
    	frame.setVisible(true);
    	com.sun.awt.AWTUtilities.setWindowShape(frame, new  Ellipse2D.Double(0, 0, 500, 500));
        }
    
    }
    

    效果如下:
    在这里插入图片描述
    但是这明显不是我想要的,折腾了小两年,最后新版本提供了一行代码搞定,感觉受到了伤害。并且,在编译CircleFrame的时候,提示如下:

    root@name-VirtualBox:~# javac CircleFrame.java
    CircleFrame.java:16: warning: AWTUtilities is internal proprietary API and may be removed in a future release
    	com.sun.awt.AWTUtilities.setWindowShape(frame, new  Ellipse2D.Double(0, 0, 500, 500));
    	           ^
    1 warning
    

    但这种API是完全无法把控的,明显这种API使用JNI的方式实现特定的功能,严重依赖了平台,并且API本身提供方拥有最终解释权,换句话说,使用这种API是危险⚠️的,搞不好哪天就废弃或者改变了!毕竟com.sun的包嘛…不稳定,现如今这公司都不在了,唏嘘。


    正确的做法,当然是 不依赖API,所有事情自己做了。

    Java AWT 和 Java Swing


    当初在学习Java的时候,了解到Java提供了两种GUI API:

    • Java AWT
    • Java Swing

    我当时特意了解到这两者的本质不同:

    • Java AWT
      调用操作系统GUI平台原生的操作来生成控件,换句话说,AWT的容器,控件只是操作系统GUI容器,控件在JVM里面的一个影子,AWT操作的是系统原生的容器,控件,它们称作AWT组件的 本地同位体
      优势:效率高。
      劣势:依赖底层,不同系统的观感会不同。
    • Java Swing
      除了顶层容器,其它的控件都是不依赖底层操作系统GUI平台的,换句话说,Swing的容器和控件是JVM自己画出来的。
      优势:跨平台,因为画法是一样的。
      劣势:效率低,慢。

    不管怎样,本文将用上述AWT和Swing分别采用的两种方法,即调用底层的GUI操作以及自己画,来实现不规则的窗体和控件:

    • 不规则窗体将实现一个三角形的窗体。
    • 不规则控件将实现一个三角形按钮安装在上述三角形窗体中。

    Java AWT实现不规则控件

    先看AWT的Frame如何来做。先看Java代码:

    import java.awt.*;
    import java.awt.event.*;
    public class AWTDemo extends Frame {
    
    	// 三角形按钮btn
    	Button btn;
    	//本地方法,用来将窗口和按钮切割成三角形
    	private native void cutWindow(String title);
    	static {
    		System.loadLibrary("cutWindow");
    	}
    
    	public AWTDemo(String title, String btn_title) {
    		super(title);
    		setSize(500,500);
    		// 果断去掉窗口上方的标题栏
    		setUndecorated(true);
    
    		// 生成一个四四方方的按钮对象
    		btn = new Button(btn_title);
    		// 按钮事件处理,第一次按下换个字,第二次按下退出
    		btn.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent e) {
    				if (e.getSource() instanceof Button) {
    					Button bu = (Button)e.getSource();
    					if (bu.getLabel().equals("AWT button"))
    						bu.setLabel("To Exit");
    					else
    						System.exit(0);
    
    				}
    			}
    		});
    		// 黄色按钮
    		btn.setBackground(Color.YELLOW);
    		add(btn);
    	}
    
    	public static void main(String args[]) {
    		String title = "abc";
    		String btnpeer = "sun-awt-X11-XButtonPeer";
    		String btn_title = "AWT button";
    		// 实例化一个Frame对象
    		AWTDemo frame = new AWTDemo(title, btn_title);
    		// 显示它
    		frame.setVisible(true);
    		try {
    			// 至此为止一切都是四四方方的
    			Thread.sleep(2000);
    			frame.cutWindow(title);
    			// 窗口成了三角形
    			Thread.sleep(2000);
    			frame.cutWindow(btnpeer);
    			// 按钮也成了三角形
    		} catch (Exception e) {
    		}
    	}
    }
    

    先看效果,再说JNI代码:
    在这里插入图片描述
    点击一下三角形的黄色按钮:
    在这里插入图片描述

    这是如何实现的呢?嗯,这是调用了本地方法cutWindow,要理解cutWindow的逻辑,这里简单说点X Window的东西,如果是在Windows平台做实验,那么就需要了解下Windows API了。

    X Window系统是一个超级复杂的C/S模式的GUI系统,其实它的架构是比较简单的,类似我们远程登录的Telnet/SSH这些。我来类比一下:

    • Telnet/SSH
      你在 负责输入输出显示的客户端 上用 键盘 敲入字符以及控制键,字符序列传输至Telnet/SSH服务器处理,然后服务器回复另一字符序列,Telnet/SSH客户端收到后解释它们,以便按照协议规范在终端 回显字符,最终生成一个CLI。
    • X window
      你在 负责输入/输出和显示的X服务器 上用 键盘,鼠标 执行一系列操作,这些操作被X服务器传输至X客户端处理,然后X客户端按照X协议规范回复数据序列,X服务器收到后解释它们,以便按照X协议规范决定 在X服务器的Display 显示器哪个像素绘制什么颜色,最终生成一个GUI。

    它们非常类似,简直是一个逻辑。唯一不同的是,X window貌似和Telnet/SSH的客户端/服务器是反着的。事实上理解起来很简单,毕竟X服务器就是用来显示的嘛。

    X Window系统封装了一系列的容器,控件,比如画布,按钮,复选框…X客户端可以以这些封装好的容器,控件为单位进行操作。

    深入的X window本文不谈,现在回到Java AWT程序。

    刚才说了,每一个AWT容器或者控件,都只是操作系统本地GUI的一个容器或者控件的影子,那么 如果我们想操作这个AWT对象,就要找到它的本地对象!

    我们用X系统的实现之一X11提供的 xwininfo 命令来探究一下内外。关于xwininfo,了解下面的就够了:

    0.你可以把display 理解成一个显示器加上一套鼠标/键盘的套件。
    1. X系统一个Display对象的所有窗体控件均按照Tree形式嵌套组织,比如一个Frame上的Button就是该Frame的child,这与Linux的进程组织非常类似。
    2. xwininfo可以枚举系统当前Display上的所有的窗体,并且给出其组织关系。

    简单修改一下代码,做一个 AWTDemo2.java ,去掉那些本地方法调用,仅仅保留主干:

    import java.awt.*;
    public class AWTDemo2 extends Frame {
    
    	Button btn;
    
    	public AWTDemo2(String title, String btn_title) {
    		super(title);
    		setSize(500,500);
    		setUndecorated(true);
    
    		btn = new Button(btn_title);
    		add(btn);
    	}
    
    	public static void main(String args[]) {
    		String title = "abc";
    		String btn_title = "AWT button";
    		AWTDemo2 frame = new AWTDemo2(title, btn_title);
    		frame.setVisible(true);
    	}
    }
    

    先执行 java AWTDemo2 打开AWT Frame界面,然后我们通过 xwininfo 看看能不能找到我们的AWT对象。

    root@name-VirtualBox:~# xwininfo -tree -root
    	...
               # 下面的Frame就是我们的AWT Frame
         0x1c00007 "abc": ("sun-awt-X11-XFramePeer" "AWTDemo2")  500x500+67+27  +67+27
            2 children:
            0x1c0001f "FocusProxy": ("Focus-Proxy-Window" "FocusProxy")  1x1+-1+-1  +66+26
            0x1c0001c "Content window": ("sun-awt-X11-XContentWindow" "AWTDemo2")  500x500+0+0  +67+27
               1 child:# 下面的这个child就是我们的AWT Button
               0x1c00020 "sun-awt-X11-XButtonPeer": ("sun-awt-X11-XButtonPeer" "AWTDemo")  500x500+0+0  +67+27
         ...
    

    果真是找到了:

    0x1c00007 "abc": ("sun-awt-X11-XFramePeer" "AWTDemo2")  500x500+67+27  +67+27
    0x1c00020 "sun-awt-X11-XButtonPeer": ("sun-awt-X11-XButtonPeer" "AWTDemo2")  500x500+0+0  +67+27
    

    Java代码里没有任何与外界的交互,但是X系统里还是找到了。所以说:
    Frame/Button这种AWT组件对于X系统是可见的,同样在Windows系统上,它也是可见的!

    如果我们换成Swing控件呢?相同的布局,只是将Frame换成JFrame,将Button换成JButton,如何呢?让我们试一下:

    import javax.swing.*;
    
    public class SwingDemo2 extends JFrame {
    
    	JButton btn;
    
    	public SwingDemo2(String title) {
    		super(title);
    		setSize(500,500);
    		setUndecorated(true);
    
    		btn = new JButton("Swing button");
    		add(btn);
    	}
    
    	public static void main(String args[]) {
    		String title = "abc";
    		SwingDemo2 demo = new SwingDemo2(title);
    		demo.setVisible(true);
    	}
    }
    

    用java SwingDemo2执行的同时,xwininfo的结果如何呢?看一下:

         0x2000007 "abc": ("sun-awt-X11-XFramePeer" "SwingDemo2")  500x500+67+27  +67+27
            2 children:
            0x200001f "FocusProxy": ("Focus-Proxy-Window" "FocusProxy")  1x1+-1+-1  +66+26
            0x200001c "Content window": ("sun-awt-X11-XContentWindow" "SwingDemo2")  500x500+0+0  +67+27
    

    Content window不再有Button这个child了!那么这个JButton到底在哪里呢?

    JButton对于X系统是不可见的,同样在Windows系统上,它也是不可见的!Java Swing除了顶层容器,其它都是自己在JVM里画大的,JVM系统外不可见!

    这下就清晰多了!我们通过xwininfo彻底理解了AWT和Swing到底区别在哪里了!


    是时候上JNI调用的本地代码了!这是用C写成的,基于X11编写。

    所谓的 本地代码 ,顾名思义就是跨平台解释执行的Java JVM之外的不受JVM控制的代码,脱离Java规范的约束 。这种支持在Java规范里叫做JNI。类似内联汇编对C语言的降维打击(内联汇编不受外部C语言规范的约束!)一样,本地代码也能对Java代码实施降维打击。所以如果你写了本地代码,你一定要知道你在干什么,通过Java的调试方法是无法知道本地代码的细节的!

    接下来,若要Java代码和本地代码对接起来,需要一个 接口 ,该接口就是通过 javah AWTDeom 生成的一个 AWTDemo.h 的C/C++头文件:

    #include <jni.h>
    
    JNIEXPORT void JNICALL Java_AWTDemo_cutWindow
      (JNIEnv *, jobject, jstring);
    

    接下来就是实现 Java_AWTDemo_cutWindow 函数了。

    JNI规范要求单独做一个动态连接库,在Linux系统,就是一个叫做 libAWTDemo.so 的动态库文件,注意,文件名一定要有 lib 前缀,因为Linux加载器就是这样加载库的。

    我下面直接给出带有注释的 cutWindow.c 文件:

    #include <X11/Xos.h>
    // 这个shape extension非常重要
    #include <X11/extensions/shape.h>
    #include <X11/Xlib.h>
    #include <X11/Xutil.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include "AWTDemo.h"
    
    // 该函数执行根据名字查找组件句柄的逻辑,不解释,详情参见X11编程文档。
    // 如果是Windows系统,那么就是另一种方式获取组件的句柄了。
    // 注意:这个例子并没有借助使用AWT本地同位体的概念,所以需要手工自己查找
    Window findWindowByName(Display *dpy, Window parent, char *name)
    {
    	unsigned int num, i;
    	Window *subwindow, dummy;
    	Window result = 0;
    	char *title;
    
    	// 按照名称查找,如果名称匹配,就返回当前的组件。
    	XFetchName(dpy, parent, &title);
        	if (title && !strcmp(name, title)) {
    		return parent;
    	}
    
    	// 否则递归查找其所有的children
    	if (!XQueryTree(dpy, parent, &dummy, &dummy, &subwindow, &num))
    		return 0;
    
    	for (i = 0; i < num; i ++)  {
    		result = findWindowByName(dpy, subwindow[i], name);
    		if (result) {
    			goto free_and_ret;;
    		}
    	}
    
    free_and_ret:
    	if (subwindow)
    		XFree ((char *)subwindow);
    	return result;
    }
    
    JNIEXPORT void JNICALL Java_AWTDemo_cutWindow (JNIEnv *jenv, jobject o1, jstring title)
    {
        Window window;
        Display *dpy;
        Region region;
        XPoint p[3];
        XPoint pbtn[3];
    
       unsigned char *name = NULL;
       // 首先需要把Frame或者Button的title通过JNI传递到C/C++代码里
       name = (*jenv)->GetStringUTFChars(jenv, title, 0);
    
        dpy = XOpenDisplay(":0");
        if (!dpy) {
            exit(1);
        }
    
    	// 按照X11的API文档查找名字为name的容器或者控件,也就是做类似于xwininfo命令做的事情
    	// 这实际上是一个枚举所有组件的过程。
    	// 事实上,可以通过AWT本地同位体将window句柄通过参数传递过来的。为了不引入同位体的概念,暂时不考虑。
        window = findWindowByName(dpy, DefaultRootWindow(dpy), name);
        if (!window) {
            exit(1);
        }
    	// Frame的三角形外形三点定义
        p[0].x = 250; p[0].y = 125;
        p[1].x = 450; p[1].y = 425;
        p[2].x = 50; p[2].y = 425;
    	
    	// Button的三角形外形三点定义
        pbtn[0].x = 250; pbtn[0].y = 190;
        pbtn[1].x = 320; pbtn[1].y = 290;
        pbtn[2].x = 180; pbtn[2].y = 290;
    
        if (!strcmp(name, "abc")) { 
        	// Frame的区域
        	region = XPolygonRegion(p, 3, EvenOddRule);
        } else {
        	// Button的区域
        	region = XPolygonRegion(pbtn, 3, EvenOddRule);
        }
        // 设置组件的外形
        XShapeCombineRegion(dpy, window, ShapeBounding, 0, 0, region, ShapeSet);
    	// close该Display时会flush/repaint组件。
        XCloseDisplay(dpy);
    }
    

    代码写好了,接下来看看如何将其做成动态链接库。JNI的规范里对这个步骤也有说明,我下面直接给出:

    root@name-VirtualBox:~# gcc -fPIC -c -I"/usr/lib/jvm/java-8-openjdk-amd64/include" -I"/usr/lib/jvm/java-8-openjdk-amd64/include/linux" cutWindow.c -o cutWindow.o
    root@name-VirtualBox:~# gcc -shared -o libcutWindow.so cutWindow.o
    

    OK!现如今,该例子的AWTDemo.class运行所需的所有依赖都已经准备好了,我们希望JVM在当前路径下去加载 libcutWindow.so

    root@name-VirtualBox:~# javac AWTDemo.java
    root@name-VirtualBox:~# java -Djava.library.path=. AWTDemo
    

    执行起来就是上面图示的效果!

    本地代码是通过控件的Title来查找本地同位体的,如果是Windows平台,查找组件的操作要简单的多,只有有API可用:

    HWND FindWindowA(
      LPCSTR lpClassName,
      LPCSTR lpWindowName
    );
    

    详情参见:https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-findwindowa
    【PS:我十多年前的不规则窗口版本就是在Windows平台做的,用的就是上面这个FindWindowA】

    事实上可以通过参数将本地同位体的控件句柄直接从Java代码传递到本地代码中的,这个后面再说。

    上述的本地代码动态库是基于X11实现的,我本来想用GTK,KDE来做例子,然而这些并不是原汁原味的,会引入很多额外的东西,比如看了GTK代码后,就会纠结于GTK是怎么回事…我们是在做Java不规则控件,而不是在学习GTK。所以越底层越好。

    在实现中,代码中使用了X11的Shape extension,这是一个扩展,并不包含在原生的X11中,基于它可以实现不规则的窗口和控件,它非常复杂,详细参见:
    https://en.wikipedia.org/wiki/Shape_extension
    https://www.x.org/releases/X11R7.7/doc/xextproto/shape.html
    本文不赘述,还是那句话,Java以外的,点到为止。

    AWT版本的自定义不规则组件的介绍就是这么多。接下来看看Swing的版本。


    Java Swing实现不规则控件

    前面说了,Java Swing除了顶层的容器,其它的控件组件都是自己画出来的,那么要实现不规则的JFrame,作为顶层容器的JFrame,由于其依然是映射到本地的真实窗口,所以依然是上面的JNI的方法去切割。

    重点是JButton的不规则化如何来做。换句话说,如何把它 画出来

    JButton在本地的GUI系统里根本就找不到,就像上面xwininfo展示的结果那样,怎么办?

    画一个就是了!

    如何来画呢?我还是直接给出完整的代码吧, SwingDemo.java 列如下:

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    
    // 从JButton派生一个子类,实现三角形JButton
    class triangleButton extends JButton {
    	// 此Button的三角形区域
    	Polygon triangle;
    	triangleButton(String title, int x[], int y[]) {
    		super(title);
    		// 根据参数初始化三角形区域
    		triangle = new Polygon(x, y, 3);
    		// 不显示按钮边框!完全由我们自己绘制的三角形来决定
    		setBorderPainted(false);
    		setContentAreaFilled(false);
    	}
    
    	// 重写paintComponent方法,区分按钮按下和释放时显示不同的颜色,显得逼真!
    	public void paintComponent(Graphics g) {
    		if (this.getModel().isPressed()) {
    			g.setColor(Color.BLACK);
    		} else {
    			g.setColor(Color.LIGHT_GRAY);
    		}
    		// 用不同的颜色画同一个三角形
    		// 如果能细化边缘凸凹高亮,那就更美观了!但是那样代码太长。
    		g.fillPolygon(triangle);
    		super.paintComponent(g);
    	}
    
    	// 重写contains,判断鼠标当前的焦点是不是属于该按钮的区域范围内。
    	// 这是Java Swing的创举,委托UI管理器来实现范围限定约束的托管!
    	public boolean contains(int x, int y) {
    		if (triangle.contains(x, y))
    			return true;
    		return false;
    	}
    }
    
    public class SwingDemo extends JFrame {
    	JButton btn;
    	// 定义按钮三角形的三点
    	int button_x[] = {250, 315, 185};
    	int button_y[] = {327, 227, 227};
    
    	// 用户切割JFrame的本地方法
    	private native void cutWindow(String title);
    	static {
    		System.loadLibrary("cutWindow");
    	}
    
    	public SwingDemo(String title) {
    		super(title);
    		setSize(500,500);
    		setUndecorated(true);
    
    		btn = new triangleButton("Swing button", button_x, button_y);
    		btn.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent e) {
    				if (e.getSource() instanceof triangleButton) {
    					triangleButton bu = (triangleButton)e.getSource();
    					if (bu.getText().equals("Swing button"))
    						bu.setText("To Exit");
    					else
    						System.exit(0);
    
    				}
    			}
    		});
    		add(btn);
    	}
    
    	public static void main(String args[]) {
    		String title = "abc";
    		SwingDemo demo = new SwingDemo(title);
    		demo.setVisible(true);
    		// 切割JFrame为三角形,同AWTDemo
    		demo.cutWindow(title);
    	}
    }
    

    当执行 java SwingDemo 时,展示一下效果:
    在这里插入图片描述
    点击一下试试看,点击的瞬间,三角形变成了黑色,这个太快了,没法截屏,但是松开鼠标后,按钮的字变了:在这里插入图片描述
    再点一下,如代码逻辑所示,退出。

    别看这是Swing画出来的,但 这是真正的三角形按钮,不仅仅是视觉上的,你只有点击那个小三角形区域,才会有效果,别的区域是不行的。

    Swing版本的不规则窗口和按钮总结下来就是:

    • Swing窗口依然采用JNI的方式在X11 API实现的动态库里进行切割;
    • Swing窗口上的按钮,自己编码绘制完成不规则化。

    纯Java的完整例子(?窗口,?按钮)

    能不能不用JNI?

    可以的。

    不用JNI实现窗口切割,不用说也能猜到原理,Java的工具包自己帮你JNI了呗。

    当我们已经理解了上述细节后,表示可以完全hold住java的api后,便可以自由使用一开始我并不提倡的Java自带的com.sun.awt.AWTUtilities了。

    com.sun.awt.AWTUtilities ,它就是用JNI实现窗口切割的。那么我便使用它直接来完成一个不规则窗口,不然我还要自己写本地代码,我又不想深入去学习X11。那么OK,我直接用AWTUtilities了!

    这个例子中,我使用一双大皮鞋图片作为窗口,一双小皮鞋图片作为按钮,窗口的形状是大皮鞋,按钮的形状是小皮鞋,皮鞋是不规则的,所以窗口和控件是不规则的。

    代码只有一个文件, SkinShoeDemo.java ,列如下:

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.geom.*;
    import java.awt.image.*;
    import javax.imageio.ImageIO;
    import java.net.URL;
    import java.io.*;
    
    // 实现不规则的小皮鞋按钮
    class LittleSkinShoeButton extends JButton {
    	ImageIcon img;
    	BufferedImage imgpixe;
    	LittleSkinShoeButton(ImageIcon img, String icon){
    		super();
    		this.img = img;
    		setBorderPainted(false);
    		setContentAreaFilled(false);
    		setSize(img.getIconWidth(),img.getIconHeight());
    		try{
    			// 需要完整的图片像素来确定哪些像素属于皮鞋,哪些像素不属于皮鞋,这个涉及到“抠图”,后面会讲
    			imgpixe = ImageIO.read(SkinShoeDemo.class.getResource(icon));
    		} catch (Exception e){
    			System.exit(0);
    		}
    	}
    
    	// 当鼠标点击小皮鞋按钮“皮鞋区域”内部时,要展示出被点击的样子来,向下凹陷一下。
    	public void paintComponent(Graphics g){
    		if(this.getModel().isPressed()){
    			// 向下凹陷5个像素,向右平移5个像素,感觉像是被点击了。
    			g.drawImage(img.getImage(), 5, 5, this);
    		}else{
    			// 不被点击时,保持在原来的位置。
    			g.drawImage(img.getImage(),0,0,this);
    		}
    	}
    
    	// 重写contains方法。
    	public boolean contains(int x,int y){
    		int rgb,alpha;
    		try{
    			rgb = imgpixe.getRGB(x,y);
    			// 获取像素的alpha值,如果是被“抠去”的,就不属于皮鞋内部。
    			// 当初制作皮鞋图的时候,不是皮鞋范围的都“抠掉成透明”的了。
    			alpha = (rgb>>24) & 0xFF;
    			if(alpha != 0){
    				System.out.println("属于小皮鞋范围,点击有效:[" + x + "," + y );
    				return true;
    			}
    		}catch(ArrayIndexOutOfBoundsException e){
    		}
    		System.out.println("不属于小皮鞋范围,点击无效:[" + x + "," + y );
    		return false;
    	}
    }
    
    // 实现JFrame的背景图片,即一双大皮鞋。
    class SkinShoePanel extends JComponent {
        private Image image;
        public SkinShoePanel(Image image) {
            this.image = image;
        }
    	
    	// 重绘,也就是画大皮鞋
        protected void paintComponent(Graphics g) {
            g.drawImage(image, 0, 0, this);
        }
    }
    
    // 大皮鞋窗口主类
    public class SkinShoeDemo extends JFrame {
    
    	JButton btn;
    
    	private JPanel pixieimgPanel;
    	public SkinShoeDemo(BufferedImage img, ImageIcon btn_img, String icon) {
    		super("SkinShoe");
    		setSize(img.getWidth(), img.getHeight());
    
    		btn = new LittleSkinShoeButton(btn_img, icon);
    		btn.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent e) {
    				if (e.getSource() instanceof LittleSkinShoeButton) {
    					// TODO
    				}
    			}
    		});
    		this.setContentPane(new SkinShoePanel(img));
    		btn.setLocation(280, 280);
    		this.setUndecorated(true); // 这个必须调用
    		// 按钮添加在画布上。
    		this.getContentPane().add(btn);
    	}
    
    	public Shape getSkinshoeShape(BufferedImage pixieimg) {
    		Area pixie = new Area();
    		int width;
    		int height;
    		int x,y, temp; // temp起到了优化的作用,批量添加区域
    		int rgb, alpha;
    
    		width = pixieimg.getWidth();
    		height = pixieimg.getHeight();
    		for (y = 0; y < height; y++)  {
    			temp = 0;
    			for (x = 0; x < width; x++) {
    				rgb = pixieimg.getRGB(x, y);
    		 		alpha = (rgb>>24)&0xFF;
    		 		/* 下面的if-else语句的含义就是下面注释版的if语句的优化版,即:
    		 		 * 将“不透明”的像素拼接成一个“区域”。
    		 		 * 所有“不透明”的像素就是在抠图时没有被抠掉的像素。
    		 		 * 如果是使用下面注释版本的话,要一个像素一个像素添加,那么大一双皮鞋,要两分钟!!
    		 		 * if (alpha != 0) {
    		 		 *		Rectangle temppixe = new Rectangle(x, y, 1, 1);
    				 *		pixie.add(new Area(temppixe));
    		 		 * }
    		 		 */
    		 		if(alpha != 0) {
    					if (temp == 0)
    						temp = x;
    				} else {
    					if (temp != 0) {
    						Rectangle temppixe = new Rectangle(temp, y, x - temp, 1);
    						pixie.add(new Area(temppixe));
    						temp = 0;
    					}
    				}
    			}
    		}
    		return pixie;
    	}
    
    	public static void main(String args[]) {
    		// 22.png就是哪个小皮鞋
    		String pixieicon = "22.png";
    		// 11.png是那双大皮鞋
    		File file = new File("11.png");
    		BufferedImage pixieImage = null;
    		ImageIcon button_icon = null;
    		try {
    			pixieImage = ImageIO.read(file);
    			button_icon = new ImageIcon(SkinShoeDemo.class.getResource(pixieicon));
    		} catch (Exception e) {
    		}
    		SkinShoeDemo demo = new SkinShoeDemo(pixieImage, button_icon, pixieicon);
    		demo.setVisible(true);
    		// 切割吧!
    		com.sun.awt.AWTUtilities.setWindowShape(demo, demo.getSkinshoeShape(pixieImage));
    	}
    }
    

    看看效果呗:
    在这里插入图片描述
    只要不是大皮鞋区域,鼠标点击都是透明的:
    在这里插入图片描述

    这是真正意义的 不规则窗口


    关于抠图

    我现在简单说一下这个效果的前置要求,必须对两双皮鞋的图片进行 抠图 预处理。

    先来看一下定义窗口外观的大皮鞋原图:
    在这里插入图片描述
    显然,按照常规,这是一张 四四方方 的图,矩形的。也就是说,这张图包含了 皮鞋前景白色背景 两个部分。如果拿这个图做我们的不规则窗口的11.png,将会是失败的,因为那个白色的背景并不会由于其Alpha值(含义马上会讲,这里只是代码的观感)等于0而被排除在有效范围之外:

    rgb = pixieimg.getRGB(x, y);
    alpha = (rgb>>24)&0xFF;
    

    Alpha的值等于0而被放逐在我们需要的有效区域以外,但是原图的矩形区域内包括背景在内的所有像素的Alpha值均不为0,为什么?

    我们单看白色的背景,换句话讲,白色背景的特性如下:

    • 颜色:有颜色,白色
    • 透明度:不透明(是的,它并不透明!)

    一张图片的每一个像素,均会包含上述两个性质,一个颜色,一个透明度。这两个性质被保存在一个4字节的数字里,其中的颜色占据3个字节,分别保存三原色的各自分量,余下的1个字节保存透明度信息,很巧妙。

    最常用的定义,4个字节定义如下:

    typedef struct RGB_info {
    u8 rgbBlue; 	// 蓝色分量
    u8 rgbGreen; 	// 绿色分量
    u8 rgbRed; 		// 红色分量
    u8 rgbAlpha; 	// 透明度
    } RGB。
    

    这个叫做 带Alpha通道的RGB24 标准。

    很少有图片在生成的时候就会设置某些像素的Alpha为完全透明,所以这个需要我们自己来 ,把背景抠掉即可,所谓的抠掉,就是将背景像素的Alpha值设置为0。

    一般抠图程序很容易写,但是比较麻烦,原理很简单, 只要能通过RGB颜色信息识别出前景和背景,那么把背景像素的Alpha设置为0即可。

    问题是如何识别。在我们这个例子中,很简单,把白颜色的给设置透明即可,但是有些像素并不是纯白,而是 接近白,灰白… 这让程序去定义一个范围吗?似乎这个范围如何来界定又是一个问题,最终就陷入了AI,哦,很高大上的领域!

    还是用肉眼识别吧,一切抠图者自己来决定。这就要使用抠图工具了。这种工具一般让你自己用圈点指针自己标示那些部分要设置为透明,比如套索,画笔之类的。

    昨晚让老婆用美图秀秀给帮忙把皮鞋前景给抠出来,但是没有成功,后来我找了一个在线的工具:
    https://ue.818ps.com/clip/
    在这里插入图片描述
    还算挺方便的。反正也只是用一次,足够了。

    最终把那双要做按钮的小皮鞋也完成了抠图:
    在这里插入图片描述
    用这两张图就可以制作不规则窗口和不规则按钮了。

    最后让我们的SkinShoeDemo换一张图,看看效果:
    在这里插入图片描述
    给包括小小在内的演示了这个之后,都说好。


    X11的畅想

    本来我是想用JNI调用X11实现的动态库来实现这个 大皮鞋?窗口 的,但是失败了。我一直以为这是很容易成功的。

    当初既然我可以用三个点来定义一个三角形,我就自然而然想到可以用 皮鞋轮廓的N个像素点 来定义一个N边形来 模拟大皮鞋?围绕着的区域 ,N是多少呢?就看大皮鞋图案的外轮廓有多少个点了。这个想法很合理。

    首先我要先得到大皮鞋的轮廓。

    我用下面的代码获得,命名为Outline.java:

    import java.awt.image.*;
    import java.io.*;
    import javax.imageio.ImageIO;
    
    public class Outline {
    	static File src = null; 
    	static File dst = null;
    	static BufferedImage img = null;
    	static BufferedImage outline = null;
    
    	public static void main(String[] args) throws IOException {
    		int i, j, width, height;
    		int rgb, rgb1, rgb2, a1, a2, a3;
    		src = new File(args[0]);
    		dst = new File("outline_" + args[0]);
    		img = ImageIO.read(src);
    		width = img.getWidth();
    		height = img.getHeight();
    
    		outline = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    		// 纵向扫描
    		for (i = 1; i < width - 1; i++) {
    			for (j = 1; j < height-1; j++) {
    				// 捕捉变化
    				rgb = img.getRGB(i, j);
    				rgb1 = img.getRGB(i + 1, j);
    				rgb2 = img.getRGB(i - 1, j);
    				a1 = (rgb>>24)&0xFF;
    				a2 = (rgb1>>24)&0xFF;
    				a3 = (rgb2>>24)&0xFF;
    				if(a1 != 0 && (a2 == 0 || a3 == 0)) {
    					// 为了轮廓线清晰,特使用白色来描绘“边界像素周边的四个点”
    					rgb |= 0xffffffff;
    					outline.setRGB(i, j, rgb);
    					outline.setRGB(i, j - 1, rgb);
    					outline.setRGB(i, j + 1, rgb);
    					outline.setRGB(i + 1, j, rgb);
    					outline.setRGB(i - 1, j, rgb);
    				}
    			}
    		}
    
    		// 横向扫描
    		for (i = 1; i < height - 1; i++) {
    			for( j = 1; j < width-1; j++) {
    				// 捕捉变化
    				rgb = img.getRGB(i, j);
    				rgb1 = img.getRGB(i, j + 1);
    				rgb2 = img.getRGB(i, j - 1);
    				a1 = (rgb>>24)&0xFF;
    				a2 = (rgb1>>24)&0xFF;
    				a3 = (rgb2>>24)&0xFF;
    				if (a1 != 0 && (a2 == 0|| a3 == 0)) {
    					// 为了轮廓线清晰,特使用白色来描绘“边界像素周边的四个点”
    					rgb |= 0xffffffff;
    					outline.setRGB(i, j, rgb);
    					outline.setRGB(i, j + 1, rgb);
    					outline.setRGB(i, j - 1, rgb);
    					outline.setRGB(i + 1, j, rgb);
    					outline.setRGB(i - 1, j, rgb);
    				}
    			}
    		}
    
    		ImageIO.write(outline, "png", dst);
    	}
    }
    

    以上这个程序,当我用大皮鞋图片的名称 11.png 作为参数执行时,会输出一张轮廓图片 outline_11.png ,输出图片效果如下图所示:
    在这里插入图片描述
    嗯,是一双皮鞋?!!

    我用这个轮廓干什么呢?我要得到一个数组啊。于是我在上述代码的 outline.setRGB 处打印一个序列:

    int idx = 0;
    ...
    System.out.println("outl[" + idx + "].x=" + i + "; out[" + idx + "].y=" + j + ";");
    idx ++;
    

    打印的结果就是:

    root@name-VirtualBox:~# java Outline 11.png |more
    outl[0].x=29; out[0].y=722;
    outl[1].x=29; out[1].y=723;
    outl[2].x=29; out[2].y=724;
    outl[3].x=29; out[3].y=725;
    outl[4].x=29; out[4].y=726;
    outl[5].x=29; out[5].y=728;
    outl[6].x=29; out[6].y=729;
    outl[7].x=29; out[7].y=730;
    outl[8].x=29; out[8].y=731;
    outl[9].x=29; out[9].y=732;
    ...
    outl[1567].x=772; out[1567].y=293;
    

    我将这个打印结果重定向到一个文件中,然后将其include到JNI的本地代码。

    我的意图是希望这些 XPoint 可以生成一个 1568边形! 我希望它们可以定一个Region,然后让X11 shape去框定窗口的裁剪范围:

    outl[9].x=29; out[9].y=732;
    ...
    outl[1567].x=772; out[1567].y=293;
    	
    region = XPolygonRegion(outl, 1568, EvenOddRule);
    XShapeCombineRegion(dpy, window, ShapeBounding, 0, 0, region, ShapeSet);
    

    初看这些操作,和 Java com.sun.awt.AWTUtilities.setWindowShape 的操作几乎是一样的,定义一个Region而已,我用1568边形来框定这个窗口的范围,可行啊!

    然而事与愿违!

    问题出在了 如何画N边形 这件事上。我们希望系统会 逐个地将我们的皮鞋外轮廓点连接起来,形成一个多边形 ,但系统如何解释 逐个地 ,这是问题。

    请注意 XPolygonRegion 函数,它的最后一个参数确定了 如何判断一个点是否在该Region的内部。 它的取值只有两个:

    The fill-rule defines what pixels are inside (drawn) for paths given in XFillPolygon() requests and can be set to EvenOddRule or WindingRule. For EvenOddRule , a point is inside if an infinite ray with the point as origin crosses the path an odd number of times. For WindingRule , a point is inside if an infinite ray with the point as origin crosses an unequal number of clockwise and counterclockwise directed path segments. A clockwise directed path segment is one that crosses the ray from left to right as observed from the point. A counterclockwise segment is one that crosses the ray from right to left as observed from the point. The case where a directed line segment is coincident with the ray is uninteresting because you can simply choose a different ray that is not coincident with a segment.

    For both EvenOddRule and WindingRule, a point is infinitely small, and the path is an infinitely thin line. A pixel is inside if the center point of the pixel is inside and the center point is not on the boundary. If the center point is on the boundary, the pixel is inside if and only if the polygon interior is immediately to its right (x increasing direction). Pixels with centers on a horizontal edge are a special case and are inside if and only if the polygon interior is immediately below (y increasing direction).

    此乃问题之所在了。fill-rule 的局限导致了皮鞋内部的点不一定被判定为 内部

    虽然没能成功使用外轮廓数组构造一个Region调用XShapeCombineRegion完成不规则窗口的切割,但是我知道已经提供 com.sun.awt.AWTUtilities.* 的肯定是有办法做到的:

    • 要么自己调用JNI,加以适配
    • 要么直接返回UNSPPORTED

    没空研究X11细节,GUI本来也就不是我的关注点。但偶尔,我依然会花点时间探究一下若干年前遗落的问题,这是改变不了的事实。


    JNI直接操作AWT本地同位体

    洋洋洒洒写到这里,相信已经把Java如何调用JNI或者自身的API实现不规则窗体说的很清楚了,但是还有点小遗憾。

    我们看上文中引述的本地代码 cutWindow.c ,其中操作的window句柄是通过字符串Title查找而来的:

    window = findWindowByName(dpy, DefaultRootWindow(dpy), name);
    XShapeCombineRegion(dpy, window, ShapeBounding, 0, 0, region, ShapeSet);
    

    如果启动了同样Title的两个实例,会怎样?这就不得不将这多个同样Title的window通过别的键值进行区分。

    正确且直接的做法应该是Java直接将window句柄通过参数传递进来!这样就可以直接操作明确的window控件了!

    问题是Java代码中如何获得本地同位体的句柄呢?理论上来讲,这个系统底层的概念在Java API的层面应该是不可见的,Java代码只认识 跨平台的Frame/JFrame 这种,不可能会认识 Windows的HWND,X11的Window 的!

    然而,如果你不追求通用性,不追求跨平台(窗口不规则切割这件事本来就是平台相关的),办法嘛,必然是有的。我下面直接给出代码吧。

    1. 先看 PeerDemo.java:
    import java.awt.*;
    import java.awt.peer.*;
    import sun.awt.X11.*;
    
    public class PeerDemo extends Frame {
    
    	private native void cutWindow(long display, long hwnd);
    	static {
    		System.loadLibrary("cutWindow");
    	}
    	WindowPeer peer = null;
    	XBaseWindow base = null;
    	long hwnd = 0;
    
    	public PeerDemo() {
    		setSize(500,500);
    		setUndecorated(true);
    		setVisible(true);
    		this.peer = (WindowPeer)this.getPeer();
    		this.base =(XBaseWindow)peer;
    		this.hwnd = base.getWindow();
    	}
    
    	public static void main(String args[]) {
    		PeerDemo demo = new PeerDemo();
    
    		demo.cutWindow(XToolkit.getDisplay(), demo.hwnd);
    	}
    }
    
    1. 再看本地方法声明 PeerDemo.h:
    JNIEXPORT void JNICALL Java_PeerDemo_cutWindow
      (JNIEnv *, jobject, jlong, jlong);
    
    1. 最后看本地动态库代码 cutWindow.c:
    #include <X11/extensions/shape.h>
    # include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <jni.h>
    
    JNIEXPORT void JNICALL Java_PeerDemo_cutWindow (JNIEnv *jenv, jobject o1, jlong display, jlong hwnd)
    {
    	Window window;
    	Display *disp;
    	Region region;
    	XPoint p[3];
    
    	disp = (Display *)display;
    	window = (Window)hwnd;
    
    	p[0].x = 250; p[0].y = 100;
    	p[1].x = 450; p[1].y = 425;
    	p[2].x = 50; p[2].y = 425;
    
    	region = XPolygonRegion(p, 3, EvenOddRule);
    	XShapeCombineRegion(disp, window, ShapeBounding, 0, 0, region, ShapeSet);
    }
    

    非常简单的一气呵成,看看效果:
    在这里插入图片描述
    编译时的Warning是必然的,Java的Doc上已经说的很明白了:
    在这里插入图片描述
    getPeer意味着关联了本地,承诺跨平台的Java怎么可能暴露出这么低层次的接口呢。

    结束了吗?嗯,差不多了。但是且慢!

    还记得上文中我的失败吗?我想勾勒出皮鞋?的轮廓,然后把这些轮廓的像素点(超级多的点,至少好几千个吧)传递给本地代码,企图在本地代码中用这些轮廓点构建出一个X11的Region结构体:

    region = XPolygonRegion(outline_points, 3188/*举个例子,或许更多吧*/, EvenOddRule);
    

    然后呢,将其传递给X11的 XShapeCombineRegion 函数进行切割。然而遗憾的是,X11并不是如我希望的那般将这些点按照皮鞋的样子顺序连接起来成为一个皮鞋外形的,它有自己的连接方式。很遗憾,失败了(也许是我对X11不太了解,我暂时只能理解到这个程度)。

    然而,X11提供了另一种 构建任意形状 的方法,即 组合矩形

    这很好理解,既然所有的图像在计算机中都是一个个的像素组成的,而每一个像素就是一个长宽均为1的矩形,那么 任意形状至少可以用这么多像素矩形组合而成 。作为优化,还可以将矩形的数量减少到最少,这是一个算法问题,我这里仅仅给出一个思想。比如,依然是那个皮鞋的轮廓,矩形可以如下分割:
    在这里插入图片描述
    细微之处我没有画,反正就是这个意思。

    X11提供了组合不同大小矩形的API,即:

    void XShapeCombineRectangles (
    	Display *dpy,
    	XID dest,
    	int destKind,
    	int xOff,
    	int yOff,
    	XRectangle *rects,
    	int n_rects,
    	int op,
    	int ordering);
    

    参数很丰富,我还真没搞明白,详情可以参见X11 Shape Extension的文档:
    http://www.xfree86.org/current/shape.pdf

    不过给出个例子还是可以的。Java代码如下:

    import java.awt.*;
    import java.awt.peer.*;
    import sun.awt.X11.*;
    import javax.imageio.ImageIO;
    import java.net.URL;
    import java.io.*;
    import java.awt.geom.*;
    import java.awt.image.*;
    
    public class PeerDemo extends Frame {
    
    	private native void cutWindow(long display, long window);
    	static {
    		System.loadLibrary("cutWindow");
    	}
    	WindowPeer peer = null;
    	XBaseWindow base = null;
    	long hwnd = 0;
    
    	public PeerDemo() {
    		setSize(500,500);
    		setLocation(200,300);
    		setUndecorated(true);
    		setVisible(true);
    		this.peer = (WindowPeer)this.getPeer();
    		this.base =(XBaseWindow)peer;
    		this.hwnd = base.getWindow();
    	}
    
    	public static void main(String args[]) {
    		PeerDemo demo = new PeerDemo();
    		demo.cutWindow(XToolkit.getDisplay(), demo.hwnd);
    	}
    }
    

    本地代码如下:

    #include <X11/extensions/shape.h>
    # include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <jni.h>
    
    JNIEXPORT void JNICALL Java_PeerDemo_cutWindow (JNIEnv *jenv, jobject obj, 
    										jlong display, jlong hwnd)
    {
    	Window window;
    	Display *disp; 
    	XRectangle rects[4];
    	XRectangle *pRect = &rects[0];
    
    	disp = (Display *)display;
    	window = (Window)hwnd;
    
    	// 我们组合下面4个矩形:
    	rects[0].x = 0;
    	rects[0].y = 0;
    	rects[0].width = 100;
    	rects[0].height = 100;
    	rects[1].x = 100;
    	rects[1].y = 0;
    	rects[1].width = 100;
    	rects[1].height = 50;
    	rects[2].x = 200;
    	rects[2].y = 0;
    	rects[2].width = 10;
    	rects[2].height = 400;
    	rects[3].x = 0;
    	rects[3].y = 100;
    	rects[3].width = 10;
    	rects[3].height = 300;
    
    	XShapeCombineRectangles(disp, window, 
    							ShapeBounding, 0, 0, pRect, 4, 
    							ShapeSet, YXSorted);
    
    }
    

    效果如下图所示:
    在这里插入图片描述
    有点这个意思了。如果把这些矩形按照这个思路不断细化,就可以出现皮鞋外观了。


    什么是编程

    还记得 《Java Swing 2nd Edition》 这本书吗?

    我当时买了中文版之后,太厚,切割成了3部分,然而最终还是没有读完,惭愧。现在它的代码资源放到github了:
    https://resources.oreilly.com/examples/9780596004088
    我记得第28章的那个圆形飞梭控件非常好,嗯,Java Swing自己定义的不规则控件,是的,它是Swing自己画出来的。当时非常震撼。

    现在回头想想,有啥好震撼的呢,计算机屏幕上的所有像素不都是画出来的吗?关键是 如何画 才是根本,而这又涉及到了算法。

    如果不知道JNI,很难用Java做出不规则控件,如果知道了JNI,至少知道了有个渠道可以做,然而此时你还必须懂Win32 API/X11 API这些,你才能真的动手去做。之后如果这些全都懂了呢?就以为自己可以任意画界面了吗?

    不不不,这才到了关键的地方,这才是刚刚开始,比如上文中所说的,如何把皮鞋切割成数量最少的矩形,这比如何调用X11 API重要得多了,这里面奥妙太深了。

    换句话说,你想画画,目前你只是买了本子,画笔,画板,并且知道了如何使用它们,这些都是 必先利其器 的事,并不是画画本身!

    所以说呢,不是精通几个API,精通几门编程语言语法,精通几个工具的使用,就是精通编程了。甚至即便你精通很多系统底层的调试方法,你也不一定懂编程。你只是精通工具如何使用而已。

    比如我,我精通如何摆置系统底层,如何摆置协议栈,但是这并不意味着我精通编程,编程的核心是算法,而不是如何摆置代码本身。

    编程是一种如何组织逻辑的艺术,它不是一种如何使用编程语言的技术 编程语言只有一种用法,就是 用对它

    换句话说,如果你精于如何组织逻辑,那么即便是使用自然语言,你也可以精通编程(这通常见于物理科学家,律师,外交官等职业)。编程语言只是实现这种逻辑组织的手段而已(所以生物学家,文学家很多也精于几种编程语言,大多数用于数据分析)。所以,一定要把 会编程语言会编程 区别开来。

    以上形而上的说法如何形而下落地?这就要将行业的分工细化为 算法工程 两个方向。

    算法侧重业务逻辑本身,而工程则是为了更好的组织算法,使得其符合工业约束,成为优质的产品。比如说可扩展性这个就是工程要做的事,而效率则大部分是算法的工作。

    最后,我觉得我还是不会编程,在历经的学习和工作过程中,我掌握了系统的工作原理,底层的调试技巧,但是我依然吃力于编程,这意味着我的逻辑组织混乱,但是这并不是什么缺点更不是缺陷,这意味着我可以天马行空,而这正是定位棘手问题所需要的。所以说,摆正自己的位置最重要,不会编程没什么丢人的。


    勘误:全文均没有出现 “一双皮鞋” ,仅仅是 “一只皮鞋” 而已!更正。

    浙江温州皮鞋湿,下雨进水不会胖。

    展开全文
  • 8000字干货:那些很厉害的是怎么构建知识体系的

    万次阅读 多人点赞 2019-09-29 11:18:27
    分辨知识知识体系的差别 理解如何用八大问发现知识的连接点; 掌握致用类知识体系的构建方法; 能够应用甜蜜区模型找到特定领域来构建知识体系。 1. 知识体系?有必要吗? 小张准备通过跑步锻炼身体,可因为之前...

    本文约8000字,正常阅读需要15~20分钟。读完本文可以获得如下收益:

    1. 分辨知识和知识体系的差别
    2. 理解如何用八大问发现知识的连接点;
    3. 掌握致用类知识体系的构建方法;
    4. 能够应用甜蜜区模型找到特定领域来构建知识体系。

    1. 知识体系?有必要吗?

    小张准备通过跑步锻炼身体,可因为之前听说过小腿变粗、膝盖受伤、猝死等等与跑步有关的意外状况,有点担心自己会掉进各种坑里,就在微信上问朋友圈一直晒跑步里程的朋友老安。

    小张问老安:“老安,我想跑步,有什么要注意的没?”

    老安回答:“注意跑姿就行了,要前脚掌先着地,不然容易损伤膝盖。”说完还给小张发了张片。

    小张大喜,心想,幸亏问了老安,不然膝盖就废了。

    第二天早上上班,小张碰见一个部门的黑子,想起来黑子前段时间说他参加了什么夜跑团,就问黑子:“黑子,听说你参加了夜跑团,跑步经验肯定很丰富了,我最近也想跑步,担心跑出问题,给点建议怎么样?”

    黑子说:“建议嘛,的确有一条啊,别轻信网络上各种文章说的什么脚后跟先着地容易损伤膝盖应该前脚掌先着地之类的话。你知道吗,我参加的夜跑团里,有很多经验丰富的跑者,他们说呀,80%的马拉松跑者跑步时都是后脚跟先着地。人家跑那么久那么远都没事儿,就说明,脚后跟先着地是正确的选择。”黑子说着,拿出手机,找了张图给小张看。

    小张边看图边说:“有道理,有道理,还是黑子专业。”可他心里面却犯嘀咕,怎么回事儿啊这是,老安和黑子,说的完全是相反的。

    小张决定找一位资深的跑者问问,想来想去,想到他的大学同学飞将军,他常年跑步,全马都跑了6次了,最近好像还开发了一门如何跑步的课程。于是中午吃饭时,他用微信发了条消息给飞将军:“飞将军啊,我最近有点想跑步,你是专业的,给我点建议,怎么跑比较好呢?”

    过了一会儿,飞将军发过来一条消息:“你先回答我几个问题:1)你是想跑马还是慢跑锻炼?2)你身高、体重、体脂率各是多少?3)体力如何?4)有没有心脏病、高血压、头晕等情况?5)有没膝关节受伤、腰椎间盘突出等情况?6)准备在哪里跑?塑胶跑道、跑步机、水泥马路还是别的?……”

    小张一看飞将军连珠炮式发出的12个问题,不禁叫了一声“天呐”。

    好啦,现在请你回答一个问题:老安、黑子和飞将军,这三个人,哪一位更专业?

    如果我没猜错,你的答案和我一样:飞将军更为专业。

    为什么呢?

    因为飞将军没有像老安和黑子那样直接给一个前脚掌先着地或脚后跟先着地的建议,而是先望闻问切,从各个方面了解小张的情况,然后才给出适合小张的建议。

    那为什么他能够做到这一点呢?

    因为飞将军在跑步这个领域,拥有丰富的知识,涵盖跑姿、配速、相关疾病、路面、心率等等,这些知识有机地结合在一起,形成了一套知识体系,能够从多个维度综合评估一个人该如何开始跑步、如何进阶。

    不仅仅跑步这个领域有知识体系,各个领域都有知识体系,我们生活中遇到的大部分问题,都可以找到一个围绕着它形成的知识体系。

    针对某个领域、某个问题的知识体系,对我们有非常多的好处,典型的有三类:

    1. 表达能力升级。这是因为构建知识体系的过程,会用到并培养成长思维、批判思维和系统思维,提升思考能力。当我们能够把一件事情的逻辑、层面、各个维度想明白,就能讲明白。
    2. 从零到一成为专家。知识体系是结构化的,知识点之间彼此关联,有无数回路,四通八达。这样的好处是,我们遇到一个问题,就会触发某个知识点,我们捕捉到这个知识点,就可以沿着知识体系的无数关联和回路,快速找到相关的其他知识的点,组合起来,形成针对所遇到问题的解决方案,就表现出专业水准,就当得起“领域专家”之称。
    3. 智慧影响待人接物。知识内化,形成体系,吐故纳新,不断进化,我们的智慧就会跟着升级,就能应用于实际生活。一方面知道的越多越能体会知识无涯越会虚心、包容,越能与他人和谐相处,另一方面体系越完善越能帮助自己和他人解决问题,促进彼此关系。

    举两个例子对比一下,我们马上能感受到拥有知识体系的好处。

    医生D1,接诊一位大腿中箭的士兵,马上想到:“中箭了就得赶紧拔出来才好。”于是他一顿操作猛如虎,拔掉了士兵屁大腿上的长箭。不料鲜血迸出,他赶紧找来一团棉花,一条线绳给士兵包扎上。然后拍拍手,告诉士兵,长箭已除,万事大吉。

    医生D2,也接诊了一位屁股中箭的士兵,他没有立即动手拔箭,而是先问了士兵中箭的详情、各个身体部位的感觉、以往的箭伤治疗史等等情况,然后准备了清洗、消炎、包扎要用的各种物品,接下来消炎、拔箭、敷药、包扎,最后又叮嘱了饮食禁忌、日常护理、活动建议、复诊、常见并发症和应对策略等等事情,才结束治疗。结果这位士兵很快痊愈了。

    D1缺乏诊治箭伤的知识体系,脑中只有几点零散的知识,只会头痛医头脚痛医脚,想不到感染、发烧、饮食禁忌等事情,结果他的病人回去后,伤口感染、化脓,引起各种并发症,后来救治无效,含恨离世。

    反观D2,因为有知识体系,能够系统的、多维度的、多环节的考虑各种问题,妥善制定箭伤医治策略,他的病人得到了很好的诊治,很快就痊愈了。

    医疗领域的知识体系,可以治病救人,效用立竿见影。其他领域的知识体系,在处理该领域相关的问题时,也有类似的效果。我们的工作和生活中,就能找到很多例子,你不妨回想一下自己过去的经历,找一件事能够说明知识体系作用的事情出来,这样能加深我们对知识体系重要性的理解。

    2. 善用八大问发现知识的连接点

    从跑步和治病这几个例子中,我们可以看到,知识点是一个一个的,散乱的,比如“跑步时前脚掌先着地不伤膝盖”、“中箭后要赶快把箭拔出来”,就像我们小时候玩的木质积木。

    而知识体系是某个人结合自己的问题和实践形成的知识集合,知识点之间彼此以形式多样的方式关联,形成了特定的结构。比如飞将军关于跑步的知识体系,就是由跑姿、配速、跑量、心率、常见相关病症、跑鞋、路况、饮食等知识点相互链接而成。这样的知识体系,像我们乐高积木搭起来的建筑物。


    那么,现在有一个问题:用我们前面图片中的木质积木,可以搭建出上面的积木房子吗?

    估计我们的答案是一样的:不能。这也是为什么传统的木质积木无法风靡的关键原因——很难搭建出有稳固的、有创造性的东西,不耐玩。

    那为什么乐高积木就能够搭建出种类繁多、形式各异的“建筑物”呢?

    请大家看一下乐高积木的积木块:


    发现它们和传统积木块的不同了吗

    对,它们上面有很多凸起的圆点点!这些圆点点非常关键,它们可以嵌入其他积木块预留的凹槽,把两个积木块连接起来。

    也就是说,乐高积木块上有连接点,所以可以彼此连接,拼搭出各种具有稳定结构的“建筑物”!

    把知识比作积木块,如果知识上生出连接点,就也可以彼此连接,构建出适用于特定领域和特定问题的知识体系

    那么问题来了:知识有连接点吗?

    答案是肯定的,因为在各个领域都存在知识体系,知识体系是由若干知识榫合而成,知识要彼此榫合,就一定有连接点。

    知识本身就有连接点,而我们无法利用这些连接点构建出知识体系,往往是因为:我们没有发现连接点。就是说,连接点原本就存在,只是我们没看到。

    进一步说,只要有办法找到并标注出知识的连接点,散乱的知识就可能彼此连接成一个体系。

    那么问题来了:如何找到知识的连接点呢?

    拆书帮的创始人赵周老师,在讲如何将移动时代手机阅读到的信息转化为知识时,总结了一个结构化的方法,叫“八大问”。我们可以借助“八大问”,来分析信息、观点、事件、经历等,从中整理出知识和连接点。

    我们先介绍八大问,然后来看怎么使用它来挖掘知识的连接点。

    八大问是一个提问的框架,将针对信息、观点、事件、经历等的提问,分为“前、因、后、果,适、用、边、界”八类。

    这八类问题,可用来分析或整理信息,把信息变成知识,并找到知识的连接点。八大问又可以分为两组,前因后果和适用边界,前因后果用于分析信息,适用边界用于整理信息。

    为方便记忆,赵周老师找了八个成语帮助我们,前对应前车可鉴,因对应相因相生,后对应以观后效,果对应自食其果,适对应适得其反,用对应使用条件,边对应旁敲边鼓,界对应楚河汉界。

    赵周老师针对每一大问,都提供了几个典型问题,帮助我们理解和使用这一问。具体来讲,是这样的:

    • 前(前车可鉴):1)为什么这件事对我重要?2)他是怎么引出这个信息的?
    • 因(相因相生):1)作者提出了哪些关于原因的假设?2)怎么验证或排除这些假设?3)还有其他可能性吗?
    • 后(以观后效):1)若已从信息去做之后会怎样?2)对我的好处(效用)是什么?
    • 果(自食其果):1)不这么做的后果是什么?2)不改变的问题有多严重?
    • 适(适得其反):1)有没有相反的观点?2)有没有不支持这个的实例?
    • 用(使用条件):1)要这样做,需要具备哪些条件(考虑成本收益、态度、能力……)?2)这件事可以用什么其他方式来完成?
    • 边(旁敲边鼓):1)从前有没有类似的(或乍看起来差不多的)信息?2)其他领域/行业/人如何解决类似问题?
    • 界(楚河汉界):1)无论是相反的还是类似的信息,和这个信息的真正区别是什么?交界在哪里?

    在使用八大问时,不一定要原模原样的问赵周老师给出的示范问题,可以在理解这八类问题的前提下,针对具体情境,提出适合的问题。也就说,八大问最重要的是提供了一种提出有洞察力的问题的框架,这是它的意义所在,如果你有能力,可以领会心法,保持心法不变,提出新的问题,如果一开始不知道怎么用,则建议直接使用示范问题或在其基础上做变化。

    现在,我们举个例子,演示一下八大问的用法。

    小薇热情、活泼、健谈、点子多、标新立异、夸张、情绪波动大,她从事保险销售,业绩很好,领导安排她开发一个内部课程,给大家讲讲如何发展客户、维系客户。小薇答应两天搞定讲义,然后给大家讲。她动作很快,马上就开始写PPT,可总是被各种事务打断,一会儿有客户打电话,一会儿微信上有人咨询,一会儿又要出去见客户签合同,一会儿有同事请教,结果一个星期过去了,PPT只有一页标题。

    领导问小薇了两次次,什么时候可以讲,小薇都说太忙了,PPT才刚开了个头,一直没找出时间做。第三次的时候,领导皱着眉头给了小薇一个建议:“小薇啊,你时间管理能力还有很大提升空间啊,建议你好好学习下史蒂芬·柯维的时间管理四象限,把手头上的事情理一理。”小薇连忙称是,开始学习时间管理四象限。

    熟悉这个场景吗?你我的时间管理学习之路,基本上都是这么开始的。

    这种模式,其实也是我们切入一个新领域时的模式:遇到一个问题,要解决,就要用到新的知识、新的技能,开始学习之路,慢慢在实践中积累这个领域的知识。

    但是,我们积累的知识,能否形成体系,却依赖于我们能否做到“发现知识的连接点、主动链接不同的知识”。

    不管以前我们做得怎样,现在我们可以使用八大问来加速这个过程。

    让我们再次聚焦小薇的问题,看看怎样应用八大问来发现知识的连接点。现在,小薇知道了一个时间管理的方法,叫作“时间管理四象限”。

    先问“前”,这里小薇可以问“为什么掌握时间管理四象限对我很重要?”,答案可能是“做好时间管理,能帮助我合理安排任务,找到完成讲义开发的时间。”所以,学习时间管理四象限是有必要的。

    再问“因”,领导让小薇学习时间管理四象限,他提出了什么关于原因的假设呢?答案是“小薇时间管理能力欠缺,不能合理安排各种事务。”领导认为小薇只要管理好时间,就能抽出时间来完成PPT。这个假设正确吗?这是小薇没能在两天内完成讲义的唯一的原因吗?不一定哦,小薇没有完成PPT,除了时间管理的缘故,还有一个很重要的原因,是她内心不觉得“开发课程传授经验”这件事有多么重要,在她看来,最重要的事情是搞定客户赢得保单。

    接下来问“后”,小薇可以问“学习时间管理四象限对我的好处是什么?”她对照着时间管理四象限图,翻看《高效能人士的七个习惯》,很快想到了好处:聚焦要事,从容工作,提高工作质量和产出。简单讲,要事第一,提高工作效能。

    然后问“果”,直接用赵周老师的示范问题,“不这么做的后果是什么?”后果很明显,课程的交付时间会一再拖延,领导会认为小薇不重视自己的工作安排,甚至会怀疑小薇别有用心,进而影响小薇后续的工作开展,甚至影响升职加薪。所以从这个角度看,小薇需要尊重领导的工作安排,尽快搞定这项任务,所以她还是有必要学习时间管理。这样分析,小薇发现了一个点,某些工作任务的重要性,取决于领导的看法,所以她还应该学习目标管理、向上管理。

    再接下来问“适”,小薇问了自己这个问题“有没有人学了时间管理四象限却还是安排不好工作?”她马上想到同事小兰,曾经学习过时间管理四象限,可现在依然是每天忙东忙西丢东忘西经常出状况。

    然后问“用”,小薇可以这样问:“使用时间管理四象限的前提条件是什么?”经过研究,小薇发现,史蒂芬·柯维是一名企业管理者,同时也是为企业负责人提供咨询的人,他提出的时间管理矩阵,更多是针对管理者的,因为管理者有更多的掌控感和自由度,能够决定某件事情是否重要,进而根据紧急性来判断是立即做还是规划时间做。而像她和小兰,都在执行层面,多数时候无法决定一件事情的重要性,而不能判别重要性,就难以应用时间管理四象限。这也是小兰学了时间管理四象限依然工作忙乱的一个原因,同时也是她的领导觉得时间管理四象限管用的原因。这样一想,小薇知道了使用时间管理四象限的两个前提条件:1)个人具有分辨事情重要性的能力;2)个人对事情具有一定的掌控性和自由度。

    接下来问“边”,小薇先问自己“有没有与时间管理四象限类似的时间管理方法?”小薇一搜索,发现时间管理方法很多,GTD(衣柜整理法)、番茄工作法、三只青蛙、日历、猴子法则、Unbroken Time、思维导图、黄金工作时间……全都和时间管理有关。她一一搜集相关信息,进一步了解这些时间管理方法。接下来她又想到,管理时间其实是为了管理工作任务,那关于任务管理,有哪些方法呢?她一搜索,发现了看板、思维导图、80/20法则、清单、OKR、SMART法则等。

    最后问“界”的问题,“时间管理四象限的适用情况是什么?”小薇觉得,时间管理四象限,更适合这两类人:1)管理者;2)可以在一定范围内自我安排工作任务和时间的人。

    经过一轮八大问,小薇对时间管理四象限的认识更深入了,管理者、任务管理、时间管理、重要性、向上管理、要事第一、工作效能、工作自主度、规划……这些关键词留在了她的记忆中,成了可以与其他知识链接的连接点。

    八大问的目的,正是帮助我们从不同角度、维度来分析和整理信息,加深我们对知识的理解、认识,进而帮助我们为知识建立各个层面、各种角度的连接点,有了这些连接点,知识点彼此之间更容易产生关系,知识也更容易被外界问题刺激、唤醒。

    3. 如何构建知识体系

    一旦我们标注出知识的连接点,就可以把具备可连接性的两个知识经由连接点链接在一起,围绕着不同知识的不同类型的连接点,持续地、反复地执行这个操作,就可以慢慢形成知识体系。

    我们以小薇为例来说明这个过程。

    前面我们通过八大问中的“界(楚河汉界)”这一问,找到了“时间管理四象限”这个知识的一个连接点——管理者,即时间管理四象尤其适用于职场中的管理者。

    假如小薇在检索时间管理方法时,搜到了“猴子法则”,买了一本书叫作《别让猴子跳回背上》,学习了一下,运用八大问做了分析,画出了猴子法则和它的连接点,如下图所示:

    对比时间管理四象限的连接点和猴子法则的连接点,可以发现,通过“管理者”这个连接点,就可以把两者链接起来。

    假设小薇要考取中国寿险理财规划师,需要持续的看书学习,可她看书时总是分神,无法专注,于是就向一位特别爱看书的朋友请教如何才能专注看书,朋友告诉她使用“番茄工作法”,她了解了一下,发现番茄工作法是弗朗西斯科·西里洛上大学时为了专心学习发明的方法,特别适合看书学习这类伏案工作,她接着用八大问分析,画出了番茄工作法的连接点,如下图所示:

    番茄工作法属于时间管理领域的方法,那它就可以通过“时间管理”这个连接点,和时间管理四象限链接起来。

    随着小薇不断践行时间管理,了解到越来越多的方法,于是她就慢慢形成了时间管理方面的知识体系:

    现在可以看到知识体系的完整构建过程了:我们基于一个场景(问题),找到一个知识,运用八大问分析,挖掘各式各样的连接点,在持续实践中接触新的知识,反思回顾,把新知和旧知经由含义相近的连接点链接起来,形成体系。

    这个过程用文字来描述,只需要几百个字,阅读只需两三分钟,于是你可能会想,构建知识体系原来这么简单这么快呀,我很快就能搭建出自己的知识体系。实际上并非如此,知识体系的形成,是在生活实践中,不断解决问题,不断反思经验,不断发现连接点,不断链接知识,慢慢完成的,决不是一朝一夕之功。

    比如小薇从时间管理四象限链接到猴子法则,只有在她当了管理者,深受下属抛给她的猴子之苦时,才可能真正用到猴子法则,才能真正完成链接。这个过程,时机不成熟,就不会发生,所以看似简单的一个链接,分分钟的事情,实际上可能需要一年、两年,甚至三五年。

    经过实践和时间的考验,我们构建出了某个领域的知识体系,是不是万事大吉了呢?

    非也!

    我们还需要不断更新迭代自己在某个领域的知识体系,因为时代在发展,环境日新月异,问题层出不穷,用于解决问题的知识也会因之而快速演变。

    比如老韩是PPT设计师,对PPT 2016非常熟悉,各种功能了然于心,可2019年初Office 2019发布后,他就需要更新自己围绕着PPT 2016构建出来的知识体系,如果不这么做,遇到客户提出的特定于PPT 2019的问题,就没办法解决。

    比如小卢是增粉达人,原来微博微信的各种增粉策略、方法、实践,了然于胸,专门为各类自媒体团队提供增粉服务,现在短视频大火,抖音崛起,迅速成为国民级应用,那小胡就需要迅速学习基于抖音平台的增粉策略,包括抖音的推荐规则、封杀原则、内容分布、流量特点、视频结构等等,只有这样,他才能继续我他的客户提供有价值的服务。

    幸运的是,不管环境如何演变,只要我们掌握构建知识体系的方法,挖掘出新知识的连接点,就可以将新知识纳入既有知识体系,继而知识体系就可以获得更新,我们就能借助既有知识体系的势能,在新的领域打开局面。

    4. 在哪个领域建立自己的知识体系

    前面我们着重介绍了八大问发现知识的连接点、用连接点链接不同知识构建知识体系,并反复强调,拥有知识体系,可以专业、系统、全面、多维度的看待问题,创造难以复制的价值。

    现在,有的小伙伴心头可能会升起一个问号——“既然知识体系如此重要,那我是不是要在生活中的每个领域都构建出知识体系呢?”

    非也非也!

    我们不可能在每个领域都搭建一套知识体系——我们没那么多时间和精力。同时,也没那个必要。

    请想象如下场景:你发烧了三天,不见好转,头疼、浑身无力、骨肉疼痛,无法工作。

    你会怎么办?

    去研究关于发烧的各种知识、构建出发烧的知识体系,然后把自己治好,还是说,去医院,花钱让医生帮忙看病?

    你选的是去医院看病,对吧,很明智的选择。这也是我们面对多数领域问题的最佳选择——付费请目标领域的专业人士帮助解决。

    多数领域的问题,都可以外包给他人来解决,我们不用费尽心力在各方面都成为拥有强大知识体系的专家。

    我们要做的,是找到自己的根据地,发展出自己的知识体系,用自己的知识体系创造价值,交换其他领域的专业服务。

    那么,问题来了,怎么确定想要建立知识体系的领域呢?

    下面三个问题,提供了一个定位框架:

    1. 我擅长什么?
    2. 我爱好什么?
    3. 这个世界需要什么?

    这三个问题对应答案的交集,就是我们构建知识体系的甜蜜区:

    请反复探寻,找到自己的甜蜜区,锚定它,投入时间和精力,在实践中将各种知识融会贯通,构建出知识体系。

    最后,问个问题:现在的你,拥有(或打算构建)哪个领域的知识体系?欢迎留言和我分享_


    新上线了一门视频课程——程序员面试宝典,金九银十招聘季,想找工作的你,可以来看看!程序员面试宝典

    展开全文
  • Arduino是一个比你的台式电脑更能够用来感应控制现实物理世界的一套工具。 它由一个基于简易单片机并且开放源码的计算机平台,一套为Arduino板编写程序 的开发环境组成。  Arduino可以用来开发交互产品,...


     
    Arduino是一个比你的台式电脑更能够用来感应和控制现实物理世界的一套工具。 它由一个基于简易单片机并且开放源码的计算机平台,和一套为Arduino板编写程序 的开发环境组成。

     
    Arduino可以用来开发交互产品,比如它可以读取大量的开关和传感器信号,并且可以控制 各式各样的电灯、电机和其他物理设备。Arduino项目可以是单独的,也可以在运行时和 你电脑中运行的程序(例如:Flash,Processing,MaxMSP)进行通讯。Arduino板你可以 选择自己去手动组装或是购买已经组装好的;Arduino开源的IDE可以免费下载得到。

    Arduino的编程语言就像似在对一个类似于物理的计算平台进行相应的连线,它基于处理多媒体 的编程环境。为什么要使用Arduino?

    有很多的单片机和单片机平台都适合用做交互式系统的设计。例如:Parallax Basic Stamp, Netmedia’s BX-24,Phidgets,MIT’s Handyboard 和其它等等提供类似功能的。 所有这些工具,你都不需要去关心单片机编程繁琐的细节,提供给你的是一套容易使用的工具包。 Arduino同样也简化了同单片机工作的流程,但同其它系统相比Arduino在很多地方更具有优越性, 特别适合老师,学生和一些业余爱好者们使用:

    • 便宜 - 和其它单片机平台相比,Arduino板算是相当便宜了。最便宜的Arduino版本可以自己 动手制作,即使是组装好的成品,其价格也不会超过$50(在中国更便宜)。
    • 跨平台 - Arduino软件可以运行在Windows,Macintosh OSX,和Linux操作系统。大部分其它的单片机系统都只能运行在Windows上。
    • 简易的编程环境 - 初学者很容易就能学会使用Arduino编程环境,同时它又能为高级用户提供足够多的高级应用。对于老师们来说,一般都能很方便的使用Processing 编程环境,所以如果学生学习过使用Processing 编程环境的话,那他们在使用Arduino开发环境的时候就会觉得很相似很熟悉。
    • 软件开源并可扩展 - Arduino软件是开源的,对于有经验的程序员可以对其进行扩展。Arduino编程语言可以通过C++库进行扩展,如果有人想去了解技术上的细节,可以跳过Arduino语言而直接使用AVR C 编程语言(因为Arduino语言实际上是基于AVR C的)。类似的,如果你需要的话,你也可以直接往你的Arduino程序中添加AVR-C 代码。
    • 硬件开源并可扩展 - Arduino板基于 Atmel 的ATMEGA8 和ATMEGA168 单片机。Arduino基于Creative Commons 许可协议,所以有经验的电路设计师能够根据需求设计自己的模块,可以对其扩展或改进。甚至是对于一些相对没有什么经验的用户,也可以通过制作试验板来理解Arduino是怎么工作的,省钱又省事。

    该如何使用Arduino?

    请根据你的操作系统,选择Arduino的入门指南(Windows, Mac OS X, Linux); 或其它特定硬件的指南:Arduino Mini, Arduino BT, 或shield: Xbee。
     
     

    一个偶然的机会,接触到了一个叫做Arduino的东西。
    Arduino距离传感器
    Arduino是什么?
    Arduino是意大利一个开放源代码的硬件项目,包括一块接口板和一套简单的软件开发环境。可以用于开发各种各样的自动控制和交互式的电子作品,在机器人制作领域有着广泛的应用,很多机器人项目都是基于Arduino构建的。

    Arduino可以做什么?
    Arduino可以通过传感器感知外界环境(比如温度、压强、湿度、颜色等等),并且当外界环境发生变化时,可以按照预先设定的程序通过控制马达、制动器、继电器等对环境进行干预。其实简单说就是可以认识世界并改造世界,只是没有人类那么厉害罢了,呵呵。要了解更多Arduino的应用,可以去YouTube查看(可能得翻墙)。

    几个月前,刚接触arduino的时候,做了这么一个小玩意儿:
    把一个距离传感器(超声波测距)连接到arduino上,然后连接到电脑上,距离传感器能够探测到前面障碍物的距离,实时地将距离记录在电脑中并实时绘图。效果如下:
    Arduino距离传感器
    用到的硬件:电脑,Arduino板子,URF04超声波测距传感器
    用到的软件:Arduino软件,gobetwinokst

    一些关于Arduino的网站:
    Arduino官方网站:http://www.arduino.cc/
    Arduino台湾网站:http://arduino.tw/
    Arduino豆瓣小组:http://www.douban.com/group/arduino/

    展开全文
  • 今天是 22 岁的最后一天。几个月前,我从沃顿商学院毕业,用文凭上“最高荣誉毕业”的标签安抚了已经年过... 我肯定不是第一个做过这样事的,也肯定不会是最后一个。所以在说自己的一些有趣故事前,我想借用大家(包
  • 接触型的卡片表面有金属贴片,用于连接卡内芯片和外界器具(洗衣机,电表,打水器)。而非接触性的卡片则是通过射频信号与器具进行信息传递。IC卡本身是无源的,需要依靠外部器具提供的能量驱动内部电路。时下更流行...
  • 转自Denny Liu 因为原链接找到,特此声明
  • 电子产品接触不良问题物理解释

    千次阅读 2013-11-05 09:46:55
    而且,有时的接触不良体现出来的现象会令迷惑不解。以下我将从物理的角度来解释接触不良问题,使大家更加明朗深刻理解此现象。我之前并没有看到有类似深刻分析接触不良的文章,所以难以取得权威的资料,因此,...
  • 作为科技企业,华为多年来一步一脚印,着力于研发核心科技技术,近年来在智能手机、5G网络技术、芯片等等行业里取得了辉煌的成就,不少用“中华有为”称赞华为。   随着华为今年在手机行业里销量成功超越苹果...
  • 可毕业后回首,却很少有能说,自己从来没有迷茫过。迷茫,仿佛就是一团乌云,笼罩在每一个心中怀有抱负的的头上。每当夜深人静,思绪归于对自己人生未来的严肃思考,不知去往何处的苦闷,再加之不断迫近的升学/...
  • 要记得,你世界上所有一样。 关于标签 “牛逼”是过去几年里笔者听到的比较多的一个形容词。当我们喜欢的称赞我们的时候,我们总是P颠P颠的。在这里为自己开脱一下,觉得这挺好,说明活得挺...
  • 接触式IC卡的分类(三)

    千次阅读 2016-07-15 11:52:09
    1、RF卡,即射频卡(或无线卡),是指应用RFID技术,就是感应式的...射频卡的信息存取是通过无线电波来完成的,主机射频之间没有机械接触点。主要用做身份识别,特指低频的用作门禁,考勤方面的卡,比如HID,INDARA,TI
  • 他不善言辞,从不接受媒体采访,却以“J.Wong”的论坛ID为外界熟谙;他彻夜潜水于魅族论坛,偶尔喋喋不休,偶尔勃然大怒;他深居简出捣腾动辄万元的音乐器材,一月只出一次门理发,以至于公司五层的办公室空空荡荡。...
  • 四无年轻如何逆袭

    万次阅读 多人点赞 2017-04-26 06:53:25
    普普通通的年轻,没关系没资源,没有一技之长,没有什么兴趣爱好,该如何逆袭?
  • IT都很忙(茫)

    千次阅读 热门讨论 2014-07-03 20:28:02
     大学期间,知道未来要干什么,很多清楚应该学习哪些知识技能,是否需要考证,是否要上培训班,有的甚至在考虑是否要上课? 毕业了到毕业3年,找到了工作,经常加班,可能已经厌倦了这种生活,比如...
  • 虽然经常使用美团大众点评,但之前并没有真正的去了解“新美大”的历史,偶然读了这篇美团网 CEO 王兴写的文章之后,发现自己对美团有了更深的认知,无论是从使命还是愿景来说,美团网都应该算是一个值得尊敬的...
  • 中年发福,减肥越来越难,看到0糖0卡饮料眼前一亮,失眠焦虑成为常态,发际线加速后移……这是我们接触的30岁左右互联网、新经济从业者们身上,正在发生的事情。 高压勤奋的工作带来的财富收入增长固然重要...
  • 领导秘书,一般都是什么样的

    千次阅读 2020-11-28 11:45:58
    从事秘书工作,很重要的一个要求,就是嘴要严,秘书领导长时间近距离接触,对于领导的所言所语、所做作为,了解非常清楚,有很多是涉及到领导隐私,或者是方便外界了解的事情。作为秘书,必须要守口如瓶,能把...
  • 你首先是一个,然后你才是程序员。

    万次阅读 多人点赞 2017-03-23 11:47:26
     这个层面,一直都是程序员的弱项,程序员被外界一直冠以“内向”、“爱说话”的美名,不是没有原因的。就LZ接触过的程序员,有不少确实是这种性格,这其实很利于未来的发展。  当然了,LZ也想说“应该多...
  • 第四章 机交互技术概述 1.机交互的输入模式(第二版)  由于输入设备是多种多样的,而且对一个应用程序而言,可以有多个输入设备,同一个设备又可能为多个任务服务,这就要求对输入过程的处理要有合理的模式...
  • 在电影《盗梦空间》中,男主角科布妻子在梦境中生活了50年,从楼宇、商铺,到河流浅滩、一草一木,这两位造梦师用意念建造了属于自己的梦境空间。你也许并不会想到,在不久未来,这看似科幻的情节将走入人们的日常...
  • 因为,我妻子是一个没有正式工作的,现在给别人打工,也可能有什么发展。文凭也高,估计再过几年,也就会成为全职的家庭主妇。我的收入尚可,因此,到目前为止,我们还没有感到很大的经济压力。但是,以后呢?...
  • 我们如何成为一个自律的

    千次阅读 2018-11-02 15:11:44
    自律,指外界约束情感支配,据自己善良意志按自己颁布的道德规律而行事的道德原则。 “能自律,何以正?” — 唐·张九龄《贬韩朝宗洪州刺史制》 自律是一种可或缺的人格力量,没有它,一切纪律都会变得...
  • 30岁经理为何职涯蹉跎

    千次阅读 2007-12-09 11:02:00
    30岁经理为何职涯蹉跎 30岁意味着什么?是人生中最自信、最狂傲的年龄,是总自以为了不起而在... 我接触过大量的高级经理,其中30岁出头的经理,绝对是同龄中出类拔萃的精英。他们大多学历显赫,职场头10
  • 陆陆续续接触了一些闭包的知识,也犯过几次因为理解闭包导致的错误,一年多了资料也看了一些,但还是不是非常明白,最近偶然看了一下 jQuery基础教程 的附录,发现附录A对JavaScript的闭包的介绍简单易懂,于是...
  • 进入其感应范围则输出高电平,离开感应范围则自动延时关闭高电平,输出低电平。 ②光敏控制 (可选) 模块预留有位置,可设置光敏控制,白天或光线强时感应。光敏控制为可选功能 ,出厂时未安装光敏电阻。如果...
  • 当今先进的机交互技术

    千次阅读 2013-07-24 10:59:31
    自计算机以一个庞然大物...机交互技术也不再局限于键盘输入、手柄操作,而是以更加新奇的方式出现:手指的微小动作、声波在空气中的振动、眼珠舌头的转动,都可以实现信息传递,完成与机器之间的“对话”。  
  • 的社会属性

    千次阅读 2019-05-28 15:41:08
    ——研究的本质属性,防先研究的所有属性。 ——为什么的本质属性是的社会属性呢? ——的本质属性是用来区分与其他生命或物体的? ——的自然属性能用来区分与其他生命或物体么? 的社会...
  • 一个分享的经历的故事感悟

    万次阅读 2015-09-08 00:55:36
    要记得,你世界上所有一样。  关于标签    “牛逼”是过去几年里笔者听到的比较多的一个形容词。当我们喜欢的称赞我们的时候,我们总是P颠P颠的。在这里为自己开脱一下,觉得这挺好,说明活得挺...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 23,708
精华内容 9,483
关键字:

不和外界接触的人