热门标签 | 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。


推荐阅读
  • 掌握Android UI设计:利用ZoomControls实现图片缩放功能
    本文介绍了如何在Android应用中通过使用ZoomControls组件来实现图片的缩放功能。ZoomControls提供了一种简单且直观的方式,让用户可以通过点击放大和缩小按钮来调整图片的显示大小。文章详细讲解了ZoomControls的基本用法、布局设置以及与ImageView的结合使用方法,适合初学者快速掌握Android UI设计中的这一重要功能。 ... [详细]
  • 技术分享:深入解析GestureDetector手势识别机制
    技术分享:深入解析GestureDetector手势识别机制 ... [详细]
  • Android 图像色彩处理技术详解
    本文详细探讨了 Android 平台上的图像色彩处理技术,重点介绍了如何通过模仿美图秀秀的交互方式,利用 SeekBar 实现对图片颜色的精细调整。文章展示了具体的布局设计和代码实现,帮助开发者更好地理解和应用图像处理技术。 ... [详细]
  • 为了优化直播应用底部聊天框的弹出机制,确保在不同设备上的布局稳定性和兼容性,特别是在配备虚拟按键的设备上,我们对用户交互流程进行了调整。首次打开应用时,需先点击首个输入框以准确获取键盘高度,避免直接点击第二个输入框导致的整体布局挤压问题。此优化通过调整 `activity_main.xml` 布局文件实现,确保了更好的用户体验和界面适配。 ... [详细]
  • 如何在Android应用中设计和实现专业的启动欢迎界面(Splash Screen)
    在Android应用开发中,设计与实现一个专业的启动欢迎界面(Splash Screen)至关重要。尽管Android设计指南对使用Splash Screen的态度存在争议,但一个精心设计的启动界面不仅能提升用户体验,还能增强品牌识别度。本文将探讨如何在遵循最佳实践的同时,通过技术手段实现既美观又高效的启动欢迎界面,包括加载动画、过渡效果以及性能优化等方面。 ... [详细]
  • APKAnalyzer(1):命令行操作体验与功能解析
    在对apkChecker进行深入研究后,自然而然地关注到了Android Studio中的APK分析功能。将APK文件导入IDE中,系统会自动解析并展示其中各类文件的详细信息。官方文档提供了详细的命令行工具使用指南,帮助开发者快速上手。本文以一个RecyclerView的Adapter代理开源库为例,探讨了如何利用这些工具进行高效的APK分析。 ... [详细]
  • 本文探讨了资源访问的学习路径与方法,旨在帮助学习者更高效地获取和利用各类资源。通过分析不同资源的特点和应用场景,提出了多种实用的学习策略和技术手段,为学习者提供了系统的指导和建议。 ... [详细]
  • 开发笔记:深入解析Android自定义控件——Button的72种变形技巧
    开发笔记:深入解析Android自定义控件——Button的72种变形技巧 ... [详细]
  • 本文探讨了在Android应用中实现动态滚动文本显示控件的优化方法。通过详细分析焦点管理机制,特别是通过设置返回值为`true`来确保焦点不会被其他控件抢占,从而提升滚动文本的流畅性和用户体验。具体实现中,对`MarqueeText.java`进行了代码层面的优化,增强了控件的稳定性和兼容性。 ... [详细]
  • 本文深入解析了如何通过自定义 ViewGroup 实现类似支付宝风格的酷炫雷达脉冲动画效果。文章详细介绍了自定义 ViewGroup 的原理和实现步骤,并结合实际案例展示了如何在 Android UI 设计中应用这一技术,为开发者提供了宝贵的参考和实践指导。 ... [详细]
  • 深入解析 Android 中 EditText 的 getLayoutParams 方法及其代码应用实例 ... [详细]
  • 在处理遗留数据库的映射时,反向工程是一个重要的初始步骤。由于实体模式已经在数据库系统中存在,Hibernate 提供了自动化工具来简化这一过程,帮助开发人员快速生成持久化类和映射文件。通过反向工程,可以显著提高开发效率并减少手动配置的错误。此外,该工具还支持对现有数据库结构进行分析,自动生成符合 Hibernate 规范的配置文件,从而加速项目的启动和开发周期。 ... [详细]
  • 使用CardView实现圆角和圆形效果:边角与半径的精准控制 ... [详细]
  • 深入解析 Android Drawable:第六阶段进阶指南 ... [详细]
  • Android ListView 自定义 CheckBox 实现列表项多选功能详解
    本文详细介绍了在Android开发中如何在ListView的每一行添加CheckBox,以实现列表项的多选功能。用户不仅可以通过点击复选框来选择项目,还可以通过点击列表的任意一行来完成选中操作,提升了用户体验和操作便捷性。同时,文章还探讨了相关的事件处理机制和布局优化技巧,帮助开发者更好地实现这一功能。 ... [详细]
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社区 版权所有