热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

四、连接屏幕流

四、连接屏幕流各位读者好!我们已经到了应用开发的一个重要阶段——连接屏幕。如您所知,我们在上一章

四、连接屏幕流

各位读者好!我们已经到了应用开发的一个重要阶段——连接屏幕。如您所知,我们在上一章中创建了屏幕,在这一章中,我们将使用安卓强大的框架来连接它们。我们将继续我们的工作,并且,使用安卓,我们将对我们的用户界面做更严肃的事情。做好准备,专注于本章的每一个方面。会很有意思的!我们保证!

在本章中,我们将涵盖以下主题:


  • 创建应用栏

  • 使用抽屉导航

  • 安卓意图

  • 在活动和片段之间传递信息


创建应用栏

我们正在通过安卓应用开发继续我们的旅程。到目前为止,我们已经为我们的应用创建了一个基础,定义了用户界面的基础,并创建了主屏幕;但是,这些屏幕没有连接。在这一章中,我们将把它们联系起来,进行奇妙的互动。

因为一切都是从我们的MainActivity类开始的,所以在我们设置一些动作来触发其他屏幕之前,我们将应用一些改进。我们必须用一个应用栏来包装。什么是应用栏?它是用户界面的一部分,用于访问应用的其他部分,并提供具有交互元素的视觉结构。我们已经有了一个,但它不是通常的安卓应用栏。在这一点上,我们的应用是有一个修改的应用栏,我们希望它有一个标准的安卓应用栏。

在这里,我们将向您展示如何创建一个。

首先将顶级活动扩展替换为AppCompatActivity。我们需要访问应用栏所需的功能。AppCompatActivity将这些附加功能添加到标准FragmentActivity中。您的BaseActivity定义现在应该是这样的:

abstract class BaseActivity : AppCompatActivity() {
...

然后更新使用的主题应用,这样就可以使用应用栏了。打开安卓清单,设置一个新的主题如下:

...
<application
android:name=".Journaler"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
...

现在打开你的activity_main布局。删除标题中包含的指令,并添加Toolbar:


xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_s">"match_parent"
android:layout_s">"match_parent"
android:orientation="vertical">

android:id="@+id/toolbar"
android:layout_s">"match_parent"
android:layout_s">"50dp"
android:background="@color/colorPrimary"
android:elevation="4dp" />

android:id="@+id/pager"
android:layout_s">"match_parent"
android:layout_s">"match_parent" />

对所有布局应用相同的更改。完成后,更新你的BaseActivity代码,使用新的Toolbar。你的onCreate()方法现在应该是这样的:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(getLayout())
setSupportActionBar(toolbar)
Log.v(tag, "[ ON CREATE ]")
}

我们通过调用setSupportActionBar()方法并从布局中传递工具栏的 ID 来分配应用栏。如果运行该应用,它将如下所示:

我们丢了我们头上的纽扣!别担心,我们会把他们找回来的!我们将创建一个菜单来处理动作,而不是按钮。在安卓系统中,菜单是用来管理项目的界面,你可以定义自己的菜单资源。在/res目录中,创建一个menu文件夹。右键单击menu文件夹,选择新建|新建菜单资源文件。称它为主。将打开一个新的 XML 文件。根据此示例更新其内容:


xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

app:showAsAction="ifRoom"
android:orderInCategory="1"
android:id="@+id/drawing_menu"
android:icon="@android:drawable/ic_dialog_dialer"
android:title="@string/mnu" />

app:showAsAction="ifRoom"
android:orderInCategory="2"
android:id="@+id/options_menu"
android:icon="@android:drawable/arrow_down_float"
android:title="@string/mnu" />

我们设置公共属性、图标和顺序。要确保您的图标可见,请使用以下方法:

app:showAsAction="ifRoom"

这样,如果有任何可用空间,菜单中的项目将被展开;否则,可以通过上下文菜单访问它们。安卓系统中你可以选择的其他间距选项如下:


  • 始终:该按钮始终位于应用栏中

  • 从不:此按钮从不放在应用栏中

  • 折叠视图:该按钮可以显示为一个小部件

  • 带文字:该按钮带文字显示

