程序启动

程序的入口:ConversationList.java,对应主页中短信的快捷方式。由此进入短信列表模块。

短信列表模块

该模块的展示是由ConversationList.java类实现的,该类继承自ListActivity,以列表的形式展示所有短信记录。模块启动的onCreate()方法中初始化listview的数据源mListAdapter与actionbar

    private void initListAdapter() {
mListAdapter = new ConversationListAdapter(this, null);
mListAdapter.setOnContentChangedListener(mContentChangedListener);
setListAdapter(mListAdapter);
getListView().setRecyclerListener(mListAdapter);
}
    private void setupActionBar() {
ActionBar actionBar = getActionBar();
 
ViewGroup v = (ViewGroup)LayoutInflater.from(this)
.inflate(R.layout.conversation_list_actionbar, null);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
ActionBar.DISPLAY_SHOW_CUSTOM);
actionBar.setCustomView(v,
new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT,
ActionBar.LayoutParams.WRAP_CONTENT,
Gravity.CENTER_VERTICAL | Gravity.RIGHT));
 
mUnreadConvCount = (TextView)v.findViewById(R.id.unread_conv_count);
}

在ConversationList.java类的onStart()方法中调用startAsyncQuery();实现异步查询短信数据

    private void startAsyncQuery() {
try {
((TextView)(getListView().getEmptyView())).setText(R.string.loading_conversations);
 
Conversation.startQueryForAll(mQueryHandler, THREAD_LIST_QUERY_TOKEN);
Conversation.startQuery(mQueryHandler, UNREAD_THREADS_QUERY_TOKEN, Threads.READ + "=0");
} catch (SQLiteException e) {
SqliteWrapper.checkSQLiteException(this, e);
}
}

异步查询数据的功能主要是由ThreadListQueryHandler.java类来实现,该类继承自ConversationQueryHandler.java,而ConversationQueryHandler.java类又继承自AsyncQueryHandler.java类。ThreadListQueryHandler.java类调用父类AsyncQueryHandler.java的startQuery()方法开始异步查询。同时ThreadListQueryHandler.java类重写了父类的onQueryComplete()方法,来实现将查询出来的数据更新listview绑定的mListAdapter对象,实现异步刷新并展现的功能。

    @Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
case THREAD_LIST_QUERY_TOKEN:
mListAdapter.changeCursor(cursor);
if (mListAdapter.getCount() == 0) {
((TextView)(getListView().getEmptyView())).setText(R.string.no_conversations);
}
if (mNeedToMarkAsSeen) {
mNeedToMarkAsSeen = false;
Conversation.markAllConversationsAsSeen(getApplicationContext());
mHandler.postDelayed(mDeleteObsoleteThreadsRunnable,DELETE_OBSOLETE_THREAD_DELAY);
}
// some menu items depend on the adapter's count
if (mDeleteAllItem != null)
mDeleteAllItem.setVisible(mListAdapter.getCount() > 0);
break;
case UNREAD_THREADS_QUERY_TOKEN:
int count = 0;
if (cursor != null) {
count = cursor.getCount();
cursor.close();
}
mUnreadConvCount.setText(count > 0 ? Integer.toString(count) : null);
break;
case HAVE_LOCKED_MESSAGES_TOKEN:
@SuppressWarnings("unchecked")
Collection<Long> threadIds = (Collection<Long>)cookie;
confirmDeleteThreadDialog(new DeleteThreadListener(threadIds, mQueryHandler,
ConversationList.this), threadIds,
cursor != null && cursor.getCount() > 0,
ConversationList.this);
if (cursor != null) {
cursor.close();
}
break;
default:
Log.e(TAG, "onQueryComplete called with unknown token " + token);
}
}

listview中的每个item点击后会进入到短信发送模块,由此方式进入到短信发送模块会传递一个联系人参数,在短信发送模块中会显示与该联系人的相关短信记录,并可向该联系人发送短信

    @Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// Note: don't read the thread id data from the ConversationListItem view passed in.
