自定义固定头文字的多行编辑框控件的实现分析

写这个自定义控件的缘由:公司新的UI界面实现需要实现一个可以固定头文字,并且可以代码去配置这个头文字,用户不能去修改和删除,在网上查阅了相关资料,貌似没有人去做过相关的自定义控件,于是自己花几个小时写了一遍,其中也想过多种方案,踩过许多坑。


首先,按照国际惯例,要实现自己的EditText当然得继承EditText控件于是乎我开始创建一个自定义的EditText,也开始了第一步入坑:

/**
 * 主要功能: 左边有固定文字EditText
 * Created by wz on 2016/8/5
 * 修订历史:
 */
public class FixedEditText extends EditText {
    private String fixedText;
    private View.OnClickListener mListener;

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);

    }

    @Override
    public void setGravity(int gravity) {
        super.setGravity(gravity);

    }

    @Override
    public int getCompoundPaddingLeft() {
        return 20;
    }

    public FixedEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setFixedText(String text) {
        fixedText = text;
        int left = (int) getPaint().measureText(fixedText)+ getPaddingLeft();
        setPadding(left, 0, 0, 100);
        invalidate();
    }

    public void setDrawableClk(View.OnClickListener listener) {
        mListener = listener;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint=new Paint();
        if (!TextUtils.isEmpty(fixedText)) {
            canvas.drawText(fixedText, 0,  getTextSize(), getPaint());
            this.setSelection(fixedText.length());
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (mListener != null && getCompoundDrawables()[2] != null) {

            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    int i = getMeasuredWidth() - getCompoundDrawables()[2].getIntrinsicWidth();
                    int h=getMeasuredHeight() - getCompoundDrawables()[2].getIntrinsicHeight();
                    if (event.getX() > i||event.getY()>h) {
                        mListener.onClick(this);
                        return true;
                    }
                    break;
                case MotionEvent.ACTION_UP:

                    break;
                default:
                    break;
            }

        }

        return super.onTouchEvent(event);
    }
}

这段代码当我敲完,我以为可以完美的去装逼了,但是当我在准备运行时候,我忽然想到了,我需要的是多行文本,而且多行文本左对齐,运行来看,不能多行,设置完属性,发现多行特别并不是左对齐后面的活动文本左对齐了(UI要求的是第二行开始下面的活动文本都得与第一行的固定文本对齐),然后这样的Code当然是不能实现的。然后无脑的我马上想到了第二种解决办法,既然继承EditText得不到我想要的结果,而且显示不出我想要的效果,于是转换了一下思路,所谓固定值,也就是不能让用户删除,对吧?然而我们能够监听用户对键盘的操作,所以当我们判定到用户是否正在删除固定文字,如果是就拦截操作,这里面会涉及到光标的操作,和用户点击的操作,处理好这两个的关系,就会很简单的实现所谓的固定多行文本框啦。
下面贴上类代码供参考


/**
 * 主要功能:
 * Created by wz on 2016/8/5
 * 修订历史:
 */
public class FixEditView extends EditText {
    private Context context;
    private String fixText;
    private boolean isDelete = false;//是否是可删除
    private boolean isFix = false;//表示是否需要显示固定文字
    private ColorStateList defColor;//默认的字体颜色
    private int fixColor=0;

    public String getFixText() {
        return fixText;
    }
    public void setFixTextColor(int color){
        this.fixColor=color;
    }
    public void setFixText(String fixText) {
        this.fixText = fixText;
        if(fixColor!=0){
            this.setTextColor(fixColor);
        }
        this.setText(fixText);
        if (fixText.length() > 0) {
            isFix = true;
        }
        //初始化光标位置
        this.setSelection(fixText.length());
    }

    public FixEditView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public FixEditView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    public void init() {
        defColor=this.getTextColors();
        this.addTextChangedListener(textWatcher);
        this.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
        this.setLongClickable(false);
    }

    TextWatcher textWatcher = new TextWatcher() {
        private CharSequence temp;

        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            temp = charSequence;
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

        }

        @Override
        public void afterTextChanged(Editable editable) {
            Random random=new Random();
            //产生100000-999999的随机数
            int randNum=random.nextInt(100000)%(999999-100000+1) + 100000;
            //监听用户的输入状态
            if (temp.length() == fixText.length() || temp.length() < fixText.length()) {
                isDelete = false;
            } else {
                //产生随机颜色代码,只为装13,辣眼睛
                int color=Color.parseColor("#"+randNum);
                FixEditView.this.setTextColor(color);
                isDelete = true;
            }
        }
    };

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

    }

    @Override
    protected void onSelectionChanged(int selStart, int selEnd) {
        //限制光标的位置,使得最初光标总是从固定文本之后开始的
        if (isFix) {
            selStart = selStart < fixText.length() ? fixText.length() : selStart;
            selEnd = selStart;
            this.setSelection(selStart, selEnd);
        }
        super.onSelectionChanged(selStart, selEnd);
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == 67) {//删除键
            //当标记为不能删除的时候拦截操作
            if (!isDelete) {
                return !isDelete;
            }
        }
        return super.dispatchKeyEvent(event);
    }
}

xml布局文件代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:background="@color/bg_page"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="250px"
        android:layout_marginTop="14px"
        android:background="@color/white"
        android:orientation="horizontal"
        android:visibility="visible">

        <TextView
            android:layout_width="170px"
            android:layout_height="wrap_content"
            android:layout_marginTop="30px"
            android:gravity="center"
            android:singleLine="true"
            android:text="工作要求"
            android:textColor="@color/text_gray_normal"
            android:textSize="28px" />

        <View
            android:layout_width="1px"
            android:layout_height="206px"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10px"
            android:background="@color/line_main" />

        <com.example.lenovo.diyedittext.FixEditView
            android:id="@+id/fev_work_info"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="40px"
            android:layout_marginTop="30px"
            android:layout_weight="1"
            android:background="@color/transparent"
            android:gravity="top"
            android:hint="请输入相关内容,岗位要求,要求20-1500字。"
            android:maxHeight="220px"
            android:maxLength="1500"
            android:minHeight="220px"
            android:padding="5dp"
            android:textColor="@color/text_gray_normal"
            android:textSize="28px" />
    </LinearLayout>
</LinearLayout>

Activity中如何使用:

/**
 * 主要功能:
 * Created by wz on 2016/8/5
 * 修订历史:
 */
public class MainActivity extends Activity {

    FixEditView fev_work_info;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maple);
        fev_work_info= (FixEditView) findViewById(R.id.fev_work_info);
        fev_work_info.setFixTextColor(Color.parseColor("#30C27B"));
        fev_work_info.setFixText("这是固定文字。");
        fev_work_info.setGravity(Gravity.LEFT);
    }
}

感受:不要总是用别人造的轮子,新手主要是用现成的轮子,普通的主要是改造成自己的轮子,高手都是造轮子,所以一步步来,能自己写出来的东西尽量自己写,相信总有一天会蜕变成那个造轮子的嘛。
内容大概就是这样,如果有什么不对的地方,欢迎指正,相互学习很有必要。