从零开始基于Android10的慧农帮app开发-登录模块

从零开始基于Android10的慧农帮app开发-登录模块

欢迎大家学习

这是一个的简单的开发教程,欢迎大家学习,初学者请完全按照教程一步一步走,所有命名请按照文中教程。 第一次写博客,有问题欢迎大家指出。
开发工具:android studio版本4.1.1, 开发语言:Kotlin
所有代码是跟着郭霖老师的第一行代码的第三版学的,不懂的可以看看书。
网络部分Retrofit,MVVM架构,推荐多看看书中viewmodel部分。

创建项目

首先创建一个项目,点击Empty Activity,然后Next。
在这里插入图片描述
全部和图片一致。
在这里插入图片描述
选择project
在这里插入图片描述

app目录下build.gradle中对应图片位置添加

//jvm版本
kotlinOptions { jvmTarget = 1.8}

在这里插入图片描述
app目录下build.gradle中对应图片位置添加

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
}

在这里插入图片描述

//依赖
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
    implementation 'androidx.work:work-runtime:2.2.0'
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'com.squareup.retrofit2:retrofit:2.6.1'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
    implementation 'de.hdodenhof:circleimageview:3.0.1'

    implementation 'com.youth.banner:banner:2.1.0'
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    implementation 'androidx.navigation:navigation-fragment:2.1.0'
    implementation 'androidx.navigation:navigation-ui:2.1.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0'  
    implementation 'androidx.navigation:navigation-ui-ktx:2.1.0'
    implementation 'com.permissionx.aixlibrary:aixlibrary:1.0.2'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}

在这里插入图片描述
点击右上角Sync Now
在这里插入图片描述

创建包logic,ui,util
创建类HnbApplication
在这里插入图片描述
HnbApplication文件

package com.hnb.huinongbang

import android.annotation.SuppressLint
import android.app.Application
import android.content.Context

//用于全局获取context
class HnbApplication : Application() {

    companion object {
        @SuppressLint("StaticFieldLeak")  //这是Application的contxt,不是activity或service的,所以不存在内存泄漏,故注解。
        lateinit var context: Context
    }

    override fun onCreate() {
        super.onCreate()
        context = applicationContext
    }
}

创建平时开发用的工具包类LogUtil,ToastUtil(先创Util包)
在这里插入图片描述

package com.hnb.huinongbang.util

import android.util.Log

object LogUtil {
    private const val VERBOSE = 1
    private const val DEBUG = 2
    private const val INFO = 3
    private const val WARN = 4
    private const val ERROR = 5
    private var level = VERBOSE

    fun v(tag: String, msg: String){
        if (level <= VERBOSE) {
            Log.v(tag, msg)
        }
    }

    fun d(tag: String, msg: String){
        if (level <= DEBUG) {
            Log.d(tag, msg)
        }
    }
    fun i(tag: String, msg: String){
        if (level <= INFO) {
            Log.i(tag, msg)
        }
    }
    fun w(tag: String, msg: String){
        if (level <= WARN) {
            Log.w(tag, msg)
        }
    }
    fun e(tag: String, msg: String){
        if (level <= ERROR) {
            Log.e(tag, msg)
        }
    }
}
package com.hnb.huinongbang.util

import android.widget.Toast
import com.hnb.huinongbang.HNBApplication

object ToastUtil {
    fun show(message: String){
        Toast.makeText(HNBApplication.context, message, Toast.LENGTH_SHORT).show()
    }
    fun showLong(message: String){
        Toast.makeText(HNBApplication.context, message, Toast.LENGTH_LONG).show()
    }
}

先看logic包下的总体结构
创建logic内的包dao,model,network,worker
文件Repository
在这里插入图片描述
model下的UserResponse

package com.hnb.huinongbang.logic.model

import java.text.SimpleDateFormat
import java.util.*

//用户信息类
//code(返回状态) user(用户)
data class UserResponse(val code: Int, val data: User, val message: String)