// It's unreliable to read the cached data stored in the view because the ListItem
// can be recycled, and the same view could be assigned to a different position
// if you click the list item fast enough. Instead, get the cursor at the position
// clicked and load the data from the cursor.
// (ConversationListAdapter extends CursorAdapter, so getItemAtPosition() should
// return the cursor object, which is moved to the position passed in)
Cursor cursor = (Cursor) getListView().getItemAtPosition(position);
Conversation conv = Conversation.from(this, cursor);
long tid = conv.getThreadId();
 
if (LogTag.VERBOSE) {
Log.d(TAG, "onListItemClick: pos=" + position + ", view=" + v + ", tid=" + tid);
}
 
openThread(tid);
}
    private void openThread(long threadId) {
startActivity(ComposeMessageActivity.createIntent(this, threadId));
}

在菜单栏中也可以进入短信发送模块,该MenuItem在XML中定义如下。由此方式进入到短信发送模块不会传递具体的联系人参数,此时在短信发送模块中可以选择需要发送短信的联系人,也可是实现短信群发的功能。

    <item android:id="@+id/action_compose_new"
android:title="@string/new_message"
android:icon="@drawable/ic_menu_msg_compose_holo_dark"
android:showAsAction="always|withText" />
    @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.action_compose_new:
createNewMessage();
break;
case R.id.action_delete_all:
// The invalid threadId of -1 means all threads here.
confirmDeleteThread(-1L, mQueryHandler);
// hide the item: "Delete all threads"
if (mDeleteAllItem != null)
mDeleteAllItem.setVisible(false);
break;
case R.id.action_settings:
Intent intent = new Intent(this, MessagingPreferenceActivity.class);
startActivityIfNeeded(intent, -1);
break;
case R.id.action_debug_dump:
LogTag.dumpInternalTables(this);
break;
default:
return true;
}
return false;
}
    private void createNewMessage() {
startActivity(ComposeMessageActivity.createIntent(this, 0));
}

短信发送模块

短信发送模块的页面展示是ComposeMessageActivity.java来实现的。在该模块中有个较为复杂的自定义组件RecipientsEditor。在onCreate()方法中用initRecipientsEditor()方法初始化该组件

    private void initRecipientsEditor() {
if (isRecipientsEditorVisible()) {
return;
}
ContactList recipients = getRecipients();
ViewStub stub = (ViewStub)findViewById(R.id.recipients_editor_stub);
if (stub != null) {
View stubView = stub.inflate();
mRecipientsEditor = (RecipientsEditor) stubView.findViewById(R.id.recipients_editor);
mRecipientsPicker = (ImageButton) stubView.findViewById(R.id.recipients_picker);
} else {
mRecipientsEditor = (RecipientsEditor)findViewById(R.id.recipients_editor);
mRecipientsEditor.setVisibility(View.VISIBLE);
mRecipientsPicker = (ImageButton)findViewById(R.id.recipients_picker);
mRecipientsPicker.setVisibility(View.VISIBLE);
}
mRecipientsPicker.setOnClickListener(this);
mRecipientsEditor.setAdapter(new ChipsRecipientAdapter(this));
mRecipientsEditor.populate(recipients);
mRecipientsEditor.setOnCreateContextMenuListener(mRecipientsMenuCreateListener);
mRecipientsEditor.addTextChangedListener(mRecipientsWatcher);
mRecipientsEditor.setOnSelectChipRunnable(new Runnable() {
@Override
public void run() {
if (mRecipientsEditor.getRecipientCount() == 1) {
// if we're in extract mode then don't request focus
final InputMethodManager inputManager = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputManager == null || !inputManager.isFullscreenMode()) {
mTextEditor.requestFocus();
}
}
}
});
mRecipientsEditor.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
RecipientsEditor editor = (RecipientsEditor) v;
ContactList contacts = editor.constructContactsFromInput(false);
updateTitle(contacts);
}
}
});
PhoneNumberFormatter.setPhoneNumberFormattingTextWatcher(this, mRecipientsEditor);
 