要将菜单分配给应用栏,请在BaseActivity中添加以下内容:

override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main, menu)
return true
}

最后,将动作连接到菜单项,并通过添加以下代码扩展MainActivity:

override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.drawing_menu -> {
Log.v(tag, "Main menu.")
return true
}
R.id.options_menu -> {
Log.v(tag, "Options menu.")
return true
}
else -> return super.onOptionsItemSelected(item)
}
}

这里,我们覆盖了onOptionsItemSelected()方法,并处理了菜单项标识的情况。在每次选择时,我们都会添加一条日志消息。现在运行您的应用。您应该会看到以下菜单项:

在每个项目上点击几次,观察日志。您应该会看到类似如下的日志:

V/Main activity: Main menu.
V/Main activity: Options menu.
V/Main activity: Options menu.
V/Main activity: Options menu.
V/Main activity: Main menu.
V/Main activity: Main menu.

我们成功地将标题切换到应用栏。它与我们在应用线框中的标题有很大不同。这在目前并不重要,因为我们将在接下来的章节中做一些重要的造型。我们的应用栏看起来会有所不同。

在下一节中,我们将处理导航抽屉,并开始组装应用的导航。

使用导航抽屉

您可能还记得,在我们的模型中,我们已经展示了将会有到过滤数据(Notes 和 Todos)的链接。我们将使用导航抽屉过滤这些内容。每个现代应用都使用一个导航抽屉。它是显示应用导航选项的用户界面的一部分。要定义抽屉,我们必须在布局中放置DrawerLayout视图。打开activity_main并进行以下修改:


xmlns:android=
"http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_s">"match_parent"
android:layout_s">"match_parent">

android:layout_s">"match_parent"
android:layout_s">"match_parent"
android:orientation="vertical">

android:id="@+id/toolbar"
android:layout_s">"match_parent"
android:layout_s">"50dp"
android:background="@color/colorPrimary"
android:elevation="4dp" />
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_s">"match_parent"
android:layout_s">"match_parent" />


android:id="@+id/left_drawer"
android:layout_s">"240dp"
android:layout_s">"match_parent"
android:layout_gravity="start"
android:background="@android:color/darker_gray"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividers">"1dp" />

屏幕的主要内容必须是DrawerLayout的第一个孩子。导航抽屉使用第二胎作为抽屉的内容。在我们的例子中,是ListView。要告诉导航抽屉导航应该位于左侧还是右侧,请使用layout_gravity属性。如果我们计划使用位于右侧的导航抽屉,我们应该将属性值设置为end

现在我们有一个空的导航抽屉,我们必须用一些按钮填充它。为每个导航项目创建一个新的布局文件。称之为adapter_navigation_drawer。将其定义为内部只有一个按钮的简单线性布局:


xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_s">"match_parent"
android:layout_s">"match_parent"
android:orientation="vertical">

android:id="@+id/drawer_item"
android:layout_s">"match_parent"
android:layout_s">"wrap_content" />

然后,创建一个名为navigation的新包。在这个包中,创建一个新的 Kotlin data类,如下所示:

package com.journaler.navigation
data class NavigationDrawerItem(
val title: String,
val onClick: Runnable
)

我们定义了一个抽屉项目实体。现在再创建一个类:

