1、闲聊

  • 说到组合控件,人人都会。为什么要这么说呢?可以这样说我们每一个layout布局都是你定义的组合控件,但是,当你需要实现同一种功能的时候,有N个界面都需要这种组合控件,你们可能会想了:我每次使用 “include”标签导入一下布局不就行了吗?我想说的逻辑呢?难道用CV?

2、实现方法

说说实现方法吧

  • 使用xml写一个布局,然后定义一个View来加载这个布局
  • 直接继承RelativeLayout、LinearLayout等布局方式,然后动态添加你所需的View

我们简单了解一下第一种吧,因为可说的是大家都会,定义一个View来加载,需要多次使用并且逻辑相同的布局,通过“findViewById”来得到子控件,并书写逻辑,这个自定义控件就基本完成了,我这里说的是第二种纯 java 代码实现。

3、实例

不BB了,我们直接上代码

public class DelInputView extends RelativeLayout implements TextWatcher, View.OnClickListener {

    public DelInputView(Context context) {
        super(context);
        init(context);
    }

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

    public DelInputView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public DelInputView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    private static final String TAG = "DelInputView";
    private Context context;
    private Integer delResId;
    private Bitmap delBitmap;
    private EditText editText;
    private ImageView imageView;

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void init(Context context) {
        this.context = context;
        editText = new EditText(context);
        LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        editText.setLayoutParams(lp);
        editText.setBackground(null);
        addView(editText);
        editText.addTextChangedListener(this);
        if (null == getBackground()) {
            int left = getPaddingLeft();
            int top = getPaddingTop();
            int right = getPaddingRight();
            int bottom = getPaddingBottom();
            setBackgroundResource(R.drawable.delinputview);
            // 这里调用了setBackgroundResource方法后
            // 会遗忘padding所以需要重新调用一下
            setPadding(left, top, right, bottom);
        }
    }

    private void createImageView() {
        imageView = new ImageView(context);
        int width = getWidth();
        int height = getHeight() / 2;
        LayoutParams lp = new LayoutParams(height, height);
        imageView.setLayoutParams(lp);
        lp.topMargin = height / 2;
        lp.leftMargin = (int) (width - height * 1.5f - getPaddingLeft());
        if (null != delResId) {
            imageView.setImageResource(delResId);
        } else if (null != delBitmap) {
            imageView.setImageBitmap(delBitmap);
        } else {
            imageView.setImageResource(R.drawable.custom_control_del);
        }
        addView(imageView);
        imageView.setOnClickListener(this);
        if (TextUtils.isEmpty(editText.getText().toString())) {
            if (imageView.isShown()) {
                imageView.setVisibility(GONE);
            }
        } else {
            if (!imageView.isShown()) {
                imageView.setVisibility(VISIBLE);
            }
        }
    }

    private Runnable updateImageView = new Runnable() {
        @Override
        public void run() {
            if (getHeight() <= 0) {
                new Handler().postDelayed(updateImageView, 100);
            } else {
                createImageView();
            }
        }
    };

    @Override
    public void onClick(View v) {
        if (v == imageView) {
            editText.setText("");
        }
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (null == imageView) {
            new Handler().postDelayed(updateImageView, 0);
        }
        if (TextUtils.isEmpty(s.toString())) {
            if (null != imageView) {
                if (imageView.isShown()) {
                    imageView.setVisibility(GONE);
                }
            }
        } else {
            if (null != imageView) {
                if (!imageView.isShown()) {
                    imageView.setVisibility(VISIBLE);
                }
            }
        }
    }

    public EditText getEditText() throws RuntimeException {
        if (null == editText)
            throw new RuntimeException("The input box hasn't been created yet .");
        return editText;
    }

    /**
     * 为删除按钮设置本地图片资源的方法
     *
     * @param resId 资源ID
     */
    public void setDelImageResource(int resId) {
        //如果没有创建删除按钮,则把资源ID赋值给全局
        //如果创建了删除按钮,则当场更改删除图片资源
        if (null == imageView) {
            delResId = resId;
        } else {
            imageView.setImageResource(resId);
        }
    }

    /**
     * 为删除按钮设置静态位图的方法
     *
     * @param bm 图片位图
     */
    public void setDelImageBitmap(Bitmap bm) {
        if (null == imageView) {
            delBitmap = bm;
        } else {
            imageView.setImageBitmap(bm);
        }
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void afterTextChanged(Editable s) {
    }
}

简单说一说,这里为什要用 “updateImageView ”线程来完成对 ImageView 的创建及其定位,因为我在调用 “getEditText().setText(“123456”);”为 EditText 赋值时,可能这个自定义组合控件还未初始化完成,getWidth 得到的值会为0,建立的删除按钮就会不可见。那么为什么不在创建 EditText 的时候创建呢?我本着不浪费资源的原则,就要在我需要的时候才创建。所以在这里我就是用 异步线程。


4、demo资源

开源地址:

https://github.com/fountaintao/CustomControl

5、请大家多多指教,贵在分享,思想上的碰撞才能更璀璨!

有问题?我们探讨一下!