mTopPanel.setVisibility(View.VISIBLE);
}

点击该组件中的imagebutton即mRecipientsPicker可以进入联系人选择页面,选择需要发送短信的联系人,可以多选。进入到联系人选择页面是采用startActivityForResult()的方式进入的

    private void launchMultiplePhonePicker() {
Intent intent = new Intent("com.android.contacts.action.MULTI_PICK",Contacts.CONTENT_URI);
String exsitNumbers = mRecipientsEditor.getExsitNumbers();
if (!TextUtils.isEmpty(exsitNumbers)) {
intent.putExtra(Intents.EXTRA_PHONE_URIS, exsitNumbers);
}
// We have to wait for the constructing complete.
try {
mIsPickingContact = true;
startActivityForResult(intent, REQUEST_CODE_PICK);
} catch (ActivityNotFoundException ex) {
Toast.makeText(this, R.string.contact_app_not_found, Toast.LENGTH_SHORT).show();
}
}

通过startActivityForResult()进入到选择联系人界面,在ComposeMessageActivity.java类中自然会有onActivityResult()来从返回结果中获取联系人信息,这个返回的数据是联系人在sqlite数据库中对应的id。

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
...
if (requestCode == REQUEST_CODE_PICK) {
mWorkingMessage.asyncDeleteDraftSmsMessage(mConversation);
 
// if the sms draft be deleted, flag mIsSmsDraftAlreadyDeleted is true.
mIsSmsDraftAlreadyDeleted = true;
}
 
...
switch (requestCode) {
...
case REQUEST_CODE_PICK:
if (data != null) {
processPickResult(data);
}
break;
 
...
default:
if (LogTag.VERBOSE) log("bail due to unknown requestCode=" + requestCode);
break;
}
}

processPickResult方法主要是用来判断要发送短信的联系人数量是否超出了100个,默认是不允许超过100个。

    private void processPickResult(final Intent data) {
// The EXTRA_PHONE_URIS stores the phone's urls that were selected by user in the
// multiple phone picker.
Bundle bundle = data.getExtras().getBundle("result");
final Set<String> keySet = bundle.keySet();
final int recipientCount = (keySet != null) ? keySet.size() : 0;
 
// if total recipients count > recipientLimit,
// then forbid add reipients to RecipientsEditor
final int recipientLimit = MmsConfig.getRecipientLimit();
int totalRecipientsCount = mExistsRecipientsCount + recipientCount;
if (recipientLimit != Integer.MAX_VALUE && totalRecipientsCount > recipientLimit) {
new AlertDialog.Builder(this)
.setMessage(getString(R.string.too_many_recipients, totalRecipientsCount, recipientLimit))
.setPositiveButton(android.R.string.ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// if already exists some recipients,
// then new pick recipients with exists recipients count
// can't more than recipient limit count.
int newPickRecipientsCount = recipientLimit - mExistsRecipientsCount;
if (newPickRecipientsCount <= 0) {
return;
}
processAddRecipients(keySet, newPickRecipientsCount);
}
})
.setNegativeButton(android.R.string.cancel, null)
.create().show();
return;
}
 
processAddRecipients(keySet, recipientCount);
}

processAddRecipients方法中开启了三条条线程,第一条线程是判断对从联系人选择界面返回的数据的格式化处理是否超过1秒,是则显示一个progressDialog,否则不显示。第二条是获取从联系人选择界面返回的数据并格式化这些数据,然后与之前已经存在的需要发送的联系人集合合并,去除重复的联系人信息。第三条则是将联系人信息按照一定的方式填充到RecipientsEditor组件中

    private void processAddRecipients(final Set<String> keySet, final int newPickRecipientsCount) {
// if process pick result that is pick recipients from Contacts
mIsProcessPickedRecipients = true;
final Handler handler = new Handler();
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setTitle(getText(R.string.pick_too_many_recipients));
progressDialog.setMessage(getText(R.string.adding_recipients));
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false);
 
