可拖动EditText的实现

2015-06-26

昨天,群里面一个小伙伴发了个求助,要求实现一个可以拖动的EditText,本来以为这个应该不难,自定义继承EditText然后监听下OnTouch事件应该就可以了,后来想了想还有一些细节问题容易忽略,比如区分点击和滑动,软键盘呼出以后又回到原来地方的缘由,这个还是有一些细节需要深究的,废话不多说,上代码。

首先自定义DrawEdxtView,继承至EditText:

package com.yong.drawedit;

import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class DrawEdxtView extends EditText implements OnTouchListener {

    private Context mContext;

    private int screenWidth, screenHeight;
    private void getDisplayMetrics() {
        DisplayMetrics dm = getResources().getDisplayMetrics();
        screenWidth = dm.widthPixels;
        screenHeight = dm.heightPixels - 50;
    }

    private int lastX, lastY;

    private int downX, downY; // 按下View的X,Y坐标
    private int upX, upY; // 放手View的X,Y坐标
    private int rangeDifferenceX, rangeDifferenceY; // 放手和按下X,Y值差
    private int mDistance = 10; // 设定点击事件的移动距离值
    private int mL,mB,mR,mT;//重绘时layout的值

    public DrawEdxtView(Context context) {
        super(context);
        mContext = context;
        getDisplayMetrics();
        setOnTouchListener(this);
    }

        public DrawEdxtView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        getDisplayMetrics();
        setOnTouchListener(this);
    }

    public DrawEdxtView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        getDisplayMetrics();
        setOnTouchListener(this);
    }

    /*隐藏键盘*/
    public static void hideSoftInput(Activity mContext, View view) {
        InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm.isActive()) {
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    }

    /*弹出键盘*/
    public static void showSoftInput(Activity mContext, View view) {
        InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm.isActive()) {
            imm.showSoftInput(view, 0);
        }
    }

    public interface IOnKeyboardStateChangedListener{
        public void openKeyboard();
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            downX = (int) event.getRawX();
            downY = (int) event.getRawY();

            lastX = (int) event.getRawX();// 获取触摸事件触摸位置的原始X坐标
            lastY = (int) event.getRawY();

            Log.d("按下:", downX + "----X轴坐标");
            Log.d("按下:", downY + "----Y轴坐标");
            break;
        case MotionEvent.ACTION_MOVE:
            int dx = (int) event.getRawX() - lastX;
            int dy = (int) event.getRawY() - lastY;

            mL = v.getLeft() + dx;
            mB = v.getBottom() + dy;
            mR = v.getRight() + dx;
            mT = v.getTop() + dy;

            if (mL < 0) {
                mL = 0;
                mR = mL + v.getWidth();
            }

            if (mT < 0) {
                mT = 0;
                mB = mT + v.getHeight();
            }

            if (mR > screenWidth) {
                mR = screenWidth;
                mL = mR - v.getWidth();
            }

            if (mB > screenHeight) {
                mB = screenHeight;
                mT = mB - v.getHeight();
            }
            v.layout(mL, mT, mR, mB);
            Log.d("绘制:", "l="+mL+ ";t="+ mT+";r="+mR+";b="+mB);

            lastX = (int) event.getRawX();
            lastY = (int) event.getRawY();
            v.postInvalidate();

            v.setFocusable(false);
            v.setFocusableInTouchMode(false);
            hideSoftInput((Activity)mContext, v);
            break;
        case MotionEvent.ACTION_UP:
            upX = (int) event.getRawX();
            upY = (int) event.getRawY();

            Log.d("离开:", upX + "----X轴坐标");
            Log.d("离开:", upY + "----Y轴坐标");

            rangeDifferenceX = upX - downX;
            rangeDifferenceY = upY - downY;
            if (rangeDifferenceX > 0 && rangeDifferenceX <= mDistance) {
                if (rangeDifferenceY >= 0 && rangeDifferenceY <= mDistance) {
                    v.setFocusable(true);
                    v.setFocusableInTouchMode(true);
                    Log.d("是否是点击事件:", true + "");
                
                } else {
                    if (rangeDifferenceY <= 0 && rangeDifferenceY >= -mDistance) {
                        v.setFocusable(true);
                        v.setFocusableInTouchMode(true);
                        Log.d("是否是点击事件:", true + "");

                    } else {
                        v.setFocusable(false);
                        v.setFocusableInTouchMode(false);
                        Log.d("是否是点击事件:", false + "");
                    }
                }
            } else {
                if (rangeDifferenceX <= 0 && rangeDifferenceX >= -mDistance) {
                    v.setFocusable(true);
                    v.setFocusableInTouchMode(true);
                    Log.d("是否是点击事件:", true + "");

                } else {
                    v.setFocusable(false);
                    v.setFocusableInTouchMode(false);
                    Log.d("是否是点击事件:", false + "");
                }
            }
            break;
        default:
            break;
        }
        return false;
    }

}

在这个自定义控件里面主要干了一件事情,监听OnTouch事件,然后判断是拖动还是点击事件,如果是拖动,就让EditText无法获取焦点,保证拖动事件不会被焦点事件中断,在拖动的过程中,不断的postInvalidate(),更新界面。

其次,在用到的地方使用布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/keyboardRelativeLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <com.yong.drawedit.DrawEdxtView
        android:id="@+id/drawEdit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal|center_vertical"
        android:layout_marginLeft="50dip"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:gravity="center_vertical|center_horizontal"
        android:hint="拖动试试"
        android:text="" />

</LinearLayout>

最后还有一个需要注意的地方:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yong.drawedit"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.yong.drawedit.MainActivity"
            android:label="@string/app_name" 
            android:windowSoftInputMode="adjustNothing">
            <intent-filter>
                    <action android:name="android.intent.action.MAIN" />

                    <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

在Manifest中需要设置软键盘的模式,不然呼出软键盘以后会对布局有一定的影响。

代码奉上:DrawViewDemo.zip

3 条回应:“可拖动EditText的实现”

已有 3 条评论

  1. edward edward说道:

    你好,我用的android 5.0版本也遇到楼上的问题,请问有解决的办法吗

  2. 小白 小白说道:

    你这个还是有bug,输入etittext里面的内容的时候,就会自动给顶到原位置去

      1. chengyong chengyong说道:

        是所有机型还是个别机型?我当时用的原生系统没有问题,可能没有关注到兼容性问题。

发表评论

电子邮件地址不会被公开。 必填项已用*标注