2015年6月

昨天,群里面一个小伙伴发了个求助,要求实现一个可以拖动的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

今天项目中遇到一个很奇葩的问题,布局文件中相关代码如下:

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="@dimen/px98"
            android:background="@drawable/box_head"
            android:gravity="center_vertical"
            android:orientation="horizontal" >

            <TextView
                android:id="@+id/tv_cancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/px80"
                android:text="@string/activity_send_notification_cancel"
                android:textColor="@color/activity_edit_notification_btn_back_text_selector"
                android:textSize="@dimen/px40" />

            <View
                android:layout_width="0dp"
                android:layout_height="1dp"
                android:layout_weight="1.0" />

            <TextView
                android:id="@+id/tv_send"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="@dimen/px80"
                android:text="@string/activity_send_notification_send"
                android:textColor="@color/activity_edit_notification_btn_send_text_selector"
                android:textSize="@dimen/px40" />
        </LinearLayout>

代码中 @drawable/box_head 实际是一个.9图片,这个.9图片由于不规范,导致LinearLayout内部的 TextView 不能正常显示,这个问题困扰了我好几个小时,一开始没有意识到是LinearLayout背景图片问题,后来当我去掉背景图片时,恢复正常。

那么现在问题来了,项目中又需要这个.9背景图片,改怎么办呢?
答案是:规范化.9图片,不仅仅需要设置拉伸范围,还得设置内容范围,这样规范以后,就能正常显示出来了。

引言:什么?你还在用Eclipse ?是的,你没有看错,在习惯了一年多Android Studio以后,换新公司依然还在使用Eclipse,可是我有什么办法呢?唉,就这样吧。

为了方便我记忆这些快捷键,我baidu、Google了一些常用的快捷键,收集于此,以备查。

**Command + O:显示大纲
Command + 1:快速修复
Command + D:删除当前行
Command + Option + J:快速添加类注释
Control + /:注释当前行,再按则取消注释
Option + /:代码助手完成一些代码的插入(俗称“智能提示”)**

Command + Option + ↓:复制当前行到下一行
Command + Option + ↑:复制当前行到上一行

Option + ↓:当前行和下面一行交互位置
Option + ↑:当前行和上面一行交互位置
Option + ←:前一个编辑的页面
Option + →:下一个编辑的页面
Option + Return:显示当前选择资源的属性
Shift + Return:在当前行的下一行插入空行
Shift + Control + Return:在当前行插入空行

Control + Q:定位到最后编辑的地方
Control + M:最大化当前的Edit或View(再按则最小化)

Command + T:快速显示当前类的继承结构
Command + W:关闭当前Editer
Command + K:参照当前选中的Word快速定位到下一个
Command + E:快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)

Command + Shift + E:显示管理当前打开的所有的View的管理器
Command + J:正向增量查找(按下Command + J后,你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有,则在Stutes Line中显示没有找到了)
Command + Shift + J:反向增量查找
Command + Shift + W:关闭所有打开的Editer
Command + Shift + X:把当前选中的文本全部变为大写