final Runnable showProgress = new Runnable() {
@Override
public void run() {
progressDialog.show();
}
};
// Only show the progress dialog if we can not finish off parsing the return data in 1s,
// otherwise the dialog could flicker.
handler.postDelayed(showProgress, 1000);
 
new Thread(new Runnable() {
@Override
public void run() {
Uri[] newuris = new Uri[newPickRecipientsCount];
final ContactList list;
try {
Iterator<String> it = keySet.iterator();
int i = 0;
while (it.hasNext()) {
String id = it.next();
newuris[i++] = ContentUris.withAppendedId(Phone.CONTENT_URI, Integer.parseInt(id));
if (i == newPickRecipientsCount) {
break;
}
}
list = ContactList.blockingGetByUris(newuris);
} finally {
handler.removeCallbacks(showProgress);
}
if (mRecipientsEditor != null) {
ContactList exsitList = mRecipientsEditor.constructContactsFromInput(true);
// Remove the repeat recipients.
if(exsitList.equals(list)){
exsitList.clear();
list.addAll(0, exsitList);
}else{
list.removeAll(exsitList);
list.addAll(0, exsitList);
}
}
 
// TODO: there is already code to update the contact header widget and recipients
// editor if the contacts change. we can re-use that code.
final Runnable populateWorker = new Runnable() {
@Override
public void run() {
// We must remove this listener before dealing with the contact list.
// Because the listener will take a lot of time, this will cause an ANR.
mRecipientsEditor.removeTextChangedListener(mRecipientsWatcher);
mRecipientsEditor.populate(list);
// Set value for mRecipientsPickList and
// mRecipientsWatcher will update the UI.
mRecipientsPickList = list;
updateTitle(list);
 
mRecipientsEditor.addTextChangedListener(mRecipientsWatcher);
 
// if process finished, then dismiss the progress dialog
progressDialog.dismiss();
}
};
handler.post(populateWorker);
}
}, "ComoseMessageActivity.processPickResult").start();
}

RecipientsEditor组件的主要功能是承担短信发送的联系人的容器,RecipientsEditor.java类继承自RecipientEditTextView.java类。其populate方法通过调用父类append方法来实现填充联系人的功能。

    public void populate(ContactList list) {
if (list.size() == 0) {
setText(null);
} else {
// Clear the recipient when add contact again
setText("");
for (Contact c : list) {
CharSequence charSequence = contactToToken(c);
if (charSequence != null && charSequence.length() > 0) {
append( charSequence+ ", ");
}
}
}
}

RecipientEditTextView.java类中的append方法具体实现了填充联系人信息的功能。

    @Override
public void append(CharSequence text, int start, int end) {
// We need care about watching text changes while appending ',' or ';'.
if (!TextUtils.isEmpty(text)) {
String textString = text.toString().trim();
if (textString.equals(String.valueOf(COMMIT_CHAR_COMMA))
|| textString.equals(String.valueOf(COMMIT_CHAR_SEMICOLON))) {
super.append(text, start, end);
return;
}
}
// We don't care about watching text changes while appending.
if (mTextWatcher != null) {
removeTextChangedListener(mTextWatcher);
}
super.append(text, start, end);
if (!TextUtils.isEmpty(text) && TextUtils.getTrimmedLength(text) > 0) {
final String displayString = text.toString();
int seperatorPos = displayString.indexOf(COMMIT_CHAR_COMMA);
if (seperatorPos != 0 && !TextUtils.isEmpty(displayString)
&& TextUtils.getTrimmedLength(displayString) > 0) {
mPendingChipsCount++;
mPendingChips.add(text.toString());
}
}
// Put a message on the queue to make sure we ALWAYS handle pending chips.
if (mPendingChipsCount > 0) {
postHandlePendingChips();
}
mHandler.post(mAddTextWatcher);
}

