Android开发之PopupWindow

 

/*

 *  Android开发之PopupWindow

 *

 *  Created on: 2011-8-8

 *  Author: blueeagle

 *  Email: liujiaxiang@gmail.com

 */

       聪明的人善于总结,记录,不知道这是谁说的了,反正要当一个聪明人,我得先学会总结,记录。最近在Android的学习过程中,发现 PopupWindow也是值得研究一番的一个东东,因此拿过来说道说道。与其相似的就归纳到一起说道吧,那就是AlertDialog和Toast。

PopupWindow

java.lang.Object

   ↳ android.widget.PopupWindow

手册中,对PopupWindow的描述是这样的:它是一个弹出的窗口,可以用来显示一个任意的视图。弹出窗口是当前活动上出现一个浮动容器。

       PopupWindow的性质,其实就是我们通常所说的“模态对话框”。只有在其退出之后,才可以进行外部线程的操作。下面对其进行详细说明。

简单的PopupWindow

       如图所示,通过主Activity中的一个按钮来触发弹出窗口操作。这个窗口什么都不干,就是显示一句话:“狂热的Android开发者”。为了增加可见性,我设置了不同的背景色加以区分。

       源码如下:

  1. /* 
  2.  
  3.  *  Android开发之PopupWindow、AlertDialog和Toast 
  4.  
  5.  *  PopWinEx.java 
  6.  
  7.  *  Created on: 2011-8-8 
  8.  
  9.  *  Author: blueeagle 
  10.  
  11.  *  Email: liujiaxiang@gmail.com 
  12.  
  13.  */  
  14.   
  15. package com.blueeagle;  
  16.   
  17.    
  18.   
  19. import android.app.Activity;  
  20.   
  21. import android.os.Bundle;  
  22.   
  23. import android.view.View;  
  24.   
  25. import android.widget.Button;  
  26.   
  27. import android.widget.PopupWindow;  
  28.   
  29.    
  30.   
  31. public class PopWinEx extends Activity {  
  32.   
  33.     /** Called when the activity is first created. */  
  34.   
  35.     Button MyButton;  
  36.   
  37.     PopupWindow pw;  
  38.   
  39.     View myView;  
  40.   
  41.     @Override  
  42.   
  43.     public void onCreate(Bundle savedInstanceState) {  
  44.   
  45.         super.onCreate(savedInstanceState);  
  46.   
  47.         setContentView(R.layout.main);  
  48.   
  49.           
  50.   
  51.         MyButton = (Button)findViewById(R.id.myButton);  
  52.   
  53.           
  54.   
  55.         MyButton.setOnClickListener(new Button.OnClickListener(){  
  56.   
  57.    
  58.   
  59.            @Override  
  60.   
  61.            public void onClick(View v) {  
  62.   
  63.               // TODO Auto-generated method stub              
  64.   
  65.               myView = getLayoutInflater().inflate(R.layout.pop,null);    
  66.   
  67.               pw = new PopupWindow(myView,500,200,true);   
  68.   
  69.               pw.showAsDropDown(MyButton);  
  70.   
  71.            }  
  72.   
  73.           
  74.   
  75.         });  
  76.   
  77.     }  
  78.   
  79. }  


 

我们给PopupWindow和主界面,分别定义一个layout。对应的XML文件如下:

  1. pop.xml  
  2.   
  3. <?xml version="1.0" encoding="utf-8"?>  
  4.   
  5. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  6.   
  7.     android:orientation="vertical"  
  8.   
  9.     android:layout_width="fill_parent"  
  10.   
  11.     android:layout_height="fill_parent"  
  12.   
  13.     android:background="#ffff00"  
  14.   
  15.     >  
  16.   
  17. <TextView    
  18.   
  19.     android:id="@+id/mytextview"  
  20.   
  21.     android:layout_width="fill_parent"   
  22.   
  23.     android:layout_height="wrap_content"   
  24.   
  25.     android:text="@string/hello"  
  26.   
  27.     />  
  28.   
  29. </LinearLayout>  
  30.   
  31. main.xml  
  32.   
  33. <?xml version="1.0" encoding="utf-8"?>  
  34.   
  35. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  36.   
  37.     android:orientation="vertical"  
  38.   
  39.     android:layout_width="fill_parent"  
  40.   
  41.     android:layout_height="fill_parent"  
  42.   
  43.     android:background="#ff00ff"  
  44.   
  45.     >  
  46.   
  47. <Button    
  48.   
  49.     android:id="@+id/myButton"  
  50.   
  51.     android:layout_width="fill_parent"   
  52.   
  53.     android:layout_height="wrap_content"   
  54.   
  55.     android:text="@string/hello"  
  56.   
  57.     />  
  58.   
  59. </LinearLayout>  


 

