会话编辑界面(一)
1、前言
与短信会话界面一样是短信UI中最重要的部分,它比短信会话界面更复杂,由于它包含了短信和彩信两部分,虽然会话界面也是短彩信公共的,但由于新建会话界面涉及到彩信附件的添加、删除、替换以及短彩信的相互转换等等功能。基于该界面的复杂性,大致分为4快来讲解该界面,一是查询显示该会话已经存在的短彩信;二是添加新的短彩信;三是长按某短信息的长按menu;四是menu;要讲解这四块内容首先会介绍主界面的布局文件,以及涉及的类。
2、涉及的类
这里只列举了其中特别重要的几个类,没有完全列举.从上面的类来看有关幻灯片相关都是以Slid开头的类,下面简单的做了一个类图来说明它们的关系,关于Slide这块还有一些比较重要的类上面没有列表,在类图中分别作了做了简单介绍。
2.1 附件view的类图
2.2 主界面相关类图
从上图可以看到主界面要完成ui的绘制以及短信的添加、短彩信的发送功能,实际上还需要很多类协同工作。
2.3 Model 类
这里还剩下一个特别重要的类图,那就是将附件中的元素如Image、vedio、audio等抽象成了一个model,这里就简单的看看这些类之间的关系,有助于我们队对彩信附件的理解。
这里有一个SlideShowModel、SlideModel,大家肯定奇怪了这两个长得这么像双胞胎,那其实他们的关系有点暧昧。SlideShowModel它代表的是所有的幻灯片,就是一个装幻灯片的容器;而SlideModel一个幻灯片,一个幻灯片又可以添加image、audio、vedio等。
3、UI总体布局
通过上述类图,我们可以大致了解到会话编辑界面涉及到的相关的类以及他们之间的复杂关系,会话编辑界面分为两种情况:一是新建一个会话,二是编辑已存在的会话,但他们的实现都是有ComposeMessageActivity类来完成的。
图1,新建会话界面 图2 ,打开已存在会话界面
从上面的图片可以看出新建会话界面和打开已存在的会话界面区别在于:已存在的会话界面会去加载历史记录以及他们的发送者/接收者UI被隐藏,代替的使用title来显示发送者/接收者的号码,而新建回话需要用户编辑接收者。那从代码上是怎么区分的?实现的类都是一样。
3.1 接收者/发送者 UI
ComposeMessageActivity是根据什么来判断当前是打开已存在的还是新建,这里有一点小的涉及技巧,首先在xml布局文件中声明该UI默认是gone,然后在ComposeMessageActivity的onCreate方法做以下判断:
- // Show the recipients editor if we don't have a valid thread. Hide it otherwise.
- if (mConversation.getThreadId() <= 0) {
- // Hide the recipients editor so the call to initRecipientsEditor won't get
- // short-circuited.
- hideRecipientEditor();
- initRecipientsEditor();
- // Bring up the softkeyboard so the user can immediately enter recipients. This
- // call won't do anything on devices with a hard keyboard.
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE |
- WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
- } else {
- hideRecipientEditor();
- }
// Show the recipients editor if we don't have a valid thread. Hide it otherwise.
if (mConversation.getThreadId() <= 0) {
// Hide the recipients editor so the call to initRecipientsEditor won't get
// short-circuited.
hideRecipientEditor();
initRecipientsEditor();
// Bring up the softkeyboard so the user can immediately enter recipients. This
// call won't do anything on devices with a hard keyboard.
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE |
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} else {
hideRecipientEditor();
}
从上述代码你可以看到的是google工程师是通过判断当前会话的ThreadID,那如果是新建的threadId肯定小于0,如果是已存在的其id肯定大于0。可以看到的是小于0时,表示是新建,做了三个操作:一个是隐藏,接收者UI,然后重新初始化该UI,这里有一点小小的疑惑,为什么先要隐藏然后重新初始化?这个问题留给大家,最后了将输入键盘弹出来。然后大于0表示该会话存在,这里就是隐藏接收者UI。
大家又要问了那当前activity的title什么时候设置的??
- private void updateTitle(ContactList list) {
- String s;
- switch (list.size()) {
- case 0: {
- String recipient = "";
- if (mRecipientsEditor != null) {
- recipient = mRecipientsEditor.getText().toString();
- }
- s = recipient;
- break;
- }
- case 1: {
- s = list.get(0).getNameAndNumber();
- break;
- }
- default: {
- // Handle multiple recipients
- s = list.formatNames(", ");
- break;
- }
- }
- mDebugRecipients = list.serialize();
- if(list.size()>1){
- getWindow().setTitle(list.size()+":"+s);
- }else {
- getWindow().setTitle(s);
- }
- }
private void updateTitle(ContactList list) {
String s;
switch (list.size()) {
case 0: {
String recipient = "";
if (mRecipientsEditor != null) {
recipient = mRecipientsEditor.getText().toString();
}
s = recipient;
break;
}
case 1: {
s = list.get(0).getNameAndNumber();
break;
}
default: {
// Handle multiple recipients
s = list.formatNames(", ");
break;
}
}
mDebugRecipients = list.serialize();
if(list.size()>1){
getWindow().setTitle(list.size()+":"+s);
}else {
getWindow().setTitle(s);
}
}
该方法是在onCreate方法中通过initialize来调用的
- // Let the working message know what conversation it belongs to
- mWorkingMessage.setConversation(mConversation);
// Let the working message know what conversation it belongs to
mWorkingMessage.setConversation(mConversation);
通过上面的解析,应该基本上清楚了新建和打开已存在的他们在接收者UI上的处理,非常精妙。
3.2 布局文件解析
对于新建和打开已存在的会话关于接收者UI的不同显示做了一个简单的解析,下面了来看看该界面的布局文件。
1)首先,再说明布局文件之前和大家来认识几个变量,不要认为几个变量无关紧要,读者要表达的是这些变量直接关系到你能否看懂它的整个实现,弄清楚这些变量是做什么的,来看布局文件你就大致清楚了。
- private View mTopPanel; // View containing the recipient and subject editors,该变量就是我们上面所说的接收者UI
- private View mBottomPanel; // View containing the text editor, send button, ec.
- private EditText mTextEditor; // Text editor to type your message into
- private TextView mTextCounter; // Shows the number of characters used in text editor
- private Button mSendButton; // Press to detonate
- private EditText mSubjectTextEditor; // Text editor for MMS subject
- private ImageView mAddRecipientButton; 添加接这button
- private AttachmentEditor mAttachmentEditor;用于显示附件
- private MessageListView mMsgListView; // ListView for messages in this conversation
- public MessageListAdapter mMsgListAdapter; // and its corresponding ListAdapter
- private ImageButton mSmsTemplateButton; // Button used to import template message.
- private RecipientsEditor mRecipientsEditor; // UI control for editing recipients
- private TextView mCountDownTimerText; 用于对字符的计数
- private WorkingMessage mWorkingMessage; 用于存储一条当前短信的信息,包含接收者、短信内容等等
- private Conversation mConversation; 表示当前编辑的会话
private View mTopPanel; // View containing the recipient and subject editors,该变量就是我们上面所说的接收者UI
private View mBottomPanel; // View containing the text editor, send button, ec.
private EditText mTextEditor; // Text editor to type your message into
private TextView mTextCounter; // Shows the number of characters used in text editor
private Button mSendButton; // Press to detonate
private EditText mSubjectTextEditor; // Text editor for MMS subject
private ImageView mAddRecipientButton; 添加接这button
private AttachmentEditor mAttachmentEditor;用于显示附件
private MessageListView mMsgListView; // ListView for messages in this conversation
public MessageListAdapter mMsgListAdapter; // and its corresponding ListAdapter
private ImageButton mSmsTemplateButton; // Button used to import template message.
private RecipientsEditor mRecipientsEditor; // UI control for editing recipients
private TextView mCountDownTimerText; 用于对字符的计数
private WorkingMessage mWorkingMessage; 用于存储一条当前短信的信息,包含接收者、短信内容等等
private Conversation mConversation; 表示当前编辑的会话
2)了解上面这些变量后,我们再来看布局文件,通过看布局文件我们可以很清楚清晰的明白这些ui组件怎么放置的
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/white_background"
- android:orientation="vertical">
- <!-----------接收者UI-------------------------------------------------->
- <LinearLayout
- android:id="@+id/recipients_subject_linear"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="5dip"
- android:paddingBottom="5dip"
- android:paddingLeft="5dip"
- android:paddingRight="5dip"
- android:orientation="vertical"
- android:visibility="gone">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <ViewStub android:id="@+id/recipients_editor_stub"
- android:layout="@layout/recipients_editor"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1.0"
- />
- <ViewStub android:id="@+id/add_recipients_bt_stub"
- android:layout="@layout/add_recipients_bt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="5dip"
- />
- </LinearLayout>
- <EditText android:id="@+id/subject" 彩信主题
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:capitalize="sentences"
- android:autoText="true"
- android:singleLine="true"
- android:maxLength="40"
- android:hint="@string/subject_hint"
- android:visibility="gone"/>
- </LinearLayout>
- <!---------------------end---------------------------------------->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="bottom">
- <!---------------------历史记录----------------------------------------->
- <view class="com.android.mms.ui.MessageListView"
- style="?android:attr/listViewWhiteStyle"
- android:id="@+id/history"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1.0"
- android:listSelector="@drawable/chat_history_selector"
- android:drawSelectorOnTop="true"
- android:transcriptMode="alwaysScroll"
- android:scrollbarAlwaysDrawVerticalTrack="true"
- android:scrollbarStyle="insideInset"
- android:stackFromBottom="true"
- android:visibility="gone"
- android:fadingEdge="none"
- android:layout_marginBottom="1dip"
- android:cacheColorHint="@android:color/white"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <!-----------------------附件UI---------------------------------------------------------------->
- <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_weight="1.0"
- android:layout_width="match_parent"
- android:layout_height="0dip">
- <view class="com.android.mms.ui.AttachmentEditor"
- android:id="@+id/attachment_editor"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <ViewStub android:id="@+id/image_attachment_view_portrait_stub"
- android:layout="@layout/image_attachment_view_portrait"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <ViewStub android:id="@+id/video_attachment_view_portrait_stub"
- android:layout="@layout/video_attachment_view_portrait"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <ViewStub android:id="@+id/audio_attachment_view_portrait_stub"
- android:layout="@layout/audio_attachment_view_portrait"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <ViewStub android:id="@+id/slideshow_attachment_view_portrait_stub"
- android:layout="@layout/slideshow_attachment_view_portrait"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <ViewStub android:id="@+id/image_attachment_view_landscape_stub" 图像附件
- android:layout="@layout/image_attachment_view_landscape"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <ViewStub android:id="@+id/video_attachment_view_landscape_stub" vedio附件
- android:layout="@layout/video_attachment_view_landscape"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <ViewStub android:id="@+id/audio_attachment_view_landscape_stub" audio 附件
- android:layout="@layout/audio_attachment_view_landscape"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <ViewStub android:id="@+id/slideshow_attachment_view_landscape_stub"
- android:layout="@layout/slideshow_attachment_view_landscape"
- android:layout_width="match_parent" 幻灯片附件
- android:layout_height="wrap_content"/>
- </view>
- </ScrollView>
- <!----------------------end------------------------------------------------------->
- <LinearLayout
- android:id="@+id/bottom_panel"
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="5dip"
- android:paddingBottom="5dip"
- android:paddingLeft="5dip"
- android:paddingRight="5dip"
- android:background="@drawable/bottombar_landscape_565">
- <!-----------------------关于短信发送按钮、短信模版、短信内容编辑--UI----------------->
- <RelativeLayout android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:layout_weight="1.0"
- android:orientation="horizontal"
- android:id="@+id/relativeLayout1">
- <EditText
- android:id="@+id/embedded_text_editor" 短信内容编辑框
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1.0"
- android:autoText="true"
- android:capitalize="sentences"
- android:nextFocusRight="@+id/send_button"
- android:hint="@string/type_to_compose_text_enter_to_send"
- android:maxLines="3"
- android:inputType="textShortMessage|textAutoCorrect|textCapSentences|textMultiLine"
- android:imeOptions="actionSend|flagNoEnterAction"
- android:background="@android:drawable/edit_text"
- android:maxLength="2000"
- />
- <ImageButton
- android:id="@+id/smstemp_button" 短信模版按钮
- android:layout_marginTop="1dip"
- android:layout_marginBottom="3dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignRight="@id/embedded_text_editor"
- android:src="@drawable/import_sms_template"
- />
- </RelativeLayout>
- <LinearLayout
- android:id="@+id/button_with_counter"
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="match_parent" >
- <Button
- android:id="@+id/send_button" 发送按钮
- android:layout_marginLeft="5dip"
- android:layout_width="wrap_content"
- android:layout_height="0dip"
- android:layout_weight="1.0"
- style="?android:attr/buttonStyle"
- android:nextFocusLeft="@+id/embedded_text_editor"
- android:text="@string/send"
- />
- <TextView
- android:id="@+id/text_counter" 文本计数
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal|bottom"
- android:textColor="#ffffffff"
- android:textSize="11sp"
- android:textStyle="bold"
- android:paddingLeft="3dip"
- android:paddingRight="3dip"
- android:paddingBottom="5dip"
- android:visibility="gone"
- />
- </LinearLayout>
- </LinearLayout>
- </LinearLayout>
- </LinearLayout>
- </LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/white_background"
android:orientation="vertical">
<!-----------接收者UI-------------------------------------------------->
<LinearLayout
android:id="@+id/recipients_subject_linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ViewStub android:id="@+id/recipients_editor_stub"
android:layout="@layout/recipients_editor"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1.0"
/>
<ViewStub android:id="@+id/add_recipients_bt_stub"
android:layout="@layout/add_recipients_bt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
/>
</LinearLayout>
<EditText android:id="@+id/subject" 彩信主题
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:capitalize="sentences"
android:autoText="true"
android:singleLine="true"
android:maxLength="40"
android:hint="@string/subject_hint"
android:visibility="gone"/>
</LinearLayout>
<!---------------------end---------------------------------------->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="bottom">
<!---------------------历史记录----------------------------------------->
<view class="com.android.mms.ui.MessageListView"
style="?android:attr/listViewWhiteStyle"
android:id="@+id/history"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:listSelector="@drawable/chat_history_selector"
android:drawSelectorOnTop="true"
android:transcriptMode="alwaysScroll"
android:scrollbarAlwaysDrawVerticalTrack="true"
android:scrollbarStyle="insideInset"
android:stackFromBottom="true"
android:visibility="gone"
android:fadingEdge="none"
android:layout_marginBottom="1dip"
android:cacheColorHint="@android:color/white"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-----------------------附件UI---------------------------------------------------------------->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_weight="1.0"
android:layout_width="match_parent"
android:layout_height="0dip">
<view class="com.android.mms.ui.AttachmentEditor"
android:id="@+id/attachment_editor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ViewStub android:id="@+id/image_attachment_view_portrait_stub"
android:layout="@layout/image_attachment_view_portrait"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/video_attachment_view_portrait_stub"
android:layout="@layout/video_attachment_view_portrait"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/audio_attachment_view_portrait_stub"
android:layout="@layout/audio_attachment_view_portrait"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/slideshow_attachment_view_portrait_stub"
android:layout="@layout/slideshow_attachment_view_portrait"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/image_attachment_view_landscape_stub" 图像附件
android:layout="@layout/image_attachment_view_landscape"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/video_attachment_view_landscape_stub" vedio附件
android:layout="@layout/video_attachment_view_landscape"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/audio_attachment_view_landscape_stub" audio 附件
android:layout="@layout/audio_attachment_view_landscape"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/slideshow_attachment_view_landscape_stub"
android:layout="@layout/slideshow_attachment_view_landscape"
android:layout_width="match_parent" 幻灯片附件
android:layout_height="wrap_content"/>
</view>
</ScrollView>
<!----------------------end------------------------------------------------------->
<LinearLayout
android:id="@+id/bottom_panel"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:background="@drawable/bottombar_landscape_565">
<!-----------------------关于短信发送按钮、短信模版、短信内容编辑--UI----------------->
<RelativeLayout android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_weight="1.0"
android:orientation="horizontal"
android:id="@+id/relativeLayout1">
<EditText
android:id="@+id/embedded_text_editor" 短信内容编辑框
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:autoText="true"
android:capitalize="sentences"
android:nextFocusRight="@+id/send_button"
android:hint="@string/type_to_compose_text_enter_to_send"
android:maxLines="3"
android:inputType="textShortMessage|textAutoCorrect|textCapSentences|textMultiLine"
android:imeOptions="actionSend|flagNoEnterAction"
android:background="@android:drawable/edit_text"
android:maxLength="2000"
/>
<ImageButton
android:id="@+id/smstemp_button" 短信模版按钮
android:layout_marginTop="1dip"
android:layout_marginBottom="3dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@id/embedded_text_editor"
android:src="@drawable/import_sms_template"
/>
</RelativeLayout>
<LinearLayout
android:id="@+id/button_with_counter"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent" >
<Button
android:id="@+id/send_button" 发送按钮
android:layout_marginLeft="5dip"
android:layout_width="wrap_content"
android:layout_height="0dip"
android:layout_weight="1.0"
style="?android:attr/buttonStyle"
android:nextFocusLeft="@+id/embedded_text_editor"
android:text="@string/send"
/>
<TextView
android:id="@+id/text_counter" 文本计数
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|bottom"
android:textColor="#ffffffff"
android:textSize="11sp"
android:textStyle="bold"
android:paddingLeft="3dip"
android:paddingRight="3dip"
android:paddingBottom="5dip"
android:visibility="gone"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
这里不给大家一个一个来讲解,我想只要知道一些android ui的同胞看上面的布局文件应该没有问题
3)对照关系
那实际上对应上面的变量,就可以猜出在布局文件中所占的位置,这里以代码为证;
- mMsgListView = (MessageListView) findViewById(R.id.history);
- mBottomPanel = findViewById(R.id.bottom_panel);
- mTextEditor = (EditText) findViewById(R.id.embedded_text_editor);
- mTextCounter = (TextView) findViewById(R.id.text_counter);
- mSendButton = (Button) findViewById(R.id.send_button);
- mTopPanel = findViewById(R.id.recipients_subject_linear);
- mAttachmentEditor = (AttachmentEditor) findViewById(R.id.attachment_editor);
- mSmsTemplateButton = (ImageButton)findViewById(R.id.smstemp_button);
mMsgListView = (MessageListView) findViewById(R.id.history);
mBottomPanel = findViewById(R.id.bottom_panel);
mTextEditor = (EditText) findViewById(R.id.embedded_text_editor);
mTextCounter = (TextView) findViewById(R.id.text_counter);
mSendButton = (Button) findViewById(R.id.send_button);
mTopPanel = findViewById(R.id.recipients_subject_linear);
mAttachmentEditor = (AttachmentEditor) findViewById(R.id.attachment_editor);
mSmsTemplateButton = (ImageButton)findViewById(R.id.smstemp_button);
这里基本上列举了对上述变量的赋值。
4、小结
这里大致讲解了一下整个界面的一些布局元素,以及相关类之间的关系。短信会话编辑界面的具体功能,这里对于该UI来说模块比较清晰了,大致分为4块:BottomPanel(发送按钮、短信编辑框、短信模版添加按钮)、TopPanel(包含接收者编辑框和从联系人添加按钮、以及彩信主题)、MsgListView(历史记录,非常重要这里有一些细节的东西)、AttachmentEditor(彩信附件UI),后面文章会继续对每个模块进行分析。