网址:
http://blog.sina.com.cn/s/blog_9f233c070101bqvp.html
--------------------------------------------------------------------------
前文有朋友回复说想深入了解一下拨号盘源码的实现,也许是上文中,我对此说的不够细致,现将拨号盘点击事件及处理,拨号键的点击等。 对于拨号盘的界面显示,在此就不在浪费大家的时间,不清楚的朋友可以点击下面的连接(拨号盘界面的显示)。下面主要描述一下点击拨号盘中某个按钮后,是怎么将对应的数字显示在输入框中和点击控制键(拨号,删除,搜索)所触发的事件。 1.拨号盘中数字键在DialpadFragment.java中,通过onCreateView方法,加载出了拨号界面,在该方法中有一句这样的令人费解的代码 View oneButton = fragmentView.findViewById(R.id.one); if (oneButton != null) { setupKeypad(fragmentView); } 首先通过View类的findViewById方法获得oneButton对象,当oneButton不为null的时候才绑定键盘。然后调用setupKeypad(fragmentView)方法,进行键盘监听事件的绑定,现截取代码片段与大家分享。 private void setupKeypad(View fragmentView) { // Setup the listeners for the buttons View view = fragmentView.findViewById(R.id.one); view.setOnClickListener(this); view.setOnLongClickListener(this); …. } 废话不多说,看代码! View view = fragmentView.findViewById(R.id.one); 通过View类的findViewById方法获得oneButton按钮,下面将分设置点击事件和长按事件两部分来进行描述。 1.1数字键盘的点击事件view.setOnClickListener(this); 为其设置点击事件监听器。此时该监听器为当前对象this,看样子该Fragment实现了view的onClickListener接口,是不是呢?我们看代码 DialpadFragment extends Fragment implements View.OnClickListener 好,既然如此,肯定要覆盖onClick(View view) 方法,我们继续跟进 onClick方法的片段如下 @Override public void onClick(View view) { switch (view.getId()) { case R.id.one: { playTone(ToneGenerator.TONE_DTMF_1); keyPressed(KeyEvent.KEYCODE_1); return; }//case结束 。。。 }//switch结束 }//onClick结束 当点击按钮1 时出会触发onClick事件,然后进入R.id.one分支 好,如果该分支后,会看到两个方法的调用,继续跟进playTone(ToneGenerator.TONE_DTMF_1); 这个方法主要是根据不同的按键发出不同的声音,最总调到本地方法,和显示关系不大,就不做详细描述,有需要的朋友自己看一下,java代码比较简单。 keyPressed(KeyEvent.KEYCODE_1); 我们来看这句的代码 private void keyPressed(int keyCode) { mHaptic.vibrate(); KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode); mDigits.onKeyDown(keyCode, event); final int length = mDigits.length(); if (length == mDigits.getSelectionStart() && length == mDigits.getSelectionEnd()) { mDigits.setCursorVisible(false); } } 第一句mHaptic.vibrate(); 我们先来看看mHaptic 到底是个什么东西吧 // Vibration (haptic feedback) for dialer key presses. private HapticFeedback mHaptic = new HapticFeedback(); 该HapticFeedback是一个Android定义的类,该类的vibrate()方法的大体上就是是手机震动一下。 继续跟进 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode); mDigits.onKeyDown(keyCode, event); 通过传递过来的键盘码,构造键盘点击事件,然后将该事件交给EditText的子类对象mDigits进行处理。处理后就会将keyCode所代表的字符显示在输入框内。 final int length = mDigits.length(); if (length == mDigits.getSelectionStart() && length == mDigits.getSelectionEnd()) { mDigits.setCursorVisible(false); } 后面几句,就是设置光标的可见与否。onCilck事件我们已经分析完毕,所有按键的点击均会触发该事件,回过头来,我们继续跟进setupKeypad方法。 1.2数字键盘的长按事件view.setOnLongClickListener(this); 为view对象设置长按事件,长按事件的监听器还是this,当前的对象,不用多说,该类肯定也是实现了View的OnLongClickListener接口。我们直接看onLongClick方法。 相关代码片段如下: public boolean onLongClick(View view) { final Editable digits = mDigits.getText(); int id = view.getId(); switch (id) { 。。。 case R.id.one: { if (isDigitsEmpty()) { if (isVoicemailAvailable()) { callVoicemail(); } else if (getActivity() != null) { DialogFragment dialogFragment = ErrorDialogFragment.newInstance( R.string.dialog_voicemail_not_ready_title, R.string.dialog_voicemail_not_ready_message); dialogFragment.show(getFragmentManager(), "voicemail_not_ready"); } return true; } return false; }//one 处理结束 。。。 }//switch 语句结束 return false; }//onLongClick方法结束 公共代码分析 final Editable digits = mDigits.getText(); int id = view.getId(); 获得输入框EditText的输入数据,获得view的id号码,用于switch语句的分支确认。 通过上面的基本操作,我们找到了长按1事件的主体,R.id.one分支 映入眼帘的第一句 if (isDigitsEmpty()) 看代码 private boolean isDigitsEmpty() { return mDigits.length() == 0; } 简单的说就是判断该输入框内是否有输入 当没有输入时首先执行isVoicemailAvailable()方法,我们看源码 private boolean isVoicemailAvailable() { try { return (TelephonyManager.getDefault().getVoiceMailNumber() != null); } catch (SecurityException se) { // Possibly no READ_PHONE_STATE privilege. Log.w(TAG, "SecurityException is thrown. Maybe privilege isn't sufficient."); } return false; } 通过源码可以看出,该方法实际上是判断了VoiceMailNumber是否为null,按照源码中的意思,这个条件永远成立,有兴趣的朋友可以去看一下源码。 当没有输入且可以拨打voicemail时执行如下 callVoicemail(); 我们查看源码 public void callVoicemail() { startActivity(newVoicemailIntent()); mDigits.getText().clear(); // TODO: Fix bug 1745781 getActivity().finish(); } 继续跟进 private Intent newVoicemailIntent() { final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts("voicemail", EMPTY_NUMBER, null)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return intent; } 不用多说,是跳转到另一个动作,其实voicemail类似于快捷拨号,在settings中可以设置成自己需要拨打的特殊号码,这样当长按1时,就可以自动拨打出去。 在不符合上面条件的前提下 当没有输入而Activity不等于null时执行dialogFragment 的弹出操作,在此不做过多的说明。 通过对1按键的描述,我想大家已经可以按该过程有所了解。 2.拨号盘中删除键我们从onCreateView方法中,找到 mDelete = mAdditionalButtonsRow.findViewById(R.id.deleteButton); mDelete.setOnClickListener(this); mDelete.setOnLongClickListener(this); 接下来我们分删除键的点击事件和长按事件两部分描述。 2.1 删除键的点击描述通过上文mDelete.setOnClickListener(this),我们知道,单击事件需要找onClick方法,直接定位到指定位置。 public void onClick(View view) { switch (view.getId()) { 。。。。。。。。 case R.id.deleteButton: { keyPressed(KeyEvent.KEYCODE_DEL); return; } 。。。。。。 } 同上,最终调用 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL); mDigits.onKeyDown(keyCode, KeyEvent.KEYCODE_DEL); mDigits就会将光标的前一个位置的字符删除。 2.2 删除键长按描述废话不说,直接看定位代码 public boolean onLongClick(View view) { ……………….. switch (id) { case R.id.deleteButton: { digits.clear(); mDelete.setPressed(false); return true; } ……….. } } Digits.clear()方法,直接清空输入框内的数字 mDelete.setPressed(false); 设置删除键的按下状态 3.拨号盘中拨号键同理,在onClick方法中找到拨号键 ………….. case R.id.dialButton: { mHaptic.vibrate(); dialButtonPressed(); return; } ………………. mHaptic.vibrate(); 震动一下 继续查看dialButtonPressed()方法 在该方法中,通过输入域内是否为null建立了两个分支结构,在两个分支中均做了比较多的分支,最终页面实现跳转或对话框的弹出等,比较简单,需要的朋友请自己查看dialButtonPressed()方法的源码。 4.拨号盘中搜索键搜索按键的处理与其它按键的处理类似,但是又有些不同,在后文将详细的分析一下,感兴趣的朋友自己走一下onClick流程。 希望我的分析,对朋友你有所帮助。
|