data class User(val user_ID: Int,  //id
                val user_phone: String, //电话号码
                val user_password: String, //密码
                val user_name: String, //用户名称
                val user_nickname: String, //用户昵称
                val user_sex: String, //用户性别
                val user_birthday: Date, //用户生日
                val user_address: String, //用户住址
                val user_introduce: String, //个人介绍
                val user_identitysign: Int, //是否实名认证
                val user_poorsign: Int, //是否贫困认证
                val user_doctorsign: Int, //是否专家认证
                val user_ptopsign: Int, //是否发布点对点资助
                val user_admin: Int,  //是否管理-员
                val integral: Float, //积分
                val money: Float, //钱
                val user_identityNumber: String, //身份证号
                val user_identityType: String, //用户身份证类型
                val identityReason: String, //身份证审核备注
                val poorReason: String, //贫困认证审核备注
                val doctorReason: String, //专家审核备注
                val ptopReason: String //点对点审核备注
){
    fun getUser_birthday_year(): String? {
        val birthday: Date = user_birthday
        val sdf = SimpleDateFormat("yyyy")
        return sdf.format(birthday)
    }

    fun getUser_birthday_month(): String? {
        val birthday: Date = user_birthday
        val sdf = SimpleDateFormat("MM")
        return sdf.format(birthday)
    }

    fun getUser_birthday_day(): String? {
        val birthday: Date = user_birthday
        val sdf = SimpleDateFormat("dd")
        return sdf.format(birthday)
    }
    fun getAnonymousName(): String? { //获取匿名
        if (null == user_nickname) return "  "
        if (user_nickname.length <= 1) return "*"
        if (user_nickname.length == 2) return user_nickname.substring(0, 1) + "*"
        val cs = user_nickname.toCharArray()
        for (i in 1 until cs.size - 1) {
            cs[i] = '*'
        }
        return String(cs)
    }
}

//登陆时传输到服务器的数据
data class LoginData(val name: String, val password: String)

dao下的UserDAO

package com.hnb.huinongbang.logic.dao

import android.content.Context
import androidx.core.content.edit
import com.google.gson.Gson
import com.hnb.huinongbang.HNBApplication
import com.hnb.huinongbang.logic.model.User

//用户的SharedPerferences
object UserDAO {

    //存储用户
    fun saveUser(user: User) {
        sharedPreferences().edit {
            putString("user", Gson().toJson(user))
        }
    }
    //获取用户
    fun getUser(): User {
        val userJson = sharedPreferences().getString("user", "")
        return Gson().fromJson(userJson, User::class.java)
    }
    //用户是否存在
    fun isUserSaved() = sharedPreferences().contains("user")
    //是否记住密码
    fun isRemembered() = sharedPreferences().getBoolean("remember_password", false)
    //记住密码
    fun remember() = sharedPreferences().edit {
        putBoolean("remember_password", true)
    }
    //不记住密码
    fun unremember() = sharedPreferences().edit {
        putBoolean("remember_password", false)
    }
    //获取SP
    private fun sharedPreferences() = HNBApplication.context.getSharedPreferences("HNB", Context.MODE_PRIVATE)
}

network下的ServiceCreator,UserService,HNBNetwork

package com.hnb.huinongbang.logic.network

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

//Retrofit构造器
object ServiceCreator {
    //    模拟器 "http://10.0.2.2/"
    private const val BASE_URL = "https://www.huinongbang.club/"
    private val retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL) //所有请求的根目录
            .addConverterFactory(GsonConverterFactory.create()) //解析数据使用的转换库 --Gson直接转成对象
            .build()  //构建出Retrofit对象

    fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass) //创建出相应Service接口的动态代理对象
    //示例: val appservice = ServiceCreator.create(AppService::class.java)

    inline fun <reified T> create(): T = create(T::class.java) //泛型实化
    //示例: val appservice = ServiceCreator.create<AppService>()
}
package com.hnb.huinongbang.logic.network

import com.hnb.huinongbang.logic.model.LoginData
import com.hnb.huinongbang.logic.model.UserResponse
import retrofit2.Call
import retrofit2.http.*

interface UserService {
    //登录
    @POST("foremlogin")
    fun login(@Query("name") name: String, @Query("password") password: String): Call<UserResponse>
}
package com.hnb.huinongbang.logic.network

import com.hnb.huinongbang.logic.model.LoginData
import com.hnb.huinongbang.util.LogUtil
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.lang.RuntimeException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

//定义一个统一的网络数据源访问入口,对所有网络请求的API进行封装
object HNBNetwork {
    //封装user的网络请求
    private val userService = ServiceCreator.create<UserService>()
    suspend fun login(loginData: LoginData) = userService.login(loginData.name, loginData.password).await()