说明:这里值得注意的是,我们要给弹出窗口设置焦点,pw = new PopupWindow(myView,500,200,true);这句中的true表示弹出窗口可以获得焦点。如果弹出窗口没有获得焦点的时候,不断点击按钮的,最终程序将退出,这时是因为内存耗尽的原因,可以通过查看log得到。

 

有一定布局的PopupWindow

如图所示,同样是通过主Activity中的一个按钮来触发弹出窗口操作。但是现在这个弹出窗口可以进行一些操作,因为其具有了一定的布局,目前我们暂且其可以进行的操作为:

1.       可编辑功能

2.       可传递信息

3.       弹出窗口上再次弹出窗口

4.       可以取消当前弹出窗口

为了增加可见性,我依然设置了不同的背景色加以区分。

       源码如下:

  1. /* 
  2.  
  3.  *  Android开发之PopupWindow、AlertDialog和Toast 
  4.  
  5.  *  PopWinEx.java 
  6.  
  7.  *  Created on: 2011-8-9 
  8.  
  9.  *  Author: blueeagle 
  10.  
  11.  *  Email: liujiaxiang@gmail.com 
  12.  
  13.  */  
  14.   
  15.    
  16.   
  17. package com.blueeagle;  
  18.   
  19.    
  20.   
  21. import android.app.Activity;  
  22.   
  23. import android.app.AlertDialog;  
  24.   
  25. import android.content.Context;  
  26.   
  27. import android.os.Bundle;  
  28.   
  29. import android.view.Gravity;  
  30.   
  31. import android.view.LayoutInflater;  
  32.   
  33. import android.view.View;  
  34.   
  35. import android.view.View.OnClickListener;  
  36.   
  37. import android.widget.Button;  
  38.   
  39. import android.widget.EditText;  
  40.   
  41. import android.widget.PopupWindow;  
  42.   
  43. import android.widget.TextView;  
  44.   
  45.    
  46.   
  47. public class PopWinEx extends Activity {  
  48.   
  49.     /** Called when the activity is first created. */  
  50.   
  51.     Button MyButton;  
  52.   
  53.     Button MyExit;  
  54.   
  55.     @Override  
  56.   
  57.     public void onCreate(Bundle savedInstanceState) {  
  58.   
  59.         super.onCreate(savedInstanceState);  
  60.   
  61.         setContentView(R.layout.main);  
  62.   
  63.         MyButton = (Button)findViewById(R.id.myButton);  
  64.   
  65.         MyExit = (Button)findViewById(R.id.myExit);  
  66.   
  67.        MyButton.setOnClickListener(new clickEvent());  
  68.   
  69.        MyExit.setOnClickListener(new ExitEvent());  
  70.   
  71.          
  72.   
  73.    
  74.   
  75.     }  
  76.   
  77.     class clickEvent implements OnClickListener {  
  78.   
  79.    
  80.   
  81.        @Override  
  82.   
  83.        public void onClick(View v) {  
  84.   
  85.            // TODO Auto-generated method stub  
  86.   
  87.            if(v==MyButton) {  
  88.   
  89.               showPopWindow(PopWinEx.this,MyButton);  
  90.   
  91.            }  
  92.   
  93.            }  
  94.   
  95.        }  
  96.   
  97.     class ExitEvent implements OnClickListener {  
  98.   
  99.    
  100.   
  101.        @Override  
  102.   
  103.        public void onClick(View v) {  
  104.   
  105.            // TODO Auto-generated method stub  
  106.   
  107.            if(v==MyExit) {  
  108.   
  109.               finish();  
  110.   
  111.            }  
  112.   
  113.            }  
  114.   
  115.        }  
  116.   
  117.     private void showPopWindow(Context context, View parent) {  
  118.   
  119.        // TODO Auto-generated method stub  
  120.   
  121.        final PopupWindow pw;         
  122.   
  123.        View myView;        
  124.   
  125.        LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  126.   
  127.        myView = inflater.inflate(R.layout.pop, null);        
  128.   
  129.        Button pop_OK = (Button)myView.findViewById(R.id.button_ok);  
  130.   
  131.        Button pop_Cancel = (Button)myView.findViewById(R.id.button_cancel);  
  132.   
  133.        final EditText pop_User = (EditText)myView.findViewById(R.id.edittext);  
  134.   
  135.        final EditText pop_Password = (EditText)myView.findViewById(R.id.password);       
  136.   
  137.        pw = new PopupWindow(myView,500,200,true);         
  138.   
  139.        pop_OK.setOnClickListener(new OnClickListener(){  
  140.   
  141.            @Override  
  142.   
  143.            public void onClick(View v) {  
  144.   
  145.               showPop_PopWindow();  
  146.   
  147.               // TODO Auto-generated method stub                     
  148.   
  149.            }  
  150.   
  151.            private void showPop_PopWindow() {  
  152.   
  153.               // TODO Auto-generated method stub  
  154.   
  155.               View myView1;  
  156.   
  157.               myView1 = getLayoutInflater().inflate(R.layout.pop1,null);  
  158.   
  159.               TextView User_Is = (TextView)myView1.findViewById(R.id.useris);  
  160.   
  161.               TextView Password_Is = (TextView)myView1.findViewById(R.id.passwordis);  
  162.   
  163.               User_Is.setText(pop_User.getText().toString());  
  164.   
  165.               Password_Is.setText(pop_Password.getText().toString());  
  166.   
  167.               final PopupWindow pw_pw;  
  168.   
  169.               pw_pw = new PopupWindow(myView1,500,200,true);  
  170.   
  171.                 
  172.   
  173.               Button pop_pop_OK = (Button)myView1.findViewById(R.id.button_ok1);  
  174.   
  175.               Button pop_pop_Cancel = (Button)myView1.findViewById(R.id.button_cancel1);  
  176.   
  177.               pop_pop_Cancel.setOnClickListener(new OnClickListener(){  
  178.   
  179.                   @Override  
  180.   
  181.                   public void onClick(View v) {  
  182.   
  183.                      pw_pw.dismiss();//         
  184.   
  185.                        
  186.   
  187.                   }  
  188.   
  189.                });              
  190.   
  191.               pop_pop_OK.setOnClickListener(new OnClickListener(){  
  192.   
  193.                   @Override  
  194.   
  195.                   public void onClick(View v) {  
  196.   
  197.                       AlertDialog.Builder my_ADialog =new AlertDialog.Builder(PopWinEx.this);  
  198.   
  199.                       my_ADialog.setTitle("我是弹出对话框的弹出对话框");  
  200.   
  201.                       my_ADialog.setMessage("怎么样?学会了吗?");  
  202.   
  203.                       my_ADialog.show();       
  204.   
  205.                        
  206.   
  207.                   }  
  208.   
  209.                });  
  210.   
  211.               pw_pw.showAtLocation(myView1.findViewById(R.id.button_ok1), Gravity.CENTER, 200, 200);  
  212.   
  213.            }  
  214.   
  215.         });       
  216.   
  217.        pop_Cancel.setOnClickListener(new OnClickListener(){  
  218.   
  219.            @Override  
  220.   
  221.            public void onClick(View v) {  
  222.   
  223.               pw.dismiss();//                    
  224.   
  225.            }  
  226.   
  227.         });    
  228.   
  229.        pw.showAsDropDown(MyButton);  
  230.   
  231.     }     
  232.   
  233. }  


 

