prerequisite
Install and configure Android Studio
Android Studio Electric Eel | 2022.1.1 Patch 2
Build #AI-221.6008.13.2211.9619390, built on February 17, 2023
Runtime version: 11.0.15+0-b2043.56-9505619 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Windows 11 10.0
GC: G1 Young Generation, G1 Old Generation
Memory: 1280M
Cores: 6
Registry:
external.system.auto.import.disabled=true
ide.text.editor.with.preview.show.floating.toolbar=false
ide.balloon.shadow.size=0
Non-Bundled Plugins:
com.intuit.intellij.makefile (1.0.15)
com.github.setial (4.0.2)
com.alayouni.ansiHighlight (1.2.4)
GsonOrXmlFormat (2.0)
GLSL (1.19)
com.mistamek.drawablepreview.drawable-preview (1.1.5)
com.layernet.plugin.adbwifi (1.0.5)
com.likfe.ideaplugin.eventbus3 (2020.0.2)
gradle-wrapper.properties
#Tue Apr 25 13:34:44 CST 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
build.gradle(:Project)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
}
setting.gradle
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
}
rootProject.name = "logindemo"
include ':app'
build.gralde(:app)
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.example.logindemo'
compileSdk 33
defaultConfig {
applicationId "com.example.logindemo"
minSdk 26
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// 沉浸式状态栏 https://github.com/gyf-dev/ImmersionBar
api 'com.gyf.immersionbar:immersionbar:3.0.0'
api 'com.gyf.immersionbar:immersionbar-components:3.0.0' // fragment快速实现(可选)
api 'com.gyf.immersionbar:immersionbar-ktx:3.0.0' // kotlin扩展(可选)
}
Basic understanding of the Kotlin language
The basic configuration was written in the previous blog. If you don’t understand the content of this article, you can go to the previous article first.
add title
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:fitsSystemWindows="true"
tools:context=".MainActivity">
<TextView
android:id="@+id/titleTv"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="微信"
android:textColor="#000000"
android:textSize="20sp"
android:gravity="center"
android:background="@color/title"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:tabIndicatorColor="@color/teal_200"
app:tabSelectedTextColor="@color/teal_200"
app:tabTextColor="@color/black" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@+id/titleTv"
app:layout_constraintBottom_toTopOf="@+id/tabLayout"/>
</androidx.constraintlayout.widget.ConstraintLayout>
After adding the title, when switching fragments, the corresponding title text needs to be changed
package com.example.logindemo
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
import androidx.viewpager.widget.ViewPager
import com.example.logindemo.fragment.ChatFragment
import com.example.logindemo.fragment.ContactsFragment
import com.example.logindemo.fragment.DiscoverFragment
import com.google.android.material.tabs.TabLayout
import com.gyf.immersionbar.ImmersionBar
class MainActivity : AppCompatActivity() {
private lateinit var viewPager: ViewPager
private lateinit var tabLayout: TabLayout
private lateinit var titleTv: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.statusBarColor(R.color.title)
.navigationBarColor(R.color.white)
.navigationBarDarkIcon(true)
.init()
setContentView(R.layout.activity_main)
val fragments = listOf(
ChatFragment(),
ContactsFragment(),
DiscoverFragment()
)
titleTv = findViewById(R.id.titleTv)
viewPager = findViewById(R.id.viewPager)
tabLayout = findViewById(R.id.tabLayout)
viewPager.adapter = ViewPagerAdapter(supportFragmentManager, fragments)
tabLayout.setupWithViewPager(viewPager)
tabLayout.getTabAt(0)?.text = "聊天"
tabLayout.getTabAt(1)?.text = "联系人"
tabLayout.getTabAt(2)?.text = "发现"
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
titleTv.text = tab?.text
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
})
}
class ViewPagerAdapter(
fragmentManager: androidx.fragment.app.FragmentManager,
private val fragments: List<Fragment>
) : FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment {
return fragments[position]
}
override fun getCount(): Int {
return fragments.size
}
}
}
modify fragment
Added RecyclerView to display multiple items in the layout
fragment_chat.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_contacts.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_discover.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The content in the fragment is increased as follows
package com.example.logindemo.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.logindemo.R
import com.example.logindemo.base.BaseAdapter
import com.example.logindemo.bean.ChatBean
class ChatFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_chat, container, false)
recyclerView = view.findViewById(R.id.recyclerView)
val data = ArrayList<ChatBean>()
data.add(ChatBean("头像0", "用户0", "聊天记录0", "4月25日"))
data.add(ChatBean("头像1", "用户1", "聊天记录1", "4月24日"))
data.add(ChatBean("头像2", "用户2", "聊天记录2", "4月23日"))
data.add(ChatBean("头像3", "用户3", "聊天记录3", "4月22日"))
data.add(ChatBean("头像4", "用户4", "聊天记录4", "4月21日"))
data.add(ChatBean("头像5", "用户5", "聊天记录5", "4月20日"))
data.add(ChatBean("头像6", "用户6", "聊天记录6", "4月19日"))
data.add(ChatBean("头像7", "用户7", "聊天记录7", "4月18日"))
data.add(ChatBean("头像8", "用户8", "聊天记录8", "4月17日"))
data.add(ChatBean("头像9", "用户9", "聊天记录9", "4月16日"))
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = BaseAdapter(data)
return view
}
}
package com.example.logindemo.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.logindemo.R
import com.example.logindemo.base.BaseAdapter
import com.example.logindemo.bean.ChatBean
class ContactsFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_contacts, container, false)
recyclerView = view.findViewById(R.id.recyclerView)
val data = ArrayList<ChatBean>()
data.add(ChatBean("头像0", "用户0", "", ""))
data.add(ChatBean("头像1", "用户1", "", ""))
data.add(ChatBean("头像2", "用户2", "", ""))
data.add(ChatBean("头像3", "用户3", "", ""))
data.add(ChatBean("头像4", "用户4", "", ""))
data.add(ChatBean("头像5", "用户5", "", ""))
data.add(ChatBean("头像6", "用户6", "", ""))
data.add(ChatBean("头像7", "用户7", "", ""))
data.add(ChatBean("头像8", "用户8", "", ""))
data.add(ChatBean("头像9", "用户9", "", ""))
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = BaseAdapter(data)
return view
}
}
package com.example.logindemo.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.logindemo.R
import com.example.logindemo.base.BaseAdapter
import com.example.logindemo.bean.ChatBean
class DiscoverFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_discover, container, false)
recyclerView = view.findViewById(R.id.recyclerView)
val data = ArrayList<ChatBean>()
data.add(ChatBean("头像0", "朋友圈", "", ""))
data.add(ChatBean("头像1", "视频号", "", ""))
data.add(ChatBean("头像2", "直播", "", ""))
data.add(ChatBean("头像2", "扫一扫", "", ""))
data.add(ChatBean("头像2", "摇一摇", "", ""))
data.add(ChatBean("头像2", "看一看", "", ""))
data.add(ChatBean("头像2", "搜一搜", "", ""))
data.add(ChatBean("头像2", "附近", "", ""))
data.add(ChatBean("头像2", "购物", "", ""))
data.add(ChatBean("头像2", "游戏", "", ""))
data.add(ChatBean("头像2", "小程序", "", ""))
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = BaseAdapter(data)
return view
}
}
entry adapter
package com.example.logindemo.base
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.logindemo.R
import com.example.logindemo.bean.ChatBean
class BaseAdapter(private val data: List<ChatBean>) :
RecyclerView.Adapter<BaseAdapter.BaseHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.base_item, null, false);
return BaseHolder(view)
}
override fun getItemCount(): Int {
return data.size
}
override fun onBindViewHolder(holder: BaseHolder, position: Int) {
holder.headTv.visibility = if (data[position].head.isEmpty()) View.GONE else View.VISIBLE
holder.nickTv.visibility = if (data[position].nick.isEmpty()) View.GONE else View.VISIBLE
holder.newestTv.visibility = if (data[position].newest.isEmpty()) View.GONE else View.VISIBLE
holder.dateTv.visibility = if (data[position].date.isEmpty()) View.GONE else View.VISIBLE
holder.headTv.text = data[position].head
holder.nickTv.text = data[position].nick
holder.newestTv.text = data[position].newest
holder.dateTv.text = data[position].date
}
class BaseHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val headTv: TextView = itemView.findViewById(R.id.headTv)
val nickTv: TextView = itemView.findViewById(R.id.nickTv)
val newestTv: TextView = itemView.findViewById(R.id.newestTv)
val dateTv: TextView = itemView.findViewById(R.id.dateTv)
}
}
type of data
package com.example.logindemo.bean
data class ChatBean(val head: String, val nick: String, val newest: String, val date: String) {
}