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

来源:互联网 发布:背包装什么 知乎 编辑:程序博客网 时间:2024/05/16 15:14

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei。


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

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

小例子

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

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 0
原创粉丝点击