class NavigationDrawerAdapter(
val ctx: Context,
val items: List
) : BaseAdapter() {
override fun getView(position: Int, v: View?, group: ViewGroup?):
View {
val inflater = LayoutInflater.from(ctx)
var view = v
if (view == null) {
view = inflater.inflate(
R.layout.adapter_navigation_drawer, null
) as LinearLayout
}
val item = items[position]
val title = view.findViewById

这里显示的这个类扩展了安卓的BaseAdapter并覆盖了适配器提供视图实例所需的方法。适配器创建的所有视图将被分配到我们的导航抽屉中展开ListView

最后,我们将分配这个适配器。为此,我们需要通过执行以下代码来更新我们的MainActivity类:

class MainActivity : BaseActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
pager.adapter = ViewPagerAdapter(supportFragmentManager)
val menuItems = mutableListOf()
val today = NavigationDrawerItem(
getString(R.string.today),
Runnable {
pager.setCurrentItem(0, true)
}
)
val next7Days = NavigationDrawerItem(
getString(R.string.next_seven_days),
Runnable {
pager.setCurrentItem(1, true)
}
)
val todos = NavigationDrawerItem(
getString(R.string.todos),
Runnable {
pager.setCurrentItem(2, true)
}
)
val notes = NavigationDrawerItem(
getString(R.string.notes),
Runnable {
pager.setCurrentItem(3, true)
}
)
menuItems.add(today)
menuItems.add(next7Days)
menuItems.add(todos)
menuItems.add(notes)
val navgationDraweAdapter =
NavigationDrawerAdapter(this, menuItems)
left_drawer.adapter = navgationDraweAdapter
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.drawing_menu -> {
drawer_layout.openDrawer(GravityCompat.START)
return true
}
R.id.options_menu -> {
Log.v(tag, "Options menu.")
return true
}
else -> return super.onOptionsItemSelected(item)
}
}
}

在这个代码示例中,我们实例化了几个NavigationDrawerItem实例,然后,我们为将要执行的按钮和Runnable动作分配了一个标题。每个Runnable都会跳转到我们视图页面的特定页面。我们将所有实例作为一个单一的可变列表传递给适配器。您可能也注意到了我们更改了drawing_menu项目的线路。点击它,我们将展开我们的导航抽屉。请遵循以下步骤:


  1. 构建并运行您的应用。

  2. 单击主屏幕右上角的菜单按钮,或从屏幕最左侧向右滑动展开导航抽屉。

  3. 点击按钮。

  4. 您会注意到,视图页导航正在为其在导航抽屉下方的页面位置制作动画。

连接活动

大家记得,除了MainActivity,我们还有一些更多的活动。在我们的应用中,我们创建了创建/编辑笔记和待办事项的活动。我们的计划是将它们连接到按钮点击事件,然后,当用户点击按钮时,适当的屏幕将会打开。我们将首先定义一个enum,它代表我们将在一个打开的活动中执行的操作。当我们打开它时,我们可以查看、创建或更新笔记或待办事项。创建一个名为modelenum的名为MODE的新包。确保您有以下enum值:

enum class MODE(val mode: Int) {
CREATE(0),
EDIT(1),
VIEW(2);
companion object {
val EXTRAS_KEY = "MODE"
fun getByValue(value: Int): MODE {
values().forEach {
item ->
if (item.mode == value) {
return item
}
}
return VIEW
}
}
}

我们在这里添加了一些内容。在enum的伴随对象中,我们定义了额外的键定义。很快,你会需要它,你会明白它的目的。我们还创建了一个方法,根据它的价值给我们一个enum

您可能还记得,使用 Notes 和 Todos 的两个活动共享同一个类。打开ItemActivity并如下展开:

abstract class ItemActivity : BaseActivity() {
protected var mode = MODE.VIEW
override fun getActivityTitle() = R.string.app_name
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val modeToSet = intent.getIntExtra(MODE.EXTRAS_KEY,
MODE.VIEW.mode)
mode = MODE.getByValue(modeToSet)
Log.v(tag, "Mode [ $mode ]")
}
}

我们引入了我们刚刚定义的类型的字段模式,它将告诉我们是否正在查看、创建或编辑 Note 或 Todo 项。然后,我们否决了onCreate()方法。这很重要!当我们点击按钮并打开活动时,我们会传递一些值给它。这段代码片段检索我们传递的值。为此,我们访问Intent实例(在下一节中,我们将解释intents)和名为MODE(值为MODE.EXTRAS_KEY)的整数字段。给我们这个值的方法叫做getIntExtra()。每种类型都有一个方法版本。如果没有值,则返回MODE.VIEW.mode。最后,我们将模式设置为通过从整数值中获取MODE实例而获得的值。

谜题的最后一部分是触发活动开始。打开ItemsFragment并如下展开:

class ItemsFragment : BaseFragment() {
...
override fun onCreateView(
inflater: LayoutInflater?,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater?.inflate(getLayout(), container, false)
val btn = view?.findViewById<FloatingActionButton>
(R.id.new_item)
btn?.setOnClickListener {
val items = arrayOf(
getString(R.string.todos),
getString(R.string.notes)
)
val builder =
AlertDialog.Builder(this@ItemsFragment.context)
.setTitle(R.string.choose_a_type)
.setItems(
items,
{ _, which ->
when (which) {
0 -> {
openCreateTodo()
}
1 -> {
openCreateNote()
}
else -> Log.e(logTag, "Unknown option selected
[ $which ]")
}
}
)
builder.show()
}
return view
}
private fun openCreateNote() {
val intent = Intent(context, NoteActivity::class.java)
intent.putExtra(MODE.EXTRAS_KEY, MODE.CREATE.mode)
startActivity(intent)
}
private fun openCreateTodo() {
val intent = Intent(context, TodoActivity::class.java)
intent.putExtra(MODE.EXTRAS_KEY, MODE.CREATE.mode)
startActivity(intent)
}
}

我们访问了FloatingActionButton实例,并分配了一个点击监听器。单击后,我们将创建一个包含两个选项的对话框。这些选项中的每一个都会触发一个适当的活动打开方法。两种方法的实现非常相似。举个例子,我们将重点介绍openCreateNote()

我们将创建一个新的Intent实例。在安卓系统中,Intent代表了我们做某件事的意图。要开始一个活动,我们必须传递我们想要开始的活动的上下文和类。我们还必须赋予它一些价值。这些值将被传递给一个活动实例。在我们的例子中,我们正在传递MODE.CREATE. startActivity()方法的整数值,该方法将执行意图,并且将出现一个屏幕。

运行应用,单击屏幕右下角的圆形按钮,并从对话框中选择一个选项,如下图所示:

这将带您进入此屏幕:

这将带您进一步添加您自己的带有日期和时间的数据:

深入了解安卓意图

你计划在安卓系统中执行的大多数操作都是通过Intent类定义的。Intents可以像我们一样启动活动,启动服务(后台运行的进程),或者发送广播消息。

Intent通常接受我们想要传递给某个类的动作和数据。我们可以设置的动作属性有,例如,ACTION_VIEWACTION_EDITACTION_MAIN

除了动作和数据,我们可以为意图设置一个类别。该类别为我们设置的操作提供了附加信息。我们还可以设置意图的类型和代表我们将使用的显式组件类名的组件。

intents有两种类型:


  • 明确的意图

  • 隐含的意图

显式意图具有显式组件集,该组件集提供要运行的显式类。隐式意图没有显式组件,但是系统根据我们分配的数据和属性决定如何处理它。意图解决流程负责处理此类intents

这些参数的组合是无穷无尽的。我们将给出一些例子,以便您更好地理解intents的目的:


  • 打开网页:


val intent = Intent(Intent.ACTION_VIEW,
Uri.parse("http://google.com"))
startActivity(intent)
Sharing:
val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TEXT, "Check out this cool app!")
startActivity(intent)



  • 从相机捕捉图像:


val takePicture = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePicture.resolveActivity(packageManager) != null) {
startActivityForResult(takePicture, REQUEST_CAPTURE_PHOTO +
position)
} else {
logger.e(tag, "Can't take picture.")
}



  • 从图库中选取图像:


val pickPhoto = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
startActivityForResult(pickPhoto, REQUEST_PICK_PHOTO +
position)

如您所见,intents是安卓框架的关键部分。在下一节中,我们将扩展我们的代码以更多地使用intents

在活动和片段之间传递信息

为了在我们的活动之间传递信息,我们将使用安卓捆绑包。Bundle 可以包含不同类型的多个值。我们将通过扩展我们的代码来说明 Bundle 的使用。打开ItemsFragemnt,更新如下:

private fun openCreateNote() {
val intent = Intent(context, NoteActivity::class.java)
val data = Bundle()
data.putInt(MODE.EXTRAS_KEY, MODE.CREATE.mode)
intent.putExtras(data)
startActivityForResult(intent, NOTE_REQUEST)
}
private fun openCreateTodo() {
val date = Date(System.currentTimeMillis())
val dateFormat = SimpleDateFormat("MMM dd YYYY", Locale.ENGLISH)
val timeFormat = SimpleDateFormat("MM:HH", Locale.ENGLISH)
val intent = Intent(context, TodoActivity::class.java)
val data = Bundle()
data.putInt(MODE.EXTRAS_KEY, MODE.CREATE.mode)
data.putString(TodoActivity.EXTRA_DATE, dateFormat.format(date))
data.putString(TodoActivity.EXTRA_TIME,
timeFormat.format(date))
intent.putExtras(data)
startActivityForResult(intent, TODO_REQUEST)
}
override fun onActivityResult(requestCode: Int, resultCode: Int,
data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
TODO_REQUEST -> {
if (resultCode == Activity.RESULT_OK) {
Log.i(logTag, "We created new TODO.")
} else {
Log.w(logTag, "We didn't created new TODO.")
}
}
NOTE_REQUEST -> {
if (resultCode == Activity.RESULT_OK) {
Log.i(logTag, "We created new note.")
} else {
Log.w(logTag, "We didn't created new note.")
}
}
}
}

在这里,我们介绍了一些重要的变化。首先,我们开始了笔记和待办事项活动,作为子活动。这意味着我们的MainActivity课取决于那些活动的工作结果。当开始子活动而不是startActivity()方法时,我们使用了startActivityForResult()。我们传递的参数是意图和请求号。为了得到执行的结果,我们重写了onActivityResult()方法。如您所见,我们检查了哪个活动完成了,以及该执行是否产生了成功的结果。

我们也改变了传递信息的方式。我们创建了Bundle实例并分配了多个值,就像 Todo 活动一样。我们添加了模式、日期和时间。使用putExtras()方法将束分配给意图。为了使用这些额外的东西,我们也更新了我们的活动。打开ItemsActivity并应用更改,如下所示:

abstract class ItemActivity : BaseActivity() {
protected var mode = MODE.VIEW
protected var success = Activity.RESULT_CANCELED
override fun getActivityTitle() = R.string.app_name
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val data = intent.extras
data?.let{
val modeToSet = data.getInt(MODE.EXTRAS_KEY, MODE.VIEW.mode)
mode = MODE.getByValue(modeToSet)
}
Log.v(tag, "Mode [ $mode ]")
}
override fun onDestroy() {
super.onDestroy()
setResult(success)
}
}

这里,我们介绍了活动工作的现场举行结果。我们还更新了处理传递信息的方式。正如您所看到的,如果有任何额外的可用,我们将为模式获取一个整数值。最后,onDestroy()方法设置可用于父活动的工作结果。

打开TodoActivity并应用以下更改:

class TodoActivity : ItemActivity() {
companion object {
val EXTRA_DATE = "EXTRA_DATE"
val EXTRA_TIME = "EXTRA_TIME"
}
override val tag = "Todo activity"
override fun getLayout() = R.layout.activity_todo
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val data = intent.extras
data?.let {
val date = data.getString(EXTRA_DATE, "")
val time = data.getString(EXTRA_TIME, "")
pick_date.text = date
pick_time.text = time
}
}
}

我们已经获得了日期和时间的附加值,并将其设置为日期/时间选择器按钮。运行您的应用并打开 Todo 活动。您的待办事项屏幕应该如下所示:

当您离开待办事项活动并返回主屏幕时,请观察您的日志。将出现一个日志,内容如下:

W/Items 片段-我们没有创建新的待办事项。

因为我们还没有创建任何 Todo 项目,所以我们传递了一个正确的结果。我们通过返回主屏幕取消了创建过程。在后面的章节中,以及接下来的章节中,我们将成功地创建 Notes 和 Todos。

摘要

我们用这一章来连接我们的接口,并建立一个真实的应用流。我们通过为用户界面元素设置适当的动作来建立屏幕之间的连接。我们把数据从一点传到另一点。这一切都用一种非常简单的方式!我们有东西在工作,但它看起来很丑。在下一章,我们将确保它看起来很漂亮!我们将为它设计风格,并添加一些不错的视觉效果。准备好迎接安卓强大的 UI API。


推荐阅读
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
author-avatar
多米音乐_35692689
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有