我们给PopupWindow,弹出窗口的弹出窗口和主界面,分别定义一个layout。对应的XML文件如下:

  1. main.xml  
  2.   
  3. <?xml version="1.0" encoding="utf-8"?>  
  4.   
  5. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  6.   
  7.     android:orientation="vertical"  
  8.   
  9.     android:layout_width="fill_parent"  
  10.   
  11.     android:layout_height="fill_parent"  
  12.   
  13.     android:background="#ff00ff"  
  14.   
  15.     >  
  16.   
  17. <Button    
  18.   
  19.     android:id="@+id/myButton"  
  20.   
  21.     android:layout_width="fill_parent"   
  22.   
  23.     android:layout_height="wrap_content"   
  24.   
  25.     android:text="@string/hello"  
  26.   
  27.     />  
  28.   
  29.     <Button    
  30.   
  31.     android:id="@+id/myExit"  
  32.   
  33.     android:layout_width="fill_parent"   
  34.   
  35.     android:layout_height="wrap_content"   
  36.   
  37.     android:text="退出程序"  
  38.   
  39.     />  
  40.   
  41. </LinearLayout>  
  42.   
  43. pop1.xml  
  44.   
  45. <?xml version="1.0" encoding="utf-8"?>  
  46.   
  47. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  48.   
  49.     android:orientation="vertical"  
  50.   
  51.     android:layout_width="fill_parent"  
  52.   
  53.     android:layout_height="fill_parent"  
  54.   
  55.     android:background="#ffff00"  
  56.   
  57.     >  
  58.   
  59. <TextView    
  60.   
  61.     android:id="@+id/mytextview"  
  62.   
  63.     android:layout_width="wrap_content"   
  64.   
  65.     android:layout_height="wrap_content"   
  66.   
  67.     android:text="@string/hello"  
  68.   
  69.     />  
  70.   
  71.     <TextView    
  72.   
  73.     android:text="您输入的账号是:"  
  74.   
  75.     android:layout_width="180dip"   
  76.   
  77.     android:layout_height="wrap_content"   
  78.   
  79.     />  
  80.   
  81.     <TextView    
  82.   
  83.     android:id="@+id/useris"  
  84.   
  85.     android:layout_width="wrap_content"   
  86.   
  87.     android:layout_height="wrap_content"   
  88.   
  89.     />  
  90.   
  91.     <TextView    
  92.   
  93.     android:text="您输入的密码是:"  
  94.   
  95.     android:layout_width="180dip"   
  96.   
  97.     android:layout_height="wrap_content"   
  98.   
  99.     />  
  100.   
  101.     <TextView    
  102.   
  103.     android:id="@+id/passwordis"  
  104.   
  105.     android:layout_width="wrap_content"   
  106.   
  107.     android:layout_height="wrap_content"   
  108.   
  109.     />  
  110.   
  111.     <LinearLayout   
  112.   
  113.     android:orientation="horizontal"  
  114.   
  115.     android:layout_width="fill_parent"  
  116.   
  117.     android:layout_height="fill_parent"  
  118.   
  119.     android:background="#ffff00"  
  120.   
  121.     >  
  122.   
  123.     <Button    
  124.   
  125.     android:id="@+id/button_ok1"  
  126.   
  127.     android:layout_width="wrap_content"   
  128.   
  129.     android:layout_height="wrap_content"   
  130.   
  131.     android:text="确定"  
  132.   
  133.     />  
  134.   
  135.     <Button    
  136.   
  137.     android:id="@+id/button_cancel1"  
  138.   
  139.     android:layout_width="wrap_content"   
  140.   
  141.     android:layout_height="wrap_content"   
  142.   
  143.     android:text="取消"  
  144.   
  145.     />  
  146.   
  147.     </LinearLayout>  
  148.   
  149. </LinearLayout>  
  150.   
  151. pop.xml  
  152.   
  153. <?xml version="1.0" encoding="utf-8"?>  
  154.   
  155. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  156.   
  157.     android:orientation="vertical"  
  158.   
  159.     android:layout_width="fill_parent"  
  160.   
  161.     android:layout_height="fill_parent"  
  162.   
  163.     android:background="#ffff00"  
  164.   
  165.     >  
  166.   
  167. <TextView    
  168.   
  169.     android:id="@+id/mytextview"  
  170.   
  171.     android:layout_width="wrap_content"   
  172.   
  173.     android:layout_height="wrap_content"   
  174.   
  175.     android:text="@string/hello"  
  176.   
  177.     />  
  178.   
  179.     <TextView    
  180.   
  181.     android:id="@+id/mytextview"  
  182.   
  183.     android:layout_width="wrap_content"   
  184.   
  185.     android:layout_height="wrap_content"   
  186.   
  187.     android:text="账号"  
  188.   
  189.     />  
  190.   
  191. <EditText    
  192.   
  193.     android:id="@+id/edittext"  
  194.   
  195.     android:layout_width="180dip"   
  196.   
  197.     android:layout_height="wrap_content"   
  198.   
  199.     />  
  200.   
  201.     <TextView    
  202.   
  203.       
  204.   
  205.     android:layout_width="wrap_content"   
  206.   
  207.     android:layout_height="wrap_content"   
  208.   
  209.     android:text="密码"  
  210.   
  211.     />  
  212.   
  213.     <EditText    
  214.   
  215.     android:id="@+id/password"  
  216.   
  217.     android:layout_width="180dip"   
  218.   
  219.     android:layout_height="wrap_content"   
  220.   
  221.     android:password="true"  
  222.   
  223.     />  
  224.   
  225.     <LinearLayout   
  226.   
  227.     android:orientation="horizontal"  
  228.   
  229.     android:layout_width="fill_parent"  
  230.   
  231.     android:layout_height="fill_parent"  
  232.   
  233.     android:background="#ffff00"  
  234.   
  235.     >  
  236.   
  237.     <Button    
  238.   
  239.     android:id="@+id/button_ok"  
  240.   
  241.     android:layout_width="wrap_content"   
  242.   
  243.     android:layout_height="wrap_content"   
  244.   
  245.     android:text="确定"  
  246.   
  247.     />  
  248.   
  249.     <Button    
  250.   
  251.     android:id="@+id/button_cancel"  
  252.   
  253.     android:layout_width="wrap_content"   
  254.   
  255.     android:layout_height="wrap_content"   
  256.   
  257.     android:text="取消"  
  258.   
  259.     />  
  260.   
  261.     </LinearLayout>  
  262.   
  263. </LinearLayout>  


 