    //协程suspend
    private suspend fun <T> Call<T>.await(): T {
        return suspendCoroutine { continuation ->

            enqueue(object : Callback<T> {
                override fun onResponse(call: Call<T>, response: Response<T>) {
                    val body = response.body()
                    LogUtil.d("Test", body.toString())
                    LogUtil.d("HNBNetwork", "服务器返回成功")
                    if (body != null) continuation.resume(body) //服务器返回成功
                    else continuation.resumeWithException(
                        //服务器返回成功,但是值为空
                        RuntimeException("response body is null 服务器返回成功,但是值为空")
                    )
                }

                override fun onFailure(call: Call<T>, t: Throwable) {
                    //服务器返回失败
                    LogUtil.d("HNBNetwork", "服务器返回失败")
                    continuation.resumeWithException(t)
                }
            })

        }
    }
}

logic包下的Repository

package com.hnb.huinongbang.logic

import androidx.lifecycle.liveData
import com.hnb.huinongbang.logic.dao.UserDAO
import com.hnb.huinongbang.logic.model.LoginData
import com.hnb.huinongbang.logic.model.User
import com.hnb.huinongbang.logic.network.HNBNetwork
import com.hnb.huinongbang.util.LogUtil
import kotlinx.coroutines.Dispatchers
import kotlin.coroutines.CoroutineContext

object Repository {

    //获取用户 Dispatchers.IO函数线程类型设置,里面的代码全在子线程运行
    fun login(loginData: LoginData) = fire(Dispatchers.IO) {
        val userResponse = HNBNetwork.login(loginData)
        if (userResponse.code == 1) { //根据状态来处理
            LogUtil.d("登录模块", "登录成功,用户名:${userResponse.data.user_name}")
            val user = userResponse.data
            Result.success(user)
        } else {
            LogUtil.d("登录模块", "登录失败,${userResponse.message}")
            Result.failure(RuntimeException("response status is ${userResponse.message}"))
        }
    }

    //简化函数
    private fun <T> fire(context: CoroutineContext, block: suspend () -> Result<T>) = liveData<Result<T>>(context) {
        val result = try {
            block()
        } catch (e: Exception) {
            Result.failure<T>(e)
        }
        emit(result)
    }

    //    用户操作封装
    fun saveUser(user: User) = UserDAO.saveUser(user)
    fun getUser() = UserDAO.getUser()
    fun isUserSaved() = UserDAO.isUserSaved()
    //记住密码
    fun remember() = UserDAO.remember()
    fun unremember() = UserDAO.unremember()
    fun isRemembered() = UserDAO.isRemembered()
}

在ui包下新建login包,并新建LoginActivity
在这里插入图片描述
在这里插入图片描述

package com.hnb.huinongbang.ui.login

import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.lifecycle.ViewModelProviders
import com.hnb.huinongbang.BottomActivity
import com.hnb.huinongbang.HNBApplication.Companion.context
import com.hnb.huinongbang.MainActivity
import com.hnb.huinongbang.R
import com.hnb.huinongbang.logic.Repository
import com.hnb.huinongbang.logic.model.LoginData
import com.hnb.huinongbang.logic.model.UserResponse
import com.hnb.huinongbang.logic.network.ServiceCreator
import com.hnb.huinongbang.logic.network.UserService
import com.hnb.huinongbang.util.LogUtil
import com.hnb.huinongbang.util.ToastUtil
import kotlinx.android.synthetic.main.activity_login.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response


class LoginActivity : AppCompatActivity() {

    val viewModel by lazy { ViewModelProviders.of(this).get(LoginViewModel::class.java)}

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        //        记住账号
        if (Repository.isUserSaved()){
            val user = Repository.getUser()
            username.setText(user.user_phone)
            //        记住密码功能
            if (Repository.isRemembered()){
                password.setText(user.user_password)
                rememberPass.isChecked = true
            }
        }

//        登录按钮
        loginBtn.setOnClickListener {
            val name = username.text.toString()
            val password = password.text.toString()
            val loginData = LoginData(name, password)
            viewModel.login(loginData)
        }
        //监听登录结果
        viewModel.loginLiveData.observe(this, {result ->
            val userResponse = result.getOrNull()
            if(userResponse != null){
                Repository.saveUser(userResponse)
                if(rememberPass.isChecked){
                    Repository.remember()
                }else{
                    Repository.unremember()
                }

                val intent = Intent(context, MainActivity::class.java)
                startActivity(intent)
            }else{
                ToastUtil.show("账号或密码错误")
            }
        })
    }


}

