如何使用Firebase创建Android聊天应用程序
来源:互联网 发布:企业名录查询软件 编辑:程序博客网 时间:2024/03/29 06:29
How to Create an Android Chat App Using Firebase
With Firebase, creating real-time social applications is a walk in the park. And the best thing about it: you don't have to write a single line of server-side code. 使用Firebase,创建实时社交应用程序像在公园里散步。 最好的事情:你不必写一行服务器端代码。
In this tutorial, I'll show you how to leverage Firebase UI to create a group chat app you can share with your friends. It's going to be a very simple app with just one chat room, which is open to all users.在本教程中,我将向您展示如何利用Firebase UI创建一个可以与您的朋友分享的群聊应用程序。 它将是一个非常简单的应用程序,只有一个聊天室,这是开放给所有用户。
As you might have guessed, the app will depend on Firebase Auth to manage user registration and sign in. It will also use Firebase's real-time database to store the group chat messages. 正如您可能已经猜到的,应用程序将依靠Firebase Auth来管理用户注册和登录。它还将使用Firebase的实时数据库来存储组聊天消息。
Prerequisites
To be able to follow this step-by-step tutorial, you'll need the following:
- The latest version of Android Studio
- A Firebase account
For instructions on how to set up a Firebase account and get ready for Firebase development in Android Studio, see my tutorial Get Started With Firebase for Android here on Envato Tuts+.
要能够按照此分步教程,您需要以下内容:
最新版本的Android Studio
Firebase帐户
有关如何设置Firebase帐户并准备在Android Studio中进行Firebase开发的说明,请参阅我的教程开始使用Firebase for Android在这里Envato Tuts +。
1. Create an Android Studio Project
Fire up Android Studio and create a new project with an empty activity called MainActivity. 启动Android Studio并创建一个名为MainActivity的空活动的新项目。
To configure the project to use the Firebase platform, open the Firebase Assistant window by clicking onTools > Firebase. While using the Firebase platform, it's usually a good idea to add Firebase Analytics to the project. Therefore, inside the Firebase Assistant window, go to the Analytics section and press Log an Analytics event. 要将项目配置为使用Firebase平台,请单击工具> Firebase打开Firebase Assistant窗口。在使用Firebase平台时,通常最好将Firebase Analytics添加到项目中。 因此,在Firebase Assistant窗口中,转到Google Analytics(分析)部分,然后按记录Google Analytics(分析)事件。
Next, press the Connect to Firebase button and make sure that the Create new Firebase project option is selected. Once the connection is established, press the Add Analytics to your app button. 接下来,按“连接到Firebase”按钮,确保选中了“创建新Firebase项目”选项。 建立连接后,按向应用添加Google Analytics(分析)按钮。
At this point, the Android Studio project is not only integrated with Firebase Analytics, it is also ready to use all other Firebase services. 在这一点上,Android Studio项目不仅与Firebase Analytics集成,它还可以使用所有其他Firebase服务。
2. Add Dependencies 添加依赖关系
We'll be using two libraries in this project: Firebase UI, and the Android design support library. Therefore, open the build.gradle file of the app
module and add the following compile
dependencies to it: 我们将在这个项目中使用两个库:Firebase UI和Android设计支持库。因此,打开应用程序模块的build.gradle文件,并向其添加以下编译依赖关系:
compile
'com.android.support:design:23.4.0'
compile
'com.firebaseui:firebase-ui:0.6.0'
Press the Sync Now button to update the project.
3. Define Layouts 定义布局
The activity_main.xml file, which is already bound to MainActivity
, defines the contents of the home screen of the app. In other words, it will represent the chat room. 已经绑定到MainActivity的activity_main.xml文件定义应用程序主屏幕的内容。换句话说,它将代表聊天室。
Like most other group chat apps available today, our app will have the following UI elements:
- A list that displays all the group chat messages in a chronological order
- An input field where the user can type in a new message
- A button the user can press to post the message
与目前可用的大多数其他群聊应用一样,我们的应用将具有以下UI元素:
一个列表,按时间顺序显示所有组聊天消息
用户可以键入新消息的输入字段
用户可以按下的按钮来发布消息
Therefore, activity_main.xml must have a ListView
, an EditText
, and a FloatingActionButton
. After placing them all inside a RelativeLayout
widget, your layout XML should look like this: 因此,activity_main.xml必须有一个ListView,一个EditText和一个FloatingActionButton。将它们全部放在RelativeLayout窗口部件中后,您的布局XML应如下所示:
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
RelativeLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
xmlns:app
=
"http://schemas.android.com/apk/res-auto"
xmlns:tools
=
"http://schemas.android.com/tools"
android:id
=
"@+id/activity_main"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:paddingBottom
=
"@dimen/activity_vertical_margin"
android:paddingLeft
=
"@dimen/activity_horizontal_margin"
android:paddingRight
=
"@dimen/activity_horizontal_margin"
android:paddingTop
=
"@dimen/activity_vertical_margin"
tools:context
=
"com.tutsplus.mychatapp.MainActivity"
>
<
android.support.design.widget.FloatingActionButton
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:clickable
=
"true"
android:src
=
"@drawable/ic_send_black_24dp"
android:id
=
"@+id/fab"
android:tint
=
"@android:color/white"
android:layout_alignParentBottom
=
"true"
android:layout_alignParentEnd
=
"true"
app:fabSize
=
"mini"
/>
<
android.support.design.widget.TextInputLayout
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_toLeftOf
=
"@id/fab"
android:layout_alignParentBottom
=
"true"
android:layout_alignParentStart
=
"true"
>
<
EditText
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:hint
=
"Input"
android:id
=
"@+id/input"
/>
</
android.support.design.widget.TextInputLayout
>
<
ListView
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:layout_alignParentTop
=
"true"
android:layout_alignParentStart
=
"true"
android:layout_above
=
"@id/fab"
android:dividerHeight
=
"16dp"
android:divider
=
"@android:color/transparent"
android:id
=
"@+id/list_of_messages"
android:layout_marginBottom
=
"16dp"
/>
</
RelativeLayout
>
Note that I've placed the EditText
widget inside a TextInputLayout
widget. Doing so adds a floating label to the EditText
, which is important if you want to adhere to the guidelines of material design.
Now that the layout of the home screen is ready, we can move on to creating a layout for the chat messages, which will be items inside the ListView
. Start by creating a new layout XML file called message.xml, whose root element is RelativeLayout
.
The layout must have TextView
widgets to display the chat message's text, the time it was sent, and its author. You are free to place them in any order. Here's the layout I'll be using:
注意,我把EditText小部件放在TextInputLayout小部件内。 这样做会在EditText中添加一个浮动标签,如果您希望遵循材料设计指南,这是非常重要的。
现在主屏幕的布局已准备好,我们可以继续为聊天消息创建布局,这将是ListView中的项目。 首先创建一个名为message.xml的新布局XML文件,其根元素为RelativeLayout。
布局必须具有TextView小部件,以显示聊天消息的文本,发送时间及其作者。 你可以自由地放置在任何顺序。 这里是我将使用的布局:
<
RelativeLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
>
<
TextView
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_alignParentTop
=
"true"
android:layout_alignParentStart
=
"true"
android:id
=
"@+id/message_user"
android:textStyle
=
"normal|bold"
/>
<
TextView
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_alignBottom
=
"@+id/message_user"
android:layout_alignParentEnd
=
"true"
android:id
=
"@+id/message_time"
/>
<
TextView
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_below
=
"@+id/message_user"
android:layout_alignParentStart
=
"true"
android:layout_marginTop
=
"5dp"
android:id
=
"@+id/message_text"
android:textAppearance
=
"@style/TextAppearance.AppCompat.Body1"
android:textSize
=
"18sp"
/>
</
RelativeLayout
>
4. Handle User Authentication处理用户验证
Allowing users to anonymously post messages to the chat room would be a very bad idea. It could lead to spam, security issues, and a less than ideal chatting experience for the users. Therefore, let us now configure our app such that only registered users can read and post messages.
Start by going to the Auth section of the Firebase Console and enabling Email/Password as a sign-in provider. 允许用户匿名地向聊天室发布消息将是一个很糟糕的主意。 这可能会导致垃圾邮件,安全问题,以及对用户不太理想的聊天体验。 因此,让我们现在配置我们的应用程序,使只有注册用户可以阅读和发布消息。
首先转到Firebase控制台的Auth部分,然后启用电子邮件/密码作为登录提供程序。
Feel free to enable OAuth 2.0 sign-in providers as well. However, FirebaseUI v0.6.0 seamlessly supports only Google Sign-In and Facebook Login. 随时启用OAuth 2.0登录提供程序。但是,FirebaseUI v0.6.0仅支持Google登录和Facebook登录。
Step 1: Handle User Sign-In
As soon as the app starts, it must check if the user is signed in. If so, the app should go ahead and display the contents of the chat room. Otherwise, it must redirect the user to either a sign-in screen, or a sign-up screen. With FirebaseUI, creating those screens takes a lot less code than you might imagine.
Inside the onCreate()
method of MainActivity
, check if the user is already signed in by checking if the currentFirebaseUser
object is not null
. If it is null
, you must create and configure an Intent
object that opens a sign-in activity. To do so, use the SignInIntentBuilder
class. Once the intent is ready, you must launch the sign-in activity using the startActivityForResult()
method.
Note that the sign-in activity also allows new users to sign up. Therefore, you don't have write any extra code to handle user registration.
Add the following code to the onCreate()
method:
步骤1:处理用户登录
应用程序启动后,它必须检查用户是否已登录。如果是,应用程序应该继续并显示聊天室的内容。否则,它必须将用户重定向到登录屏幕或注册屏幕。使用FirebaseUI,创建这些屏幕所需的代码比你想象的少得多。
在MainActivity的onCreate()方法中,通过检查currentFirebaseUser对象是否为null来检查用户是否已经登录。如果为null,则必须创建和配置打开登录活动的Intent对象。为此,请使用SignInIntentBuilder类。一旦意图准备就绪,您必须使用startActivityForResult()方法启动登录活动。
请注意,登录活动还允许新用户注册。因此,您没有写任何额外的代码来处理用户注册。
将以下代码添加到onCreate()方法中:
if
(FirebaseAuth.getInstance().getCurrentUser() ==
null
) {
// Start sign in/sign up activity
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.build(),
SIGN_IN_REQUEST_CODE
);
}
else
{
// User is already signed in. Therefore, display
// a welcome Toast
Toast.makeText(
this
,
"Welcome "
+ FirebaseAuth.getInstance()
.getCurrentUser()
.getDisplayName(),
Toast.LENGTH_LONG)
.show();
// Load chat room contents
displayChatMessages();
}
As you can see in the above code, if the user is already signed in, we first display a Toast
welcoming the user, and then call a method named displayChatMessages. For now, just create a stub for it. We'll be adding code to it later. 正如你在上面的代码中可以看到的,如果用户已经登录,我们首先显示欢迎用户的Toast,然后调用名为displayChatMessages的方法。 现在,只是为它创建一个存根。 我们稍后将添加代码。
private
void
displayChatMessages() {
}
Once the user has signed in, MainActivity
will receive a result in the form of an Intent
. To handle it, you must override the onActivityResult()
method.
If the result's code is RESULT_OK
, it means the user has signed in successfully. If so, you must call thedisplayChatMessages()
method again. Otherwise, call finish()
to close the app. 一旦用户登录,MainActivity将以Intent的形式接收结果。 要处理它,您必须覆盖onActivityResult()方法。
如果结果的代码是RESULT_OK,则表示用户已成功登录。 如果是这样,您必须再次调用displayChatMessages()方法。 否则,调用finish()关闭应用程序。
@Override
protected
void
onActivityResult(
int
requestCode,
int
resultCode,
Intent data) {
super
.onActivityResult(requestCode, resultCode, data);
if
(requestCode == SIGN_IN_REQUEST_CODE) {
if
(resultCode == RESULT_OK) {
Toast.makeText(
this
,
"Successfully signed in. Welcome!"
,
Toast.LENGTH_LONG)
.show();
displayChatMessages();
}
else
{
Toast.makeText(
this
,
"We couldn't sign you in. Please try again later."
,
Toast.LENGTH_LONG)
.show();
// Close the app
finish();
}
}
}
At this point, you can run the app and take a look at the sign-in and sign-up screens. 在这一点上,您可以运行应用程序,并查看登录和注册屏幕。
Step 2: Handle User Sign-Out
By default, FirebaseUI uses Smart Lock for Passwords. Therefore, once the users sign in, they'll stay signed in even if the app is restarted. To allow the users to sign out, we'll now add a sign-out option to the overflow menu of MainActivity
.
Create a new menu resource file called main_menu.xml and add a single item
to it, whose title
attribute is set to Sign out. The contents of the file should look like this:
步骤2:处理用户注销
默认情况下,FirebaseUI对密码使用Smart Lock。因此,一旦用户登录,即使应用重新启动,他们仍会保持登录状态。为了允许用户注销,我们现在将添加一个注销选项到MainActivity的溢出菜单。
创建一个名为main_menu.xml的新菜单资源文件,并向其添加单个项目,其标题属性设置为“注销”。文件的内容应如下所示:
<
menu
xmlns:android
=
"http://schemas.android.com/apk/res/android"
xmlns:app
=
"http://schemas.android.com/apk/res-auto"
>
<
item
android:title
=
"Sign out"
app:showAsAction
=
"never"
android:id
=
"@+id/menu_sign_out"
/>
</
menu
>
To instantiate the menu resource inside MainActivity
, override the onCreateOptionsMenu()
method and call theinflate()
method of the MenuInflater
object. 要在MainActivity中实例化菜单资源,重写onCreateOptionsMenu()方法并调用MenuInflater对象的inlate()方法。
@Override
public
boolean
onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return
true
;
}
Next, override the onOptionsItemSelected()
method to handle click events on the menu item. Inside the method, you can call the signOut()
method of the AuthUI
class to sign the user out. Because the sign-out operation is executed asynchronously, we'll also add an OnCompleteListener
to it. 接下来,重写onOptionsItemSelected()方法来处理菜单项上的点击事件。在方法中,您可以调用AuthUI类的signOut()方法来签署用户。因为签出操作是异步执行的,我们还将向其添加一个OnCompleteListener。
@Override
public
boolean
onOptionsItemSelected(MenuItem item) {
if
(item.getItemId() == R.id.menu_sign_out) {
AuthUI.getInstance().signOut(
this
)
.addOnCompleteListener(
new
OnCompleteListener<Void>() {
@Override
public
void
onComplete(
@NonNull
Task<Void> task) {
Toast.makeText(MainActivity.
this
,
"You have been signed out."
,
Toast.LENGTH_LONG)
.show();
// Close activity
finish();
}
});
}
return
true
;
}
Once the user has signed out, the app should close automatically. That's the reason why you see a call to the finish()
method in the code above. 用户退出后,应用程序应自动关闭。这就是为什么你在上面的代码中看到调用finish()方法的原因。
5. Create a Model 创建模型
In order to store the chat messages in the Firebase real-time database, you must create a model for them. The layout of the chat message, which we created earlier in this tutorial, has three views. To be able to populate those views, the model too must have at least three fields.为了将聊天消息存储在Firebase实时数据库中,必须为它们创建一个模型。我们在本教程前面创建的聊天消息的布局有三个视图。为了能够填充这些视图,模型也必须至少有三个字段。
Create a new Java class called ChatMessage.java and add three member variables to it: messageText
,messageUser
, and messageTime
. Also add a constructor to initialize those variables.
To make the model compatible with FirebaseUI, you must also add a default constructor to it, along with getters and setters for all the member variables.
At this point, the ChatMessage
class should look like this:
创建一个名为ChatMessage.java的新Java类,并向其添加三个成员变量:messageText,messageUser和messageTime。 还要添加一个构造函数来初始化这些变量。
要使模型与FirebaseUI兼容,您还必须向其添加一个默认构造函数,以及所有成员变量的getter和setter。
在这一点上,ChatMessage类应该看起来像这样:
public
class
ChatMessage {
private
String messageText;
private
String messageUser;
private
long
messageTime;
public
ChatMessage(String messageText, String messageUser) {
this
.messageText = messageText;
this
.messageUser = messageUser;
// Initialize to current time
messageTime =
new
Date().getTime();
}
public
ChatMessage(){
}
public
String getMessageText() {
return
messageText;
}
public
void
setMessageText(String messageText) {
this
.messageText = messageText;
}
public
String getMessageUser() {
return
messageUser;
}
public
void
setMessageUser(String messageUser) {
this
.messageUser = messageUser;
}
public
long
getMessageTime() {
return
messageTime;
}
public
void
setMessageTime(
long
messageTime) {
this
.messageTime = messageTime;
}
}
6. Post a Chat Message 发布聊天消息
Now that the model is ready, we can easily add new chat messages to the Firebase real-time database.
To post a new message, the user will press the FloatingActionButton
. Therefore, you must add anOnClickListener
to it.
Inside the listener, you must first get a DatabaseReference
object using the getReference()
method of theFirebaseDatabase
class. You can then call the push()
and setValue()
methods to add new instances of theChatMessage
class to the real-time database.
The ChatMessage
instances must, of course, be initialized using the contents of the EditText
and the display name of the currently signed in user.
Accordingly, add the following code to the onCreate()
method:
现在该模型已经准备就绪,我们可以轻松地添加新的聊天消息到Firebase实时数据库。
要发布新消息,用户将按下FloatingActionButton。因此,您必须向其中添加一个OnClickListener。
听器中,您必须首先使用FirebaseDatabase类的getReference()方法获取一个DatabaseReference对象。然后可以调用push()和setValue()方法将ChatMessage类的新实例添加到实时数据库。
当然,ChatMessage实例必须使用EditText的内容和当前登录的用户的显示名称进行初始化。
因此,将以下代码添加到onCreate()方法:
FloatingActionButton fab =
(FloatingActionButton)findViewById(R.id.fab);
fab.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
EditText input = (EditText)findViewById(R.id.input);
// Read the input field and push a new instance
// of ChatMessage to the Firebase database
FirebaseDatabase.getInstance()
.getReference()
.push()
.setValue(
new
ChatMessage(input.getText().toString(),
FirebaseAuth.getInstance()
.getCurrentUser()
.getDisplayName())
);
// Clear the input
input.setText(
""
);
}
});
Data in the Firebase real-time database is always stored as key-value pairs. However, if you observe the code above, you'll see that we're calling setValue()
without specifying any key. That's allowed only because the call to the setValue()
method is preceded by a call to the push()
method, which automatically generates a new key. Firebase实时数据库中的数据始终作为键值对存储。然而,如果你遵守上面的代码,你会看到我们调用setValue()没有指定任何键。这是因为调用setValue()方法之前是push()方法的调用,它会自动生成一个新的键。
7. Display the Chat Messages 显示聊天消息
FirebaseUI has a very handy class called FirebaseListAdapter, which dramatically reduces the effort required to populate a ListView
using data present in the Firebase real-time database. We'll be using it now to fetch and display all the ChatMessage
objects that are present in the database. FirebaseUI有一个非常方便的类叫FirebaseListAdapter,它大大减少了使用Firebase实时数据库中的数据填充ListView所需的工作量。我们现在将使用它来获取和显示数据库中存在的所有ChatMessage对象。
Add a FirebaseListAdapter
object as a new member variable of the MainActivity
class. 添加一个FirebaseListAdapter对象作为MainActivity类的一个新的成员变量。
private
FirebaseListAdapter<ChatMessage> adapter;
Inside the displayChatMessages()
method, initialize the adapter using its constructor, which expects the following arguments:
- A reference to the
Activity
- The
class
of the object you're interested in - The layout of the list items
- A
DatabaseReference
object
FirebaseListAdapter
is an abstract class and has an abstract populateView()
method, which must be overridden.
在displayChatMessages()方法中,使用其构造函数初始化适配器,该构造函数需要以下参数:
对Activity的引用
您感兴趣的对象的类
列表项的布局
一个DatabaseReference对象
FirebaseListAdapter是一个抽象类,并有一个抽象的populateView()方法,必须重写。
As its name suggests, populateView()
is used to populate the views of each list item. If you are familiar with the ArrayAdapter
class, you can think of populateView()
as an alternative to the getView()
method. 顾名思义,populateView()用于填充每个列表项的视图。如果您熟悉ArrayAdapter类,则可以将populateView()视为getView()方法的替代方法。
Inside the method, you must first use findViewById()
to get references to each TextView
that's present in themessage.xml layout file. You can then call their setText()
methods and populate them using the getters of the ChatMessage
class. 在方法内部,您必须首先使用findViewById()获取对存在于themessage.xml布局文件中的每个TextView的引用。然后你可以调用他们的setText()方法,并使用ChatMessage类的getters填充它们。
At this point, the contents of the displayChatMessages()
method should like this: 此时,displayChatMessages()方法的内容应该是这样的:
ListView listOfMessages = (ListView)findViewById(R.id.list_of_messages);
adapter =
new
FirebaseListAdapter<ChatMessage>(
this
, ChatMessage.
class
,
R.layout.message, FirebaseDatabase.getInstance().getReference()) {
@Override
protected
void
populateView(View v, ChatMessage model,
int
position) {
// Get references to the views of message.xml
TextView messageText = (TextView)v.findViewById(R.id.message_text);
TextView messageUser = (TextView)v.findViewById(R.id.message_user);
TextView messageTime = (TextView)v.findViewById(R.id.message_time);
// Set their text
messageText.setText(model.getMessageText());
messageUser.setText(model.getMessageUser());
// Format the date before showing it
messageTime.setText(DateFormat.format(
"dd-MM-yyyy (HH:mm:ss)"
,
model.getMessageTime()));
}
};
listOfMessages.setAdapter(adapter);
The group chat app is ready. Run it and a post new messages to see them pop up immediately in theListView
. If you share the app with your friends, you should be able to see their messages too as soon as they post them. 群聊应用已准备就绪。运行它和一个帖子新消息,看到他们立即弹出在ListView。如果您与朋友分享此应用,则一旦他们发布,您就可以看到他们的邮件了。
Conclusion
In this tutorial, you learned how to use Firebase and FirebaseUI to create a very simple group chat application. You also saw how easy it is to work with the classes available in FirebaseUI to quickly create new screens and implement complex functionality. 在本教程中,您学习了如何使用Firebase和FirebaseUI创建一个非常简单的群聊应用程序。您还看到了使用FirebaseUI中的类来轻松创建新屏幕和实现复杂功能是多么容易。
- 如何使用Firebase创建Android聊天应用程序
- 如何创建聊天应用程序
- Firebase Android 使用整理
- Android使用firebase
- Android使用Firebase
- Android 集成FireBase Realtime DataBase实现聊天
- 不依赖Parse或Firebase,如何开始为你的iOS应用程序创建后端
- 如何使用Socket.IO编写聊天应用程序
- 使用Firebase介绍,附带聊天实现的功能code地址
- Android使用Firebase无法获取ArrayList数据
- 【Android】Firebase配置与使用(上)
- 【Android】Firebase配置与使用(下)
- Firebase-config 在android中的使用
- 【使用SignalR+Asp.net创建实时聊天应用程序】
- Firebase 教程: iOS 实时聊天
- Ajax 和 XML: 将 Ajax 用于聊天-使用 Ajax 和 PHP 创建聊天应用程序
- [Android]如何将Android项目连接到Firebase数据库
- Android Google的firebase云消息的使用。
- 小白笔记=----------------------计算机网络(1)
- /etc/crontab和crontab -e的区别
- Undefined symbols for architecture x86_64
- Centos 安装python MySQL 模块 -- MySQLdb
- Mysql的row_format(fixed与dynamic)
- 如何使用Firebase创建Android聊天应用程序
- js/jq input file获取本地文件路径 将要上传图片显示到页面
- ASP.NET代码分页
- 隔行取数据
- ZooKeeper系列3:ZooKeeper命令、命令行工具及简单操作
- Maven学习 二
- 11.Spring学习笔记_通过工厂方法配置Bean(by尚硅谷_佟刚)
- IOS OC Navigation 导航条动态隐藏
- Linux下chkconfig命令详解