说明:这里值得注意的是,弹出窗口上再次弹出窗口需要将调用findViewById函数的视图明确。然后将相应的变量设置成final类型。弹出窗口其实是一个View,这个View需要用     LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

       myView = inflater.inflate(R.layout.pop, null);来进行索引。找到相应的xml布局文件,来安排弹出窗口是什么样的。当然,在实际开发中,可能会遇见没有xml布局文件的View,这怎么办呢?直接new出来就可以了,比如:

           mView = new PopView(mContext, 800, 400);

       PopupWindow pw= new PopupWindow(mView,800,400,true);

这里我没有对string做特别的处理,因为时间比较紧张。这并不是一个好的编程习惯。希望大家把语言类的东西都放在string里去,不要硬写在程序代码里。

PopupWindow的特殊效果

PopupWindow的效果可以做的很炫,可以有边框,圆角,透明,渐变色,动画等。下面逐一来实现。

比如我要在上面的例子中实现这些操作,即可添加一个语句:

       myView.setBackgroundResource(R.drawable.round_win);

当然,最主要的就是round_win.xml里面所写的内容了:

  1. round_win.xml  
  2.   
  3. <?xml version="1.0" encoding="utf-8"?>      
  4.   
  5. <shape xmlns:android="http://schemas.android.com/apk/res/android"  
  6.   
  7.         android:shape="rectangle">  
  8.   
  9.         <gradient android:startColor="#e0000fff" android:endColor="#e000ff00"  
  10.   
  11.                 android:angle="90" /><!--背景颜色渐变 -->  
  12.   
  13.         <stroke android:dashWidth="2dp" android:dashGap="2dp"  
  14.   
  15.                 android:width="2dp" android:color="#ff0000"></stroke>  
  16.   
  17.         <!--描边 -->  
  18.   
  19.         <corners android:bottomRightRadius="5dp"  
  20.   
  21.                 android:bottomLeftRadius="5dp" android:topLeftRadius="5dp"  
  22.   
  23.                 android:topRightRadius="5dp" /><!--设置圆角-->  
  24.   
  25. </shape>  


 

