在我们做项目的时候,经常要自定义组件,比如输入框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() {
}
});
}
}