編輯:關於Android編程
前不久由於項目的需要,要做一個自定義的軟鍵盤,我也上網看了很多,都覺得很繁瑣,所以想自己動手實現個。以備不時之需把。我選擇了參考百度錢包的軟鍵盤,看起來還不錯:
vc+88rWlo6y+zbK7tuDLtcHLPC9wPg0KPHByZSBjbGFzcz0="brush:java;">
public class SoftInputBoard extends RelativeLayout
implements View.OnClickListener{
private Scroller mScroller;
private int mScreenHeigh = 0;
private int mScreenWidth = 0;
private Boolean isMoving = false;
private int viewHeight = 0;
public boolean isShow = false;
public boolean mEnabled = true;
public boolean mOutsideTouchable = true;
private int mDuration = 800;
private boolean userIsLongPress = false;
@Override
public void onClick(View v) {
String value = "";
int id = v.getId();
if(id == R.id.tv0){
value = "0";
}else if(id == R.id.tv1){
value = "1";
}else if(id == R.id.tv2){
value = "2";
}else if(id == R.id.tv3){
value = "3";
}else if(id == R.id.tv4){
value = "4";
}else if(id == R.id.tv5){
value = "5";
}else if(id == R.id.tv6){
value = "6";
}else if(id == R.id.tv7){
value = "7";
}else if(id == R.id.tv8){
value = "8";
}else if(id == R.id.tv9){
value = "9";
}else if(id == R.id.iv_delete){
value = "delete";
}else if(id == R.id.tvx){
value = "X";
}
if(payListener != null){
payListener.chooseWay(value);
}
}
public interface ChoosePayWayListener{
void chooseWay(String value);
}
public ChoosePayWayListener payListener;
public void setOnChoosePayWayListener(ChoosePayWayListener payListener){
this.payListener = payListener;
}
private final static String TAG = "SoftInputView";
public SoftInputBoard(Context context) {
super(context);
init(context);
}
public SoftInputBoard(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SoftInputBoard(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setFocusable(true);
mScroller = new Scroller(context);
mScreenHeigh = BaseTools.getWindowHeigh(context);
mScreenWidth = BaseTools.getWindowWidth(context);
this.setBackgroundColor(Color.argb(0, 0, 0, 0));
final View view = LayoutInflater.from(context).inflate(R.layout.view_soft_input, null);
TextView tv0 = (TextView)view.findViewById(R.id.tv0);
TextView tv1 = (TextView)view.findViewById(R.id.tv1);
TextView tv2 = (TextView)view.findViewById(R.id.tv2);
TextView tv3 = (TextView)view.findViewById(R.id.tv3);
TextView tv4 = (TextView)view.findViewById(R.id.tv4);
TextView tv5 = (TextView)view.findViewById(R.id.tv5);
TextView tv6 = (TextView)view.findViewById(R.id.tv6);
TextView tv7 = (TextView)view.findViewById(R.id.tv7);
TextView tv8 = (TextView)view.findViewById(R.id.tv8);
TextView tv9 = (TextView)view.findViewById(R.id.tv9);
TextView tvx = (TextView)view.findViewById(R.id.tvx);
final RelativeLayout ivDelete = (RelativeLayout)view.findViewById(R.id.iv_delete);
ivDelete.setClickable(true);
tv0.setOnClickListener(this);
tv1.setOnClickListener(this);
tv2.setOnClickListener(this);
tv3.setOnClickListener(this);
tv4.setOnClickListener(this);
tv5.setOnClickListener(this);
tv6.setOnClickListener(this);
tv7.setOnClickListener(this);
tv8.setOnClickListener(this);
tv9.setOnClickListener(this);
tvx.setOnClickListener(this);
ivDelete.setOnClickListener(this);
ivDelete.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(payListener != null){
payListener.chooseWay("xdelete");
}
return false;
}
});
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);
addView(view, params);
this.setBackgroundColor(Color.argb(0, 0, 0, 0));
view.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
viewHeight = view.getHeight();
}
});
SoftInputBoard.this.scrollTo(0, mScreenHeigh);
ImageView btn_close = (ImageView)view.findViewById(R.id.btn_close);
btn_close.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
dismiss();
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(!mEnabled){
return false;
}
return super.onInterceptTouchEvent(ev);
}
public void startMoveAnim(int startY, int dy, int duration) {
isMoving = true;
mScroller.startScroll(0, startY, 0, dy, duration);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
isMoving = true;
} else {
isMoving = false;
}
super.computeScroll();
}
public void show(){
if(!isShow && !isMoving){
SoftInputBoard.this.startMoveAnim(-viewHeight, viewHeight, mDuration);
isShow = true;
Log.d("isShow", "true");
changed();
}
}
public int getSoftInputBoardHeight(){
return viewHeight;
}
public void dismiss(){
if(isShow && !isMoving){
SoftInputBoard.this.startMoveAnim(0, -viewHeight, mDuration);
isShow = false;
Log.d("isShow", "false");
changed();
if(l!=null){
l.dismiss();
}
}
}
public interface dismissListener{
void dismiss();
}
private dismissListener l;
public void setOnDismissListener(dismissListener l){
this.l = l;
}
public boolean isShow(){
return isShow;
}
public boolean isSlidingEnabled() {
return mEnabled;
}
public void setSlidingEnabled(boolean enabled) {
mEnabled = enabled;
}
public void setOnStatusListener(onStatusListener listener){
this.statusListener = listener;
}
public void setOutsideTouchable(boolean touchable) {
mOutsideTouchable = touchable;
}
public void changed(){
if(statusListener != null){
if(isShow){
statusListener.onShow();
}else{
statusListener.onDismiss();
}
}
}
public onStatusListener statusListener;
public interface onStatusListener{
public void onShow();
public void onDismiss();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
super.onLayout(changed, l, t, r, b);
}
}
其實,開發了這麼久的android了,覺得Scoller這個類還是很重要的,可以幫助我們實現很多效果,比如這個控件的出現和消失。關於Scoller這個類還不熟悉的可以看我之前總結的文章 — 帶你徹徹底底弄懂Scroller。
2.第二步,也是我在開發中遇到的問題,就是如果我們的EditText太靠底端了,就是說,當我們的SoftInputBoard 調用show()方法的時候,會把用戶填寫的EditText給遮擋住,我們不是繼承系統給我們的KeyBoard這個類,所以這個問題還是得我們自己解決。這裡我還是用Scroller這類把SoftInputBoard這個控件的外布局給滑動到上面去:
/**
* Created by Nipuream on 2016/4/15 0015.
*/
public class AutoPopLayout extends RelativeLayout {
private Scroller mScroller;
private boolean isMove = false;
private SoftInputBoard softInputBoard;
private Context context;
private int currentCursorIndex = 0;
private static final int ADD = 0x45;
private static final int DE = 0x46;
//默認是加
private int addOrde = ADD;
public AutoPopLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
DecelerateInterpolator interpolator = new DecelerateInterpolator();
mScroller = new Scroller(context,interpolator);
post(new Runnable() {
@Override
public void run() {
softInputBoard = new SoftInputBoard(AutoPopLayout.this.context);
RelativeLayout rl = (RelativeLayout) AutoPopLayout.this.getParent();
rl.addView(softInputBoard);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) softInputBoard.getLayoutParams();
params.width = LayoutParams.MATCH_PARENT;
params.height = LayoutParams.WRAP_CONTENT;
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
softInputBoard.setLayoutParams(params);
softInputBoard.setOnDismissListener(new SoftInputBoard.dismissListener() {
@Override
public void dismiss() {
AutoPopLayout.this.startMoveAnim(AutoPopLayout.this.getScrollY(),- pixDistance,500);
pixDistance = 0;
}
});
softInputBoard.setOnChoosePayWayListener(new SoftInputBoard.ChoosePayWayListener() {
@Override
public void chooseWay(String value) {
try{
String preStr = ed.getText().toString();
//銀行帳號
if(!TextUtils.equals(value,"delete")){
//長按刪除
if(TextUtils.equals(value,"xdelete")){
ed.setText("");
currentCursorIndex = 0;
return;
}
addOrde = ADD;
currentCursorIndex = getEditTextCursorIndex(ed);
insertText(ed,value);
preStr = ed.getText().toString();
ed.setText(preStr);
}else{
if(!TextUtils.isEmpty(preStr)){
addOrde = DE;
currentCursorIndex = getEditTextCursorIndex(ed);
deleteText(ed);
preStr = ed.getText().toString();
ed.setText(preStr);
}
}
}catch (Exception e){
}
}
});
}
});
}
/**獲取EditText光標所在的位置*/
private int getEditTextCursorIndex(EditText mEditText){
return mEditText.getSelectionStart();
}
/**向EditText指定光標位置插入字符串*/
private void insertText(EditText mEditText, String mText){
mEditText.getText().insert(getEditTextCursorIndex(mEditText), mText);
}
/**向EditText指定光標位置刪除字符串*/
private void deleteText(EditText mEditText){
if(!TextUtils.isEmpty(mEditText.getText().toString())){
mEditText.getText().delete(getEditTextCursorIndex(mEditText)-1, getEditTextCursorIndex(mEditText));
}
}
private int pixDistance = 0;
private void AutoPilled(final EditText view){
currentCursorIndex = 0;
view.post(new Runnable() {
@Override
public void run() {
// int screenHeight = UIUtil.getScreenHeight(getActivity().getWindowManager());
int screenHeight = BaseTools.getWindowHeigh(context);
int softInputHeight = softInputBoard.getSoftInputBoardHeight();
/**
*
*
* --------------------------------------------------- <---- screenHeight-softInputHeight
* | --------------------------- |
* | | 輸入框 | |
* | ---------------------------- <---- bottom |
* | |
* | |
* ---------------------------------------------------
*
*
*/
int bottom = 0;
Rect rect = new Rect();
boolean isGet = view.getGlobalVisibleRect(rect);
if(isGet){
bottom = rect.bottom;
}
if(bottom != 0) {
if (bottom > (screenHeight - softInputHeight)) {
pixDistance = bottom - (screenHeight - softInputHeight);
AutoPopLayout.this.startMoveAnim(AutoPopLayout.this.getScrollY(),pixDistance,500);
}
}
}
});
}
private EditText ed;
private WeakReference ref;
// 隱藏系統鍵盤
public void hideSoftInputMethod(final List ets, WeakReference ref){
this.ref = ref;
currentCursorIndex = 0;
for(final EditText ed:ets){
ed.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// focus = WhichFocus.BANK_CARD;
// AutoPopLayout.this.hideSoftInputMethod(view,ref);
AutoPopLayout.this.ed = ed;
if(!softInputBoard.isShow){
softInputBoard.show();
}
if(! AutoPopLayout.this.isMove()){
if(softInputBoard.isShow){
AutoPilled(ed);
}
}
return false;
}
});
ed.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String value = s.toString();
int length = value.length();
int index = getEditTextCursorIndex(ed);
try{
if(currentCursorIndex < length){
if(addOrde == ADD){
ed.setSelection(currentCursorIndex+1);
}else{
ed.setSelection(currentCursorIndex-1);
}
}else{
ed.setSelection(length);
}
}catch (Exception e){
}
}
});
ref.get().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
int currentVersion = android.os.Build.VERSION.SDK_INT;
String methodName = null;
if(currentVersion >= 16){
// 4.2
methodName = "setShowSoftInputOnFocus";
}
else if(currentVersion >= 14){
// 4.0
methodName = "setSoftInputShownOnFocus";
}
if(methodName == null){
ed.setInputType(InputType.TYPE_NULL);
}
else{
Class cls = EditText.class;
Method setShowSoftInputOnFocus;
try {
setShowSoftInputOnFocus = cls.getMethod(methodName, boolean.class);
setShowSoftInputOnFocus.setAccessible(true);
setShowSoftInputOnFocus.invoke(ed, false);
} catch (NoSuchMethodException e) {
ed.setInputType(InputType.TYPE_NULL);
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void destroyAutoPopLayout(){
if(ref != null){
ref = null;
this.removeAllViews();
}
}
public void startMoveAnim(int startY, int dy, int duration) {
isMove = true;
mScroller.startScroll(0, startY, 0, dy, duration);
invalidate();//通知UI線程的更新
}
@Override
public void computeScroll() {
//判斷是否還在滾動,還在滾動為true
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
//更新界面
postInvalidate();
isMove = true;
} else {
isMove = false;
}
super.computeScroll();
}
public boolean isMove(){
return isMove;
}
}
上面的AutoPopLayout 裡面除了解決SoftInputBoard對EditText遮擋的問題,還實現了大量的對EditText處理的問題,包括我點擊SoftInputBoard,講鍵盤TextView中的內容,setText()到EditText上面去,當然,你可以對TextView的內容進行加密處理,然後傳輸到服務端去,不然我們自定義鍵盤也沒有必要了,這裡我這步操作就省略了。
可以看到,其實我在AutoPopLayout外面還嵌套了一層RelativeLayout,因為我們的SoftInputBoard不能在AutoPopLayout裡面,不然,我們的SoftInputBoard就會隨著我們的AutoPopLayout上移了。
3.第三步,我們來處理下對於EditText的處理,當然界面層上的EditText要傳到我們這來,還需要個Activity的context,需要這個context去隱藏系統的軟鍵盤
// 隱藏系統鍵盤
public void hideSoftInputMethod(final List ets, WeakReference ref){
this.ref = ref;
currentCursorIndex = 0;
for(final EditText ed:ets){
ed.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// focus = WhichFocus.BANK_CARD;
// AutoPopLayout.this.hideSoftInputMethod(view,ref);
AutoPopLayout.this.ed = ed;
if(!softInputBoard.isShow){
softInputBoard.show();
}
if(! AutoPopLayout.this.isMove()){
if(softInputBoard.isShow){
AutoPilled(ed);
}
}
return false;
}
});
ed.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String value = s.toString();
int length = value.length();
int index = getEditTextCursorIndex(ed);
try{
if(currentCursorIndex < length){
if(addOrde == ADD){
ed.setSelection(currentCursorIndex+1);
}else{
ed.setSelection(currentCursorIndex-1);
}
}else{
ed.setSelection(length);
}
}catch (Exception e){
}
}
});
ref.get().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
int currentVersion = android.os.Build.VERSION.SDK_INT;
String methodName = null;
if(currentVersion >= 16){
// 4.2
methodName = "setShowSoftInputOnFocus";
}
else if(currentVersion >= 14){
// 4.0
methodName = "setSoftInputShownOnFocus";
}
if(methodName == null){
ed.setInputType(InputType.TYPE_NULL);
}
else{
Class cls = EditText.class;
Method setShowSoftInputOnFocus;
try {
setShowSoftInputOnFocus = cls.getMethod(methodName, boolean.class);
setShowSoftInputOnFocus.setAccessible(true);
setShowSoftInputOnFocus.invoke(ed, false);
} catch (NoSuchMethodException e) {
ed.setInputType(InputType.TYPE_NULL);
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
這裡我們的EditText是集合的形式傳遞過來的,因為界面上可能有多個EditText,我們通過弱引用的形式傳遞過來了,因為會導致內存洩漏,當然,你在Activity的onDestory()裡面去把AutoPopLayout的context置為null,也是可以的。上面隱藏的方式我也是從網友那裡借鑒過來的,不同版本都有不同的處理方式。
隱藏系統的軟鍵盤之後,還需要處理光標,雖然你可以設置ed.setCursorVisible(true),但是它總是會顯示在EditText的最前面,不管你有沒有輸入文字,不信,你可以嘗試下。那怎麼辦呢,我們貌似只有監聽EditText的addText的方法了:
ed.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String value = s.toString();
int length = value.length();
int index = getEditTextCursorIndex(ed);
try{
if(currentCursorIndex < length){
if(addOrde == ADD){
ed.setSelection(currentCursorIndex+1);
}else{
ed.setSelection(currentCursorIndex-1);
}
}else{
ed.setSelection(length);
}
}catch (Exception e){
}
}
});
在這裡我們處理了一些可能,主要為了光標能夠隨著文字而動,但是問題又來了,如果用戶假如不小心輸漏了一位怎麼辦?當用戶把光標移動到某個位置的時候,我們還是處理下這個Bug。
softInputBoard.setOnChoosePayWayListener(new SoftInputBoard.ChoosePayWayListener() {
@Override
public void chooseWay(String value) {
try{
String preStr = ed.getText().toString();
//銀行帳號
if(!TextUtils.equals(value,"delete")){
//長按刪除
if(TextUtils.equals(value,"xdelete")){
ed.setText("");
currentCursorIndex = 0;
return;
}
addOrde = ADD;
currentCursorIndex = getEditTextCursorIndex(ed);
insertText(ed,value);
preStr = ed.getText().toString();
ed.setText(preStr);
}else{
if(!TextUtils.isEmpty(preStr)){
addOrde = DE;
currentCursorIndex = getEditTextCursorIndex(ed);
deleteText(ed);
preStr = ed.getText().toString();
ed.setText(preStr);
}
}
}catch (Exception e){
}
}
});
}
});
在SoftInputBoard傳遞給我們值的時候,我們之前要確定下光標的位置,然後在去設置,當然,刪除亦是如此。當然還有很多細節沒有描述,雖然看起來有點煩躁,幸運的是,我把他們全封裝好了,直接在MainActivity調用就ok了:
public class MainActivity extends AppCompatActivity {
private AutoPopLayout autoPopLayout;
private EditText et1;
private EditText et2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
autoPopLayout = (AutoPopLayout)findViewById(R.id.autoPopLayout);
et1 = (EditText)findViewById(R.id.auto_et1);
et2 = (EditText)findViewById(R.id.auto_et2);
List ets = new ArrayList();
ets.add(et1);
ets.add(et2);
autoPopLayout.hideSoftInputMethod(ets,new WeakReference(this));
}
@Override
protected void onDestroy() {
try{
autoPopLayout.destroyAutoPopLayout();
}catch (Exception e){
}
super.onDestroy();
}
}
怎麼樣,是不是很簡單!!!下面看看效果圖:
導入Android工程出現unable to get system library for the project 錯誤提示問題與現象:當向eclipse導
Matrix的數學原理在Android中,如果你用Matrix進行過圖像處理,那麼一定知道Matrix這個類。Android中的Matrix是一個3 x 3的矩陣,其內容
前言 最近項目做用戶登錄模塊需要一個右邊帶圖片的EditText,圖片可以設置點擊效果,所以就查資料做了一個自定義EditText出來,方便以後復用。原理 下面是自定
Android中動畫分為三種,逐幀動畫,補間動畫,屬性動畫,這篇先總結逐幀動畫和補間動畫。逐幀動畫1, 是什麼字面上理解,幀之間追逐,幀動畫是順序的播放一系列圖片,從而產