在append方法中调动postHandlePendingChips();方法,对联系人信息数据进行异步处理,具体方法是handlePendingChips()

    /*package*/ void handlePendingChips() {
if (getViewWidth() <= 0) {
// The widget has not been sized yet.
// This will be called as a result of onSizeChanged
// at a later point.
return;
}
if (mPendingChipsCount <= 0) {
return;
}
 
synchronized (mPendingChips) {
Editable editable = getText();
// Tokenize!
if (mPendingChipsCount <= MAX_CHIPS_PARSED) {
for (int i = 0; i < mPendingChips.size(); i++) {
String current = mPendingChips.get(i);
int tokenStart = editable.toString().indexOf(current);
int tokenEnd = tokenStart + current.length();
if (tokenStart >= 0) {
// When we have a valid token, include it with the token
// to the left.
if (tokenEnd < editable.length() - 2
&& editable.charAt(tokenEnd) == COMMIT_CHAR_COMMA) {
tokenEnd++;
}
createReplacementChip(tokenStart, tokenEnd, editable);
}
mPendingChipsCount--;
}
sanitizeEnd();
} else {
mNoChips = true;
}
 
if (mTemporaryRecipients != null && mTemporaryRecipients.size() > 0
&& mTemporaryRecipients.size() <= RecipientAlternatesAdapter.MAX_LOOKUPS) {
if (hasFocus() || mTemporaryRecipients.size() <= CHIP_LIMIT) {
new RecipientReplacementTask().execute();
mTemporaryRecipients = null;
} else {
// Create the "more" chip
mIndividualReplacements = new IndividualReplacementTask();
mIndividualReplacements.execute(new ArrayList<RecipientChip>(
mTemporaryRecipients.subList(0, CHIP_LIMIT)));
 
// Remove the exists more chip before create, or it may
// create repeat more chip.
removeMoreChip();
createMoreChip();
}
} else {
// There are too many recipients to look up, so just fall back
// to showing addresses for all of them.
mTemporaryRecipients = null;
removeMoreChip();
createMoreChip();
}
mPendingChipsCount = 0;
mPendingChips.clear();
}
}

