zoukankan      html  css  js  c++  java
  • Android Touch事件传递机制 二:单纯的(伪生命周期) 这个清楚一点

    转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165

    在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在我想使用一个demo以及一个实例来学习一下Andorid中的Touch事件处理过程。

    在Android系统中,和Touch事件分发和处理紧密相关的三个函数如下:
    (1) public boolean dispatchTouchEvent(MotionEvent ev)
    (2) public boolean onInterceptTouchEvent(MotionEvent ev)//这个方法在一个事件周期(down,move,up)内只要onInterceptTouchEvent(down/move(up已经是最后一次不算)只要拦截了一次)或onTouchEvent(中的ACTION_DOWN)有一个返回true,就不会再走第二次。
    (3) public boolean onTouchEvent(MotionEvent event)


    这三个方法我在前一篇文章中都对他们的源码进行了分析:方法1主要是对Touch事件进行分发,方法2主要是对Touch事件进行拦截,方法3是对Touch事件进行处理

    这三个方法主要存在于ViewGroup,View,Activity中,具体情况如下图:

      ViewGroup View  Activity
    dispatchTouchEvent
    onInterceptTouchEvent
    onTouchEvent

    下面我们就使用一个demo来看看这些方法的执行流程:
    自定义一个类:MyLayoutFirst.java

    [java] view plain copy
     
     print?
    1. public class MyLayoutFirst extends LinearLayout  
    2. {  
    3.   private static final String TAG = "MyLayoutFirst";  
    4.   public MyLayoutFirst(Context context, AttributeSet attrs)  
    5.   {  
    6.     super(context, attrs);  
    7.   }  
    8.     
    9.   @Override  
    10.   public boolean onInterceptTouchEvent(MotionEvent ev)  
    11.   {  
    12.     Log.w("yzy", "MyLayoutFirst->onInterceptTouchEvent->"+MyUtils.getActionName(ev));  
    13.     return super.onInterceptTouchEvent(ev);  
    14.   }  
    15.     
    16.   @Override  
    17.   public boolean onTouchEvent(MotionEvent event)  
    18.   {  
    19.     Log.e("yzy", "MyLayoutFirst->onTouchEvent->"+MyUtils.getActionName(event));  
    20.     return super.onTouchEvent(event);  
    21.   }  
    22.     
    23.   @Override  
    24.   public boolean dispatchTouchEvent(MotionEvent ev)  
    25.   {  
    26.     Log.i("yzy", "MyLayoutFirst->dispatchTouchEvent->"+MyUtils.getActionName(ev));  
    27.     return super.dispatchTouchEvent(ev);  
    28.   }  
    29.     
    30. }  


    自定义一个类;MyLayoutSecond.java

    [java] view plain copy
     
     print?
    1. public class MyLayoutSecond extends LinearLayout  
    2. {  
    3.   private static final String TAG = "MyLayoutSecond";  
    4.   public MyLayoutSecond(Context context, AttributeSet attrs)  
    5.   {  
    6.     super(context, attrs);  
    7.   }  
    8.     
    9.   @Override  
    10.   public boolean onTouchEvent(MotionEvent event)  
    11.   {  
    12.     Log.e("yzy", "MyLayoutSecond->MyLayoutSecond->"+MyUtils.getActionName(event));  
    13.     return super.onTouchEvent(event);  
    14.   }  
    15.     
    16.   @Override  
    17.   public boolean onInterceptTouchEvent(MotionEvent ev)  
    18.   {  
    19.     Log.w("yzy", "MyLayoutSecond->onInterceptTouchEvent->"+MyUtils.getActionName(ev));  
    20.     return super.onInterceptTouchEvent(ev);  
    21.   }  
    22.     
    23.   @Override  
    24.   public boolean dispatchTouchEvent(MotionEvent ev)  
    25.   {  
    26.     Log.i("yzy", "MyLayoutSecond->dispatchTouchEvent->"+MyUtils.getActionName(ev));  
    27.     return super.dispatchTouchEvent(ev);  
    28.   }  
    29.   
    30.    
    31. }  


    加入到main_layout.xml中

    [html] view plain copy
     
     print?
    1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="match_parent"  
    5.    >  
    6.   
    7.     <com.event.demo.MyLayoutFirst   
    8.         android:id="@+id/layout_first"  
    9.         android:layout_width="match_parent"  
    10.         android:layout_height="match_parent"  
    11.         android:background="#FF0000"  
    12.         >  
    13.         <com.event.demo.MyLayoutSecond  
    14.             android:id="@+id/layout_second"  
    15.             android:layout_width="320dip"  
    16.             android:layout_height="120dip"  
    17.             android:layout_gravity="center"  
    18.             android:background="#0000FF"  
    19.         >  
    20.           
    21.     </com.event.demo.MyLayoutSecond>  
    22.     </com.event.demo.MyLayoutFirst>  
    23.   
    24. </RelativeLayout>  


    MainActivity中加入onTouchEvent方法

    [java] view plain copy
     
     print?
    1. public class MainActivity extends Activity  
    2. {  
    3.   
    4.   @Override  
    5.   protected void onCreate(Bundle savedInstanceState)  
    6.   {  
    7.     super.onCreate(savedInstanceState);  
    8.     setContentView(R.layout.activity_main);  
    9.   }  
    10.   
    11.   @Override  
    12.   public boolean onCreateOptionsMenu(Menu menu)  
    13.   {  
    14.     // Inflate the menu; this adds items to the action bar if it is present.  
    15.     getMenuInflater().inflate(R.menu.main, menu);  
    16.     return true;  
    17.   }  
    18.     
    19.   @Override  
    20.   public boolean dispatchTouchEvent(MotionEvent ev)  
    21.   {  
    22.     Log.i("yzy", "MainActivity->dispatchTouchEvent->"+MyUtils.getActionName(ev));  
    23.     return super.dispatchTouchEvent(ev);  
    24.   }  
    25.     
    26.   @Override  
    27.   public boolean onTouchEvent(MotionEvent event)  
    28.   {  
    29.     Log.e("yzy", "MainActivity->onTouchEvent->"+MyUtils.getActionName(event));  
    30.     return super.onTouchEvent(event);  
    31.   }  
    32. }  


    最后就一个工具类,用来将事件id转换为字符串。

    [java] view plain copy
     
     print?
    1. public class MyUtils  
    2. {  
    3.   private static final String TAG = "MyUtils";  
    4.   public static String getActionName(MotionEvent event)  
    5.   {  
    6.     String name="";  
    7.     switch(event.getAction())  
    8.     {  
    9.       case MotionEvent.ACTION_DOWN:  
    10.         name="ACTION_DOWN";  
    11.         break;  
    12.       case MotionEvent.ACTION_MOVE:  
    13.         name="ACTION_MOVE";  
    14.         break;  
    15.       case MotionEvent.ACTION_UP:  
    16.         name="ACTION_UP";  
    17.         break;  
    18.     }  
    19.     return name;  
    20.   }  
    21. }  


    运行效果如图:

    第一中情况:

     

    MainActivity

    MyLayoutFirst

    MyLayoutSecond

    dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    onInterceptTouchEvent

    --

    super.onInterceptTouchEvent(ev)

    super.onInterceptTouchEvent(ev)

    onTouchEvent

    super.onTouchEvent

    super.onTouchEven

    super.onTouchEvent

    运行结果:

    其中蓝色部分是MyLayoutSecond.Java ,红色部分是MyLayoutFirst.java
    现在我点击一下蓝色部分:运行结果如图:

    从图中可以看出,事件最先被Activity捕获,然后分发给 MyLayoutFirst,MyLayoutFirst首先调用自身的onInterceptTouchEvent判断是否将该事件拦截,由于默认返回是false,所以没有拦截,从而事件分发给了MyLayoutSecond,MyLayoutSecond同样通过dispatchTouchEvent分发出去,分发出去之前同样检查是否被拦截,默认都是没有被拦截的,但是由于MyLayoutSecond是没有子视图的,所有最终事件有自己处理,调用自身的onTouchEvent方法,由于该方法默认返回的是false,所以认为此事件是没有被消费掉的,继续传递到了MyLayoutFirst中,同样也没有消费这个事件,最终传递到了Mainactivity,继续往后看发现后面的ACTION_MOVE和ACTION_UP并没有传入MyLayoutFirst和MyLayoutSecond,这是因为一旦某一个事件没有被处理,后面的事件是不会被分发的。所以ACTION_MOVE和ACTION_UP直接被MainActivity处理掉了。

    下面再看第二种情况:

     

    MainActivity

    MyLayoutFirst

    MyLayoutSecond

    dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    onInterceptTouchEvent

    --

    true

    super.onInterceptTouchEvent(ev)

    onTouchEvent

    super.onTouchEvent

    super.onTouchEvent

    super.onTouchEvent

    运行结果如下:

    从图中可以看出,事件传递到了MyLayoutFirst后没有分发到MyLayoutSecond,直接调用自身的onTouchEvent,由于返回的是false,导致事件没有消费,最终传递给了MainActivity,
    而且后续事件也没有传递到MyLayoutFirst和MyLayoutSecond,直接被MainActivity处理

    第三种情况:

     

    MainActivity

    MyLayoutFirst

    MyLayoutSecond

    dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    onInterceptTouchEvent

    --

    true

    super.onInterceptTouchEvent(ev)

    onTouchEvent

    super.onTouchEvent

    true

    super.onTouchEvent

    运行结果:

    和情况二不同的是MyLayoutFirst的onTouchEvent返回了true,也就是说MyLayoutFirst消费了此事件,所以ACTION_DOWN也没有再传给MainActivity,并且ACTION_MOVE和ACTION_UP
    均传给了MyLayoutFirst

    第四中情况:

     

    MainActivity

    MyLayoutFirst

    MyLayoutSecond

    dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    onInterceptTouchEvent

    --

    super.onInterceptTouchEvent(ev)

    super.onInterceptTouchEvent(ev)

    onTouchEvent

    super.onTouchEvent

    super.onTouchEven

    true

    运行结果:

    发现所有的事件都是传递到了MyLayoutSecond后被消费了

    第五种情况:

     

    MainActivity

    MyLayoutFirst

    MyLayoutSecond

    dispatchTouchEvent down

                move

                up

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    onInterceptTouchEvent down

                move

                up

    --

    false

    true/false

    false

    super.onInterceptTouchEvent(ev)

    super.onInterceptTouchEvent(ev)

    super.onInterceptTouchEvent(ev)

    onTouchEvent    down

               move

               up

    super.onTouchEvent

    super.onTouchEvent

    super.onTouchEvent

    true

    super.onTouchEven

    super.onTouchEven

    super.onTouchEvent

    super.onTouchEvent

    super.onTouchEvent表示false

    运行结果:

    08-12 10:20:10.592: I/yzy(28806): MainActivity->dispatchTouchEvent->ACTION_DOWN
    08-12 10:20:10.602: I/yzy(28806): MyLayoutFirst->dispatchTouchEvent->ACTION_DOWN
    08-12 10:20:10.602: W/yzy(28806): MyLayoutFirst->onInterceptTouchEvent->ACTION_DOWN
    08-12 10:20:10.612: I/yzy(28806): MyLayoutSecond->dispatchTouchEvent->ACTION_DOWN
    08-12 10:20:10.612: W/yzy(28806): MyLayoutSecond->onInterceptTouchEvent->ACTION_DOWN
    08-12 10:20:10.622: E/yzy(28806): MyLayoutSecond->MyLayoutSecond->ACTION_DOWN
    08-12 10:20:10.622: E/yzy(28806): MyLayoutFirst->onTouchEvent->ACTION_DOWN
    08-12 10:20:10.692: I/yzy(28806): MainActivity->dispatchTouchEvent->ACTION_MOVE
    08-12 10:20:10.702: I/yzy(28806): MyLayoutFirst->dispatchTouchEvent->ACTION_MOVE
    08-12 10:20:10.702: E/yzy(28806): MyLayoutFirst->onTouchEvent->ACTION_MOVE
    08-12 10:20:10.702: E/yzy(28806): MainActivity->onTouchEvent->ACTION_MOVE
    08-12 10:20:10.712: I/yzy(28806): MainActivity->dispatchTouchEvent->ACTION_MOVE
    08-12 10:20:10.712: I/yzy(28806): MyLayoutFirst->dispatchTouchEvent->ACTION_MOVE
    08-12 10:20:10.722: E/yzy(28806): MyLayoutFirst->onTouchEvent->ACTION_MOVE
    08-12 10:20:10.722: E/yzy(28806): MainActivity->onTouchEvent->ACTION_MOVE
    08-12 10:20:10.732: I/yzy(28806): MainActivity->dispatchTouchEvent->ACTION_UP
    08-12 10:20:10.732: I/yzy(28806): MyLayoutFirst->dispatchTouchEvent->ACTION_UP
    08-12 10:20:10.742: E/yzy(28806): MyLayoutFirst->onTouchEvent->ACTION_UP
    08-12 10:20:10.742: E/yzy(28806): MainActivity->onTouchEvent->ACTION_UP

    这种情况表明,只要一个ViewGrounp在onTouchEvent的ACTION_DOWN中消费了事件,那么不论这个ViewGrounp的onTouchEvent的ACTION_MOVE,ACTION_UP中返回true(消费事件,事件结束)还是false(事件上传),事件都会传到这个ViewGrounp得onTouchEvent中(前提是没有被在前面就拦截掉),而且这个ViewGrounp得onInterceptTouchEvent也不会再走

    第六种情况:

     

    MainActivity

    MyLayoutFirst

    MyLayoutSecond

    dispatchTouchEvent down

                move

                up

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    onInterceptTouchEvent down

                move

                up

    --

    false

    false

    false

    super.onInterceptTouchEvent(ev)

    super.onInterceptTouchEvent(ev)

    super.onInterceptTouchEvent(ev)

    onTouchEvent    down

               move

               up

    super.onTouchEvent

    super.onTouchEvent

    super.onTouchEvent

    true

    true

    true

    true

    super.onTouchEvent

    super.onTouchEvent表示false

    运行结果:

    08-12 11:24:24.992: I/yzy(15159): MainActivity->dispatchTouchEvent->ACTION_DOWN
    08-12 11:24:25.002: I/yzy(15159): MyLayoutFirst->dispatchTouchEvent->ACTION_DOWN
    08-12 11:24:25.002: W/yzy(15159): MyLayoutFirst->onInterceptTouchEvent->ACTION_DOWN
    08-12 11:24:25.012: I/yzy(15159): MyLayoutSecond->dispatchTouchEvent->ACTION_DOWN
    08-12 11:24:25.012: W/yzy(15159): MyLayoutSecond->onInterceptTouchEvent->ACTION_DOWN
    08-12 11:24:25.022: E/yzy(15159): MyLayoutSecond->MyLayoutSecond->ACTION_DOWN
    08-12 11:24:25.032: I/yzy(15159): MainActivity->dispatchTouchEvent->ACTION_MOVE
    08-12 11:24:25.032: I/yzy(15159): MyLayoutFirst->dispatchTouchEvent->ACTION_MOVE
    08-12 11:24:25.042: W/yzy(15159): MyLayoutFirst->onInterceptTouchEvent->ACTION_MOVE
    08-12 11:24:25.042: I/yzy(15159): MyLayoutSecond->dispatchTouchEvent->ACTION_MOVE
    08-12 11:24:25.052: E/yzy(15159): MyLayoutSecond->MyLayoutSecond->ACTION_MOVE
    08-12 11:24:25.052: E/yzy(15159): MainActivity->onTouchEvent->ACTION_MOVE
    08-12 11:24:25.092: I/yzy(15159): MainActivity->dispatchTouchEvent->ACTION_MOVE
    08-12 11:24:25.092: I/yzy(15159): MyLayoutFirst->dispatchTouchEvent->ACTION_MOVE
    08-12 11:24:25.092: W/yzy(15159): MyLayoutFirst->onInterceptTouchEvent->ACTION_MOVE
    08-12 11:24:25.092: I/yzy(15159): MyLayoutSecond->dispatchTouchEvent->ACTION_MOVE
    08-12 11:24:25.102: E/yzy(15159): MyLayoutSecond->MyLayoutSecond->ACTION_MOVE
    08-12 11:24:25.102: E/yzy(15159): MainActivity->onTouchEvent->ACTION_MOVE
    08-12 11:24:25.132: I/yzy(15159): MainActivity->dispatchTouchEvent->ACTION_MOVE
    08-12 11:24:25.132: I/yzy(15159): MyLayoutFirst->dispatchTouchEvent->ACTION_MOVE
    08-12 11:24:25.132: W/yzy(15159): MyLayoutFirst->onInterceptTouchEvent->ACTION_MOVE
    08-12 11:24:25.142: I/yzy(15159): MyLayoutSecond->dispatchTouchEvent->ACTION_MOVE
    08-12 11:24:25.142: E/yzy(15159): MyLayoutSecond->MyLayoutSecond->ACTION_MOVE
    08-12 11:24:25.142: E/yzy(15159): MainActivity->onTouchEvent->ACTION_MOVE
    08-12 11:24:25.152: I/yzy(15159): MainActivity->dispatchTouchEvent->ACTION_UP
    08-12 11:24:25.152: I/yzy(15159): MyLayoutFirst->dispatchTouchEvent->ACTION_UP
    08-12 11:24:25.162: W/yzy(15159): MyLayoutFirst->onInterceptTouchEvent->ACTION_UP
    08-12 11:24:25.162: I/yzy(15159): MyLayoutSecond->dispatchTouchEvent->ACTION_UP
    08-12 11:24:25.162: E/yzy(15159): MyLayoutSecond->MyLayoutSecond->ACTION_UP
    08-12 11:24:25.172: E/yzy(15159): MainActivity->onTouchEvent->ACTION_UP

    当一个ViewGrounp在onTouchEvent的ACTION_DOWN中消费了事件,但是在它的onTouchEvent的ACTION_MOVE,ACTION_UP中返回false的话,虽然事件还是会传递到它的onTouchEvent中,但是它却会从它自己的onTouchEvent直接上传回Activity的onTouchEvent中,中间不会经过其他的ViewGrounp


    第七种情况:

     

    MainActivity

    MyLayoutFirst

    MyLayoutSecond

    dispatchTouchEvent down

                move

                up

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    super.dispatchTouchEvent

    onInterceptTouchEvent down

                move

                up

    --

    false

    true

    false

    super.onInterceptTouchEvent(ev)

    super.onInterceptTouchEvent(ev)

    super.onInterceptTouchEvent(ev)

    onTouchEvent    down

               move

               up

    super.onTouchEvent

    super.onTouchEvent

    super.onTouchEvent

    true

    false

    false

    true

    super.onTouchEvent

    super.onTouchEvent表示false

    运行结果:

    08-12 11:34:54.897: I/yzy(18093): MainActivity->dispatchTouchEvent->ACTION_DOWN
    08-12 11:34:54.897: I/yzy(18093): MyLayoutFirst->dispatchTouchEvent->ACTION_DOWN
    08-12 11:34:54.917: W/yzy(18093): MyLayoutFirst->onInterceptTouchEvent->ACTION_DOWN
    08-12 11:34:54.937: I/yzy(18093): MyLayoutSecond->dispatchTouchEvent->ACTION_DOWN
    08-12 11:34:54.947: W/yzy(18093): MyLayoutSecond->onInterceptTouchEvent->ACTION_DOWN
    08-12 11:34:54.957: E/yzy(18093): MyLayoutSecond->MyLayoutSecond->ACTION_DOWN
    08-12 11:34:54.967: I/yzy(18093): MainActivity->dispatchTouchEvent->ACTION_MOVE
    08-12 11:34:54.967: I/yzy(18093): MyLayoutFirst->dispatchTouchEvent->ACTION_MOVE
    08-12 11:34:54.977: W/yzy(18093): MyLayoutFirst->onInterceptTouchEvent->ACTION_MOVE
    08-12 11:34:54.977: I/yzy(18093): MyLayoutSecond->dispatchTouchEvent->
    08-12 11:34:54.977: E/yzy(18093): MyLayoutSecond->MyLayoutSecond->
    08-12 11:34:54.977: E/yzy(18093): MainActivity->onTouchEvent->ACTION_MOVE
    08-12 11:34:55.017: I/yzy(18093): MainActivity->dispatchTouchEvent->ACTION_MOVE
    08-12 11:34:55.027: I/yzy(18093): MyLayoutFirst->dispatchTouchEvent->ACTION_MOVE
    08-12 11:34:55.027: E/yzy(18093): MyLayoutFirst->onTouchEvent->ACTION_MOVE
    08-12 11:34:55.037: E/yzy(18093): MainActivity->onTouchEvent->ACTION_MOVE
    08-12 11:34:55.047: I/yzy(18093): MainActivity->dispatchTouchEvent->ACTION_MOVE
    08-12 11:34:55.057: I/yzy(18093): MyLayoutFirst->dispatchTouchEvent->ACTION_MOVE
    08-12 11:34:55.067: E/yzy(18093): MyLayoutFirst->onTouchEvent->ACTION_MOVE
    08-12 11:34:55.067: E/yzy(18093): MainActivity->onTouchEvent->ACTION_MOVE
    08-12 11:34:55.077: I/yzy(18093): MainActivity->dispatchTouchEvent->ACTION_UP
    08-12 11:34:55.087: I/yzy(18093): MyLayoutFirst->dispatchTouchEvent->ACTION_UP
    08-12 11:34:55.087: E/yzy(18093): MyLayoutFirst->onTouchEvent->ACTION_UP
    08-12 11:34:55.097: E/yzy(18093): MainActivity->onTouchEvent->ACTION_UP

    MyLayoutSecond的onTouchEvent的ACTION_DOWN中消费了事件,所以事件会默认往他那儿传递,但是被MyLayoutFirst的onInterceptTouchEvent在ACTION_MOVE给半路拦截
    导致后续操作都被拦截传递给MyLayoutFirst的onTouchEvent(但由于其没有消费事件,所以会往上传)


    其实还有很多其他组合方式,大家如果又兴趣可以自己尝试改变每个函数的返回值,查看打印结果,这里我就不一一列举了。。。。。


    最后我会提供一个小demo演示如何解决滑动冲突,背景如下:
    一个ViewPager里面包含两个Framgent,有一个Fragment里面有一个HorizontalListView ,如何滑动冲突?
    我就贴出关键代码吧

    [java] view plain copy

    print?
    horizontal=(HorizontalListView)view.findViewById(R.id.hscroll);
    horizontal.setOnTouchListener(new OnTouchListener()
    {

    @Override
    public boolean onTouch(View arg0, MotionEvent event)
    {
    if(event.getAction()==MotionEvent.ACTION_DOWN)
    {
    parent.requestDisallowInterceptTouchEvent(true);
    }else if(event.getAction()==MotionEvent.ACTION_UP)
    {
    parent.requestDisallowInterceptTouchEvent(false);
    }
    return false;
    }
    });

    加入这段代码就可以避免滑动冲突了,至于为什么大家可以参考我的前以前文章《Android Touch 事件传递机制详解 上》 这两个demo的例子我均会上传下载的

  • 相关阅读:
    java 之 对象与垃圾回收
    Java 之 内部类
    java 接口(interface)
    抽象类
    Java之fianl修饰符
    类的继承
    上传图片
    一般处理程序 给图片添加文字水印
    socket
    初识linq,lambda表达式
  • 原文地址:https://www.cnblogs.com/lipeineng/p/5416724.html
Copyright © 2011-2022 走看看