按照老师的要求做了个简陋的登录界面,实现用户登录并获取头像的功能。因为本人完全是个小白,做下来踩了不少坑,这里记录一下,之后遇到类似情况方便处理。
这里只实现了安卓的部分,服务器的东西没有涉及。
参考的部分文章:
《第一行代码》
https://blog.csdn.net/hxy19971101/article/details/76177408?locationNum=3&fps=1
https://blog.csdn.net/kuankuan199308153614/article/details/103260721
先展示一下成品:首先是基本的登录界面,有账号和密码两个输入框和一个登录按钮,初始头像显示灰色,其他的组件没有派上什么用场。在没有输入用户名或者密码的情况下、输入的用户名或密码不正确的情况下弹窗提示。如果输入正确的用户名和头像,显示登录成功,并返回用户的头像:
?
?
?
?
?
?
?
?
?布局主要使用相对布局。目前我认为相对布局的重点就是给每个组件命名,放新组件的时候写清楚新组件在之前哪个组件的上下左右就好了。第一次写,命名有点乱七八糟,之后会慢慢改规范。
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/login_background"
tools:context=".LoginActivity">
<ImageView
android:id="@+id/login_logo"
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_marginTop="50dp"
android:gravity="center_horizontal"
android:src="@drawable/login_logo"/>
<ImageView
android:id="@+id/login_ima"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_below="@id/login_logo"
android:layout_marginTop="30dp"
android:layout_centerHorizontal="true"
android:src="@drawable/login_old_ima"/>
<EditText
android:id="@+id/login_account"
android:layout_width="260dp"
android:layout_height="30dp"
android:layout_below="@id/login_ima"
android:layout_marginTop="30dp"
android:layout_centerHorizontal="true"
android:background="@color/white"
android:hint=" 账号"
android:textColorHint="#A4A4A4"
android:textSize="16sp"
android:paddingLeft="5dp"
android:textColor="#2E2E2E"/>
<EditText
android:id="@+id/login_password"
android:layout_width="260dp"
android:layout_height="30dp"
android:layout_below="@id/login_account"
android:layout_marginTop="20dp"
android:layout_centerHorizontal="true"
android:background="@color/white"
android:inputType="textPassword"
android:hint=" 密码"
android:textColorHint="#A4A4A4"
android:textSize="16sp"
android:paddingLeft="5dp"
android:textColor="#2E2E2E"/>
<CheckBox
android:id="@+id/login_rempwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/login_password"
android:layout_marginTop="20dp"
android:layout_alignLeft="@id/login_account"
android:text="记住密码"
android:textColor="@color/white"
android:textSize="15sp"/>
<CheckBox
android:id="@+id/login_offline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/login_password"
android:layout_marginTop="20dp"
android:layout_alignRight="@id/login_account"
android:layout_marginRight="10dp"
android:text="离线模式"
android:textColor="@color/white"
android:textSize="15sp"/>
<Button
android:id="@+id/login_loginbutton"
android:layout_width="260dp"
android:layout_height="56dp"
android:layout_below="@id/login_rempwd"
android:layout_marginTop="20dp"
android:layout_centerHorizontal="true"
android:text="登 录"
android:textSize="20sp"/>
</RelativeLayout>
?重要的还是逻辑部分。主要分几步去实现:①okhttp向服务器提交用户名和密码进行登录,并保存session;②通过获取用户头像的接口,用okhttp获取头像地址的后半部分url;③拼接url并访问,将返回的数据以图片形式显示出来。
在写的时候感觉一定要多用log把信息显示出来,包括从服务器那里获得的返回的数据,也都显示出来,方便进行调试。
在build.gradle(:app)文件里的dependencies下添加了okhttp的依赖,还有最后没用上的glide库的依赖。
implementation 'com.squareup.okhttp:okhttp:2.7.5'
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.2'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
如果运行的时候glide库报了不能生成glidemodule的错误,可以在你新建别的java.class文件的地方新建一个java.class文件?,命名好像随便什么都行,写如下内容。我按这个做就没有报错了。
package com.example.mypractice;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
public class 你的命名 extends AppGlideModule {
}
?在AndroidManifest.xml文件里声明联网权限,写在<application前面:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mypractice">
<uses-permission android:name="android.permission.INTERNET" /> 这一句联网
<application
android:allowBackup="true" 这一句隐藏标题栏
android:icon="@mipmap/ic_launcher"
?准备工作就绪,开始实现登录功能。这里单独讲一些地方,然后直接贴代码好了:
①关于输入框里的字符串的获取:
需要先private EditText ed_account;,然后ed_account=findViewById(R.id.login_account);将
?java文件这边定义的东西和布局文件那边的联系起来,然后edi_account=ed_account.getText().toString().trim();获取字符串,trim是为了去除开始的空格。
②okhttp提交用户名和密码的表单:
//提交账号密码请求登录
RequestBody requestBody=new FormBody.Builder() //建立表单
.add("account",edi_account)
.add("password",edi_pwd)
.build();
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder()
.url("http://……/post")
.post(requestBody)
.build();
Response response=client.newCall(request).execute();
String responseData=response.body().string();
Log.d("输出结果:",responseData);
?③获取session,不然服务器不知道你是刚才登录的那个:
需要在第一次发请求的时候获取sessionid:
String session=response.headers().get("Set-Cookie");
String sessionid=session.substring(0,session.indexOf(";"));
?然后在之后发生请求的时候,都加上这个sessionid:
Request request1=new Request.Builder()
.addHeader("cookie",sessionid)
.url("http://……/post")
.post(requestBody1)
.build();
?④将获得的服务器的回应变成图像。这里本来是用glide加载的,但是一直报错,要我在主线程里面用glide。查了一下说最好不要在非主线程里面用glide,我也实在不知道怎么改才能不报错,就没有用glide了。
InputStream inputStream=response2.body().byteStream();
Bitmap bitmap= BitmapFactory.decodeStream(inputStream);
Message msg=new Message();
msg.obj=bitmap;
handler.sendMessage(msg);
不要忘了在上面重载一下handler:
private Handler handler=new Handler(){
public void handleMessage(Message msg){
Bitmap bitmap=(Bitmap)msg.obj;
ge_ima.setImageBitmap(bitmap);
}
};
?差不多就是这样了,有点写累了,贴一下完整的代码吧:
package com.example.mypractice;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import java.io.InputStream;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class LoginActivity extends AppCompatActivity {
private EditText ed_account,ed_pwd;
private Button bu_login;
private ImageView ge_ima;
String edi_account,edi_pwd;
private Handler handler=new Handler(){
public void handleMessage(Message msg){
Bitmap bitmap=(Bitmap)msg.obj;
ge_ima.setImageBitmap(bitmap);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ed_account=findViewById(R.id.login_account);
ed_pwd=findViewById(R.id.login_password);
bu_login=findViewById(R.id.login_loginbutton);
ge_ima=findViewById(R.id.login_ima);
bu_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
edi_account=ed_account.getText().toString().trim();
edi_pwd=ed_pwd.getText().toString().trim();
if(TextUtils.isEmpty(edi_account) || TextUtils.isEmpty(edi_pwd))
Toast.makeText(LoginActivity.this,"请输入用户账号名或者密码",Toast.LENGTH_SHORT).show();
else
login(edi_account,edi_pwd);
}
});
}
public void login(String edi_account,String edi_pwd){
new Thread(new Runnable() {
@Override
public void run() {
try{
Looper.prepare(); //为了在线程里使用Toast
Log.d("账号",edi_account);
Log.d("密码",edi_pwd);
//提交账号密码请求登录
RequestBody requestBody=new FormBody.Builder()
.add("account",edi_account)
.add("password",edi_pwd)
.build();
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder()
.url("http://……/post")
.post(requestBody)
.build();
Log.d("URL",request+"");
//获取回应并保存session
Response response=client.newCall(request).execute();
String responseData=response.body().string();
Log.d("输出结果:",responseData);
String session=response.headers().get("Set-Cookie");
String sessionid=session.substring(0,session.indexOf(";"));
if(!response.isSuccessful()){
Toast.makeText(LoginActivity.this,"用户名或密码错误",Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(LoginActivity.this,"用户"+responseData.substring(18,23)+"欢迎登录",Toast.LENGTH_SHORT).show();
//获取头像的url
RequestBody requestBody1=new FormBody.Builder()
.add("account",edi_account)
.build();
Request request1=new Request.Builder()
.addHeader("cookie",sessionid)
.url("http://……/post")
.post(requestBody1)
.build();
Response response1=client.newCall(request1).execute();
String responseData1=response1.body().string();
Log.d("输出结果:",responseData1);
if(!response1.isSuccessful()){
Toast.makeText(LoginActivity.this,"获取用户头像地址失败",Toast.LENGTH_SHORT).show();
}
else{
int len=responseData1.length();
String picUrl=responseData1.substring(51,len-4);//不会解析,暂时先这么写
Log.d("picUrl:",picUrl);
//拼接Url
String url1="http://……"+picUrl;
Log.d("picUrl:",url1);
//加载头像
Request request2=new Request.Builder()
.addHeader("cookie",sessionid)
.url(url1)
.build();
Response response2=client.newCall(request2).execute();
InputStream inputStream=response2.body().byteStream();
Bitmap bitmap= BitmapFactory.decodeStream(inputStream);
Message msg=new Message();
msg.obj=bitmap;
handler.sendMessage(msg);
//Glide.with(getApplicationContext()).load(url1).into(ge_ima);
}
}
Looper.loop(); //和上面的looper是一对的
}catch (Exception e){
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LoginActivity.this,"网络连接失败!",Toast.LENGTH_SHORT).show();
}
});
}
}
}).start();
}
}
|