0
点赞
收藏
分享

微信扫一扫

Pro Android学习笔记(一四七):拖拽(1):通过MotionEvent来实现


作者@恺风Wei。

拖拽(Drag and Drop)在Windows电脑很常用,用户使用很方便。在Android中,我们见图标拖入到垃圾桶进行应用删除,以及重新安排图标,这些都是拖拽的例子。

Android3.0引入了拖拽能力,而在此之前,开发者可以利用触屏MotionEvent来实现。市面当仍有少量的Android2.x机器,我们先来看看自行通过MotionEvent的实现方式。

小例子

小例子允许拖拽一个蓝点,如果拖拽到特定的计数器位置,计数器加1,蓝点复位。

Pro Android学习笔记(一四七):拖拽(1):通过MotionEvent来实现_android

layout的xml代码如下:

<?xml version="1.0" encoding="utf-8"?> 
<!-- 使用了FrameLayout,因此自定义的Dot可以层叠在LineareLayout上面 --><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" …… >
     <LinearLayout android:id="@+id/counters"  android:orientation="vertical" …….>
         <TextView android:id="@+id/counter_top" 
             android:text="0" 
             android:background="#aaaaaa" 
             android:layout_width="60dp" 
             android:layout_height="wrap_content" 
             android:layout_gravity="right" 
             android:layout_marginTop="30dp" 
             android:layout_marginBottom="30dp" 
             android:padding="10dp"/>          
         <TextView android:id="@+id/counter_middle" …… >        <!-- 同上,略  --> 
         <TextView android:id="@+id/counter_buttom" …… >        <!-- 同上,略  --> 
     </LinearLayout> 
     <!-- 自定义的View MyDot--> 
     <cn.wei.flowingflying.testdraganddrop.MyDot android:id="@+id/dot"
         android:layout_width="wrap_content" 
         android:layout_height="wrap_content"/> 
 </FrameLayout>

相关的Activity很简单,如下:

public class TestOldDrapAndDropActivity extends Activity{ 
     public LinearLayout countersLayout = null; 
     @Override 
     protected void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState); 
         setContentView(R.layout.drag_drop_old_activity); 
         countersLayout = (LinearLayout)findViewById(R.id.counters); 
     } 
 }

关键在于自定义的View,即MyDot,代码如下:

public class MyDot extends View{ 
    private Context myContext; 
     private Paint myPaint;      
     private float left = 0f; 
     private float top = 0f; 
     private float radius = 20f; 
     private float offsetX, offsetY;  
      
     public MyDot(Context context, AttributeSet attriSet){ 
         super(context,attriSet); 
         myContext = context;          
         myPaint = new Paint(); 
         myPaint.setColor(Color.BLUE); 
         myPaint.setAntiAlias(true);        
     }  

    @Override //【1】画出view,(left,top)为该Dot的左上角,圆心在(left+radius,top+radius)。
     public void draw(Canvas canvas) { 
         canvas.drawCircle(left + radius, top + radius, radius, myPaint);     }  

     @Override //【2】通过MotionEvent,自行处理拖拽。    public boolean onTouchEvent(MotionEvent event) {                 
         float eventX = event.getX(); 
         float eventY = event.getY(); 
         
         switch(event.getAction()){ 
         case MotionEvent.ACTION_DOWN: 
             //【2.1】确保按在Dot上,如果距离太远,认为用户并非真的要拖拽Dot,这时返回false,不再处理该动作。(left+radius,top+radius)为圆心,要确保在 圆心±radius范围内。由于手指比较粗大,我们将范围再扩大一点,为圆心±(radius+20)范围中
             if(!(left-20 < eventX && eventX < left + radius*2 + 20 
                     && top -20 < eventY && eventY < top + radius*2 + 20)){ 
                 return false; //该动作不再处理 
            } 
            //【2.2】由于用户不会正好地按在Dot的左上角(draw()根据此点构图),记住触点与左上角的偏差            offsetX = eventX - left; 
             offsetY = eventY - top; 
             break; 
             
         //【2.3】跟随移动    
         case MotionEvent.ACTION_MOVE:        
         case MotionEvent.ACTION_CANCEL: 
             left = eventX - offsetX; 
             top = eventY - offsetY;            
             break; 
         
         //【2.4】结束拖拽,进行相应处理:如果拖拽到特定的区域,进行相关的处理,本例子如在counter内,则counter加一,且Dot复位。在某些情况,我们在处理后会设置view不可视,或更直接地将view从viewGroup中删除removeView()
         case MotionEvent.ACTION_UP: 
             checkDrop(left + radius,top+radius); 
             break; 
         } 
         
         //-----------下面确保Dot不会移出屏幕范围-------------,从略 
         … … 
          
         invalidate(); //【2.5】redraw if can seen
        return true; 
     }  
      
    //【2.4】计算是否与Textview的范围重叠,如重叠,进行counter++,并将Dot复位到左上角    private void checkDrop(float x, float y){   
        Log.v("MyDot", "Check target : " + x + "," + y);
         int viewCount = ((TestOldDrapAndDropActivity)myContext).countersLayout.getChildCount();  
         for(int i = 0 ; i < viewCount; i ++){ 
             View view = ((TestOldDrapAndDropActivity)myContext).countersLayout.getChildAt(i);
             if(view.getClass() == TextView.class){ 
                 // Dot在TextView(左上角)的右边,才是目标X坐标位置                  if( !(x > view.getLeft() )){ 
                     continue; 
                 } 
                 
                 // Dot的y值TextView上下之间,目标Y坐标范围 
                 if( !(view.getTop() < y && y < view.getBottom())){ 
                     continue; 
                 } 
                 
                 //Dot落在TextView的范围中                 Log.v("MyDot", "--检测到在TextView中");
                 int count = Integer.parseInt(((TextView)view).getText().toString());
                 ((TextView)view).setText(String.valueOf(++count));                 
                 //Dot复位 
                 left = 0; 
                 top = 0; 
                 return; 
             } 
         } 
     } 
     
 }

 相关小例子代码:Pro Android学习:拖拽小例子


举报

相关推荐

0 条评论