在创建login中创建LoginViewModel

package com.hnb.huinongbang.ui.login

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import com.hnb.huinongbang.logic.Repository
import com.hnb.huinongbang.logic.model.LoginData

class LoginViewModel : ViewModel() {
    private val loginDataLiveData = MutableLiveData<LoginData>()
    val loginLiveData = Transformations.switchMap(loginDataLiveData){loginData ->
        Repository.login(loginData)
    }
    //外部调用函数
    fun login(loginData: LoginData){
        loginDataLiveData.value =loginData
    }
}

修改layout中自动创建的activity_login.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/login_bg">


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="70dp"
        android:textSize="30sp"
        android:textColor="#fff"
        android:text="登录 慧农帮!"/>


    <EditText
        android:id="@+id/username"
        android:layout_width="280dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="50dp"
        android:background="@drawable/logintext_bg"
        android:inputType="number"
        android:hint="请输入账号" />

    <EditText
        android:id="@+id/password"
        android:layout_width="280dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:background="@drawable/logintext_bg"
        android:inputType="textPassword"
        android:hint="请输入密码" />

    <LinearLayout
        android:layout_width="280dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp">

        <CheckBox
            android:id="@+id/rememberPass"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/logintext_bg"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="记住密码"
            android:textColor="#777777"
            />

    </LinearLayout>

    <TextView
        android:id="@+id/loginBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:text="登录"
        android:textColor="#FAF8F8"
        android:textSize="33sp"
        android:textStyle="bold" />

</LinearLayout>

图片资源
在这里插入图片描述
login_bg.jpg
在这里插入图片描述
logintext.9.png

logintext_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#90ffffff" />
    <corners android:radius="14dp" />
</shape>

接着是AndroidManifest.xml配置,先配置图中文件
在这里插入图片描述
colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
    <color name="colorPrimary">#1296db</color>
    <color name="colorPrimaryDark">#1296db</color>
    <color name="colorAccent">#03DAC5</color>
    <color name="button_selected">#00D9FF</color>
</resources>

dimens.xml

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
</resources>

strings.xml

<resources>
    <string name="app_name">Huinongbang</string>
    <string name="title_activity_login">LoginActivity</string>
    <string name="prompt_email">Email</string>
    <string name="prompt_password">Password</string>
    <string name="action_sign_in">Sign in or register</string>
    <string name="action_sign_in_short">Sign in</string>
    <string name="welcome">"Welcome !"</string>
    <string name="invalid_username">Not a valid username</string>
    <string name="invalid_password">Password must be >5 characters</string>
    <string name="login_failed">"Login failed"</string>
    <string name="title_activity_bottom">MainActivity</string>
    <string name="title_donate">捐赠</string>
    <string name="title_shopping">购物</string>
    <string name="title_planting">种植</string>
    <string name="title_policy">政策</string>
    <string name="title_my">我的</string>
</resources>

styles.xml

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <!-- 自定义loading dialog -->
    <style name="loading_dialog" parent="android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowBackground">@drawable/shape_bg_5_white</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowContentOverlay">@null</item>
    </style>

    <style name="text_style">
        <item name="android:textSize">18sp</item>
        <item name="android:paddingLeft">10dp</item>
        <item name="android:paddingRight">10dp</item>
        <item name="android:layout_gravity">center_vertical</item>

    </style>
</resources>

themes.xml

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.Huinongbang" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>

network_config.xml(配置网络,用于访问http)

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>

最重要的AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hnb.huinongbang">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:name=".HNBApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_config"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".ui.login.LoginActivity"
            android:label="@string/title_activity_login">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity" />
    </application>

</manifest>

这里有一个大坑,

<uses-permission android:name="android.permission.INTERNET" /> 

这局代码是申请网络权限的,如果是已经安装软件再写的申请网络权限,一定要把huinongbang软件卸载了,再安装。不然会出现,无法访问网络。

到这里登录功能初步开发完成,开发第一步最麻烦的结构搭建已经完成,后续开发就是在原基础上添加功能。
测试账户:13387998888 密码123456jj
或者自行去www.huinongbang.club注册。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值