当然,最主要的就是round_win.xml里面所写的内容了。对于上面,这条 shape 的定义,分别为渐变,在gradient 中startColor属性为开始的颜色,endColor 为渐变结束的颜色,下面的 angle 是角度。接下来是 stroke可以理解为边缘,dashWidth 表示宽度,dashGap 表示断续;corners 为拐角这里radius 属性为半径,最后是相对位置属性 padding。

android:color 是一个16 进制颜色。这个颜色由RGB 值指定,可带Alpha 。必须以“# ”开头,后面跟随Alpha-Red-Green-Blue。Alpha表示的是透明度,0为全透明,ff为全不透明。

       以上完成了弹出窗口的边框,渐变色,透明,圆角等操作。

下面进行有意思的动画操作。

       先设置一下弹出窗口的进入和退出的xml文件。首先要在res目录下建立文件夹anim,里面建立动画描述文件。如下所示:

  1. ani_in.xml  
  2.   
  3. <?xml version="1.0" encoding="utf-8"?>  
  4.   
  5. <set xmlns:android="http://schemas.android.com/apk/res/android">  
  6.   
  7.     <translate android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
  8.   
  9.        android:fromYDelta="-100"  
  10.   
  11.        android:toYDelta="0"  
  12.   
  13.        android:duration="1000"  
  14.   
  15.        android:fillEnabled="true"  
  16.   
  17.        android:fillAfter="true"  
  18.   
  19.        />  
  20.   
  21.         <scale android:fromXScale="0.6" android:toXScale="1.0"  
  22.   
  23.                 android:fromYScale="0.6" android:toYScale="1.0" android:pivotX="50%"  
  24.   
  25.                 android:pivotY="50%" android:duration="2000" />  
  26.   
  27.         <alpha android:interpolator="@android:anim/decelerate_interpolator"  
  28.   
  29.                 android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="2000" />  
  30.   
  31. </set>  
  32.   
  33. ani_out.xml  
  34.   
  35. <?xml version="1.0" encoding="utf-8"?>  
  36.   
  37. <set xmlns:android="http://schemas.android.com/apk/res/android"  
  38.   
  39.     android:oneshot="true"  
  40.   
  41.     >  
  42.   
  43.     <translate android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
  44.   
  45.        android:fromYDelta="0"  
  46.   
  47.        android:toYDelta="-100"  
  48.   
  49.        android:duration="1000"  
  50.   
  51.        android:fillEnabled="true"  
  52.   
  53.        android:fillAfter="true"  
  54.   
  55.        />  
  56.   
  57.                <scale android:fromXScale="1.0" android:toXScale="0.4"  
  58.   
  59.                 android:fromYScale="1.0" android:toYScale="0.4" android:pivotX="50%"  
  60.   
  61.                 android:pivotY="50%" android:duration="2000" />  
  62.   
  63.     <alpha android:interpolator="@android:anim/decelerate_interpolator"  
  64.   
  65.        android:fromAlpha="1.0"   
  66.   
  67.        android:toAlpha="0.0"   
  68.   
  69.        android:duration="2000"   
  70.   
  71.        />  
  72.   
  73. </set>  


 

在Values文件夹中建立style.xml文件如下:

style.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2.   
  3. <resources>  
  4.   
  5.     <style name="mytheme" parent="@android:style/Theme.Light">  
  6.   
  7.        <item name="android:windowNoTitle"> true </item>  
  8.   
  9.     </style>  
  10.   
  11.    
  12.   
  13.    
  14.   
  15.     <style name="PopupAnimation" parent="android:Animation">  
  16.   
  17.        <item name="android:windowEnterAnimation">@anim/ani_in</item>  
  18.   
  19.        <item name="android:windowExitAnimation">@anim/ani_out</item>  
  20.   
  21.     </style>  
  22.   
  23. </resources>  


 

    最后在Activity代码文件中加入:

       pw.setAnimationStyle(R.style.PopupAnimation);

动画效果就出来了,怎么样?很炫吧
 
原文地址:https://www.cnblogs.com/xgjblog/p/3994517.html