在我们做项目的时候,经常要自定义组件,比如输入框EditText当输入内容后右边会有一个叉号的图标,点击可以清楚输入框的内容。比如登录框需要左边也有一个小图标让用户一看就是输入用户名的,其实要实现这个很容易,但是不同人的实现方法不一样,会导致你编写组件的实用性,接下来我会采用递增方式来实现,来实现不同阶段的扩展。
一、删除输入框实现
要实现带有删除输入框组件,我们要自定义,使用一个布局来嵌套EditText以及一个ImageView,这里布局我选用的是RelativeLayout,首先还是先实现UI功能,对于这样小的界面,推荐还是使用代码布局,来减少依赖性。
1.1、删除输入框的UI实现
首先就是图片,一个移动应用程序的好坏,决定性在于UI,UI又取决于图片,如果图片做的好的话,就可以让开发人员省去不用做很多布局来实现不同设备的适应性,之前我反编译一些软件,利用使用4-5套布局来实现适应性,当然这是解决问题的一个方式,但是我只能说安卓设备太多了,而我把里面的图片使用ps重写处理了一下,实现相同的界面效果,只要使用一个布局就可以解决适应性。所以我说对于移动开发还是离不开美工,同样的开发人员最好也会一些基本的图片处理功能。
先看下面不同阶段的组件效果
最终的效果代码实现如下:
package com.jwzhangjie.smarttv_client.widget; import com.jwzhangjie.smarttv_client.R; import android.content.Context; import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.widget.EditText; import android.widget.ImageView; import android.widget.RelativeLayout; public class TVEditText extends RelativeLayout { protected EditText mEditText; protected ImageView mDelBtn; protected Context mContext; public TVEditText(Context context) { super(context); initLayout(); } public TVEditText(Context context, AttributeSet attrs) { super(context, attrs); initLayout(); } public TVEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initLayout(); } @SuppressWarnings("deprecation") private void initLayout(){ mContext = getContext(); setBackgroundResource(R.drawable.bg_edittextbox); mDelBtn = new ImageView(mContext); mDelBtn.setBackgroundResource(R.drawable.bg_deletebtn); mDelBtn.setId(mDelBtn.hashCode()); RelativeLayout.LayoutParams mDelBtnParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); mDelBtnParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); mDelBtnParams.setMargins(0, 0, 0, 0); mDelBtnParams.addRule(RelativeLayout.CENTER_VERTICAL); mDelBtnParams.rightMargin = mContext.getResources().getDimensionPixelSize(R.dimen.padding_15); addView(mDelBtn, mDelBtnParams); mEditText = new EditText(mContext); mEditText.setId(mEditText.hashCode()); mEditText.setSingleLine(true); mEditText.setEllipsize(TruncateAt.END); mEditText.setBackgroundDrawable(null); RelativeLayout.LayoutParams mEditTextParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); mEditTextParams.setMargins(0, 0, 0, 0); mEditTextParams.addRule(RelativeLayout.LEFT_OF, mDelBtn.getId()); mEditTextParams.addRule(RelativeLayout.CENTER_VERTICAL); addView(mEditText, mEditTextParams); setPadding(0, 0, 0, 0); } }
1.2删除输入框功能实现
接下来就是实现输入框输入内容,点击叉号删除输入框内的内容,首先就是判断输入框内是否有内容,如果有则显示叉号图标,如果没有则隐藏,点击删除按钮,则设置输入框内容为""
显示效果如下:
代码如下:
private OnClickListener mDeleteBtnClickListener = new OnClickListener() { @Override public void onClick(View view) { if (isDelEnable) { mEditText.setText(""); } } }; private TextWatcher mTextWatcher = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { if (s.length() > 0 && !isDelEnable) { isDelEnable = true; mDelBtn.setVisibility(View.VISIBLE); } else if (s.length() == 0 && isDelEnable) { isDelEnable = false; mDelBtn.setVisibility(View.INVISIBLE); } } };
接下来就是实现EditText本身功能,例如密码,Hint功能,其他的在项目里面需要时在添加就行。
public void setHint(int resId) { mEditText.setHint(resId); } public void setHint(CharSequence hint) { mEditText.setHint(hint); } public void setHintColor(int color) { mEditText.setHintTextColor(color); } public CharSequence getHint() { return mEditText.getHint(); } public Editable getText() { return mEditText.getText(); } public String getContent() { return getText().toString().trim(); } public boolean isEmpty() { return 0 == getContent().length(); } public void setPassword() { mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); } public void setInputType(int type) { mEditText.setInputType(type); }
到现在已经做好了一个带有删除功能的输入框,但是这并不是我想要的,如果这样设计,那么也就失去了适应性,所以我们要通过接口实现回调的方式来实现一个基础的组件,可以让这一类的组件,例如搜索框。
二、重写后的基础类
基础类不期望你把所有的功能都实现,只需要实现基本接口和UI。
package com.jwzhangjie.smarttv_client.widget; import com.jwzhangjie.smarttv_client.R; import android.content.Context; import android.graphics.drawable.Drawable; import android.text.TextUtils.TruncateAt; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.View; import android.widget.EditText; import android.widget.ImageView; import android.widget.RelativeLayout; public class TVSpanText extends RelativeLayout { protected EditText mEditText; protected ImageView mRightBtn; protected Context mContext; public RightInterface riInterface; private boolean isRightEnable = false; protected void init(){ } public TVSpanText(Context context) { super(context); initLayout(); init(); } public TVSpanText(Context context, AttributeSet attrs) { super(context, attrs); initLayout(); init(); } public TVSpanText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initLayout(); init(); } @SuppressWarnings("deprecation") private void initLayout() { mContext = getContext(); setBackgroundResource(R.drawable.bg_edittextbox); mRightBtn = new ImageView(mContext); mRightBtn.setBackgroundResource(R.drawable.bg_deletebtn); mRightBtn.setId(mRightBtn.hashCode()); mRightBtn.setOnClickListener(mRightBtnClickListener); RelativeLayout.LayoutParams mDelBtnParams = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); mDelBtnParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); mDelBtnParams.setMargins(0, 0, 0, 0); mDelBtnParams.addRule(RelativeLayout.CENTER_VERTICAL); mDelBtnParams.rightMargin = mContext.getResources() .getDimensionPixelSize(R.dimen.padding_15); addView(mRightBtn, mDelBtnParams); mEditText = new EditText(mContext); mEditText.setId(mEditText.hashCode()); mEditText.setSingleLine(true); mEditText.setEllipsize(TruncateAt.END); mEditText.setBackgroundDrawable(null); RelativeLayout.LayoutParams mEditTextParams = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); mEditTextParams.setMargins(0, 0, 0, 0); mEditTextParams.addRule(RelativeLayout.LEFT_OF, mRightBtn.getId()); mEditTextParams.addRule(RelativeLayout.CENTER_VERTICAL); addView(mEditText, mEditTextParams); setPadding(0, 0, 0, 0); mEditText.addTextChangedListener(mTextWatcher); mRightBtn.setVisibility(View.GONE); } protected void setRightEnable(boolean opt){ isRightEnable = opt; if (opt) { mRightBtn.setVisibility(View.VISIBLE); }else { mRightBtn.setVisibility(View.GONE); } } public void setText(int resid){ mEditText.setText(resid); } public void setText(CharSequence text){ mEditText.setText(text); } public void setRightDraw(int resId) { mRightBtn.setBackgroundResource(resId); } public void setRightDraw(Drawable background) { mRightBtn.setBackgroundDrawable(background); } public void setHint(int resId) { mEditText.setHint(resId); } public void setHint(CharSequence hint) { mEditText.setHint(hint); } public void setHintColor(int color) { mEditText.setHintTextColor(color); } public CharSequence getHint() { return mEditText.getHint(); } public Editable getText() { return mEditText.getText(); } public String getContent() { return getText().toString().trim(); } public boolean isEmpty() { return 0 == getContent().length(); } public void setPassword() { mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); } public void setInputType(int type) { mEditText.setInputType(type); } public void setRightInterface(RightInterface rInterface) { this.riInterface = rInterface; } protected interface RightInterface { public void onClick(); public void onShow(); public void onHide(); } private OnClickListener mRightBtnClickListener = new OnClickListener() { @Override public void onClick(View view) { if (isRightEnable && riInterface != null) { riInterface.onClick(); } } }; private TextWatcher mTextWatcher = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { if (s.length() > 0 && !isRightEnable) { if (riInterface != null) { riInterface.onShow(); } } else if (s.length() == 0 && isRightEnable) { if (riInterface != null) { riInterface.onHide(); } } } }; }
这里我用基础类,一个删除框,一个I搜索框作为对比
效果图如下:
初始化效果:
输入内容效果:
点击按钮的效果:
这样做的好处就是接口放在基础类,而实现则在具体的功能类里面实现,这样同一类界面效果的可以都用基础类。还有就是扩展的时候。
删除框类的实现代码:
package com.jwzhangjie.smarttv_client.widget; import android.content.Context; import android.util.AttributeSet; public class TVEditTextDeleteBtn extends TVSpanText{ public TVEditTextDeleteBtn(Context context) { super(context); } public TVEditTextDeleteBtn(Context context, AttributeSet attrs) { super(context, attrs); } public TVEditTextDeleteBtn(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void init(){ setHint("删除框"); setRightInterface(new RightInterface() { @Override public void onClick() { setText(""); } @Override public void onShow() { setRightEnable(true); } @Override public void onHide() { setRightEnable(false); } }); } }
搜索框代码:
package com.jwzhangjie.smarttv_client.widget; import com.jwzhangjie.smarttv_client.R; import android.content.Context; import android.util.AttributeSet; public class TVSerchInput extends TVSpanText{ public TVSerchInput(Context context) { super(context); } public TVSerchInput(Context context, AttributeSet attrs) { super(context, attrs); } public TVSerchInput(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void init() { super.init(); setHint("搜索框"); setRightEnable(true); setRightDraw(R.drawable.skin_searchbar_icon); setRightInterface(new RightInterface() { @Override public void onClick() { setText("正在搜索中..."); } @Override public void onShow() { } @Override public void onHide() { } }); } }