在handlePendingChips()方法中调用createReplacementChip()方法,完成对联系人信息的处理。RecipientsEditor组件中显示的其实是RecipientEntry.java类,createReplacementChip()方法完成将特定格式的联系人信息转换成RecipientEntry对象,在RecipientsEditor组件中显示具体的联系人姓名,但在实际的短信发送过程中,是根据联系人的号码来发送的。

    private void createReplacementChip(int tokenStart, int tokenEnd, Editable editable) {
if (alreadyHasChip(tokenStart, tokenEnd)) {
// There is already a chip present at this location.
// Don't recreate it.
return;
}
String token = editable.toString().substring(tokenStart, tokenEnd);
int commitCharIndex = token.trim().lastIndexOf(COMMIT_CHAR_COMMA);
if (commitCharIndex == token.length() - 1) {
token = token.substring(0, token.length() - 1);
}
RecipientEntry entry = createTokenizedEntry(token);
if (entry != null) {
String destText = createAddressText(entry);
// Always leave a blank space at the end of a chip.
int textLength = destText.length() - 1;
SpannableString chipText = new SpannableString(destText);
int end = getSelectionEnd();
int start = mTokenizer != null ? mTokenizer.findTokenStart(getText(), end) : 0;
RecipientChip chip = null;
try {
if (!mNoChips) {
chip = constructChipSpan(entry, start, false);
chipText.setSpan(chip, 0, textLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} catch (NullPointerException e) {
Log.e(TAG, e.getMessage(), e);
}
editable.replace(tokenStart, tokenEnd, chipText);
// Add this chip to the list of entries "to replace"
if (chip != null) {
if (mTemporaryRecipients == null) {
mTemporaryRecipients = new ArrayList<RecipientChip>();
}
chip.setOriginalText(chipText.toString());
mTemporaryRecipients.add(chip);
}
}
}

mms:源码浅析的更多相关文章

  1. 【深入浅出jQuery】源码浅析--整体架构

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  2. 【深入浅出jQuery】源码浅析2--奇技淫巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  3. MMS源码中异步处理简析

    1,信息数据的查询,删除使用AsycnQueryHandler处理 AsycnQueryHandler继承了Handler public abstract class AsyncQueryHandle ...

  4. Struts2源码浅析-ConfigurationProvider

    ConfigurationProvider接口 主要完成struts配置文件 加载 注册过程 ConfigurationProvider接口定义 public interface Configurat ...

  5. (转)【深入浅出jQuery】源码浅析2--奇技淫巧

    [深入浅出jQuery]源码浅析2--奇技淫巧 http://www.cnblogs.com/coco1s/p/5303041.html

  6. HashSet其实就那么一回事儿之源码浅析

    上篇文章<HashMap其实就那么一回事儿之源码浅析>介绍了hashMap,  本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...

  7. Android 手势识别类 ( 三 ) GestureDetector 源码浅析

    前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...

  8. Android开发之Theme、Style探索及源码浅析

    1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...

  9. 【深入浅出jQuery】源码浅析2--使用技巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

随机推荐

  1. 【Coursera】Fifth week(3)

    Ethernet 在 PARC(Xerox) 发明. 第一个 Local-Area-Network (LAN 局域网). 把 PCs 连接到 激光打印机上. 在夏威夷大学,被早期的无线网络 Aloha ...

  2. UVa 12108 特别困的学生

    https://vjudge.net/problem/UVA-12108 题意:给出n个学生的“清醒—睡眠”周期和初始时间点,每个学生在睡眠时需要判断全班睡觉人数是否严格大于清醒人数,否则在坚持一个清 ...

  3. zeptojs库解读3之ajax模块

    对于ajax,三步骤,第一,创建xhr对象:第二,发送请求:第三,处理响应. 但在编写过程中,实际中会碰到以下问题, 1.超时 2.跨域 3.后退 解决方法: 1.超时 设置定时器,规定的时间内未返回 ...

  4. BZOJ 2339 【HNOI2011】 卡农

    题目链接:卡农 听说这道题是经典题? 首先明确一下题意(我在这里纠结了好久):有\(n\)个数,要求你选出\(m\)个不同的子集,使得每个数都出现了偶数次.无先后顺序. 这道题就是一道数学题.显然我们 ...

  5. c++ 容器元素遍历打印(for_each)

    #include <iostream> // cout #include <algorithm> // for_each #include <vector> // ...

  6. 怎么彻底删除2345的各种顽固Process

    清晨打开电脑,都是2345的不良新闻,心情不美美哒 2345如何卸载? “C:\Windows\System32\drivers”目录删除Mslmedia.sys 开始-运行-cmd输入“sc del ...

  7. django模型的元数据Meta

    模型的元数据,指的是“除了字段外的所有内容”,例如排序方式.数据库表名.人类可读的单数或者复数名等等.所有的这些都是非必须的,甚至元数据本身对模型也是非必须的.但是,我要说但是,有些元数据选项能给予你 ...

  8. 解决RDP连接不上

    1,开始->运行 regedit 删除注册表项目:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft, 删除MSLicensing.2,开始--运行 mstsc /v:( ...

  9. 显示Unicode 字节的对应字符的小技巧

    在一段smali代码里看到这样的代码 const-string v0, "\u7528\u6237\u9a8c\u8bc1\u8fc7\u671f\uff0c\u8bf7\u91cd\u65 ...

  10. ISO 8859-1 对照表 (扩展ASCII码表)

    1. 0---127 是ASCII码 2.128--255 加了一些特殊符号 DEC OCT HEX BIN Symbol HTML Number HTML Name Description 128 ...